Пример #1
0
        public void ValidAnalyzerReference_DisplayName()
        {
            AnalyzerFileReference reference = CreateAnalyzerFileReference(_testFixture.Alpha.Path);

            Assert.Equal(expected: "Alpha", actual: reference.Display);
        }
Пример #2
0
        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);
            }
Пример #3
0
 protected override string GetAnalyzerAssemblyPath(AnalyzerFileReference reference)
 {
     // default implementation doesn't do shadow copying and doesn't guarantee snapshot
     return(reference.FullPath);
 }
Пример #4
0
        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]
                );
        }
Пример #5
0
        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);
            }
        }
Пример #6
0
        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()));
            }
        }
Пример #7
0
        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());
            }
        }
Пример #8
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 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);
            }
        }
Пример #10
0
        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));
            }
        }
Пример #11
0
        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);
        }
Пример #12
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;
                            }
                        }
                    }
            }
        }
Пример #13
0
        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]
                );
        }
Пример #14
0
        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();
        }
Пример #15
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, 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);
            }
Пример #16
0
        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);
            }
        }
Пример #18
0
        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;
        }
Пример #19
0
        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);
Пример #20
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;
Пример #22
0
 protected abstract string GetAnalyzerAssemblyPath(AnalyzerFileReference reference);
Пример #23
0
        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);
            }
        }
Пример #24
0
        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);
            }
        }
Пример #25
0
        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]);
        }