public void ValidAnalyzerReference_DisplayName() { AnalyzerFileReference reference = CreateAnalyzerFileReference(_testFixture.Alpha.Path); Assert.Equal(expected: "Alpha", actual: reference.Display); }
public static int Main(string[] args) { const int expectedArguments = 15; if (args.Length != expectedArguments) { Console.Error.WriteLine($"Excepted {expectedArguments} arguments, found {args.Length}: {string.Join(';', args)}"); return(1); } string analyzerRulesetsDir = args[0]; string analyzerEditorconfigsDir = args[1]; string binDirectory = args[2]; string configuration = args[3]; string tfm = args[4]; var assemblyList = args[5].Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries).ToList(); string propsFileDir = args[6]; string propsFileName = args[7]; string analyzerDocumentationFileDir = args[8]; string analyzerDocumentationFileName = args[9]; string analyzerSarifFileDir = args[10]; string analyzerSarifFileName = args[11]; var analyzerVersion = args[12]; var analyzerPackageName = args[13]; if (!bool.TryParse(args[14], out var containsPortedFxCopRules)) { containsPortedFxCopRules = false; } var allRulesById = new SortedList <string, DiagnosticDescriptor>(); var fixableDiagnosticIds = new HashSet <string>(); var categories = new HashSet <string>(); var rulesMetadata = new SortedList <string, (string path, SortedList <string, (DiagnosticDescriptor rule, string typeName, string[]? languages)> rules)>(); foreach (string assembly in assemblyList) { var assemblyName = Path.GetFileNameWithoutExtension(assembly); string path = Path.Combine(binDirectory, assemblyName, configuration, tfm, assembly); if (!File.Exists(path)) { Console.Error.WriteLine($"'{path}' does not exist"); return(1); } var analyzerFileReference = new AnalyzerFileReference(path, AnalyzerAssemblyLoader.Instance); var analyzers = analyzerFileReference.GetAnalyzersForAllLanguages(); var assemblyRulesMetadata = (path, rules : new SortedList <string, (DiagnosticDescriptor rule, string typeName, string[]? languages)>()); foreach (var analyzer in analyzers) { var analyzerType = analyzer.GetType(); foreach (var rule in analyzer.SupportedDiagnostics) { allRulesById[rule.Id] = rule; categories.Add(rule.Category); assemblyRulesMetadata.rules[rule.Id] = (rule, analyzerType.Name, analyzerType.GetCustomAttribute <DiagnosticAnalyzerAttribute>(true)?.Languages); } } rulesMetadata.Add(assemblyName, assemblyRulesMetadata); foreach (var id in analyzerFileReference.GetFixers().SelectMany(fixer => fixer.FixableDiagnosticIds)) { fixableDiagnosticIds.Add(id); } } createRulesetAndEditorconfig( "AllRulesDefault", "All Rules with default severity", @"All Rules with default severity. Rules with IsEnabledByDefault = false are disabled.", RulesetKind.AllDefault); createRulesetAndEditorconfig( "AllRulesEnabled", "All Rules Enabled with default severity", "All Rules are enabled with default severity. Rules with IsEnabledByDefault = false are force enabled with default severity.", RulesetKind.AllEnabled); createRulesetAndEditorconfig( "AllRulesDisabled", "All Rules Disabled", @"All Rules are disabled.", RulesetKind.AllDisabled); foreach (var category in categories) { createRulesetAndEditorconfig( $"{category}RulesDefault", $"{category} Rules with default severity", $@"All {category} Rules with default severity. Rules with IsEnabledByDefault = false or from a different category are disabled.", RulesetKind.CategoryDefault, categoryOpt: category); createRulesetAndEditorconfig( $"{category}RulesEnabled", $"{category} Rules Enabled with default severity", $@"All {category} Rules are enabled with default severity. {category} Rules with IsEnabledByDefault = false are force enabled with default severity. Rules from a different category are disabled.", RulesetKind.CategoryEnabled, categoryOpt: category); } // We generate custom tag based rulesets only for select custom tags. var customTagsToGenerateRulesets = ImmutableArray.Create( WellKnownDiagnosticTagsExtensions.Dataflow, FxCopWellKnownDiagnosticTags.PortedFromFxCop); foreach (var customTag in customTagsToGenerateRulesets) { createRulesetAndEditorconfig( $"{customTag}RulesDefault", $"{customTag} Rules with default severity", $@"All {customTag} Rules with default severity. Rules with IsEnabledByDefault = false and non-{customTag} rules are disabled.", RulesetKind.CustomTagDefault, customTagOpt: customTag); createRulesetAndEditorconfig( $"{customTag}RulesEnabled", $"{customTag} Rules Enabled with default severity", $@"All {customTag} Rules are enabled with default severity. {customTag} Rules with IsEnabledByDefault = false are force enabled with default severity. Non-{customTag} Rules are disabled.", RulesetKind.CustomTagEnabled, customTagOpt: customTag); } createPropsFile(); createAnalyzerDocumentationFile(); createAnalyzerSarifFile(); return(0); // Local functions. void createRulesetAndEditorconfig( string fileName, string title, string description, RulesetKind rulesetKind, string?categoryOpt = null, string?customTagOpt = null) { CreateRuleset(analyzerRulesetsDir, fileName + ".ruleset", title, description, rulesetKind, categoryOpt, customTagOpt, allRulesById, analyzerPackageName); CreateEditorconfig(analyzerEditorconfigsDir, fileName, title, description, rulesetKind, categoryOpt, customTagOpt, allRulesById); return; } void createPropsFile() { if (string.IsNullOrEmpty(propsFileDir) || string.IsNullOrEmpty(propsFileName)) { Debug.Assert(!containsPortedFxCopRules); return; } var fileContents = $@"<Project DefaultTargets=""Build"" xmlns=""http://schemas.microsoft.com/developer/msbuild/2003""> {getEditorConfigAsAdditionalFile()}{getCodeAnalysisTreatWarningsNotAsErrors()}{getRulesetOverrides()}{getFlowAnalysisFeatureFlag()} </Project>"; var directory = Directory.CreateDirectory(propsFileDir); var fileWithPath = Path.Combine(directory.FullName, propsFileName); File.WriteAllText(fileWithPath, fileContents); }
protected override string GetAnalyzerAssemblyPath(AnalyzerFileReference reference) { // default implementation doesn't do shadow copying and doesn't guarantee snapshot return(reference.FullPath); }
public void TestLoadedGeneratorOrderIsDeterministic() { AnalyzerFileReference reference = CreateAnalyzerFileReference( Assembly.GetExecutingAssembly().Location ); var csharpGenerators = reference .GetGenerators(LanguageNames.CSharp) .Select(a => a.GetType().FullName) .ToArray(); Assert.Equal(8, csharpGenerators.Length); Assert.Equal( "Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+SomeType+NestedGenerator", csharpGenerators[0] ); Assert.Equal( "Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+TestGenerator", csharpGenerators[1] ); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.BaseGenerator", csharpGenerators[2]); Assert.Equal( "Microsoft.CodeAnalysis.UnitTests.CSharpAndVisualBasicGenerator", csharpGenerators[3] ); Assert.Equal( "Microsoft.CodeAnalysis.UnitTests.ExplicitCSharpOnlyGenerator", csharpGenerators[4] ); Assert.Equal( "Microsoft.CodeAnalysis.UnitTests.SubClassedGenerator", csharpGenerators[5] ); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.TestGenerator", csharpGenerators[6]); Assert.Equal( "Microsoft.CodeAnalysis.UnitTests.VisualBasicAndCSharpGenerator", csharpGenerators[7] ); var vbGenerators = reference .GetGenerators(LanguageNames.VisualBasic) .Select(a => a.GetType().FullName) .ToArray(); Assert.Equal(3, vbGenerators.Length); Assert.Equal( "Microsoft.CodeAnalysis.UnitTests.CSharpAndVisualBasicGenerator", vbGenerators[0] ); Assert.Equal( "Microsoft.CodeAnalysis.UnitTests.VisualBasicAndCSharpGenerator", vbGenerators[1] ); Assert.Equal( "Microsoft.CodeAnalysis.UnitTests.VisualBasicOnlyGenerator", vbGenerators[2] ); // generators load in language order (C#, F#, VB), and *do not* include duplicates var allGenerators = reference .GetGeneratorsForAllLanguages() .Select(g => g.GetType().FullName) .ToArray(); Assert.Equal(10, allGenerators.Length); Assert.Equal( "Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+SomeType+NestedGenerator", allGenerators[0] ); Assert.Equal( "Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+TestGenerator", allGenerators[1] ); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.BaseGenerator", allGenerators[2]); Assert.Equal( "Microsoft.CodeAnalysis.UnitTests.CSharpAndVisualBasicGenerator", allGenerators[3] ); Assert.Equal( "Microsoft.CodeAnalysis.UnitTests.ExplicitCSharpOnlyGenerator", allGenerators[4] ); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.SubClassedGenerator", allGenerators[5]); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.TestGenerator", allGenerators[6]); Assert.Equal( "Microsoft.CodeAnalysis.UnitTests.VisualBasicAndCSharpGenerator", allGenerators[7] ); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.FSharpGenerator", allGenerators[8]); Assert.Equal( "Microsoft.CodeAnalysis.UnitTests.VisualBasicOnlyGenerator", allGenerators[9] ); }
private void UpdateProject(ProjectFileInfo projectFileInfo) { var project = _workspace.CurrentSolution.GetProject(projectFileInfo.WorkspaceId); var unusedDocuments = project.Documents.ToDictionary(d => d.FilePath, d => d.Id); foreach (var file in projectFileInfo.SourceFiles) { if (unusedDocuments.Remove(file)) { continue; } using (var stream = File.OpenRead(file)) { var sourceText = SourceText.From(stream, encoding: Encoding.UTF8); var id = DocumentId.CreateNewId(projectFileInfo.WorkspaceId); var version = VersionStamp.Create(); var loader = TextLoader.From(TextAndVersion.Create(sourceText, version)); _workspace.AddDocument(DocumentInfo.Create(id, file, filePath: file, loader: loader)); } } if (projectFileInfo.SpecifiedLanguageVersion.HasValue || projectFileInfo.DefineConstants != null) { var parseOptions = projectFileInfo.SpecifiedLanguageVersion.HasValue ? new CSharpParseOptions(projectFileInfo.SpecifiedLanguageVersion.Value) : new CSharpParseOptions(); if (projectFileInfo.DefineConstants != null && projectFileInfo.DefineConstants.Any()) { parseOptions = parseOptions.WithPreprocessorSymbols(projectFileInfo.DefineConstants); } _workspace.SetParseOptions(project.Id, parseOptions); } foreach (var unused in unusedDocuments) { _workspace.RemoveDocument(unused.Value); } var unusedProjectReferences = new HashSet <ProjectReference>(project.ProjectReferences); foreach (var projectReferencePath in projectFileInfo.ProjectReferences) { ProjectFileInfo projectReferenceInfo; if (_context.Projects.TryGetValue(projectReferencePath, out projectReferenceInfo)) { var reference = new ProjectReference(projectReferenceInfo.WorkspaceId); if (unusedProjectReferences.Remove(reference)) { // This reference already exists continue; } _workspace.AddProjectReference(project.Id, reference); } else { _logger.LogWarning($"Unable to resolve project reference '{projectReferencePath}' for '{projectFileInfo}'."); } } foreach (var unused in unusedProjectReferences) { _workspace.RemoveProjectReference(project.Id, unused); } var unusedAnalyzers = new Dictionary <string, AnalyzerReference>(project.AnalyzerReferences.ToDictionary(a => a.FullPath)); foreach (var analyzerPath in projectFileInfo.Analyzers) { if (!File.Exists(analyzerPath)) { _logger.LogWarning($"Unable to resolve assembly '{analyzerPath}'"); } else { if (unusedAnalyzers.Remove(analyzerPath)) { continue; } #if DNX451 var analyzerReference = new AnalyzerFileReference(analyzerPath, new SimpleAnalyzerAssemblyLoader()); project.AddAnalyzerReference(analyzerReference); #endif } } foreach (var analyzerReference in unusedAnalyzers.Values) { project.RemoveAnalyzerReference(analyzerReference); } var unusedReferences = new HashSet <MetadataReference>(project.MetadataReferences); foreach (var referencePath in projectFileInfo.References) { if (!File.Exists(referencePath)) { _logger.LogWarning($"Unable to resolve assembly '{referencePath}'"); } else { var metadataReference = _metadataReferenceCache.GetMetadataReference(referencePath); if (unusedReferences.Remove(metadataReference)) { continue; } _logger.LogDebug($"Adding reference '{referencePath}' to '{projectFileInfo.ProjectFilePath}'."); _workspace.AddMetadataReference(project.Id, metadataReference); } } foreach (var reference in unusedReferences) { _workspace.RemoveMetadataReference(project.Id, reference); } }
public void LoadWithDependency() { var directory = Temp.CreateDirectory(); var immutable = directory.CopyFile(typeof(ImmutableArray).Assembly.Location); var microsoftCodeAnalysis = directory.CopyFile(typeof(DiagnosticAnalyzer).Assembly.Location); var analyzerDependencyFile = CreateAnalyzerDependency(); var analyzerMainFile = CreateMainAnalyzerWithDependency(analyzerDependencyFile); var loader = new ShadowCopyAnalyzerAssemblyLoader(Path.Combine(directory.Path, "AnalyzerAssemblyLoader")); var analyzerMainReference = new AnalyzerFileReference(analyzerMainFile.Path, loader); analyzerMainReference.AnalyzerLoadFailed += (_, e) => AssertEx.Fail(e.Exception.Message); var analyzerDependencyReference = new AnalyzerFileReference(analyzerDependencyFile.Path, loader); analyzerDependencyReference.AnalyzerLoadFailed += (_, e) => AssertEx.Fail(e.Exception.Message); var analyzers = analyzerMainReference.GetAnalyzersForAllLanguages(); Assert.Equal(1, analyzers.Length); Assert.Equal("TestAnalyzer", analyzers[0].ToString()); Assert.Equal(0, analyzerDependencyReference.GetAnalyzersForAllLanguages().Length); Assert.NotNull(analyzerDependencyReference.GetAssembly()); TempFile CreateAnalyzerDependency() { var analyzerDependencySource = @" using System; using System.Collections.Immutable; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; public abstract class AbstractTestAnalyzer : DiagnosticAnalyzer { protected static string SomeString = nameof(SomeString); public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { throw new NotImplementedException(); } } public override void Initialize(AnalysisContext context) { throw new NotImplementedException(); } }"; var analyzerDependencyCompilation = CSharp.CSharpCompilation.Create( "AnalyzerDependency", new SyntaxTree[] { CSharp.SyntaxFactory.ParseSyntaxTree(analyzerDependencySource) }, new MetadataReference[] { TestMetadata.NetStandard20.mscorlib, TestMetadata.NetStandard20.netstandard, TestMetadata.NetStandard20.SystemRuntime, MetadataReference.CreateFromFile(immutable.Path), MetadataReference.CreateFromFile(microsoftCodeAnalysis.Path) }, new CSharp.CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); return(directory.CreateDirectory("AnalyzerDependency").CreateFile("AnalyzerDependency.dll").WriteAllBytes(analyzerDependencyCompilation.EmitToArray())); } TempFile CreateMainAnalyzerWithDependency(TempFile analyzerDependency) { var analyzerMainSource = @" using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; [DiagnosticAnalyzer(LanguageNames.CSharp)] public sealed class TestAnalyzer : AbstractTestAnalyzer { private static string SomeString2 = AbstractTestAnalyzer.SomeString; }"; var analyzerMainCompilation = CSharp.CSharpCompilation.Create( "AnalyzerMain", new SyntaxTree[] { CSharp.SyntaxFactory.ParseSyntaxTree(analyzerMainSource) }, new MetadataReference[] { TestMetadata.NetStandard20.mscorlib, TestMetadata.NetStandard20.netstandard, TestMetadata.NetStandard20.SystemRuntime, MetadataReference.CreateFromFile(immutable.Path), MetadataReference.CreateFromFile(microsoftCodeAnalysis.Path), MetadataReference.CreateFromFile(analyzerDependency.Path) }, new CSharp.CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); return(directory.CreateDirectory("AnalyzerMain").CreateFile("AnalyzerMain.dll").WriteAllBytes(analyzerMainCompilation.EmitToArray())); } }
public static int Main(string[] args) { if (args.Length != 10) { Console.Error.WriteLine($"Excepted 8 arguments, found {args.Length}: {string.Join(';', args)}"); return(1); } string analyzerRulesetsDir = args[0]; string binDirectory = args[1]; string configuration = args[2]; string tfm = args[3]; var assemblyList = args[4].Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries).ToList(); string propsFileDir = args[5]; string propsFileName = args[6]; string analyzerDocumentationFileDir = args[7]; string analyzerDocumentationFileName = args[8]; if (!bool.TryParse(args[9], out var containsPortedFxCopRules)) { containsPortedFxCopRules = false; } var allRulesByAssembly = new SortedList <string, SortedList <string, DiagnosticDescriptor> >(); var allRulesById = new SortedList <string, DiagnosticDescriptor>(); var fixableDiagnosticIds = new HashSet <string>(); var categories = new HashSet <string>(); foreach (string assembly in assemblyList) { var assemblyName = Path.GetFileNameWithoutExtension(assembly); string path = Path.Combine(binDirectory, assemblyName, configuration, tfm, assembly); if (!File.Exists(path)) { Console.Error.WriteLine($"'{path}' does not exist"); return(1); } var analyzerFileReference = new AnalyzerFileReference(path, AnalyzerAssemblyLoader.Instance); var analyzers = analyzerFileReference.GetAnalyzersForAllLanguages(); var rules = analyzers.SelectMany(a => a.SupportedDiagnostics); if (rules.Any()) { var rulesById = new SortedList <string, DiagnosticDescriptor>(); foreach (DiagnosticDescriptor rule in rules) { rulesById[rule.Id] = rule; allRulesById[rule.Id] = rule; categories.Add(rule.Category); } allRulesByAssembly.Add(assemblyName, rulesById); } foreach (var id in analyzerFileReference.GetFixers().SelectMany(fixer => fixer.FixableDiagnosticIds)) { fixableDiagnosticIds.Add(id); } } createRuleset( "AllRulesDefault.ruleset", "All Rules with default action", @"All Rules with default action. Rules with IsEnabledByDefault = false are disabled.", RulesetKind.AllDefault, categoryOpt: null); createRuleset( "AllRulesEnabled.ruleset", "All Rules Enabled with default action", "All Rules are enabled with default action. Rules with IsEnabledByDefault = false are force enabled with default action.", RulesetKind.AllEnabled, categoryOpt: null); createRuleset( "AllRulesDisabled.ruleset", "All Rules Disabled", @"All Rules are disabled.", RulesetKind.AllDisabled, categoryOpt: null); foreach (var category in categories) { createRuleset( $"{category}RulesDefault.ruleset", $"{category} Rules with default action", $@"All {category} Rules with default action. Rules with IsEnabledByDefault = false or from a different category are disabled.", RulesetKind.CategoryDefault, categoryOpt: category); createRuleset( $"{category}RulesEnabled.ruleset", $"{category} Rules Enabled with default action", $@"All {category} Rules are enabled with default action. {category} Rules with IsEnabledByDefault = false are force enabled with default action. Rules from a different category are disabled.", RulesetKind.CategoryEnabled, categoryOpt: category); } createPropsFile(); createAnalyzerDocumentationFile(); return(0); // Local functions. void createRuleset( string rulesetFileName, string rulesetName, string rulesetDescription, RulesetKind rulesetKind, string categoryOpt) { Debug.Assert((categoryOpt != null) == (rulesetKind == RulesetKind.CategoryDefault || rulesetKind == RulesetKind.CategoryEnabled)); var result = new StringBuilder(); startRuleset(); if (categoryOpt == null) { addRules(categoryPass: false); } else { result.AppendLine($@" <!-- {categoryOpt} Rules -->"); addRules(categoryPass: true); result.AppendLine(); result.AppendLine($@" <!-- Other Rules -->"); addRules(categoryPass: false); } endRuleset(); var directory = Directory.CreateDirectory(analyzerRulesetsDir); var rulesetFilePath = Path.Combine(directory.FullName, rulesetFileName); File.WriteAllText(rulesetFilePath, result.ToString()); return; void startRuleset() { result.AppendLine(@"<?xml version=""1.0""?>"); result.AppendLine($@"<RuleSet Name=""{rulesetName}"" Description=""{rulesetDescription}"" ToolsVersion=""15.0"">"); } void endRuleset() { result.AppendLine("</RuleSet>"); } void addRules(bool categoryPass) { foreach (var rulesByAssembly in allRulesByAssembly) { string assemblyName = rulesByAssembly.Key; SortedList <string, DiagnosticDescriptor> sortedRulesById = rulesByAssembly.Value; startRules(assemblyName); foreach (var rule in sortedRulesById) { addRule(rule.Value); } endRules(); } return; void startRules(string assemblyName) { result.AppendLine($@" <Rules AnalyzerId=""{assemblyName}"" RuleNamespace=""{assemblyName}"">"); } void endRules() { result.AppendLine(" </Rules>"); } void addRule(DiagnosticDescriptor rule) { if (shouldSkipRule(rule)) { return; } string ruleAction = getRuleAction(rule); var spacing = new string(' ', count : 15 - ruleAction.Length); result.AppendLine($@" <Rule Id=""{rule.Id}"" Action=""{ruleAction}"" /> {spacing} <!-- {rule.Title} -->"); } bool shouldSkipRule(DiagnosticDescriptor rule) { switch (rulesetKind) { case RulesetKind.CategoryDefault: case RulesetKind.CategoryEnabled: if (categoryPass) { return(rule.Category != categoryOpt); } else { return(rule.Category == categoryOpt); } default: return(false); } } string getRuleAction(DiagnosticDescriptor rule) { switch (rulesetKind) { case RulesetKind.CategoryDefault: return(getRuleActionCore(enable: categoryPass && rule.IsEnabledByDefault)); case RulesetKind.CategoryEnabled: return(getRuleActionCore(enable: categoryPass)); case RulesetKind.AllDefault: return(getRuleActionCore(enable: rule.IsEnabledByDefault)); case RulesetKind.AllEnabled: return(getRuleActionCore(enable: true)); case RulesetKind.AllDisabled: return(getRuleActionCore(enable: false)); default: throw new InvalidProgramException(); } string getRuleActionCore(bool enable) { if (enable) { return(rule.DefaultSeverity.ToString()); } else { return("None"); } } } } } void createPropsFile() { if (string.IsNullOrEmpty(propsFileDir) || string.IsNullOrEmpty(propsFileName)) { Debug.Assert(!containsPortedFxCopRules); return; } var fileContents = $@"<Project DefaultTargets=""Build"" xmlns=""http://schemas.microsoft.com/developer/msbuild/2003""> {getEditorConfigAsAdditionalFile()}{getCodeAnalysisTreatWarningsNotAsErrors()}{getRulesetOverrides()}{getFlowAnalysisFeatureFlag()} </Project>"; var directory = Directory.CreateDirectory(propsFileDir); var fileWithPath = Path.Combine(directory.FullName, propsFileName); File.WriteAllText(fileWithPath, fileContents); } string getFlowAnalysisFeatureFlag() { return(@" <PropertyGroup> <Features>$(Features);flow-analysis</Features> </PropertyGroup>"); } string getCodeAnalysisTreatWarningsNotAsErrors() { var allRuleIds = string.Join(';', allRulesByAssembly.Values.SelectMany(l => l.Keys).Distinct()); return($@" <!-- This property group prevents the rule ids implemented in this package to be bumped to errors when the 'CodeAnalysisTreatWarningsAsErrors' = 'false'. --> <PropertyGroup Condition=""'$(CodeAnalysisTreatWarningsAsErrors)' == 'false'""> <WarningsNotAsErrors>$(WarningsNotAsErrors);{allRuleIds}</WarningsNotAsErrors> </PropertyGroup>"); } string getRulesetOverrides() { if (containsPortedFxCopRules) { var rulesetOverridesBuilder = new StringBuilder(); foreach (var category in categories.OrderBy(k => k)) { // Each rule entry format is: -[Category]#[ID]; // For example, -Microsoft.Design#CA1001; var categoryPrefix = $" -Microsoft.{category}#"; var entries = allRulesByAssembly.Values .SelectMany(l => l) .Where(ruleIdAndDescriptor => ruleIdAndDescriptor.Value.Category == category && FxCopWellKnownDiagnosticTags.IsPortedFxCopRule(ruleIdAndDescriptor.Value)) .OrderBy(ruleIdAndDescriptor => ruleIdAndDescriptor.Key) .Select(ruleIdAndDescriptor => $"{categoryPrefix}{ruleIdAndDescriptor.Key};") .Distinct(); if (entries.Any()) { rulesetOverridesBuilder.AppendLine(); rulesetOverridesBuilder.Append(string.Join(Environment.NewLine, entries)); rulesetOverridesBuilder.AppendLine(); } } if (rulesetOverridesBuilder.Length > 0) { return($@" <!-- This property group contains the rules that have been implemented in this package and therefore should be disabled for the binary FxCop. The format is -[Category]#[ID], e.g., -Microsoft.Design#CA1001; --> <PropertyGroup> <CodeAnalysisRuleSetOverrides> $(CodeAnalysisRuleSetOverrides);{rulesetOverridesBuilder.ToString()} </CodeAnalysisRuleSetOverrides> </PropertyGroup>"); } } return(string.Empty); } string getEditorConfigAsAdditionalFile() { return($@" <!-- This item group adds any .editorconfig file present at the project root directory as an additional file. --> <ItemGroup Condition=""'$(SkipDefaultEditorConfigAsAdditionalFile)' != 'true' And Exists('$(MSBuildProjectDirectory)\.editorconfig')"" > <AdditionalFiles Include=""$(MSBuildProjectDirectory)\.editorconfig"" /> </ItemGroup> "); } void createAnalyzerDocumentationFile() { if (string.IsNullOrEmpty(analyzerDocumentationFileDir) || string.IsNullOrEmpty(analyzerDocumentationFileName) || allRulesById.Count == 0) { Debug.Assert(!containsPortedFxCopRules); return; } var directory = Directory.CreateDirectory(analyzerDocumentationFileDir); var fileWithPath = Path.Combine(directory.FullName, analyzerDocumentationFileName); var builder = new StringBuilder(); builder.Append(@" Sr. No. | Rule ID | Title | Category | Enabled | CodeFix | Description | --------|---------|-------|----------|---------|---------|--------------------------------------------------------------------------------------------------------------| "); var index = 1; foreach (var ruleById in allRulesById) { string ruleId = ruleById.Key; DiagnosticDescriptor descriptor = ruleById.Value; var ruleIdWithHyperLink = descriptor.Id; if (!string.IsNullOrWhiteSpace(descriptor.HelpLinkUri)) { ruleIdWithHyperLink = $"[{ruleIdWithHyperLink}]({descriptor.HelpLinkUri})"; } var hasCodeFix = fixableDiagnosticIds.Contains(descriptor.Id); var description = descriptor.Description.ToString(); if (string.IsNullOrWhiteSpace(description)) { description = descriptor.MessageFormat.ToString(); } builder.AppendLine($"{index} | {ruleIdWithHyperLink} | {descriptor.Title} | {descriptor.Category} | {descriptor.IsEnabledByDefault} | {hasCodeFix} | {description} |"); index++; } File.WriteAllText(fileWithPath, builder.ToString()); } }
public static int Main(string[] args) { const int expectedArguments = 9; if (args.Length != expectedArguments) { Console.Error.WriteLine($"Expected {expectedArguments} arguments, found {args.Length}: {string.Join(';', args)}"); return(1); } var outputDir = args[0]; var packageName = args[1]; string targetsFileDir = args[2]; string targetsFileName = args[3]; var assemblyList = args[4].Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries).ToList(); var binDirectory = args[5]; var configuration = args[6]; var tfm = args[7]; var releaseTrackingOptOutString = args[8]; if (!bool.TryParse(releaseTrackingOptOutString, out bool releaseTrackingOptOut)) { releaseTrackingOptOut = false; } using var shippedFilesDataBuilder = ArrayBuilder <ReleaseTrackingData> .GetInstance(); using var versionsBuilder = PooledHashSet <Version> .GetInstance(); // Validate all assemblies exist on disk and can be loaded. foreach (string assembly in assemblyList) { var assemblyPath = GetAssemblyPath(assembly); if (!File.Exists(assemblyPath)) { Console.Error.WriteLine($"'{assemblyPath}' does not exist"); return(2); } try { _ = Assembly.LoadFrom(assemblyPath); } #pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) #pragma warning restore CA1031 // Do not catch general exception types { Console.Error.WriteLine(ex.Message); return(3); } } // Compute descriptors by rule ID and shipped analyzer release versions and shipped data. var allRulesById = new SortedList <string, DiagnosticDescriptor>(); var sawShippedFile = false; foreach (string assembly in assemblyList) { var assemblyPath = GetAssemblyPath(assembly); var analyzerFileReference = new AnalyzerFileReference(assemblyPath, AnalyzerAssemblyLoader.Instance); analyzerFileReference.AnalyzerLoadFailed += AnalyzerFileReference_AnalyzerLoadFailed; var analyzers = analyzerFileReference.GetAnalyzersForAllLanguages(); foreach (var analyzer in analyzers) { foreach (var rule in analyzer.SupportedDiagnostics) { allRulesById[rule.Id] = rule; } } var assemblyDir = Path.GetDirectoryName(assemblyPath); if (assemblyDir is null) { continue; } var assemblyName = Path.GetFileNameWithoutExtension(assembly); var shippedFile = Path.Combine(assemblyDir, "AnalyzerReleases", assemblyName, ReleaseTrackingHelper.ShippedFileName); if (File.Exists(shippedFile)) { sawShippedFile = true; if (releaseTrackingOptOut) { Console.Error.WriteLine($"'{shippedFile}' exists but was not expected"); return(4); } try { using var fileStream = File.OpenRead(shippedFile); var sourceText = SourceText.From(fileStream); var releaseTrackingData = ReleaseTrackingHelper.ReadReleaseTrackingData(shippedFile, sourceText, onDuplicateEntryInRelease: (_1, _2, _3, _4, line) => throw new Exception($"Duplicate entry in {shippedFile} at {line.LineNumber}: '{line}'"), onInvalidEntry: (line, _2, _3, _4) => throw new Exception($"Invalid entry in {shippedFile} at {line.LineNumber}: '{line}'"), isShippedFile: true); shippedFilesDataBuilder.Add(releaseTrackingData); versionsBuilder.AddRange(releaseTrackingData.Versions); } #pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) #pragma warning restore CA1031 // Do not catch general exception types { Console.Error.WriteLine(ex.Message); return(5); } } } if (!releaseTrackingOptOut && !sawShippedFile) { Console.Error.WriteLine($"Could not find any 'AnalyzerReleases.Shipped.md' file"); return(6); } if (versionsBuilder.Count > 0) { var shippedFilesData = shippedFilesDataBuilder.ToImmutable(); // Generate global analyzer config files for each shipped version, if required. foreach (var version in versionsBuilder) { CreateEditorconfig( outputDir, $"AnalysisLevel_{GetNormalizedVersionStringForEditorconfigFileNameSuffix(version)}.editorconfig", $"Rules from '{version}' release", $"Rules with default severity and enabled state from '{version}' release. Rules that are first released in a version later then '{version}' are disabled.", allRulesById, (shippedFilesData, version)); } } CreateTargetsFile(targetsFileDir, targetsFileName, packageName); return(0);
public async Task TestHostAnalyzers_OutOfProc() { var code = @"class Test { void Method() { var t = new Test(); } }"; using (var workspace = CreateWorkspace(LanguageNames.CSharp, code)) { var analyzerType = typeof(CSharpUseExplicitTypeDiagnosticAnalyzer); var analyzerReference = new AnalyzerFileReference( analyzerType.Assembly.Location, new TestAnalyzerAssemblyLoader() ); var options = workspace.Options.WithChangedOption( CSharpCodeStyleOptions.VarWhenTypeIsApparent, new CodeStyleOption <bool>(false, NotificationOption.Suggestion) ); workspace.TryApplyChanges( workspace.CurrentSolution .WithOptions(options) .WithAnalyzerReferences(new[] { analyzerReference }) ); // run analysis var project = workspace.CurrentSolution.Projects.First(); var runner = CreateAnalyzerRunner(); var compilationWithAnalyzers = (await project.GetCompilationAsync()).WithAnalyzers( analyzerReference .GetAnalyzers(project.Language) .Where(a => a.GetType() == analyzerType) .ToImmutableArray(), new WorkspaceAnalyzerOptions(project.AnalyzerOptions, project.Solution) ); // no result for open file only analyzer unless forced var result = await runner.AnalyzeProjectAsync( project, compilationWithAnalyzers, forceExecuteAllAnalyzers : false, logPerformanceInfo : false, getTelemetryInfo : false, cancellationToken : CancellationToken.None ); Assert.Empty(result.AnalysisResult); result = await runner.AnalyzeProjectAsync( project, compilationWithAnalyzers, forceExecuteAllAnalyzers : true, logPerformanceInfo : false, getTelemetryInfo : false, cancellationToken : CancellationToken.None ); var analyzerResult = result.AnalysisResult[compilationWithAnalyzers.Analyzers[0]]; // check result var diagnostics = analyzerResult.GetDocumentDiagnostics( analyzerResult.DocumentIds.First(), AnalysisKind.Semantic ); Assert.Equal(IDEDiagnosticIds.UseExplicitTypeDiagnosticId, diagnostics[0].Id); } }
internal static ImmutableArray <DiagnosticAnalyzer> GetOrCreateAnalyzersFromFile(AnalyzerFileReference analyzerReference, string langauge = null) { string fullPath = analyzerReference.FullPath; Debug.Assert(PathUtilities.IsAbsolute(fullPath)); lock (Guard) { // may throw: FileKey key = FileKey.Create(fullPath); CachedAnalyzers cachedAnalyzers; if (s_analyzersFromFiles.TryGetValue(key, out cachedAnalyzers)) { if (cachedAnalyzers.Analyzers.IsAlive && cachedAnalyzers.Language == langauge) { return((ImmutableArray <DiagnosticAnalyzer>)cachedAnalyzers.Analyzers.Target); } else { s_analyzersFromFiles.Remove(key); var removed = s_analyzerAssemblyKeys.Remove(key); Debug.Assert(removed); Debug.Assert(!s_analyzerAssemblyKeys.Contains(key)); } } if (langauge == null) { return(CreateAnalyzersFromFile(analyzerReference)); } return(CreateAnalyzersFromFile(analyzerReference, langauge)); } }
public static int Main(string[] args) { if (args.Length != 5) { Console.Error.WriteLine($"Excepted 5 arguments, found {args.Length}: {string.Join(';', args)}"); return(1); } string analyzerRulesetsDir = args[0]; string binDirectory = args[1]; string configuration = args[2]; string tfm = args[3]; var assemblyList = args[4].Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries).ToList(); var allRulesByAssembly = new SortedList <string, SortedList <string, DiagnosticDescriptor> >(); var categories = new HashSet <string>(); foreach (string assembly in assemblyList) { var assemblyName = Path.GetFileNameWithoutExtension(assembly); string path = Path.Combine(binDirectory, assemblyName, configuration, tfm, assembly); if (!File.Exists(path)) { Console.Error.WriteLine($"'{path}' does not exist"); return(1); } var analyzerFileReference = new AnalyzerFileReference(path, AnalyzerAssemblyLoader.Instance); var analyzers = analyzerFileReference.GetAnalyzersForAllLanguages(); var rules = analyzers.SelectMany(a => a.SupportedDiagnostics); if (rules.Any()) { var rulesById = new SortedList <string, DiagnosticDescriptor>(); foreach (DiagnosticDescriptor rule in rules) { rulesById[rule.Id] = rule; categories.Add(rule.Category); } allRulesByAssembly.Add(assemblyName, rulesById); } } createRuleset( "AllRulesDefault.ruleset", "All Rules with default action", @"All Rules with default action. Rules with IsEnabledByDefault = false are disabled.", RulesetKind.AllDefault, categoryOpt: null); createRuleset( "AllRulesEnabled.ruleset", "All Rules Enabled with default action", "All Rules are enabled with default action. Rules with IsEnabledByDefault = false are force enabled with default action.", RulesetKind.AllEnabled, categoryOpt: null); createRuleset( "AllRulesDisabled.ruleset", "All Rules Disabled", @"All Rules are disabled.", RulesetKind.AllDisabled, categoryOpt: null); foreach (var category in categories) { createRuleset( $"{category}RulesDefault.ruleset", $"{category} Rules with default action", $@"All {category} Rules with default action. Rules with IsEnabledByDefault = false or from a different category are disabled.", RulesetKind.CategoryDefault, categoryOpt: category); createRuleset( $"{category}RulesEnabled.ruleset", $"{category} Rules Enabled with default action", $@"All {category} Rules are enabled with default action. {category} Rules with IsEnabledByDefault = false are force enabled with default action. Rules from a different category are disabled.", RulesetKind.CategoryEnabled, categoryOpt: category); } void createRuleset( string rulesetFileName, string rulesetName, string rulesetDescription, RulesetKind rulesetKind, string categoryOpt) { Debug.Assert((categoryOpt != null) == (rulesetKind == RulesetKind.CategoryDefault || rulesetKind == RulesetKind.CategoryEnabled)); var result = new StringBuilder(); startRuleset(); if (categoryOpt == null) { addRules(categoryPass: false); } else { result.AppendLine($@" <!-- {categoryOpt} Rules -->"); addRules(categoryPass: true); result.AppendLine(); result.AppendLine($@" <!-- Other Rules -->"); addRules(categoryPass: false); } endRuleset(); var directory = Directory.CreateDirectory(analyzerRulesetsDir); var rulesetFilePath = Path.Combine(directory.FullName, rulesetFileName); File.WriteAllText(rulesetFilePath, result.ToString()); return; void startRuleset() { result.AppendLine(@"<?xml version=""1.0""?>"); result.AppendLine($@"<RuleSet Name=""{rulesetName}"" Description=""{rulesetDescription}"" ToolsVersion=""15.0"">"); } void endRuleset() { result.AppendLine("</RuleSet>"); } void addRules(bool categoryPass) { foreach (var rulesByAssembly in allRulesByAssembly) { string assemblyName = rulesByAssembly.Key; SortedList <string, DiagnosticDescriptor> sortedRulesById = rulesByAssembly.Value; startRules(assemblyName); foreach (var rule in sortedRulesById) { addRule(rule.Value); } endRules(); } return; void startRules(string assemblyName) { result.AppendLine($@" <Rules AnalyzerId=""{assemblyName}"" RuleNamespace=""{assemblyName}"">"); } void endRules() { result.AppendLine(" </Rules>"); } void addRule(DiagnosticDescriptor rule) { if (shouldSkipRule(rule)) { return; } string ruleAction = getRuleAction(rule); var spacing = new string(' ', count : 15 - ruleAction.Length); result.AppendLine($@" <Rule Id=""{rule.Id}"" Action=""{ruleAction}"" /> {spacing} <!-- {rule.Title} -->"); } bool shouldSkipRule(DiagnosticDescriptor rule) { switch (rulesetKind) { case RulesetKind.CategoryDefault: case RulesetKind.CategoryEnabled: if (categoryPass) { return(rule.Category != categoryOpt); } else { return(rule.Category == categoryOpt); } default: return(false); } } string getRuleAction(DiagnosticDescriptor rule) { switch (rulesetKind) { case RulesetKind.CategoryDefault: return(getRuleActionCore(enable: categoryPass && rule.IsEnabledByDefault)); case RulesetKind.CategoryEnabled: return(getRuleActionCore(enable: categoryPass)); case RulesetKind.AllDefault: return(getRuleActionCore(enable: rule.IsEnabledByDefault)); case RulesetKind.AllEnabled: return(getRuleActionCore(enable: true)); case RulesetKind.AllDisabled: return(getRuleActionCore(enable: false)); default: throw new InvalidProgramException(); } string getRuleActionCore(bool enable) { if (enable) { return(rule.DefaultSeverity.ToString()); } else { return("None"); } } } } } return(0); }
public static int Main(string[] args) { const int expectedArguments = 13; if (args.Length != expectedArguments) { Console.Error.WriteLine($"Excepted {expectedArguments} arguments, found {args.Length}: {string.Join(';', args)}"); return(1); } string analyzerRulesetsDir = args[0]; string binDirectory = args[1]; string configuration = args[2]; string tfm = args[3]; var assemblyList = args[4].Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries).ToList(); string propsFileDir = args[5]; string propsFileName = args[6]; string analyzerDocumentationFileDir = args[7]; string analyzerDocumentationFileName = args[8]; string analyzerSarifFileDir = args[9]; string analyzerSarifFileName = args[10]; var analyzerVersion = args[11]; if (!bool.TryParse(args[12], out var containsPortedFxCopRules)) { containsPortedFxCopRules = false; } var allRulesByAssembly = new SortedList <string, SortedList <string, DiagnosticDescriptor> >(); var allRulesById = new SortedList <string, DiagnosticDescriptor>(); var fixableDiagnosticIds = new HashSet <string>(); var categories = new HashSet <string>(); var rulesMetadata = new SortedList <string, (string path, SortedList <string, (DiagnosticDescriptor rule, string typeName, string[] languages)> rules)>(); foreach (string assembly in assemblyList) { var assemblyName = Path.GetFileNameWithoutExtension(assembly); string path = Path.Combine(binDirectory, assemblyName, configuration, tfm, assembly); if (!File.Exists(path)) { Console.Error.WriteLine($"'{path}' does not exist"); return(1); } var analyzerFileReference = new AnalyzerFileReference(path, AnalyzerAssemblyLoader.Instance); var analyzers = analyzerFileReference.GetAnalyzersForAllLanguages(); var rulesById = new SortedList <string, DiagnosticDescriptor>(); var assemblyRulesMetadata = (path : path, rules : new SortedList <string, (DiagnosticDescriptor rule, string typeName, string[] languages)>()); foreach (var analyzer in analyzers) { var analyzerType = analyzer.GetType(); foreach (var rule in analyzer.SupportedDiagnostics) { rulesById[rule.Id] = rule; allRulesById[rule.Id] = rule; categories.Add(rule.Category); assemblyRulesMetadata.rules[rule.Id] = (rule, analyzerType.Name, analyzerType.GetCustomAttribute <DiagnosticAnalyzerAttribute>(true)?.Languages); } } allRulesByAssembly.Add(assemblyName, rulesById); rulesMetadata.Add(assemblyName, assemblyRulesMetadata); foreach (var id in analyzerFileReference.GetFixers().SelectMany(fixer => fixer.FixableDiagnosticIds)) { fixableDiagnosticIds.Add(id); } } createRuleset( "AllRulesDefault.ruleset", "All Rules with default action", @"All Rules with default action. Rules with IsEnabledByDefault = false are disabled.", RulesetKind.AllDefault, categoryOpt: null); createRuleset( "AllRulesEnabled.ruleset", "All Rules Enabled with default action", "All Rules are enabled with default action. Rules with IsEnabledByDefault = false are force enabled with default action.", RulesetKind.AllEnabled, categoryOpt: null); createRuleset( "AllRulesDisabled.ruleset", "All Rules Disabled", @"All Rules are disabled.", RulesetKind.AllDisabled, categoryOpt: null); foreach (var category in categories) { createRuleset( $"{category}RulesDefault.ruleset", $"{category} Rules with default action", $@"All {category} Rules with default action. Rules with IsEnabledByDefault = false or from a different category are disabled.", RulesetKind.CategoryDefault, categoryOpt: category); createRuleset( $"{category}RulesEnabled.ruleset", $"{category} Rules Enabled with default action", $@"All {category} Rules are enabled with default action. {category} Rules with IsEnabledByDefault = false are force enabled with default action. Rules from a different category are disabled.", RulesetKind.CategoryEnabled, categoryOpt: category); } createPropsFile(); createAnalyzerDocumentationFile(); createAnalyzerSarifFile(); return(0); // Local functions. void createRuleset( string rulesetFileName, string rulesetName, string rulesetDescription, RulesetKind rulesetKind, string categoryOpt) { Debug.Assert((categoryOpt != null) == (rulesetKind == RulesetKind.CategoryDefault || rulesetKind == RulesetKind.CategoryEnabled)); var result = new StringBuilder(); startRuleset(); if (categoryOpt == null) { addRules(categoryPass: false); } else { result.AppendLine($@" <!-- {categoryOpt} Rules -->"); addRules(categoryPass: true); result.AppendLine(); result.AppendLine($@" <!-- Other Rules -->"); addRules(categoryPass: false); } endRuleset(); var directory = Directory.CreateDirectory(analyzerRulesetsDir); var rulesetFilePath = Path.Combine(directory.FullName, rulesetFileName); File.WriteAllText(rulesetFilePath, result.ToString()); return; void startRuleset() { result.AppendLine(@"<?xml version=""1.0""?>"); result.AppendLine($@"<RuleSet Name=""{rulesetName}"" Description=""{rulesetDescription}"" ToolsVersion=""15.0"">"); } void endRuleset() { result.AppendLine("</RuleSet>"); } void addRules(bool categoryPass) { foreach (var rulesByAssembly in allRulesByAssembly) { string assemblyName = rulesByAssembly.Key; SortedList <string, DiagnosticDescriptor> sortedRulesById = rulesByAssembly.Value; startRules(assemblyName); foreach (var rule in sortedRulesById) { addRule(rule.Value); } endRules(); } return; void startRules(string assemblyName) { result.AppendLine($@" <Rules AnalyzerId=""{assemblyName}"" RuleNamespace=""{assemblyName}"">"); } void endRules() { result.AppendLine(" </Rules>"); } void addRule(DiagnosticDescriptor rule) { if (shouldSkipRule(rule)) { return; } string ruleAction = getRuleAction(rule); var spacing = new string(' ', count : 15 - ruleAction.Length); result.AppendLine($@" <Rule Id=""{rule.Id}"" Action=""{ruleAction}"" /> {spacing} <!-- {rule.Title} -->"); } bool shouldSkipRule(DiagnosticDescriptor rule) { switch (rulesetKind) { case RulesetKind.CategoryDefault: case RulesetKind.CategoryEnabled: if (categoryPass) { return(rule.Category != categoryOpt); } else { return(rule.Category == categoryOpt); } default: return(false); } } string getRuleAction(DiagnosticDescriptor rule) { switch (rulesetKind) { case RulesetKind.CategoryDefault: return(getRuleActionCore(enable: categoryPass && rule.IsEnabledByDefault)); case RulesetKind.CategoryEnabled: return(getRuleActionCore(enable: categoryPass)); case RulesetKind.AllDefault: return(getRuleActionCore(enable: rule.IsEnabledByDefault)); case RulesetKind.AllEnabled: return(getRuleActionCore(enable: true)); case RulesetKind.AllDisabled: return(getRuleActionCore(enable: false)); default: throw new InvalidProgramException(); } string getRuleActionCore(bool enable) { if (enable) { return(rule.DefaultSeverity.ToString()); } else { return("None"); } } } } } void createPropsFile() { if (string.IsNullOrEmpty(propsFileDir) || string.IsNullOrEmpty(propsFileName)) { Debug.Assert(!containsPortedFxCopRules); return; } var fileContents = $@"<Project DefaultTargets=""Build"" xmlns=""http://schemas.microsoft.com/developer/msbuild/2003""> {getEditorConfigAsAdditionalFile()}{getCodeAnalysisTreatWarningsNotAsErrors()}{getRulesetOverrides()}{getFlowAnalysisFeatureFlag()} </Project>"; var directory = Directory.CreateDirectory(propsFileDir); var fileWithPath = Path.Combine(directory.FullName, propsFileName); File.WriteAllText(fileWithPath, fileContents); } string getFlowAnalysisFeatureFlag() { return(@" <PropertyGroup> <Features>$(Features);flow-analysis</Features> </PropertyGroup>"); } string getCodeAnalysisTreatWarningsNotAsErrors() { var allRuleIds = string.Join(';', allRulesByAssembly.Values.SelectMany(l => l.Keys).Distinct()); return($@" <!-- This property group prevents the rule ids implemented in this package to be bumped to errors when the 'CodeAnalysisTreatWarningsAsErrors' = 'false'. --> <PropertyGroup Condition=""'$(CodeAnalysisTreatWarningsAsErrors)' == 'false'""> <WarningsNotAsErrors>$(WarningsNotAsErrors);{allRuleIds}</WarningsNotAsErrors> </PropertyGroup>"); } string getRulesetOverrides() { if (containsPortedFxCopRules) { var rulesetOverridesBuilder = new StringBuilder(); foreach (var category in categories.OrderBy(k => k)) { // Each rule entry format is: -[Category]#[ID]; // For example, -Microsoft.Design#CA1001; var categoryPrefix = $" -Microsoft.{category}#"; var entries = allRulesByAssembly.Values .SelectMany(l => l) .Where(ruleIdAndDescriptor => ruleIdAndDescriptor.Value.Category == category && FxCopWellKnownDiagnosticTags.IsPortedFxCopRule(ruleIdAndDescriptor.Value)) .OrderBy(ruleIdAndDescriptor => ruleIdAndDescriptor.Key) .Select(ruleIdAndDescriptor => $"{categoryPrefix}{ruleIdAndDescriptor.Key};") .Distinct(); if (entries.Any()) { rulesetOverridesBuilder.AppendLine(); rulesetOverridesBuilder.Append(string.Join(Environment.NewLine, entries)); rulesetOverridesBuilder.AppendLine(); } } if (rulesetOverridesBuilder.Length > 0) { return($@" <!-- This property group contains the rules that have been implemented in this package and therefore should be disabled for the binary FxCop. The format is -[Category]#[ID], e.g., -Microsoft.Design#CA1001; --> <PropertyGroup> <CodeAnalysisRuleSetOverrides> $(CodeAnalysisRuleSetOverrides);{rulesetOverridesBuilder.ToString()} </CodeAnalysisRuleSetOverrides> </PropertyGroup>"); } } return(string.Empty); } string getEditorConfigAsAdditionalFile() { return($@" <!-- This item group adds any .editorconfig file present at the project root directory as an additional file. --> <ItemGroup Condition=""'$(SkipDefaultEditorConfigAsAdditionalFile)' != 'true' And Exists('$(MSBuildProjectDirectory)\.editorconfig')"" > <AdditionalFiles Include=""$(MSBuildProjectDirectory)\.editorconfig"" /> </ItemGroup> "); } void createAnalyzerDocumentationFile() { if (string.IsNullOrEmpty(analyzerDocumentationFileDir) || string.IsNullOrEmpty(analyzerDocumentationFileName) || allRulesById.Count == 0) { Debug.Assert(!containsPortedFxCopRules); return; } var directory = Directory.CreateDirectory(analyzerDocumentationFileDir); var fileWithPath = Path.Combine(directory.FullName, analyzerDocumentationFileName); var builder = new StringBuilder(); builder.Append(@" Sr. No. | Rule ID | Title | Category | Enabled | CodeFix | Description | --------|---------|-------|----------|---------|---------|--------------------------------------------------------------------------------------------------------------| "); var index = 1; foreach (var ruleById in allRulesById) { string ruleId = ruleById.Key; DiagnosticDescriptor descriptor = ruleById.Value; var ruleIdWithHyperLink = descriptor.Id; if (!string.IsNullOrWhiteSpace(descriptor.HelpLinkUri)) { ruleIdWithHyperLink = $"[{ruleIdWithHyperLink}]({descriptor.HelpLinkUri})"; } var hasCodeFix = fixableDiagnosticIds.Contains(descriptor.Id); var description = descriptor.Description.ToString(); if (string.IsNullOrWhiteSpace(description)) { description = descriptor.MessageFormat.ToString(); } builder.AppendLine($"{index} | {ruleIdWithHyperLink} | {descriptor.Title} | {descriptor.Category} | {descriptor.IsEnabledByDefault} | {hasCodeFix} | {description} |"); index++; } File.WriteAllText(fileWithPath, builder.ToString()); } // based on https://github.com/dotnet/roslyn/blob/master/src/Compilers/Core/Portable/CommandLine/ErrorLogger.cs void createAnalyzerSarifFile() { if (string.IsNullOrEmpty(analyzerSarifFileDir) || string.IsNullOrEmpty(analyzerSarifFileName) || allRulesById.Count == 0) { Debug.Assert(!containsPortedFxCopRules); return; } var culture = new CultureInfo("en-us"); var directory = Directory.CreateDirectory(analyzerSarifFileDir); var fileWithPath = Path.Combine(directory.FullName, analyzerSarifFileName); using (var textWriter = new StreamWriter(fileWithPath, false, Encoding.UTF8)) using (var writer = new Roslyn.Utilities.JsonWriter(textWriter)) { writer.WriteObjectStart(); // root writer.Write("$schema", "http://json.schemastore.org/sarif-1.0.0"); writer.Write("version", "1.0.0"); writer.WriteArrayStart("runs"); foreach (var assemblymetadata in rulesMetadata) { writer.WriteObjectStart(); // run writer.WriteObjectStart("tool"); writer.Write("name", assemblymetadata.Key); if (!string.IsNullOrWhiteSpace(analyzerVersion)) { writer.Write("version", analyzerVersion); } writer.Write("language", culture.Name); writer.WriteObjectEnd(); // tool writer.WriteObjectStart("rules"); // rules foreach (var rule in assemblymetadata.Value.rules) { var ruleId = rule.Key; var descriptor = rule.Value.rule; writer.WriteObjectStart(descriptor.Id); // rule writer.Write("id", descriptor.Id); writer.Write("shortDescription", descriptor.Title.ToString(culture)); string fullDescription = descriptor.Description.ToString(culture); writer.Write("fullDescription", !string.IsNullOrEmpty(fullDescription) ? fullDescription : descriptor.MessageFormat.ToString()); writer.Write("defaultLevel", getLevel(descriptor.DefaultSeverity)); if (!string.IsNullOrEmpty(descriptor.HelpLinkUri)) { writer.Write("helpUri", descriptor.HelpLinkUri); } writer.WriteObjectStart("properties"); writer.Write("category", descriptor.Category); writer.Write("isEnabledByDefault", descriptor.IsEnabledByDefault); writer.Write("typeName", rule.Value.typeName); if ((rule.Value.languages?.Length ?? 0) > 0) { writer.WriteArrayStart("languages"); foreach (var language in rule.Value.languages.OrderBy(l => l, StringComparer.InvariantCultureIgnoreCase)) { writer.Write(language); } writer.WriteArrayEnd(); // languages } if (descriptor.CustomTags.Any()) { writer.WriteArrayStart("tags"); foreach (string tag in descriptor.CustomTags) { writer.Write(tag); } writer.WriteArrayEnd(); // tags } writer.WriteObjectEnd(); // properties writer.WriteObjectEnd(); // rule } writer.WriteObjectEnd(); // rules writer.WriteObjectEnd(); // run } writer.WriteArrayEnd(); // runs writer.WriteObjectEnd(); // root return; string getLevel(DiagnosticSeverity severity) { switch (severity) { case DiagnosticSeverity.Info: return("note"); case DiagnosticSeverity.Error: return("error"); case DiagnosticSeverity.Warning: return("warning"); case DiagnosticSeverity.Hidden: default: // hidden diagnostics are not reported on the command line and therefore not currently given to // the error logger. We could represent it with a custom property in the SARIF log if that changes. Debug.Assert(false); goto case DiagnosticSeverity.Warning; } } } } }
public void TestMetadataParse() { AnalyzerFileReference reference = CreateAnalyzerFileReference( Assembly.GetExecutingAssembly().Location ); var analyzerTypeNameMap = reference.GetAnalyzerTypeNameMap(); Assert.Equal(2, analyzerTypeNameMap.Keys.Count()); Assert.Equal(6, analyzerTypeNameMap[LanguageNames.CSharp].Count); Assert.Contains( "Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+TestAnalyzerCS", analyzerTypeNameMap[LanguageNames.CSharp] ); Assert.Contains( "Microsoft.CodeAnalysis.UnitTests.TestAnalyzerCSVB", analyzerTypeNameMap[LanguageNames.CSharp] ); Assert.Contains( "Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+TestAnalyzer", analyzerTypeNameMap[LanguageNames.CSharp] ); Assert.Contains( "Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+SomeType+NestedAnalyzer", analyzerTypeNameMap[LanguageNames.CSharp] ); Assert.Contains( "Microsoft.CodeAnalysis.UnitTests.AbstractAnalyzer", analyzerTypeNameMap[LanguageNames.CSharp] ); Assert.Contains( "Microsoft.CodeAnalysis.UnitTests.OpenGenericAnalyzer`1", analyzerTypeNameMap[LanguageNames.CSharp] ); Assert.DoesNotContain( "Microsoft.CodeAnalysis.UnitTests.Test.NotAnAnalyzer", analyzerTypeNameMap[LanguageNames.CSharp] ); Assert.Equal(6, analyzerTypeNameMap[LanguageNames.VisualBasic].Count); Assert.Contains( "Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+TestAnalyzerVB", analyzerTypeNameMap[LanguageNames.VisualBasic] ); Assert.Contains( "Microsoft.CodeAnalysis.UnitTests.TestAnalyzerCSVB", analyzerTypeNameMap[LanguageNames.VisualBasic] ); Assert.Contains( "Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+TestAnalyzer", analyzerTypeNameMap[LanguageNames.VisualBasic] ); Assert.Contains( "Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+SomeType+NestedAnalyzer", analyzerTypeNameMap[LanguageNames.VisualBasic] ); Assert.Contains( "Microsoft.CodeAnalysis.UnitTests.AbstractAnalyzer", analyzerTypeNameMap[LanguageNames.VisualBasic] ); Assert.Contains( "Microsoft.CodeAnalysis.UnitTests.OpenGenericAnalyzer`1", analyzerTypeNameMap[LanguageNames.VisualBasic] ); Assert.DoesNotContain( "Microsoft.CodeAnalysis.UnitTests.Test.NotAnAnalyzer", analyzerTypeNameMap[LanguageNames.VisualBasic] ); }
static void Main(string[] args) { SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(@" using System; namespace RoslynCompileSample { public class Writer { public void Write(string message) { var prefix = GetMessagePrefix(); Console.WriteLine(prefix + ""-"" + message); } public string GetMessagePrefix() { return ""pre""; } } }"); string assemblyName = Path.GetRandomFileName(); MetadataReference[] references = new MetadataReference[] { MetadataReference.CreateFromFile(typeof(object).Assembly.Location), MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location) }; string analyzerAssemblyPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"..\..\..\lib\DotNetDoodle.Analyzers.dll"); ImmutableArray <DiagnosticAnalyzer> diagnosticAnalyzers = new AnalyzerFileReference(analyzerAssemblyPath).GetAnalyzers(LanguageNames.CSharp); CompilationWithAnalyzers compilationWithAnalyzers = CSharpCompilation.Create( assemblyName, syntaxTrees: new[] { syntaxTree }, references: references, options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)).WithAnalyzers(diagnosticAnalyzers); ImmutableArray <Diagnostic> diagsnostics = compilationWithAnalyzers.GetAnalyzerDiagnosticsAsync().Result; using (var ms = new MemoryStream()) { EmitResult result = compilationWithAnalyzers.Compilation.Emit(ms); ImmutableArray <Diagnostic> allDiagsnostics = result.Diagnostics.Concat(diagsnostics).ToImmutableArray(); if (!result.Success) { IEnumerable <Diagnostic> failures = allDiagsnostics.Where(diagnostic => diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error); foreach (Diagnostic diagnostic in failures) { Console.Error.WriteLine("ERROR: {0}: {1}", diagnostic.Id, diagnostic.GetMessage()); } WriteWarnings(allDiagsnostics); } else { WriteWarnings(allDiagsnostics); ms.Seek(0, SeekOrigin.Begin); Assembly assembly = Assembly.Load(ms.ToArray()); Type type = assembly.GetType("RoslynCompileSample.Writer"); object obj = Activator.CreateInstance(type); type.InvokeMember("Write", BindingFlags.Default | BindingFlags.InvokeMethod, null, obj, new object[] { "Hello World" }); } } Console.ReadLine(); }
public static int Main(string[] args) { const int expectedArguments = 13; if (args.Length != expectedArguments) { Console.Error.WriteLine($"Excepted {expectedArguments} arguments, found {args.Length}: {string.Join(';', args)}"); return(1); } string analyzerRulesetsDir = args[0]; string binDirectory = args[1]; string configuration = args[2]; string tfm = args[3]; var assemblyList = args[4].Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries).ToList(); string propsFileDir = args[5]; string propsFileName = args[6]; string analyzerDocumentationFileDir = args[7]; string analyzerDocumentationFileName = args[8]; string analyzerSarifFileDir = args[9]; string analyzerSarifFileName = args[10]; var analyzerVersion = args[11]; if (!bool.TryParse(args[12], out var containsPortedFxCopRules)) { containsPortedFxCopRules = false; } var allRulesByAssembly = new SortedList <string, SortedList <string, DiagnosticDescriptor> >(); var allRulesById = new SortedList <string, DiagnosticDescriptor>(); var fixableDiagnosticIds = new HashSet <string>(); var categories = new HashSet <string>(); var rulesMetadata = new SortedList <string, (string path, SortedList <string, (DiagnosticDescriptor rule, string typeName, string[] languages)> rules)>(); foreach (string assembly in assemblyList) { var assemblyName = Path.GetFileNameWithoutExtension(assembly); string path = Path.Combine(binDirectory, assemblyName, configuration, tfm, assembly); if (!File.Exists(path)) { Console.Error.WriteLine($"'{path}' does not exist"); return(1); } var analyzerFileReference = new AnalyzerFileReference(path, AnalyzerAssemblyLoader.Instance); var analyzers = analyzerFileReference.GetAnalyzersForAllLanguages(); var rulesById = new SortedList <string, DiagnosticDescriptor>(); var assemblyRulesMetadata = (path, rules : new SortedList <string, (DiagnosticDescriptor rule, string typeName, string[] languages)>()); foreach (var analyzer in analyzers) { var analyzerType = analyzer.GetType(); foreach (var rule in analyzer.SupportedDiagnostics) { rulesById[rule.Id] = rule; allRulesById[rule.Id] = rule; categories.Add(rule.Category); assemblyRulesMetadata.rules[rule.Id] = (rule, analyzerType.Name, analyzerType.GetCustomAttribute <DiagnosticAnalyzerAttribute>(true)?.Languages); } } allRulesByAssembly.Add(assemblyName, rulesById); rulesMetadata.Add(assemblyName, assemblyRulesMetadata); foreach (var id in analyzerFileReference.GetFixers().SelectMany(fixer => fixer.FixableDiagnosticIds)) { fixableDiagnosticIds.Add(id); } } createRuleset( "AllRulesDefault.ruleset", "All Rules with default action", @"All Rules with default action. Rules with IsEnabledByDefault = false are disabled.", RulesetKind.AllDefault); createRuleset( "AllRulesEnabled.ruleset", "All Rules Enabled with default action", "All Rules are enabled with default action. Rules with IsEnabledByDefault = false are force enabled with default action.", RulesetKind.AllEnabled); createRuleset( "AllRulesDisabled.ruleset", "All Rules Disabled", @"All Rules are disabled.", RulesetKind.AllDisabled); foreach (var category in categories) { createRuleset( $"{category}RulesDefault.ruleset", $"{category} Rules with default action", $@"All {category} Rules with default action. Rules with IsEnabledByDefault = false or from a different category are disabled.", RulesetKind.CategoryDefault, categoryOpt: category); createRuleset( $"{category}RulesEnabled.ruleset", $"{category} Rules Enabled with default action", $@"All {category} Rules are enabled with default action. {category} Rules with IsEnabledByDefault = false are force enabled with default action. Rules from a different category are disabled.", RulesetKind.CategoryEnabled, categoryOpt: category); } // We generate custom tag based rulesets only for select custom tags. var customTagsToGenerateRulesets = ImmutableArray.Create( WellKnownDiagnosticTagsExtensions.Dataflow, FxCopWellKnownDiagnosticTags.PortedFromFxCop); foreach (var customTag in customTagsToGenerateRulesets) { createRuleset( $"{customTag}RulesDefault.ruleset", $"{customTag} Rules with default action", $@"All {customTag} Rules with default action. Rules with IsEnabledByDefault = false and non-{customTag} rules are disabled.", RulesetKind.CustomTagDefault, customTagOpt: customTag); createRuleset( $"{customTag}RulesEnabled.ruleset", $"{customTag} Rules Enabled with default action", $@"All {customTag} Rules are enabled with default action. {customTag} Rules with IsEnabledByDefault = false are force enabled with default action. Non-{customTag} Rules are disabled.", RulesetKind.CustomTagEnabled, customTagOpt: customTag); } createPropsFile(); createAnalyzerDocumentationFile(); createAnalyzerSarifFile(); return(0); // Local functions. void createRuleset( string rulesetFileName, string rulesetName, string rulesetDescription, RulesetKind rulesetKind, string categoryOpt = null, string customTagOpt = null) { Debug.Assert(categoryOpt == null || customTagOpt == null); Debug.Assert((categoryOpt != null) == (rulesetKind == RulesetKind.CategoryDefault || rulesetKind == RulesetKind.CategoryEnabled)); Debug.Assert((customTagOpt != null) == (rulesetKind == RulesetKind.CustomTagDefault || rulesetKind == RulesetKind.CustomTagEnabled)); var result = new StringBuilder(); startRuleset(); if (categoryOpt == null && customTagOpt == null) { addRules(categoryPass: false, customTagPass: false); } else { result.AppendLine($@" <!-- {categoryOpt ?? customTagOpt} Rules -->"); addRules(categoryPass: categoryOpt != null, customTagPass: customTagOpt != null); result.AppendLine(); result.AppendLine($@" <!-- Other Rules -->"); addRules(categoryPass: false, customTagPass: false); } endRuleset(); var directory = Directory.CreateDirectory(analyzerRulesetsDir); var rulesetFilePath = Path.Combine(directory.FullName, rulesetFileName); File.WriteAllText(rulesetFilePath, result.ToString()); return; void startRuleset() { result.AppendLine(@"<?xml version=""1.0""?>"); result.AppendLine($@"<RuleSet Name=""{rulesetName}"" Description=""{rulesetDescription}"" ToolsVersion=""15.0"">"); } void endRuleset() { result.AppendLine("</RuleSet>"); } void addRules(bool categoryPass, bool customTagPass) { foreach (var rulesByAssembly in allRulesByAssembly) { string assemblyName = rulesByAssembly.Key; SortedList <string, DiagnosticDescriptor> sortedRulesById = rulesByAssembly.Value; if (!sortedRulesById.Any(r => !shouldSkipRule(r.Value))) { // Bail out if we don't have any rule to be added for this assembly. continue; } startRules(assemblyName); foreach (var rule in sortedRulesById) { addRule(rule.Value); } endRules(); } return; void startRules(string assemblyName) { result.AppendLine($@" <Rules AnalyzerId=""{assemblyName}"" RuleNamespace=""{assemblyName}"">"); } void endRules() { result.AppendLine(" </Rules>"); } void addRule(DiagnosticDescriptor rule) { if (shouldSkipRule(rule)) { return; } string ruleAction = getRuleAction(rule); var spacing = new string(' ', count : 15 - ruleAction.Length); result.AppendLine($@" <Rule Id=""{rule.Id}"" Action=""{ruleAction}"" /> {spacing} <!-- {rule.Title} -->"); } bool shouldSkipRule(DiagnosticDescriptor rule) { switch (rulesetKind) { case RulesetKind.CategoryDefault: case RulesetKind.CategoryEnabled: if (categoryPass) { return(rule.Category != categoryOpt); } else { return(rule.Category == categoryOpt); } case RulesetKind.CustomTagDefault: case RulesetKind.CustomTagEnabled: if (customTagPass) { return(!rule.CustomTags.Contains(customTagOpt)); } else { return(rule.CustomTags.Contains(customTagOpt)); } default: return(false); } } string getRuleAction(DiagnosticDescriptor rule) { switch (rulesetKind) { case RulesetKind.CategoryDefault: return(getRuleActionCore(enable: categoryPass && rule.IsEnabledByDefault)); case RulesetKind.CategoryEnabled: return(getRuleActionCore(enable: categoryPass)); case RulesetKind.CustomTagDefault: return(getRuleActionCore(enable: customTagPass && rule.IsEnabledByDefault)); case RulesetKind.CustomTagEnabled: return(getRuleActionCore(enable: customTagPass)); case RulesetKind.AllDefault: return(getRuleActionCore(enable: rule.IsEnabledByDefault)); case RulesetKind.AllEnabled: return(getRuleActionCore(enable: true)); case RulesetKind.AllDisabled: return(getRuleActionCore(enable: false)); default: throw new InvalidProgramException(); } string getRuleActionCore(bool enable) { if (enable) { return(rule.DefaultSeverity.ToString()); } else { return("None"); } } } } } void createPropsFile() { if (string.IsNullOrEmpty(propsFileDir) || string.IsNullOrEmpty(propsFileName)) { Debug.Assert(!containsPortedFxCopRules); return; } var fileContents = $@"<Project DefaultTargets=""Build"" xmlns=""http://schemas.microsoft.com/developer/msbuild/2003""> {getEditorConfigAsAdditionalFile()}{getCodeAnalysisTreatWarningsNotAsErrors()}{getRulesetOverrides()}{getFlowAnalysisFeatureFlag()} </Project>"; var directory = Directory.CreateDirectory(propsFileDir); var fileWithPath = Path.Combine(directory.FullName, propsFileName); File.WriteAllText(fileWithPath, fileContents); }
public void TestGeneratorsCantTargetNetFramework() { var directory = Temp.CreateDirectory(); // core var errors = buildAndLoadGeneratorAndReturnAnyErrors(".NETCoreApp,Version=v5.0"); Assert.Empty(errors); // netstandard errors = buildAndLoadGeneratorAndReturnAnyErrors(".NETStandard,Version=v2.0"); Assert.Empty(errors); // no target errors = buildAndLoadGeneratorAndReturnAnyErrors(targetFramework: null); Assert.Empty(errors); // framework errors = buildAndLoadGeneratorAndReturnAnyErrors(".NETFramework,Version=v4.7.2"); Assert.Equal(1, errors.Count); Assert.Equal(AnalyzerLoadFailureEventArgs.FailureErrorCode.ReferencesFramework, errors.First().ErrorCode); List <AnalyzerLoadFailureEventArgs> buildAndLoadGeneratorAndReturnAnyErrors(string?targetFramework) { string targetFrameworkAttributeText = targetFramework is object ?$"[assembly: System.Runtime.Versioning.TargetFramework(\"{targetFramework}\")]" : string.Empty; string generatorSource = $@" using Microsoft.CodeAnalysis; {targetFrameworkAttributeText} [Generator] public class Generator : ISourceGenerator {{ public void Execute(GeneratorExecutionContext context) {{ }} public void Initialize(GeneratorInitializationContext context) {{ }} }}"; var directory = Temp.CreateDirectory(); var generatorPath = Path.Combine(directory.Path, "generator.dll"); var compilation = CSharpCompilation.Create($"generator_{targetFramework}", new[] { CSharpSyntaxTree.ParseText(generatorSource) }, TargetFrameworkUtil.GetReferences(TargetFramework.Standard, new[] { MetadataReference.CreateFromAssemblyInternal(typeof(ISourceGenerator).Assembly) }), new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); compilation.VerifyDiagnostics(); var result = compilation.Emit(generatorPath); Assert.True(result.Success); AnalyzerFileReference reference = CreateAnalyzerFileReference(generatorPath); List <AnalyzerLoadFailureEventArgs> errors = new List <AnalyzerLoadFailureEventArgs>(); void errorHandler(object?o, AnalyzerLoadFailureEventArgs e) => errors.Add(e); reference.AnalyzerLoadFailed += errorHandler; var builder = ImmutableArray.CreateBuilder <ISourceGenerator>(); reference.AddGenerators(builder, LanguageNames.CSharp); Assert.Single(builder); reference.AnalyzerLoadFailed -= errorHandler; return(errors); } }
/// <summary> /// analyzers can only use dependencies that are already in this project /// dependency versions must match those of project dependencies /// </summary> /// <param name="diagnostics"></param> ImmutableArray <DiagnosticAnalyzer> AnalyzersInner(CompileOptions options, List <Diagnostic> diagnostics) { // if Location.None is used instead, unity doesnt print the error to console. var defaultPos = Location.Create( "/Analyzers", TextSpan.FromBounds(0, 0), new LinePositionSpan() ); try { if (PlatformHelper.CurrentPlatform == Platform.Mac) { return(ImmutableArray <DiagnosticAnalyzer> .Empty); } if (!Directory.Exists(analyzersPath)) { Directory.CreateDirectory(analyzersPath); File.WriteAllText( analyzersPath + "/readme.txt", "Add Roslyn Analyzers here\r\nAdd analyzer dependencies in sub-folders" ); return(ImmutableArray <DiagnosticAnalyzer> .Empty); } var loader = new AL(); var additionalPath = CompileAnalyzers(options); var analyzerNames = Directory.GetFiles(analyzersPath).Concat(Directory.GetFiles(additionalPath)) .Where(x => x.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)) .ToArray(); var analyzerDependencies = Directory.GetDirectories(analyzersPath).SelectMany(Directory.GetFiles).ToArray(); foreach (var analyzerDependency in analyzerDependencies) { _logger.Info("Analyzer dependency: " + analyzerDependency); loader.LoadFromPath(analyzerDependency); } _logger.Info("Analyzers:"); var analyzers = analyzerNames .Select(dll => { _logger.Info("Analyzer dll: " + dll); var _ref = new AnalyzerFileReference(dll, loader); _ref.AnalyzerLoadFailed += (_, e) => { _logger.Error("failed to load analyzer: " + e.TypeName + "; " + e.Message); diagnostics.Add(Diagnostic.Create(new DiagnosticDescriptor( "A01", "Error", "Compiler couldn't load provided code analyzer: " + e.TypeName + ". Please fix or remove from /Analyzers directory. More info in compiler log.", "Error", DiagnosticSeverity.Error, true ), defaultPos)); }; return(_ref.GetAnalyzers(LanguageNames.CSharp)); }) .Aggregate(new List <DiagnosticAnalyzer>(), (list, a) => { a.ForEach(_logger.Info); list.AddRange(a); return(list); }) .ToImmutableArray(); return(analyzers); } catch (Exception e) { _logger.Error(e); diagnostics.Add(Diagnostic.Create(new DiagnosticDescriptor( "A02", "Warning", "Exception was thrown when loading analyzers: " + e.Message, "Warning", DiagnosticSeverity.Warning, true ), defaultPos)); return(ImmutableArray <DiagnosticAnalyzer> .Empty); } }
public void AssemblyLoading() { StringBuilder sb = new StringBuilder(); var directory = Temp.CreateDirectory(); EventHandler <InMemoryAssemblyProvider.AssemblyLoadEventArgs> handler = (e, args) => { var relativePath = args.Path.Substring(directory.Path.Length); sb.AppendFormat("Assembly {0} loaded from {1}", args.LoadedAssembly.FullName, relativePath); sb.AppendLine(); }; InMemoryAssemblyProvider.AssemblyLoad += handler; var alphaDll = directory.CreateFile("Alpha.dll").WriteAllBytes(TestResources.AssemblyLoadTests.AssemblyLoadTests.Alpha); var betaDll = directory.CreateFile("Beta.dll").WriteAllBytes(TestResources.AssemblyLoadTests.AssemblyLoadTests.Beta); var gammaDll = directory.CreateFile("Gamma.dll").WriteAllBytes(TestResources.AssemblyLoadTests.AssemblyLoadTests.Gamma); var deltaDll = directory.CreateFile("Delta.dll").WriteAllBytes(TestResources.AssemblyLoadTests.AssemblyLoadTests.Delta); AnalyzerFileReference alphaReference = CreateAnalyzerFileReference(alphaDll.Path); Assembly alpha = alphaReference.GetAssembly(); File.Delete(alphaDll.Path); var a = alpha.CreateInstance("Alpha.A"); a.GetType().GetMethod("Write").Invoke(a, new object[] { sb, "Test A" }); File.Delete(gammaDll.Path); File.Delete(deltaDll.Path); AnalyzerFileReference betaReference = CreateAnalyzerFileReference(betaDll.Path); Assembly beta = betaReference.GetAssembly(); var b = beta.CreateInstance("Beta.B"); b.GetType().GetMethod("Write").Invoke(b, new object[] { sb, "Test B" }); var expected = @"Assembly Alpha, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null loaded from \Alpha.dll Assembly Gamma, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null loaded from \Gamma.dll Assembly Delta, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null loaded from \Delta.dll Delta: Gamma: Alpha: Test A Assembly Beta, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null loaded from \Beta.dll Delta: Gamma: Beta: Test B "; var actual = sb.ToString(); Assert.Equal(expected, actual); var alphaDllRequestor = InMemoryAssemblyProvider.TryGetRequestingAssembly(alphaDll.Path); var betaDllRequestor = InMemoryAssemblyProvider.TryGetRequestingAssembly(betaDll.Path); var gammaDllRequestor = InMemoryAssemblyProvider.TryGetRequestingAssembly(gammaDll.Path); var deltaDllRequestor = InMemoryAssemblyProvider.TryGetRequestingAssembly(deltaDll.Path); Assert.Null(alphaDllRequestor); Assert.Null(betaDllRequestor); Assert.Equal(expected: alphaDll.Path, actual: gammaDllRequestor, comparer: StringComparer.OrdinalIgnoreCase); Assert.Equal(expected: gammaDll.Path, actual: deltaDllRequestor, comparer: StringComparer.OrdinalIgnoreCase); InMemoryAssemblyProvider.AssemblyLoad -= handler; }
public static int Main(string[] args) { const int expectedArguments = 16; if (args.Length != expectedArguments) { Console.Error.WriteLine($"Excepted {expectedArguments} arguments, found {args.Length}: {string.Join(';', args)}"); return(1); } string analyzerRulesetsDir = args[0]; string analyzerEditorconfigsDir = args[1]; string binDirectory = args[2]; string configuration = args[3]; string tfm = args[4]; var assemblyList = args[5].Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries).ToList(); string propsFileDir = args[6]; string propsFileName = args[7]; string analyzerDocumentationFileDir = args[8]; string analyzerDocumentationFileName = args[9]; string analyzerSarifFileDir = args[10]; string analyzerSarifFileName = args[11]; var analyzerVersion = args[12]; var analyzerPackageName = args[13]; if (!bool.TryParse(args[14], out var containsPortedFxCopRules)) { containsPortedFxCopRules = false; } if (!bool.TryParse(args[15], out var generateAnalyzerRulesMissingDocumentationFile)) { generateAnalyzerRulesMissingDocumentationFile = false; } var allRulesById = new SortedList <string, DiagnosticDescriptor>(); var fixableDiagnosticIds = new HashSet <string>(); var categories = new HashSet <string>(); var rulesMetadata = new SortedList <string, (string path, SortedList <string, (DiagnosticDescriptor rule, string typeName, string[]? languages)> rules)>(); foreach (string assembly in assemblyList) { var assemblyName = Path.GetFileNameWithoutExtension(assembly); string path = Path.Combine(binDirectory, assemblyName, configuration, tfm, assembly); if (!File.Exists(path)) { Console.Error.WriteLine($"'{path}' does not exist"); return(1); } var analyzerFileReference = new AnalyzerFileReference(path, AnalyzerAssemblyLoader.Instance); analyzerFileReference.AnalyzerLoadFailed += AnalyzerFileReference_AnalyzerLoadFailed; var analyzers = analyzerFileReference.GetAnalyzersForAllLanguages(); var assemblyRulesMetadata = (path, rules : new SortedList <string, (DiagnosticDescriptor rule, string typeName, string[]? languages)>()); foreach (var analyzer in analyzers) { var analyzerType = analyzer.GetType(); foreach (var rule in analyzer.SupportedDiagnostics) { allRulesById[rule.Id] = rule; categories.Add(rule.Category); assemblyRulesMetadata.rules[rule.Id] = (rule, analyzerType.Name, analyzerType.GetCustomAttribute <DiagnosticAnalyzerAttribute>(true)?.Languages); } } rulesMetadata.Add(assemblyName, assemblyRulesMetadata); foreach (var id in analyzerFileReference.GetFixers().SelectMany(fixer => fixer.FixableDiagnosticIds)) { fixableDiagnosticIds.Add(id); } } createRulesetAndEditorconfig( "AllRulesDefault", "All Rules with default severity", @"All Rules with default severity. Rules with IsEnabledByDefault = false are disabled.", RulesetKind.AllDefault); createRulesetAndEditorconfig( "AllRulesEnabled", "All Rules Enabled with default severity", "All Rules are enabled with default severity. Rules with IsEnabledByDefault = false are force enabled with default severity.", RulesetKind.AllEnabled); createRulesetAndEditorconfig( "AllRulesDisabled", "All Rules Disabled", @"All Rules are disabled.", RulesetKind.AllDisabled); foreach (var category in categories) { createRulesetAndEditorconfig( $"{category}RulesDefault", $"{category} Rules with default severity", $@"All {category} Rules with default severity. Rules with IsEnabledByDefault = false or from a different category are disabled.", RulesetKind.CategoryDefault, categoryOpt: category); createRulesetAndEditorconfig( $"{category}RulesEnabled", $"{category} Rules Enabled with default severity", $@"All {category} Rules are enabled with default severity. {category} Rules with IsEnabledByDefault = false are force enabled with default severity. Rules from a different category are disabled.", RulesetKind.CategoryEnabled, categoryOpt: category); } // We generate custom tag based rulesets only for select custom tags. var customTagsToGenerateRulesets = ImmutableArray.Create( WellKnownDiagnosticTagsExtensions.Dataflow, FxCopWellKnownDiagnosticTags.PortedFromFxCop); foreach (var customTag in customTagsToGenerateRulesets) { createRulesetAndEditorconfig( $"{customTag}RulesDefault", $"{customTag} Rules with default severity", $@"All {customTag} Rules with default severity. Rules with IsEnabledByDefault = false and non-{customTag} rules are disabled.", RulesetKind.CustomTagDefault, customTagOpt: customTag); createRulesetAndEditorconfig( $"{customTag}RulesEnabled", $"{customTag} Rules Enabled with default severity", $@"All {customTag} Rules are enabled with default severity. {customTag} Rules with IsEnabledByDefault = false are force enabled with default severity. Non-{customTag} Rules are disabled.", RulesetKind.CustomTagEnabled, customTagOpt: customTag); } createPropsFile(); createAnalyzerDocumentationFile(); createAnalyzerSarifFile(); if (generateAnalyzerRulesMissingDocumentationFile) { createAnalyzerRulesMissingDocumentationFile(); } return(0);
public static int Main(string[] args) { const int expectedArguments = 9; if (args.Length != expectedArguments) { Console.Error.WriteLine($"Expected {expectedArguments} arguments, found {args.Length}: {string.Join(';', args)}"); return(1); } var outputDir = args[0]; var packageName = args[1]; string targetsFileDir = args[2]; string targetsFileName = args[3]; var assemblyList = args[4].Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries).ToList(); var binDirectory = args[5]; var configuration = args[6]; var tfm = args[7]; var releaseTrackingOptOutString = args[8]; if (!bool.TryParse(releaseTrackingOptOutString, out bool releaseTrackingOptOut)) { releaseTrackingOptOut = false; } using var shippedFilesDataBuilder = ArrayBuilder <ReleaseTrackingData> .GetInstance(); using var versionsBuilder = PooledHashSet <Version> .GetInstance(); // Validate all assemblies exist on disk and can be loaded. foreach (string assembly in assemblyList) { var assemblyPath = GetAssemblyPath(assembly); if (!File.Exists(assemblyPath)) { Console.Error.WriteLine($"'{assemblyPath}' does not exist"); return(2); } try { _ = Assembly.LoadFrom(assemblyPath); } #pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) #pragma warning restore CA1031 // Do not catch general exception types { Console.Error.WriteLine(ex.Message); return(3); } } // Compute descriptors by rule ID and shipped analyzer release versions and shipped data. var allRulesById = new SortedList <string, DiagnosticDescriptor>(); var hasInfoOrHiddenDiagnostic = false; var sawShippedFile = false; foreach (string assembly in assemblyList) { var assemblyPath = GetAssemblyPath(assembly); var analyzerFileReference = new AnalyzerFileReference(assemblyPath, AnalyzerAssemblyLoader.Instance); analyzerFileReference.AnalyzerLoadFailed += AnalyzerFileReference_AnalyzerLoadFailed; var analyzers = analyzerFileReference.GetAnalyzersForAllLanguages(); foreach (var analyzer in analyzers) { foreach (var rule in analyzer.SupportedDiagnostics) { allRulesById[rule.Id] = rule; hasInfoOrHiddenDiagnostic = hasInfoOrHiddenDiagnostic || rule.IsEnabledByDefault && (rule.DefaultSeverity == DiagnosticSeverity.Info || rule.DefaultSeverity == DiagnosticSeverity.Hidden); } } var assemblyDir = Path.GetDirectoryName(assemblyPath); var assemblyName = Path.GetFileNameWithoutExtension(assembly); var shippedFile = Path.Combine(assemblyDir, "AnalyzerReleases", assemblyName, ReleaseTrackingHelper.ShippedFileName); if (File.Exists(shippedFile)) { sawShippedFile = true; if (releaseTrackingOptOut) { Console.Error.WriteLine($"'{shippedFile}' exists but was not expected"); return(4); } try { using var fileStream = File.OpenRead(shippedFile); var sourceText = SourceText.From(fileStream); var releaseTrackingData = ReleaseTrackingHelper.ReadReleaseTrackingData(shippedFile, sourceText, onDuplicateEntryInRelease: (_1, _2, _3, _4, line) => throw new Exception($"Duplicate entry in {shippedFile} at {line.LineNumber}: '{line}'"), onInvalidEntry: (line, _2, _3, _4) => throw new Exception($"Invalid entry in {shippedFile} at {line.LineNumber}: '{line}'"), isShippedFile: true); shippedFilesDataBuilder.Add(releaseTrackingData); versionsBuilder.AddRange(releaseTrackingData.Versions); } #pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) #pragma warning restore CA1031 // Do not catch general exception types { Console.Error.WriteLine(ex.Message); return(5); } } } if (!releaseTrackingOptOut && !sawShippedFile) { Console.Error.WriteLine($"Could not find any 'AnalyzerReleases.Shipped.md' file"); return(6); } // Bail out if following conditions hold for the analyzer package: // 1. No Info/Hidden diagnostic in the package: No need for have different global analyzer config for build and live analysis. // 2. No shipped releases: User cannot choose a version specific global analyzer config. if (!hasInfoOrHiddenDiagnostic && versionsBuilder.Count == 0) { return(0); } var shippedFilesData = shippedFilesDataBuilder.ToImmutable(); // Generate build and live analysis global analyzer config files for latest/unshipped version. CreateGlobalAnalyzerConfig( "BuildRules", "All build rules with default severity", "All build rules (warnings/errors) with default severity. Rules with IsEnabledByDefault = false or default severity Suggestion/Hidden are disabled.", EditorConfigKind.CommandLine); CreateGlobalAnalyzerConfig( "LiveAnalysisRules", "All rules with default severity", "All rules are enabled with default severity. Rules with IsEnabledByDefault = false are disabled.", EditorConfigKind.LiveAnalysis); // Generate build and live analysis global analyzer config files for each shipped version. foreach (var version in versionsBuilder) { var versionString = version.ToString().Replace(".", "_", StringComparison.Ordinal); CreateGlobalAnalyzerConfig( $"BuildRulesVersion{versionString}", $"All '{version}' build rules with default severity", $"All '{version}' build rules (warnings/errors) with default severity. Rules with IsEnabledByDefault = false or first released in a version later then {version} or default severity Suggestion/Hidden are disabled.", EditorConfigKind.CommandLine, (shippedFilesData, version)); CreateGlobalAnalyzerConfig( $"LiveAnalysisRules{versionString}", $"All '{version}' rules with default severity", $"All '{version}' rules are enabled with default severity. Rules with IsEnabledByDefault = false or first released in a version later then {version} are disabled.", EditorConfigKind.LiveAnalysis, (shippedFilesData, version)); } CreateTargetsFile(targetsFileDir, targetsFileName, packageName); return(0);
private static bool IsAnalyzerReferenceWithShadowCopyLoader(AnalyzerFileReference reference) => reference.AssemblyLoader is ShadowCopyAnalyzerAssemblyLoader;
protected abstract string GetAnalyzerAssemblyPath(AnalyzerFileReference reference);
internal static ImmutableArray <DiagnosticAnalyzer> GetOrCreateAnalyzersFromFile(AnalyzerFileReference analyzerReference, string langauge = null) { string fullPath = analyzerReference.FullPath; Debug.Assert(PathUtilities.IsAbsolute(fullPath)); lock (Guard) { // may throw: FileKey key = FileKey.Create(fullPath); CachedAnalyzers cachedAnalyzers; if (analyzersFromFiles.TryGetValue(key, out cachedAnalyzers)) { if (cachedAnalyzers.Analyzers.IsAlive && cachedAnalyzers.Language == langauge) { return((ImmutableArray <DiagnosticAnalyzer>)cachedAnalyzers.Analyzers.Target); } else { analyzersFromFiles.Remove(key); var removed = analyzerAssemblyKeys.Remove(key); Debug.Assert(removed); Debug.Assert(!analyzerAssemblyKeys.Contains(key)); } } // get all analyzers in the assembly: var builder = ImmutableArray.CreateBuilder <DiagnosticAnalyzer>(); analyzerReference.AddAnalyzers(builder, langauge); var analyzers = builder.ToImmutable(); // refresh the timestamp (the file may have changed just before we memory-mapped it): key = FileKey.Create(fullPath); analyzersFromFiles[key] = new CachedAnalyzers(analyzers, langauge); Debug.Assert(!analyzerAssemblyKeys.Contains(key)); analyzerAssemblyKeys.Add(key); EnableCompactTimer(); return(analyzers); } }
private void UpdateProject(ProjectFileInfo projectFileInfo) { var project = _workspace.CurrentSolution.GetProject(projectFileInfo.WorkspaceId); var unusedDocuments = project.Documents.ToDictionary(d => d.FilePath, d => d.Id); foreach (var file in projectFileInfo.SourceFiles) { if (unusedDocuments.Remove(file)) { continue; } using (var stream = File.OpenRead(file)) { var sourceText = SourceText.From(stream, encoding: Encoding.UTF8); var id = DocumentId.CreateNewId(projectFileInfo.WorkspaceId); var version = VersionStamp.Create(); var loader = TextLoader.From(TextAndVersion.Create(sourceText, version)); _workspace.AddDocument(DocumentInfo.Create(id, file, filePath: file, loader: loader)); } } foreach (var unused in unusedDocuments) { _workspace.RemoveDocument(unused.Value); } var unusedProjectReferences = new HashSet <ProjectReference>(project.ProjectReferences); foreach (var projectReferencePath in projectFileInfo.ProjectReferences) { ProjectFileInfo projectReferenceInfo; if (_context.Projects.TryGetValue(projectReferencePath, out projectReferenceInfo)) { var reference = new ProjectReference(projectReferenceInfo.WorkspaceId); if (unusedProjectReferences.Remove(reference)) { // This reference already exists continue; } _workspace.AddProjectReference(project.Id, reference); } else { _logger.WriteWarning(string.Format("Unable to resolve project reference '{0}' for '{1}'.", projectReferencePath, projectFileInfo.ProjectFilePath)); } } foreach (var unused in unusedProjectReferences) { _workspace.RemoveProjectReference(project.Id, unused); } var unusedAnalyzers = new Dictionary <string, AnalyzerReference>(project.AnalyzerReferences.ToDictionary(a => a.FullPath)); foreach (var analyzerPath in projectFileInfo.Analyzers) { if (!File.Exists(analyzerPath)) { _logger.WriteWarning(string.Format("Unable to resolve assembly '{0}'", analyzerPath)); } else { if (unusedAnalyzers.Remove(analyzerPath)) { continue; } #if ASPNET50 var analyzerReference = new AnalyzerFileReference(analyzerPath); project.AddAnalyzerReference(analyzerReference); #endif } } foreach (var analyzerReference in unusedAnalyzers.Values) { project.RemoveAnalyzerReference(analyzerReference); } var unusedReferences = new HashSet <MetadataReference>(project.MetadataReferences); foreach (var referencePath in projectFileInfo.References) { if (!File.Exists(referencePath)) { _logger.WriteWarning(string.Format("Unable to resolve assembly '{0}'", referencePath)); } else { var metadataReference = _metadataReferenceCache.GetMetadataReference(referencePath); if (unusedReferences.Remove(metadataReference)) { continue; } _logger.WriteVerbose(string.Format("Adding reference '{0}' to '{1}'.", referencePath, projectFileInfo.ProjectFilePath)); _workspace.AddMetadataReference(project.Id, metadataReference); } } foreach (var reference in unusedReferences) { _workspace.RemoveMetadataReference(project.Id, reference); } }
public void TestLoadedAnalyzerOrderIsDeterministic() { AnalyzerFileReference reference = CreateAnalyzerFileReference( Assembly.GetExecutingAssembly().Location ); var csharpAnalyzers = reference .GetAnalyzers(LanguageNames.CSharp) .Select(a => a.GetType().FullName) .ToArray(); Assert.Equal(4, csharpAnalyzers.Length); Assert.Equal( "Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+SomeType+NestedAnalyzer", csharpAnalyzers[0] ); Assert.Equal( "Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+TestAnalyzer", csharpAnalyzers[1] ); Assert.Equal( "Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+TestAnalyzerCS", csharpAnalyzers[2] ); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.TestAnalyzerCSVB", csharpAnalyzers[3]); var vbAnalyzers = reference .GetAnalyzers(LanguageNames.VisualBasic) .Select(a => a.GetType().FullName) .ToArray(); Assert.Equal(4, vbAnalyzers.Length); Assert.Equal( "Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+SomeType+NestedAnalyzer", vbAnalyzers[0] ); Assert.Equal( "Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+TestAnalyzer", vbAnalyzers[1] ); Assert.Equal( "Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+TestAnalyzerVB", vbAnalyzers[2] ); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.TestAnalyzerCSVB", vbAnalyzers[3]); // analyzers return C#, then VB, including duplicates var allAnalyzers = reference .GetAnalyzersForAllLanguages() .Select(a => a.GetType().FullName) .ToArray(); Assert.Equal(8, allAnalyzers.Length); Assert.Equal( "Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+SomeType+NestedAnalyzer", allAnalyzers[0] ); Assert.Equal( "Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+TestAnalyzer", allAnalyzers[1] ); Assert.Equal( "Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+TestAnalyzerCS", allAnalyzers[2] ); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.TestAnalyzerCSVB", allAnalyzers[3]); Assert.Equal( "Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+SomeType+NestedAnalyzer", allAnalyzers[4] ); Assert.Equal( "Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+TestAnalyzer", allAnalyzers[5] ); Assert.Equal( "Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+TestAnalyzerVB", allAnalyzers[6] ); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.TestAnalyzerCSVB", allAnalyzers[7]); }