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

testing #48961

Closed
wants to merge 15 commits into from
Closed

testing #48961

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 @@ -62,5 +62,7 @@ internal override SyntaxTree ParseGeneratedSourceText(GeneratedSourceText input,
internal override GeneratorDriver FromState(GeneratorDriverState state) => new CSharpGeneratorDriver(state);

internal override CommonMessageProvider MessageProvider => CSharp.MessageProvider.Instance;

internal override AdditionalSourcesCollection CreateSourcesCollection() => new AdditionalSourcesCollection(".cs");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public class AdditionalSourcesCollectionTests
[InlineData("abc{1}.cs")]
public void HintName_ValidValues(string hintName)
{
AdditionalSourcesCollection asc = new AdditionalSourcesCollection();
AdditionalSourcesCollection asc = new AdditionalSourcesCollection(".cs");
asc.Add(hintName, SourceText.From("public class D{}", Encoding.UTF8));
Assert.True(asc.Contains(hintName));

Expand All @@ -43,6 +43,21 @@ public void HintName_ValidValues(string hintName)

}

[Theory]
[InlineData("abc")] // abc.vb
[InlineData("abc.cs")] //abc.cs.vb
[InlineData("abc.vb")] // abc.vb
public void HintName_WithExtension(string hintName)
{
AdditionalSourcesCollection asc = new AdditionalSourcesCollection(".vb");
asc.Add(hintName, SourceText.From("public class D{}", Encoding.UTF8));
Assert.True(asc.Contains(hintName));

var sources = asc.ToImmutableAndFree();
Assert.True(sources[0].HintName.EndsWith(".vb"));

}

[Theory]
[InlineData("/abc/def.cs")]
[InlineData("\\")]
Expand All @@ -57,15 +72,15 @@ public void HintName_ValidValues(string hintName)
[InlineData("abc\u00A0.cs")] // unicode non-breaking space
public void HintName_InvalidValues(string hintName)
{
AdditionalSourcesCollection asc = new AdditionalSourcesCollection();
AdditionalSourcesCollection asc = new AdditionalSourcesCollection(".cs");
Assert.Throws<ArgumentException>(nameof(hintName), () => asc.Add(hintName, SourceText.From("public class D{}", Encoding.UTF8)));
}

[Fact]
public void AddedSources_Are_Deterministic()
{
// a few manual simple ones
AdditionalSourcesCollection asc = new AdditionalSourcesCollection();
AdditionalSourcesCollection asc = new AdditionalSourcesCollection(".cs");
asc.Add("file3.cs", SourceText.From("", Encoding.UTF8));
asc.Add("file1.cs", SourceText.From("", Encoding.UTF8));
asc.Add("file2.cs", SourceText.From("", Encoding.UTF8));
Expand All @@ -87,7 +102,7 @@ public void AddedSources_Are_Deterministic()
// generate a long random list, remembering the order we added them
Random r = new Random();
string[] names = new string[1000];
asc = new AdditionalSourcesCollection();
asc = new AdditionalSourcesCollection(".cs");
for (int i = 0; i < 1000; i++)
{
names[i] = r.NextDouble().ToString() + ".cs";
Expand All @@ -112,7 +127,7 @@ public void AddedSources_Are_Deterministic()
[InlineData("file", "File")]
public void Hint_Name_Must_Be_Unique(string hintName1, string hintName2)
{
AdditionalSourcesCollection asc = new AdditionalSourcesCollection();
AdditionalSourcesCollection asc = new AdditionalSourcesCollection(".cs");
asc.Add(hintName1, SourceText.From("", Encoding.UTF8));
Assert.Throws<ArgumentException>("hintName", () => asc.Add(hintName2, SourceText.From("", Encoding.UTF8)));
}
Expand All @@ -129,7 +144,7 @@ public void Hint_Name_Must_Be_Unique(string hintName1, string hintName2)
[InlineData("File.cs", "file.CS")]
public void Contains(string addHintName, string checkHintName)
{
AdditionalSourcesCollection asc = new AdditionalSourcesCollection();
AdditionalSourcesCollection asc = new AdditionalSourcesCollection(".cs");
asc.Add(addHintName, SourceText.From("", Encoding.UTF8));
Assert.True(asc.Contains(checkHintName));
}
Expand All @@ -141,7 +156,7 @@ public void Contains(string addHintName, string checkHintName)
[InlineData("file.cs", "file")]
public void Remove(string addHintName, string removeHintName)
{
AdditionalSourcesCollection asc = new AdditionalSourcesCollection();
AdditionalSourcesCollection asc = new AdditionalSourcesCollection(".cs");
asc.Add(addHintName, SourceText.From("", Encoding.UTF8));
asc.RemoveSource(removeHintName);
var sources = asc.ToImmutableAndFree();
Expand All @@ -151,7 +166,7 @@ public void Remove(string addHintName, string removeHintName)
[Fact]
public void SourceTextRequiresEncoding()
{
AdditionalSourcesCollection asc = new AdditionalSourcesCollection();
AdditionalSourcesCollection asc = new AdditionalSourcesCollection(".cs");

// fine
asc.Add("file1.cs", SourceText.From("", Encoding.UTF8));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,25 +248,74 @@ public void TestFailedLoadDoesntCauseNoAnalyzersWarning()
Assert.Equal(AnalyzerLoadFailureEventArgs.FailureErrorCode.UnableToCreateAnalyzer, errors.First().ErrorCode);
}

// can't load a framework targeting generator, which these are in desktop
[ConditionalFact(typeof(CoreClrOnly))]
public void TestLoadGenerators()
{
AnalyzerFileReference reference = CreateAnalyzerFileReference(Assembly.GetExecutingAssembly().Location);
var generators = reference.GetGenerators();
Assert.Equal(5, generators.Length);
var generators = reference.GetGeneratorsForAllLanguages();
Assert.Equal(10, generators.Length);

var typeNames = generators.Select(g => g.GetType().FullName);
Assert.Contains("Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+TestGenerator", typeNames);
Assert.Contains("Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+SomeType+NestedGenerator", typeNames);
Assert.Contains("Microsoft.CodeAnalysis.UnitTests.TestGenerator", typeNames);
Assert.Contains("Microsoft.CodeAnalysis.UnitTests.BaseGenerator", typeNames);
Assert.Contains("Microsoft.CodeAnalysis.UnitTests.SubClassedGenerator", typeNames);
Assert.Contains("Microsoft.CodeAnalysis.UnitTests.ExplicitCSharpOnlyGenerator", typeNames);
Assert.Contains("Microsoft.CodeAnalysis.UnitTests.VisualBasicOnlyGenerator", typeNames);
Assert.Contains("Microsoft.CodeAnalysis.UnitTests.CSharpAndVisualBasicGenerator", typeNames);
Assert.Contains("Microsoft.CodeAnalysis.UnitTests.VisualBasicAndCSharpGenerator", typeNames);
Assert.Contains("Microsoft.CodeAnalysis.UnitTests.FSharpGenerator", typeNames);

Assert.DoesNotContain("Microsoft.CodeAnalysis.UnitTests.TestGeneratorNoAttrib", typeNames);
Assert.DoesNotContain("Microsoft.CodeAnalysis.UnitTests.Test.NotAGenerator", typeNames);
Assert.DoesNotContain("Microsoft.CodeAnalysis.UnitTests.NotAGenerator", typeNames);
}

[ConditionalFact(typeof(CoreClrOnly))]
public void TestLoadGeneratorsWithoutArgumentOnlyLoadsCSharp()
{
AnalyzerFileReference reference = CreateAnalyzerFileReference(Assembly.GetExecutingAssembly().Location);
var generators = reference.GetGenerators(LanguageNames.CSharp);

#pragma warning disable CS0618 // Type or member is obsolete
var generators2 = reference.GetGenerators();
#pragma warning restore CS0618 // Type or member is obsolete

Assert.Equal(generators, generators2);
}

[ConditionalFact(typeof(CoreClrOnly))]
public void TestLoadCSharpGenerators()
{
AnalyzerFileReference reference = CreateAnalyzerFileReference(Assembly.GetExecutingAssembly().Location);
var generators = reference.GetGenerators(LanguageNames.CSharp);
Assert.Equal(8, generators.Length);

var typeNames = generators.Select(g => g.GetType().FullName);
Assert.Contains("Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+TestGenerator", typeNames);
Assert.Contains("Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+SomeType+NestedGenerator", typeNames);
Assert.Contains("Microsoft.CodeAnalysis.UnitTests.TestGenerator", typeNames);
Assert.Contains("Microsoft.CodeAnalysis.UnitTests.BaseGenerator", typeNames);
Assert.Contains("Microsoft.CodeAnalysis.UnitTests.SubClassedGenerator", typeNames);
Assert.Contains("Microsoft.CodeAnalysis.UnitTests.ExplicitCSharpOnlyGenerator", typeNames);
Assert.Contains("Microsoft.CodeAnalysis.UnitTests.CSharpAndVisualBasicGenerator", typeNames);
Assert.Contains("Microsoft.CodeAnalysis.UnitTests.VisualBasicAndCSharpGenerator", typeNames);
}

[ConditionalFact(typeof(CoreClrOnly))]
public void TestLoadVisualBasicGenerators()
{
AnalyzerFileReference reference = CreateAnalyzerFileReference(Assembly.GetExecutingAssembly().Location);
var generators = reference.GetGenerators(LanguageNames.VisualBasic);
Assert.Equal(3, generators.Length);

var typeNames = generators.Select(g => g.GetType().FullName);
Assert.Contains("Microsoft.CodeAnalysis.UnitTests.VisualBasicOnlyGenerator", typeNames);
Assert.Contains("Microsoft.CodeAnalysis.UnitTests.CSharpAndVisualBasicGenerator", typeNames);
Assert.Contains("Microsoft.CodeAnalysis.UnitTests.VisualBasicAndCSharpGenerator", typeNames);
}

// can't load a coreclr targeting generator on net framework / mono
[ConditionalFact(typeof(CoreClrOnly))]
public void TestGeneratorsCantTargetNetFramework()
Expand Down Expand Up @@ -455,4 +504,19 @@ public class SubClassedGenerator : BaseGenerator

[Generator]
public class NotAGenerator { }

[Generator(LanguageNames.CSharp)]
public class ExplicitCSharpOnlyGenerator : TestGenerator { }

[Generator(LanguageNames.VisualBasic)]
public class VisualBasicOnlyGenerator : TestGenerator { }

[Generator(LanguageNames.CSharp, LanguageNames.VisualBasic)]
public class CSharpAndVisualBasicGenerator : TestGenerator { }

[Generator(LanguageNames.CSharp, LanguageNames.VisualBasic)]
public class VisualBasicAndCSharpGenerator : TestGenerator { }

[Generator(LanguageNames.FSharp)]
public class FSharpGenerator : TestGenerator { }
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public AnalyzerFileReference(string fullPath, IAnalyzerAssemblyLoader assemblyLo
_assemblyLoader = assemblyLoader ?? throw new ArgumentNullException(nameof(assemblyLoader));

_diagnosticAnalyzers = new Extensions<DiagnosticAnalyzer>(this, IsDiagnosticAnalyzerAttribute, GetDiagnosticsAnalyzerSupportedLanguages, allowNetFramework: true);
_generators = new Extensions<ISourceGenerator>(this, IsGeneratorAttribute, GetGeneratorsSupportedLanguages, allowNetFramework: false);
_generators = new Extensions<ISourceGenerator>(this, IsGeneratorAttribute, GetGeneratorSupportedLanguages, allowNetFramework: false);

// Note this analyzer full path as a dependency location, so that the analyzer loader
// can correctly load analyzer dependencies.
Expand Down Expand Up @@ -111,17 +111,30 @@ public override int GetHashCode()

public override ImmutableArray<DiagnosticAnalyzer> GetAnalyzersForAllLanguages()
{
return _diagnosticAnalyzers.GetExtensionsForAllLanguages();
// This API returns duplicates of analyzers that support multiple languages.
// We explicitly retain this behaviour to ensure back compat
return _diagnosticAnalyzers.GetExtensionsForAllLanguages(includeDuplicates: true);
}

public override ImmutableArray<DiagnosticAnalyzer> GetAnalyzers(string language)
{
return _diagnosticAnalyzers.GetExtensions(language);
}

public override ImmutableArray<ISourceGenerator> GetGeneratorsForAllLanguages()
{
return _generators.GetExtensionsForAllLanguages(includeDuplicates: false);
}

[Obsolete("Use GetGenerators(string language) or GetGeneratorsForAllLanguages()")]
public override ImmutableArray<ISourceGenerator> GetGenerators()
{
return _generators.GetExtensionsForAllLanguages();
return _generators.GetExtensions(LanguageNames.CSharp);
}

public override ImmutableArray<ISourceGenerator> GetGenerators(string language)
{
return _generators.GetExtensions(language);
}

public override string Display
Expand Down Expand Up @@ -254,7 +267,33 @@ private static IEnumerable<string> GetDiagnosticsAnalyzerSupportedLanguages(PEMo
// first supported language and an array parameter for addition supported languages.
// Parse the argument blob to extract the languages.
BlobReader argsReader = peModule.GetMemoryReaderOrThrow(peModule.GetCustomAttributeValueOrThrow(customAttrHandle));
return ReadLanguagesFromAttribute(ref argsReader);
}

private static bool IsGeneratorAttribute(PEModule peModule, CustomAttributeHandle customAttrHandle)
{
return peModule.IsTargetAttribute(customAttrHandle, s_generatorAttributeNamespace, nameof(GeneratorAttribute), ctor: out _);
}

private static IEnumerable<string> GetGeneratorSupportedLanguages(PEModule peModule, CustomAttributeHandle customAttrHandle)
{
// The GeneratorAttribute has two constructors: one default, and one with a string parameter for the
// first supported language and an array parameter for addition supported languages.
BlobReader argsReader = peModule.GetMemoryReaderOrThrow(peModule.GetCustomAttributeValueOrThrow(customAttrHandle));
if (argsReader.Length == 4)
{
// default ctor
return ImmutableArray.Create(LanguageNames.CSharp);
}
else
{
// Parse the argument blob to extract the languages.
return ReadLanguagesFromAttribute(ref argsReader);
}
}

private static IEnumerable<string> ReadLanguagesFromAttribute(ref BlobReader argsReader)
{
if (argsReader.Length > 4)
{
// Arguments are present--check prologue.
Expand All @@ -278,16 +317,9 @@ private static IEnumerable<string> GetDiagnosticsAnalyzerSupportedLanguages(PEMo
}
}
}

return SpecializedCollections.EmptyEnumerable<string>();
}

private static bool IsGeneratorAttribute(PEModule peModule, CustomAttributeHandle customAttrHandle)
{
return peModule.IsTargetAttribute(customAttrHandle, s_generatorAttributeNamespace, nameof(GeneratorAttribute), ctor: out _);
}

private static IEnumerable<string> GetGeneratorsSupportedLanguages(PEModule peModule, CustomAttributeHandle customAttrHandle) => ImmutableArray.Create(LanguageNames.CSharp);

private static string GetFullyQualifiedTypeName(TypeDefinition typeDef, PEModule peModule)
{
Expand Down Expand Up @@ -325,17 +357,17 @@ internal Extensions(AnalyzerFileReference reference, AttributePredicate attribut
_lazyExtensionsPerLanguage = ImmutableDictionary<string, ImmutableArray<TExtension>>.Empty;
}

internal ImmutableArray<TExtension> GetExtensionsForAllLanguages()
internal ImmutableArray<TExtension> GetExtensionsForAllLanguages(bool includeDuplicates)
{
if (_lazyAllExtensions.IsDefault)
{
ImmutableInterlocked.InterlockedInitialize(ref _lazyAllExtensions, CreateExtensionsForAllLanguages(this));
ImmutableInterlocked.InterlockedInitialize(ref _lazyAllExtensions, CreateExtensionsForAllLanguages(this, includeDuplicates));
}

return _lazyAllExtensions;
}

private static ImmutableArray<TExtension> CreateExtensionsForAllLanguages(Extensions<TExtension> extensions)
private static ImmutableArray<TExtension> CreateExtensionsForAllLanguages(Extensions<TExtension> extensions, bool includeDuplicates)
{
// Get all analyzers in the assembly.
var map = ImmutableDictionary.CreateBuilder<string, ImmutableArray<TExtension>>();
Expand All @@ -344,7 +376,13 @@ private static ImmutableArray<TExtension> CreateExtensionsForAllLanguages(Extens
var builder = ImmutableArray.CreateBuilder<TExtension>();
foreach (var analyzers in map.Values)
{
builder.AddRange(analyzers);
foreach (var analyzer in analyzers)
{
if (includeDuplicates || !builder.Contains(t => t.GetType().Equals(analyzer.GetType())))
{
builder.Add(analyzer);
}
}
}

return builder.ToImmutable();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Immutable;

namespace Microsoft.CodeAnalysis.Diagnostics
Expand Down Expand Up @@ -63,6 +64,15 @@ public virtual string Display
/// <summary>
/// Gets all the source generators defined in this assembly reference.
/// </summary>
public virtual ImmutableArray<ISourceGenerator> GetGeneratorsForAllLanguages() { return ImmutableArray<ISourceGenerator>.Empty; }

[Obsolete("Use GetGenerators(string language) or GetGeneratorsForAllLanguages()")]
public virtual ImmutableArray<ISourceGenerator> GetGenerators() { return ImmutableArray<ISourceGenerator>.Empty; }

/// <summary>
/// Gets all the diagnostic generators defined in this assembly reference for the given <paramref name="language"/>.
/// </summary>
/// <param name="language">Language name.</param>
public virtual ImmutableArray<ISourceGenerator> GetGenerators(string language) { return ImmutableArray<ISourceGenerator>.Empty; }
}
}
6 changes: 6 additions & 0 deletions src/Compilers/Core/Portable/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
Microsoft.CodeAnalysis.GeneratorAttribute.GeneratorAttribute(string firstLanguage, params string[] additionalLanguages) -> void
Microsoft.CodeAnalysis.GeneratorAttribute.Languages.get -> string[]
Microsoft.CodeAnalysis.ITypeSymbol.IsRecord.get -> bool
Microsoft.CodeAnalysis.SymbolDisplayPartKind.RecordName = 31 -> Microsoft.CodeAnalysis.SymbolDisplayPartKind
const Microsoft.CodeAnalysis.WellKnownDiagnosticTags.CompilationEnd = "CompilationEnd" -> string
static Microsoft.CodeAnalysis.CaseInsensitiveComparison.Compare(System.ReadOnlySpan<char> left, System.ReadOnlySpan<char> right) -> int
static Microsoft.CodeAnalysis.CaseInsensitiveComparison.Equals(System.ReadOnlySpan<char> left, System.ReadOnlySpan<char> right) -> bool
override Microsoft.CodeAnalysis.Diagnostics.AnalyzerFileReference.GetGenerators(string language) -> System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.ISourceGenerator>
override Microsoft.CodeAnalysis.Diagnostics.AnalyzerFileReference.GetGeneratorsForAllLanguages() -> System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.ISourceGenerator>
virtual Microsoft.CodeAnalysis.Diagnostics.AnalyzerReference.GetGenerators(string language) -> System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.ISourceGenerator>
virtual Microsoft.CodeAnalysis.Diagnostics.AnalyzerReference.GetGeneratorsForAllLanguages() -> System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.ISourceGenerator>
Loading