├── .gitattributes ├── .gitignore ├── .nuget └── packages.config ├── CONTRIBUTING.md ├── LICENSE ├── NuGet.config ├── PublicApiAnalyzer.sln ├── PublicApiAnalyzer ├── Directory.Build.props ├── Directory.Build.targets ├── PublicApiAnalyzer.CodeFixes │ ├── ApiDesign │ │ └── DeclarePublicAPIFix.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── PublicAPI.Shipped.txt │ ├── PublicAPI.Unshipped.txt │ ├── PublicApiAnalyzer.CodeFixes.csproj │ ├── PublicApiAnalyzer.Metadata.nuspec │ ├── PublicApiAnalyzer.nuspec │ └── tools │ │ ├── install.ps1 │ │ └── uninstall.ps1 ├── PublicApiAnalyzer.Internal.ruleset ├── PublicApiAnalyzer.Test │ ├── ApiDesign │ │ └── DeclarePublicAPIAnalyzerTests.cs │ ├── AttributeTests.cs │ ├── ExportCodeFixProviderAttributeNameTest.cs │ ├── Helpers │ │ ├── CodeFixVerifier.Helper.cs │ │ ├── DiagnosticResult.cs │ │ ├── DiagnosticResultLocation.cs │ │ ├── DiagnosticVerifier.Helper.cs │ │ ├── MetadataReferences.cs │ │ ├── TestDiagnosticProvider.cs │ │ └── TestXmlReferenceResolver.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── PublicApiAnalyzer.Test.csproj │ ├── PublicApiTests.cs │ └── Verifiers │ │ ├── CodeFixVerifier.cs │ │ └── DiagnosticVerifier.cs ├── PublicApiAnalyzer.Vsix │ ├── Properties │ │ └── launchSettings.json │ ├── PublicApiAnalyzer.Vsix.csproj │ └── source.extension.vsixmanifest ├── PublicApiAnalyzer.ruleset ├── PublicApiAnalyzer │ ├── AnalyzerCategory.cs │ ├── AnalyzerConstants.cs │ ├── ApiDesign │ │ ├── DeclarePublicAPIAnalyzer.Impl.cs │ │ └── DeclarePublicAPIAnalyzer.cs │ ├── ExcludeFromCodeCoverageAttribute.cs │ ├── Helpers │ │ ├── IMethodSymbolExtensions.cs │ │ ├── ISymbolExtensions.cs │ │ └── ObjectExtensions.cs │ ├── NoCodeFixAttribute.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── PublicAPI.Shipped.txt │ ├── PublicAPI.Unshipped.txt │ ├── PublicApiAnalyzer.csproj │ ├── RoslynDiagnosticIds.cs │ ├── RoslynDiagnosticsResources.Designer.cs │ └── RoslynDiagnosticsResources.resx ├── stylecop.json └── version.json ├── README.md ├── THIRD-PARTY-NOTICES.txt ├── appveyor.yml ├── build ├── build.ps1 ├── keys │ ├── PublicApiAnalyzer.snk │ └── TestingKey.snk └── opencover-report.ps1 ├── codecov.yml ├── docs ├── RS0016.md ├── RS0017.md ├── RS0022.md ├── RS0024.md ├── RS0025.md ├── RS0026.md └── RS0027.md └── global.json /.gitattributes: -------------------------------------------------------------------------------- 1 | # Source code files 2 | *.cs text 3 | *.vsixmanifest text 4 | *.config text 5 | *.resx text 6 | *.vstemplate text 7 | *.nuspec text 8 | *.md text 9 | *.txt text 10 | *.ps1 text 11 | LICENSE text 12 | 13 | # Projects and solutions 14 | *.sln text 15 | *.csproj text 16 | 17 | # Certainly binary files 18 | *.png binary 19 | *.ico binary 20 | *.snk binary 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build output 2 | bin/ 3 | obj/ 4 | 5 | # Artifacts of the IDE and build 6 | *.sln.ide/ 7 | .vs/ 8 | packages/ 9 | *.suo 10 | *.user 11 | TestResults/ 12 | OpenCover.Reports/ 13 | .nuget/NuGet.exe 14 | build/nuget/ 15 | *.log 16 | 17 | # Visual Studio performance tools 18 | *.psess 19 | *.vsp 20 | *.vspx 21 | -------------------------------------------------------------------------------- /.nuget/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | If you want to contribute code you can get started by looking for issues marked as 4 | [up for grabs](https://github.com/DotNetAnalyzers/PublicApiAnalyzer/labels/up%20for%20grabs). 5 | We also have the [easy](https://github.com/DotNetAnalyzers/PublicApiAnalyzer/labels/easy) tag 6 | for issues suitable if you are unfamiliar with roslyn. 7 | 8 | Also see the [contributing guide](CONTRIBUTING.md). 9 | 10 | You can also help by filing issues, participating in discussions and doing code review. 11 | 12 | ## Implementing a diagnostic 13 | 14 | To start working on an issue, simply add a comment to the issue indicating you are working on implementing it. 15 | 16 | ## Building 17 | 18 | Visual Studio 2015 is required for building this repository. The Visual Studio 2015 SDK is required for building the 19 | VSIX extension project and for debugging in an experimental visual studio hive. 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) Tunnel Vision Laboratories, LLC. All rights reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | these files except in compliance with the License. You may obtain a copy of the 5 | License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | -------------------------------------------------------------------------------- /NuGet.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /PublicApiAnalyzer.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26906.1 5 | MinimumVisualStudioVersion = 15.0 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PublicApiAnalyzer", "PublicApiAnalyzer\PublicApiAnalyzer\PublicApiAnalyzer.csproj", "{627985B3-0CF1-4BD4-AE60-E54214B8563C}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{A3ABF729-C055-4ED6-B72C-75ADB618D14A}" 9 | ProjectSection(SolutionItems) = preProject 10 | .nuget\packages.config = .nuget\packages.config 11 | EndProjectSection 12 | EndProject 13 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PublicApiAnalyzer.Test", "PublicApiAnalyzer\PublicApiAnalyzer.Test\PublicApiAnalyzer.Test.csproj", "{C489533B-81AA-4240-9B55-B0CF0054B9E9}" 14 | EndProject 15 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PublicApiAnalyzer.Vsix", "PublicApiAnalyzer\PublicApiAnalyzer.Vsix\PublicApiAnalyzer.Vsix.csproj", "{B6281E4F-86D6-4526-9BA9-357CCF7B77B9}" 16 | EndProject 17 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{518166A7-F023-490D-B092-FEF1447EBC3B}" 18 | ProjectSection(SolutionItems) = preProject 19 | appveyor.yml = appveyor.yml 20 | CONTRIBUTING.md = CONTRIBUTING.md 21 | LICENSE = LICENSE 22 | README.md = README.md 23 | THIRD-PARTY-NOTICES.txt = THIRD-PARTY-NOTICES.txt 24 | EndProjectSection 25 | EndProject 26 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{7B1E5C00-D8DB-41A6-AEB4-671EF3AD3586}" 27 | ProjectSection(SolutionItems) = preProject 28 | build\build.ps1 = build\build.ps1 29 | PublicApiAnalyzer\Directory.Build.props = PublicApiAnalyzer\Directory.Build.props 30 | PublicApiAnalyzer\Directory.Build.targets = PublicApiAnalyzer\Directory.Build.targets 31 | build\opencover-report.ps1 = build\opencover-report.ps1 32 | EndProjectSection 33 | EndProject 34 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PublicApiAnalyzer.CodeFixes", "PublicApiAnalyzer\PublicApiAnalyzer.CodeFixes\PublicApiAnalyzer.CodeFixes.csproj", "{8974DAEA-DD04-44B4-86A2-B430D066E9A1}" 35 | EndProject 36 | Global 37 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 38 | Debug|Any CPU = Debug|Any CPU 39 | Release|Any CPU = Release|Any CPU 40 | EndGlobalSection 41 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 42 | {627985B3-0CF1-4BD4-AE60-E54214B8563C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 43 | {627985B3-0CF1-4BD4-AE60-E54214B8563C}.Debug|Any CPU.Build.0 = Debug|Any CPU 44 | {627985B3-0CF1-4BD4-AE60-E54214B8563C}.Release|Any CPU.ActiveCfg = Release|Any CPU 45 | {627985B3-0CF1-4BD4-AE60-E54214B8563C}.Release|Any CPU.Build.0 = Release|Any CPU 46 | {C489533B-81AA-4240-9B55-B0CF0054B9E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 47 | {C489533B-81AA-4240-9B55-B0CF0054B9E9}.Debug|Any CPU.Build.0 = Debug|Any CPU 48 | {C489533B-81AA-4240-9B55-B0CF0054B9E9}.Release|Any CPU.ActiveCfg = Release|Any CPU 49 | {C489533B-81AA-4240-9B55-B0CF0054B9E9}.Release|Any CPU.Build.0 = Release|Any CPU 50 | {B6281E4F-86D6-4526-9BA9-357CCF7B77B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 51 | {B6281E4F-86D6-4526-9BA9-357CCF7B77B9}.Debug|Any CPU.Build.0 = Debug|Any CPU 52 | {B6281E4F-86D6-4526-9BA9-357CCF7B77B9}.Release|Any CPU.ActiveCfg = Release|Any CPU 53 | {B6281E4F-86D6-4526-9BA9-357CCF7B77B9}.Release|Any CPU.Build.0 = Release|Any CPU 54 | {8974DAEA-DD04-44B4-86A2-B430D066E9A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 55 | {8974DAEA-DD04-44B4-86A2-B430D066E9A1}.Debug|Any CPU.Build.0 = Debug|Any CPU 56 | {8974DAEA-DD04-44B4-86A2-B430D066E9A1}.Release|Any CPU.ActiveCfg = Release|Any CPU 57 | {8974DAEA-DD04-44B4-86A2-B430D066E9A1}.Release|Any CPU.Build.0 = Release|Any CPU 58 | EndGlobalSection 59 | GlobalSection(SolutionProperties) = preSolution 60 | HideSolutionNode = FALSE 61 | EndGlobalSection 62 | GlobalSection(ExtensibilityGlobals) = postSolution 63 | SolutionGuid = {F897640F-51AE-4982-B0D2-FB5F255A8549} 64 | EndGlobalSection 65 | EndGlobal 66 | -------------------------------------------------------------------------------- /PublicApiAnalyzer/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | An analyzer for packages with public APIs. 6 | Public API Analyzer 7 | Tunnel Vision Laboratories, LLC 8 | Copyright © Tunnel Vision Laboratories, LLC 2015 9 | en-US 10 | 11 | 12 | 13 | False 14 | 15 | 16 | 17 | 7.1 18 | strict 19 | 20 | 21 | 22 | 23 | true 24 | 25 | 26 | 27 | portable 28 | true 29 | 30 | 31 | 32 | 39 | True 40 | $(NoWarn),1573,1591 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | stylecop.json 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /PublicApiAnalyzer/Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /PublicApiAnalyzer/PublicApiAnalyzer.CodeFixes/ApiDesign/DeclarePublicAPIFix.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | namespace PublicApiAnalyzer.ApiDesign 5 | { 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Collections.Immutable; 9 | using System.Composition; 10 | using System.Diagnostics; 11 | using System.Linq; 12 | using System.Threading; 13 | using System.Threading.Tasks; 14 | using Microsoft.CodeAnalysis; 15 | using Microsoft.CodeAnalysis.CodeActions; 16 | using Microsoft.CodeAnalysis.CodeFixes; 17 | using Microsoft.CodeAnalysis.Text; 18 | 19 | [ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic, Name = "DeclarePublicAPIFix")] 20 | [Shared] 21 | internal sealed class DeclarePublicAPIFix : CodeFixProvider 22 | { 23 | public sealed override ImmutableArray FixableDiagnosticIds { get; } = 24 | ImmutableArray.Create(RoslynDiagnosticIds.DeclarePublicApiRuleId); 25 | 26 | public sealed override FixAllProvider GetFixAllProvider() 27 | { 28 | return new PublicSurfaceAreaFixAllProvider(); 29 | } 30 | 31 | public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) 32 | { 33 | var project = context.Document.Project; 34 | TextDocument publicSurfaceAreaDocument = GetPublicSurfaceAreaDocument(project); 35 | if (publicSurfaceAreaDocument == null) 36 | { 37 | return; 38 | } 39 | 40 | var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); 41 | var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); 42 | foreach (var diagnostic in context.Diagnostics) 43 | { 44 | string minimalSymbolName = diagnostic.Properties[DeclarePublicAPIAnalyzer.MinimalNamePropertyBagKey]; 45 | string publicSurfaceAreaSymbolName = diagnostic.Properties[DeclarePublicAPIAnalyzer.PublicApiNamePropertyBagKey]; 46 | ImmutableHashSet siblingSymbolNamesToRemove = diagnostic.Properties[DeclarePublicAPIAnalyzer.PublicApiNamesOfSiblingsToRemovePropertyBagKey] 47 | .Split(DeclarePublicAPIAnalyzer.PublicApiNamesOfSiblingsToRemovePropertyBagValueSeparator.ToCharArray()) 48 | .ToImmutableHashSet(); 49 | 50 | context.RegisterCodeFix( 51 | new AdditionalDocumentChangeAction( 52 | $"Add {minimalSymbolName} to public API", 53 | c => this.GetFixAsync(publicSurfaceAreaDocument, publicSurfaceAreaSymbolName, siblingSymbolNamesToRemove, c)), 54 | diagnostic); 55 | } 56 | } 57 | 58 | private static TextDocument GetPublicSurfaceAreaDocument(Project project) 59 | { 60 | return project.AdditionalDocuments.FirstOrDefault(doc => doc.Name.Equals(DeclarePublicAPIAnalyzer.UnshippedFileName, StringComparison.Ordinal)); 61 | } 62 | 63 | private static SourceText AddSymbolNamesToSourceText(SourceText sourceText, IEnumerable newSymbolNames) 64 | { 65 | HashSet lines = GetLinesFromSourceText(sourceText); 66 | 67 | foreach (string name in newSymbolNames) 68 | { 69 | lines.Add(name); 70 | } 71 | 72 | var sortedLines = lines.OrderBy(s => s, StringComparer.Ordinal); 73 | 74 | var newSourceText = sourceText.Replace(new TextSpan(0, sourceText.Length), string.Join(Environment.NewLine, sortedLines) + GetEndOfFileText(sourceText)); 75 | return newSourceText; 76 | } 77 | 78 | private static SourceText RemoveSymbolNamesFromSourceText(SourceText sourceText, ImmutableHashSet linesToRemove) 79 | { 80 | if (linesToRemove.IsEmpty) 81 | { 82 | return sourceText; 83 | } 84 | 85 | var lines = GetLinesFromSourceText(sourceText); 86 | var newLines = lines.Where(line => !linesToRemove.Contains(line)); 87 | 88 | var sortedLines = newLines.OrderBy(s => s, StringComparer.Ordinal); 89 | 90 | string newText = sortedLines.Any() ? string.Join(Environment.NewLine, sortedLines) + GetEndOfFileText(sourceText) : string.Empty; 91 | var newSourceText = sourceText.Replace(new TextSpan(0, sourceText.Length), newText); 92 | return newSourceText; 93 | } 94 | 95 | private static HashSet GetLinesFromSourceText(SourceText sourceText) 96 | { 97 | var lines = new HashSet(); 98 | 99 | foreach (var textLine in sourceText.Lines) 100 | { 101 | string text = textLine.ToString(); 102 | if (!string.IsNullOrWhiteSpace(text)) 103 | { 104 | lines.Add(text); 105 | } 106 | } 107 | 108 | return lines; 109 | } 110 | 111 | /// 112 | /// Returns the trailing newline from the end of , if one exists. 113 | /// 114 | /// The source text. 115 | /// if ends with a trailing newline; 116 | /// otherwise, . 117 | private static string GetEndOfFileText(SourceText sourceText) 118 | { 119 | if (sourceText.Length == 0) 120 | { 121 | // An empty file is treated as though it ends with an empty line 122 | return Environment.NewLine; 123 | } 124 | 125 | var lastLine = sourceText.Lines[sourceText.Lines.Count - 1]; 126 | return lastLine.Span.IsEmpty ? Environment.NewLine : string.Empty; 127 | } 128 | 129 | private async Task GetFixAsync(TextDocument publicSurfaceAreaDocument, string newSymbolName, ImmutableHashSet siblingSymbolNamesToRemove, CancellationToken cancellationToken) 130 | { 131 | var sourceText = await publicSurfaceAreaDocument.GetTextAsync(cancellationToken).ConfigureAwait(false); 132 | var newSourceText = AddSymbolNamesToSourceText(sourceText, new[] { newSymbolName }); 133 | newSourceText = RemoveSymbolNamesFromSourceText(newSourceText, siblingSymbolNamesToRemove); 134 | 135 | return publicSurfaceAreaDocument.Project.Solution.WithAdditionalDocumentText(publicSurfaceAreaDocument.Id, newSourceText); 136 | } 137 | 138 | private class AdditionalDocumentChangeAction : CodeAction 139 | { 140 | private readonly Func> createChangedAdditionalDocument; 141 | 142 | public AdditionalDocumentChangeAction(string title, Func> createChangedAdditionalDocument) 143 | { 144 | this.Title = title; 145 | this.createChangedAdditionalDocument = createChangedAdditionalDocument; 146 | } 147 | 148 | public override string Title { get; } 149 | 150 | public override string EquivalenceKey => this.Title; 151 | 152 | protected override Task GetChangedSolutionAsync(CancellationToken cancellationToken) 153 | { 154 | return this.createChangedAdditionalDocument(cancellationToken); 155 | } 156 | } 157 | 158 | private class FixAllAdditionalDocumentChangeAction : CodeAction 159 | { 160 | private readonly List>> diagnosticsToFix; 161 | private readonly Solution solution; 162 | 163 | public FixAllAdditionalDocumentChangeAction(string title, Solution solution, List>> diagnosticsToFix) 164 | { 165 | this.Title = title; 166 | this.solution = solution; 167 | this.diagnosticsToFix = diagnosticsToFix; 168 | } 169 | 170 | public override string Title { get; } 171 | 172 | protected override async Task GetChangedSolutionAsync(CancellationToken cancellationToken) 173 | { 174 | var updatedPublicSurfaceAreaText = new List>(); 175 | 176 | foreach (var pair in this.diagnosticsToFix) 177 | { 178 | var project = pair.Key; 179 | var diagnostics = pair.Value; 180 | 181 | var publicSurfaceAreaAdditionalDocument = GetPublicSurfaceAreaDocument(project); 182 | 183 | if (publicSurfaceAreaAdditionalDocument == null) 184 | { 185 | continue; 186 | } 187 | 188 | var sourceText = await publicSurfaceAreaAdditionalDocument.GetTextAsync(cancellationToken).ConfigureAwait(false); 189 | 190 | var groupedDiagnostics = 191 | diagnostics 192 | .Where(d => d.Location.IsInSource) 193 | .GroupBy(d => d.Location.SourceTree); 194 | 195 | var newSymbolNames = new List(); 196 | var symbolNamesToRemoveBuilder = ImmutableHashSet.CreateBuilder(); 197 | 198 | foreach (var grouping in groupedDiagnostics) 199 | { 200 | var document = project.GetDocument(grouping.Key); 201 | 202 | if (document == null) 203 | { 204 | continue; 205 | } 206 | 207 | var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); 208 | var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); 209 | 210 | foreach (var diagnostic in grouping) 211 | { 212 | string publicSurfaceAreaSymbolName = diagnostic.Properties[DeclarePublicAPIAnalyzer.PublicApiNamePropertyBagKey]; 213 | 214 | newSymbolNames.Add(publicSurfaceAreaSymbolName); 215 | 216 | string siblingNamesToRemove = diagnostic.Properties[DeclarePublicAPIAnalyzer.PublicApiNamesOfSiblingsToRemovePropertyBagKey]; 217 | if (siblingNamesToRemove.Length > 0) 218 | { 219 | var namesToRemove = siblingNamesToRemove.Split(DeclarePublicAPIAnalyzer.PublicApiNamesOfSiblingsToRemovePropertyBagValueSeparator.ToCharArray()); 220 | foreach (var nameToRemove in namesToRemove) 221 | { 222 | symbolNamesToRemoveBuilder.Add(nameToRemove); 223 | } 224 | } 225 | } 226 | } 227 | 228 | var symbolNamesToRemove = symbolNamesToRemoveBuilder.ToImmutable(); 229 | 230 | // We shouldn't be attempting to remove any symbol name, while also adding it. 231 | Debug.Assert(newSymbolNames.All(newSymbolName => !symbolNamesToRemove.Contains(newSymbolName)), "Assertion failed: newSymbolNames.All(newSymbolName => !symbolNamesToRemove.Contains(newSymbolName))"); 232 | 233 | var newSourceText = AddSymbolNamesToSourceText(sourceText, newSymbolNames); 234 | newSourceText = RemoveSymbolNamesFromSourceText(newSourceText, symbolNamesToRemove); 235 | 236 | updatedPublicSurfaceAreaText.Add(new KeyValuePair(publicSurfaceAreaAdditionalDocument.Id, newSourceText)); 237 | } 238 | 239 | var newSolution = this.solution; 240 | 241 | foreach (var pair in updatedPublicSurfaceAreaText) 242 | { 243 | newSolution = newSolution.WithAdditionalDocumentText(pair.Key, pair.Value); 244 | } 245 | 246 | return newSolution; 247 | } 248 | } 249 | 250 | private class PublicSurfaceAreaFixAllProvider : FixAllProvider 251 | { 252 | public override async Task GetFixAsync(FixAllContext fixAllContext) 253 | { 254 | var diagnosticsToFix = new List>>(); 255 | string titleFormat = "Add all items in {0} {1} to the public API"; 256 | string title = null; 257 | 258 | switch (fixAllContext.Scope) 259 | { 260 | case FixAllScope.Document: 261 | { 262 | var diagnostics = await fixAllContext.GetDocumentDiagnosticsAsync(fixAllContext.Document).ConfigureAwait(false); 263 | diagnosticsToFix.Add(new KeyValuePair>(fixAllContext.Project, diagnostics)); 264 | title = string.Format(titleFormat, "document", fixAllContext.Document.Name); 265 | break; 266 | } 267 | 268 | case FixAllScope.Project: 269 | { 270 | var project = fixAllContext.Project; 271 | var diagnostics = await fixAllContext.GetAllDiagnosticsAsync(project).ConfigureAwait(false); 272 | diagnosticsToFix.Add(new KeyValuePair>(fixAllContext.Project, diagnostics)); 273 | title = string.Format(titleFormat, "project", fixAllContext.Project.Name); 274 | break; 275 | } 276 | 277 | case FixAllScope.Solution: 278 | { 279 | foreach (var project in fixAllContext.Solution.Projects) 280 | { 281 | var diagnostics = await fixAllContext.GetAllDiagnosticsAsync(project).ConfigureAwait(false); 282 | diagnosticsToFix.Add(new KeyValuePair>(project, diagnostics)); 283 | } 284 | 285 | title = "Add all items in the solution to the public API"; 286 | break; 287 | } 288 | 289 | case FixAllScope.Custom: 290 | return null; 291 | 292 | default: 293 | break; 294 | } 295 | 296 | return new FixAllAdditionalDocumentChangeAction(title, fixAllContext.Solution, diagnosticsToFix); 297 | } 298 | } 299 | } 300 | } 301 | -------------------------------------------------------------------------------- /PublicApiAnalyzer/PublicApiAnalyzer.CodeFixes/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | using System; 5 | using System.Reflection; 6 | using System.Runtime.CompilerServices; 7 | using System.Runtime.InteropServices; 8 | 9 | // General Information about an assembly is controlled through the following 10 | // set of attributes. Change these attribute values to modify the information 11 | // associated with an assembly. 12 | [assembly: AssemblyTrademark("")] 13 | [assembly: AssemblyCulture("")] 14 | [assembly: CLSCompliant(false)] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | [assembly: InternalsVisibleTo("PublicApiAnalyzer.Test, PublicKey=00240000048000009400000006020000002400005253413100040000010001008d7949d002a66db66875775e2b20a3bbf6589ea56624495d375c3d1f15d2517d2baa654575f5384b91edf0e3951c0c85a7a0228391d6a92134b14d8720e3926338e4f5b349f8066f2f98a8a83263bb54ba74a41a91ca51e02f4a3feb666a578bb38bd275397051ef4532b03256a159a9fa54102ce3d5718e5afbd794ee15df92")] 22 | -------------------------------------------------------------------------------- /PublicApiAnalyzer/PublicApiAnalyzer.CodeFixes/PublicAPI.Shipped.txt: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /PublicApiAnalyzer/PublicApiAnalyzer.CodeFixes/PublicAPI.Unshipped.txt: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /PublicApiAnalyzer/PublicApiAnalyzer.CodeFixes/PublicApiAnalyzer.CodeFixes.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | netstandard1.1;net452 6 | PublicApiAnalyzer 7 | true 8 | true 9 | PublicApiAnalyzer.nuspec 10 | PublicApiAnalyzer.Metadata.nuspec 11 | 12 | 13 | 14 | 15 | $(NoWarn),NU5105 16 | 17 | 18 | 19 | 20 | 21 | portable-net45+win8 22 | 23 | 24 | 25 | 26 | 27 | ..\PublicApiAnalyzer.ruleset 28 | 29 | 30 | 31 | true 32 | ..\..\build\keys\PublicApiAnalyzer.snk 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | DotNetAnalyzers.PublicApiAnalyzer 58 | $(BaseNuspecId) 59 | $(BaseNuspecId) 60 | 61 | $(BaseNuspecId).Unstable 62 | $(BaseNuspecId).Unstable 63 | 64 | $(ImplementationNuspecId) 65 | $(MetadataNuspecId) 66 | 67 | $(AssemblyVersion) 68 | $(NuspecUnstableVersion)-g$(GitCommitIdShort) 69 | $(PackageVersion) 70 | 71 | true 72 | 73 | $(NuspecStableVersion) 74 | $(NuspecUnstableVersion) 75 | 76 | $(NuspecUnstableVersion) 77 | $(NuspecStableVersion) 78 | 79 | id=$(NuspecId);configuration=$(Configuration);GitCommitIdShort=$(GitCommitIdShort);version=$(NuspecVersion);tag=$(NuspecStableVersion);implId=$(ImplementationNuspecId);implVersion=$(ImplementationNuspecVersion) 80 | 81 | 82 | 83 | 84 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /PublicApiAnalyzer/PublicApiAnalyzer.CodeFixes/PublicApiAnalyzer.Metadata.nuspec: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $id$ 5 | 0.0.0 6 | $id$ 7 | Sam Harwell et. al. 8 | Sam Harwell 9 | https://raw.githubusercontent.com/DotNetAnalyzers/PublicApiAnalyzer/$GitCommitIdShort$/LICENSE 10 | https://github.com/DotNetAnalyzers/PublicApiAnalyzer 11 | false 12 | An analyzer for packages with public APIs. 13 | https://github.com/DotNetAnalyzers/PublicApiAnalyzer/releases/$tag$ 14 | Copyright Sam Harwell 2015 15 | API DotNetAnalyzers Roslyn Diagnostic Analyzer 16 | true 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /PublicApiAnalyzer/PublicApiAnalyzer.CodeFixes/PublicApiAnalyzer.nuspec: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $id$ 5 | 0.0.0 6 | $id$ 7 | Sam Harwell et. al. 8 | Sam Harwell 9 | https://raw.githubusercontent.com/DotNetAnalyzers/PublicApiAnalyzer/$version$/LICENSE 10 | https://github.com/DotNetAnalyzers/PublicApiAnalyzer 11 | false 12 | An analyzer for packages with public APIs. 13 | https://github.com/DotNetAnalyzers/PublicApiAnalyzer/releases/$version$ 14 | Copyright Sam Harwell 2015 15 | API DotNetAnalyzers Roslyn Diagnostic Analyzer 16 | true 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /PublicApiAnalyzer/PublicApiAnalyzer.CodeFixes/tools/install.ps1: -------------------------------------------------------------------------------- 1 | param($installPath, $toolsPath, $package, $project) 2 | 3 | $analyzersPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers" ) * -Resolve 4 | 5 | foreach($analyzersPath in $analyzersPaths) 6 | { 7 | # Install the language agnostic analyzers. 8 | if (Test-Path $analyzersPath) 9 | { 10 | foreach ($analyzerFilePath in Get-ChildItem $analyzersPath -Filter *.dll) 11 | { 12 | if($project.Object.AnalyzerReferences) 13 | { 14 | $project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName) 15 | } 16 | } 17 | } 18 | } 19 | 20 | # $project.Type gives the language name like (C# or VB.NET) 21 | $languageFolder = "" 22 | if($project.Type -eq "C#") 23 | { 24 | $languageFolder = "cs" 25 | } 26 | if($project.Type -eq "VB.NET") 27 | { 28 | $languageFolder = "vb" 29 | } 30 | if($languageFolder -eq "") 31 | { 32 | return 33 | } 34 | 35 | foreach($analyzersPath in $analyzersPaths) 36 | { 37 | # Install language specific analyzers. 38 | $languageAnalyzersPath = join-path $analyzersPath $languageFolder 39 | if (Test-Path $languageAnalyzersPath) 40 | { 41 | foreach ($analyzerFilePath in Get-ChildItem $languageAnalyzersPath -Filter *.dll) 42 | { 43 | if($project.Object.AnalyzerReferences) 44 | { 45 | $project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName) 46 | } 47 | } 48 | } 49 | } 50 | # SIG # Begin signature block 51 | # MIIaoQYJKoZIhvcNAQcCoIIakjCCGo4CAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB 52 | # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR 53 | # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQU2Q/XtUMgeda05ZRH6iWmJDPu 54 | # oTCgghWCMIIEwzCCA6ugAwIBAgITMwAAAHPGWcJSl4OjOgAAAAAAczANBgkqhkiG 55 | # 9w0BAQUFADB3MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G 56 | # A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSEw 57 | # HwYDVQQDExhNaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EwHhcNMTUwMzIwMTczMjA0 58 | # WhcNMTYwNjIwMTczMjA0WjCBszELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp 59 | # bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw 60 | # b3JhdGlvbjENMAsGA1UECxMETU9QUjEnMCUGA1UECxMebkNpcGhlciBEU0UgRVNO 61 | # OkJCRUMtMzBDQS0yREJFMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBT 62 | # ZXJ2aWNlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp0QvcscV762c 63 | # vJQkN4+yFC55LDaPb7KevwD6jHhhG5S5Uij0cT8HGE/y6Je/f3Ow4zVsoSviUbYn 64 | # qqI1ASnzKaVQ3natkrIUuQ8Mllkya3MeSL9Q877ogSskJFB0fOph5o8RAe6yfSD1 65 | # CkMqVGVAxRwMNFDik+TCDS7gUJlQaAZ9h3v2jQWOR+Xt0ELjY93j7iXPqVCjT4K7 66 | # x5WFfasB4FBCFeBZg8lR4D2gKOh/gnzSuRoCHqhzdFfIf7gJs7pF4EfCdNSp2BLX 67 | # Lxuc1K567c/CWXMh3LDjZMMd5i8EvFv9ssV+Nua6VnlcHRWrsaB9FygH8+OpkVg8 68 | # tkWf1jVh3QIDAQABo4IBCTCCAQUwHQYDVR0OBBYEFDUsc4HZ7HD5Sj2P/0fAfApo 69 | # obgbMB8GA1UdIwQYMBaAFCM0+NlSRnAK7UD7dvuzK7DDNbMPMFQGA1UdHwRNMEsw 70 | # SaBHoEWGQ2h0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3Rz 71 | # L01pY3Jvc29mdFRpbWVTdGFtcFBDQS5jcmwwWAYIKwYBBQUHAQEETDBKMEgGCCsG 72 | # AQUFBzAChjxodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY3Jv 73 | # c29mdFRpbWVTdGFtcFBDQS5jcnQwEwYDVR0lBAwwCgYIKwYBBQUHAwgwDQYJKoZI 74 | # hvcNAQEFBQADggEBABhW2Lwu5/R0+yuB1kWyYWp9G8CaWAHqZhnXuCn1jzz09iI2 75 | # d1FUmQud9f7Fg9U7F18kV7sSywfz8omzn+eIMTZc0N0QbbGdHG5zeUCA26QRbUwQ 76 | # 6BCVoUNlxEgptx5suXvzd7dgvF0jpzSnWPUVzaasjBvdqMfy/L2f24Jaiu9s8vsu 77 | # w79c0Y2DVhPd4x2T7ReueUVSCxzhK8AzUN271fiW2JRLQ0tRCF8tnA5TKJe7RuvG 78 | # emKndxIklRnPRf1Y2R0getwBvO8Lg3pDeZDUR+AIteZ96oBsSHnsJwxb8T45Ur6a 79 | # lIw5sEMholc7XInenHZH5DEg0aJpQ86Btpv5rzgwggTsMIID1KADAgECAhMzAAAB 80 | # Cix5rtd5e6asAAEAAAEKMA0GCSqGSIb3DQEBBQUAMHkxCzAJBgNVBAYTAlVTMRMw 81 | # EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN 82 | # aWNyb3NvZnQgQ29ycG9yYXRpb24xIzAhBgNVBAMTGk1pY3Jvc29mdCBDb2RlIFNp 83 | # Z25pbmcgUENBMB4XDTE1MDYwNDE3NDI0NVoXDTE2MDkwNDE3NDI0NVowgYMxCzAJ 84 | # BgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25k 85 | # MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xDTALBgNVBAsTBE1PUFIx 86 | # HjAcBgNVBAMTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjCCASIwDQYJKoZIhvcNAQEB 87 | # BQADggEPADCCAQoCggEBAJL8bza74QO5KNZG0aJhuqVG+2MWPi75R9LH7O3HmbEm 88 | # UXW92swPBhQRpGwZnsBfTVSJ5E1Q2I3NoWGldxOaHKftDXT3p1Z56Cj3U9KxemPg 89 | # 9ZSXt+zZR/hsPfMliLO8CsUEp458hUh2HGFGqhnEemKLwcI1qvtYb8VjC5NJMIEb 90 | # e99/fE+0R21feByvtveWE1LvudFNOeVz3khOPBSqlw05zItR4VzRO/COZ+owYKlN 91 | # Wp1DvdsjusAP10sQnZxN8FGihKrknKc91qPvChhIqPqxTqWYDku/8BTzAMiwSNZb 92 | # /jjXiREtBbpDAk8iAJYlrX01boRoqyAYOCj+HKIQsaUCAwEAAaOCAWAwggFcMBMG 93 | # A1UdJQQMMAoGCCsGAQUFBwMDMB0GA1UdDgQWBBSJ/gox6ibN5m3HkZG5lIyiGGE3 94 | # NDBRBgNVHREESjBIpEYwRDENMAsGA1UECxMETU9QUjEzMDEGA1UEBRMqMzE1OTUr 95 | # MDQwNzkzNTAtMTZmYS00YzYwLWI2YmYtOWQyYjFjZDA1OTg0MB8GA1UdIwQYMBaA 96 | # FMsR6MrStBZYAck3LjMWFrlMmgofMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9j 97 | # cmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY0NvZFNpZ1BDQV8w 98 | # OC0zMS0yMDEwLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6 99 | # Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljQ29kU2lnUENBXzA4LTMx 100 | # LTIwMTAuY3J0MA0GCSqGSIb3DQEBBQUAA4IBAQCmqFOR3zsB/mFdBlrrZvAM2PfZ 101 | # hNMAUQ4Q0aTRFyjnjDM4K9hDxgOLdeszkvSp4mf9AtulHU5DRV0bSePgTxbwfo/w 102 | # iBHKgq2k+6apX/WXYMh7xL98m2ntH4LB8c2OeEti9dcNHNdTEtaWUu81vRmOoECT 103 | # oQqlLRacwkZ0COvb9NilSTZUEhFVA7N7FvtH/vto/MBFXOI/Enkzou+Cxd5AGQfu 104 | # FcUKm1kFQanQl56BngNb/ErjGi4FrFBHL4z6edgeIPgF+ylrGBT6cgS3C6eaZOwR 105 | # XU9FSY0pGi370LYJU180lOAWxLnqczXoV+/h6xbDGMcGszvPYYTitkSJlKOGMIIF 106 | # vDCCA6SgAwIBAgIKYTMmGgAAAAAAMTANBgkqhkiG9w0BAQUFADBfMRMwEQYKCZIm 107 | # iZPyLGQBGRYDY29tMRkwFwYKCZImiZPyLGQBGRYJbWljcm9zb2Z0MS0wKwYDVQQD 108 | # EyRNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMTAwODMx 109 | # MjIxOTMyWhcNMjAwODMxMjIyOTMyWjB5MQswCQYDVQQGEwJVUzETMBEGA1UECBMK 110 | # V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 111 | # IENvcnBvcmF0aW9uMSMwIQYDVQQDExpNaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBD 112 | # QTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJyWVwZMGS/HZpgICBC 113 | # mXZTbD4b1m/My/Hqa/6XFhDg3zp0gxq3L6Ay7P/ewkJOI9VyANs1VwqJyq4gSfTw 114 | # aKxNS42lvXlLcZtHB9r9Jd+ddYjPqnNEf9eB2/O98jakyVxF3K+tPeAoaJcap6Vy 115 | # c1bxF5Tk/TWUcqDWdl8ed0WDhTgW0HNbBbpnUo2lsmkv2hkL/pJ0KeJ2L1TdFDBZ 116 | # +NKNYv3LyV9GMVC5JxPkQDDPcikQKCLHN049oDI9kM2hOAaFXE5WgigqBTK3S9dP 117 | # Y+fSLWLxRT3nrAgA9kahntFbjCZT6HqqSvJGzzc8OJ60d1ylF56NyxGPVjzBrAlf 118 | # A9MCAwEAAaOCAV4wggFaMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMsR6MrS 119 | # tBZYAck3LjMWFrlMmgofMAsGA1UdDwQEAwIBhjASBgkrBgEEAYI3FQEEBQIDAQAB 120 | # MCMGCSsGAQQBgjcVAgQWBBT90TFO0yaKleGYYDuoMW+mPLzYLTAZBgkrBgEEAYI3 121 | # FAIEDB4KAFMAdQBiAEMAQTAfBgNVHSMEGDAWgBQOrIJgQFYnl+UlE/wq4QpTlVnk 122 | # pDBQBgNVHR8ESTBHMEWgQ6BBhj9odHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtp 123 | # L2NybC9wcm9kdWN0cy9taWNyb3NvZnRyb290Y2VydC5jcmwwVAYIKwYBBQUHAQEE 124 | # SDBGMEQGCCsGAQUFBzAChjhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2Nl 125 | # cnRzL01pY3Jvc29mdFJvb3RDZXJ0LmNydDANBgkqhkiG9w0BAQUFAAOCAgEAWTk+ 126 | # fyZGr+tvQLEytWrrDi9uqEn361917Uw7LddDrQv+y+ktMaMjzHxQmIAhXaw9L0y6 127 | # oqhWnONwu7i0+Hm1SXL3PupBf8rhDBdpy6WcIC36C1DEVs0t40rSvHDnqA2iA6VW 128 | # 4LiKS1fylUKc8fPv7uOGHzQ8uFaa8FMjhSqkghyT4pQHHfLiTviMocroE6WRTsgb 129 | # 0o9ylSpxbZsa+BzwU9ZnzCL/XB3Nooy9J7J5Y1ZEolHN+emjWFbdmwJFRC9f9Nqu 130 | # 1IIybvyklRPk62nnqaIsvsgrEA5ljpnb9aL6EiYJZTiU8XofSrvR4Vbo0HiWGFzJ 131 | # NRZf3ZMdSY4tvq00RBzuEBUaAF3dNVshzpjHCe6FDoxPbQ4TTj18KUicctHzbMrB 132 | # 7HCjV5JXfZSNoBtIA1r3z6NnCnSlNu0tLxfI5nI3EvRvsTxngvlSso0zFmUeDord 133 | # EN5k9G/ORtTTF+l5xAS00/ss3x+KnqwK+xMnQK3k+eGpf0a7B2BHZWBATrBC7E7t 134 | # s3Z52Ao0CW0cgDEf4g5U3eWh++VHEK1kmP9QFi58vwUheuKVQSdpw5OPlcmN2Jsh 135 | # rg1cnPCiroZogwxqLbt2awAdlq3yFnv2FoMkuYjPaqhHMS+a3ONxPdcAfmJH0c6I 136 | # ybgY+g5yjcGjPa8CQGr/aZuW4hCoELQ3UAjWwz0wggYHMIID76ADAgECAgphFmg0 137 | # AAAAAAAcMA0GCSqGSIb3DQEBBQUAMF8xEzARBgoJkiaJk/IsZAEZFgNjb20xGTAX 138 | # BgoJkiaJk/IsZAEZFgltaWNyb3NvZnQxLTArBgNVBAMTJE1pY3Jvc29mdCBSb290 139 | # IENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0wNzA0MDMxMjUzMDlaFw0yMTA0MDMx 140 | # MzAzMDlaMHcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYD 141 | # VQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xITAf 142 | # BgNVBAMTGE1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQTCCASIwDQYJKoZIhvcNAQEB 143 | # BQADggEPADCCAQoCggEBAJ+hbLHf20iSKnxrLhnhveLjxZlRI1Ctzt0YTiQP7tGn 144 | # 0UytdDAgEesH1VSVFUmUG0KSrphcMCbaAGvoe73siQcP9w4EmPCJzB/LMySHnfL0 145 | # Zxws/HvniB3q506jocEjU8qN+kXPCdBer9CwQgSi+aZsk2fXKNxGU7CG0OUoRi4n 146 | # rIZPVVIM5AMs+2qQkDBuh/NZMJ36ftaXs+ghl3740hPzCLdTbVK0RZCfSABKR2YR 147 | # JylmqJfk0waBSqL5hKcRRxQJgp+E7VV4/gGaHVAIhQAQMEbtt94jRrvELVSfrx54 148 | # QTF3zJvfO4OToWECtR0Nsfz3m7IBziJLVP/5BcPCIAsCAwEAAaOCAaswggGnMA8G 149 | # A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFCM0+NlSRnAK7UD7dvuzK7DDNbMPMAsG 150 | # A1UdDwQEAwIBhjAQBgkrBgEEAYI3FQEEAwIBADCBmAYDVR0jBIGQMIGNgBQOrIJg 151 | # QFYnl+UlE/wq4QpTlVnkpKFjpGEwXzETMBEGCgmSJomT8ixkARkWA2NvbTEZMBcG 152 | # CgmSJomT8ixkARkWCW1pY3Jvc29mdDEtMCsGA1UEAxMkTWljcm9zb2Z0IFJvb3Qg 153 | # Q2VydGlmaWNhdGUgQXV0aG9yaXR5ghB5rRahSqClrUxzWPQHEy5lMFAGA1UdHwRJ 154 | # MEcwRaBDoEGGP2h0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1 155 | # Y3RzL21pY3Jvc29mdHJvb3RjZXJ0LmNybDBUBggrBgEFBQcBAQRIMEYwRAYIKwYB 156 | # BQUHMAKGOGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljcm9z 157 | # b2Z0Um9vdENlcnQuY3J0MBMGA1UdJQQMMAoGCCsGAQUFBwMIMA0GCSqGSIb3DQEB 158 | # BQUAA4ICAQAQl4rDXANENt3ptK132855UU0BsS50cVttDBOrzr57j7gu1BKijG1i 159 | # uFcCy04gE1CZ3XpA4le7r1iaHOEdAYasu3jyi9DsOwHu4r6PCgXIjUji8FMV3U+r 160 | # kuTnjWrVgMHmlPIGL4UD6ZEqJCJw+/b85HiZLg33B+JwvBhOnY5rCnKVuKE5nGct 161 | # xVEO6mJcPxaYiyA/4gcaMvnMMUp2MT0rcgvI6nA9/4UKE9/CCmGO8Ne4F+tOi3/F 162 | # NSteo7/rvH0LQnvUU3Ih7jDKu3hlXFsBFwoUDtLaFJj1PLlmWLMtL+f5hYbMUVbo 163 | # nXCUbKw5TNT2eb+qGHpiKe+imyk0BncaYsk9Hm0fgvALxyy7z0Oz5fnsfbXjpKh0 164 | # NbhOxXEjEiZ2CzxSjHFaRkMUvLOzsE1nyJ9C/4B5IYCeFTBm6EISXhrIniIh0EPp 165 | # K+m79EjMLNTYMoBMJipIJF9a6lbvpt6Znco6b72BJ3QGEe52Ib+bgsEnVLaxaj2J 166 | # oXZhtG6hE6a/qkfwEm/9ijJssv7fUciMI8lmvZ0dhxJkAj0tr1mPuOQh5bWwymO0 167 | # eFQF1EEuUKyUsKV4q7OglnUa2ZKHE3UiLzKoCG6gW4wlv6DvhMoh1useT8ma7kng 168 | # 9wFlb4kLfchpyOZu6qeXzjEp/w7FW1zYTRuh2Povnj8uVRZryROj/TGCBIkwggSF 169 | # AgEBMIGQMHkxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYD 170 | # VQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xIzAh 171 | # BgNVBAMTGk1pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBAhMzAAABCix5rtd5e6as 172 | # AAEAAAEKMAkGBSsOAwIaBQCggaIwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQw 173 | # HAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwIwYJKoZIhvcNAQkEMRYEFNWy 174 | # JYWiXrB52VhT/lC+8pUKXim8MEIGCisGAQQBgjcCAQwxNDAyoBiAFgBpAG4AcwB0 175 | # AGEAbABsAC4AcABzADGhFoAUaHR0cDovL21pY3Jvc29mdC5jb20wDQYJKoZIhvcN 176 | # AQEBBQAEggEAPijsy7fqbQghv6HRRRSMUi3S6UmVRRL/NIehIU4uTM0SniruHlUf 177 | # YBFAp5PhTCjaj2dNnFL6J4zIcaugqI3Shk6kuopA3Vd8YIiqMOc/9CJ3lRxJ3/nI 178 | # BBAAWpEYXo4xs2500Bco5TpoMUJORWUN15onwqGp+YIc/aWYX1Jtfqvb5oaiTcvI 179 | # 2OWx1dyFfpWxc56hX4eyo3Lj5l2454Z5bB40kzLX07qCgvY+MRYSd89P1uNUtBEB 180 | # qkaymzrmVkTMZaUn4YdyAHR7CfJ7sgMmiOyQ+YySDBDD6HPycGwLpdip93Bmjjfz 181 | # Yj/4ERbiVOPRRpa2GtCAsNXtPiw0SSaQJaGCAigwggIkBgkqhkiG9w0BCQYxggIV 182 | # MIICEQIBATCBjjB3MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQ 183 | # MA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u 184 | # MSEwHwYDVQQDExhNaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0ECEzMAAABzxlnCUpeD 185 | # ozoAAAAAAHMwCQYFKw4DAhoFAKBdMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEw 186 | # HAYJKoZIhvcNAQkFMQ8XDTE1MDYxOTAyNTk0OFowIwYJKoZIhvcNAQkEMRYEFGZQ 187 | # e8ojPYUl1DjvBan3KNtqFEJdMA0GCSqGSIb3DQEBBQUABIIBAJHjS6vTvsLIuzBU 188 | # xQdQopG+qV3hJzqh7u1uPdsknaqMLWjn9zF7Qy6q7gk17eCQ+uStUXdqMCYWqX6J 189 | # GkpaBZGZpmmQ2uEau2G6TuxdN4nVFAmlO5W+RbfLBTizjTH3/VRJsLiIHNu0JpmM 190 | # SjbuKpROk3wKYiUIsbWrHU0rpWmU6lX/xGv/zIZrMskzJ0Xas7+78S1zHeHlsoWS 191 | # TMNtcy+MJfhAfAg6AX1x9Ga7T4J2uT+zo16rMqkIgH/VGmS3+/1ZIMY92ev6BOps 192 | # smrx6ksElucvRtwE41kulKtPbziSPaIhCQyoIvvalwdjO+F0nd3lip/k4dJpUpzj 193 | # d2EmeyI= 194 | # SIG # End signature block 195 | -------------------------------------------------------------------------------- /PublicApiAnalyzer/PublicApiAnalyzer.CodeFixes/tools/uninstall.ps1: -------------------------------------------------------------------------------- 1 | param($installPath, $toolsPath, $package, $project) 2 | 3 | $analyzersPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers" ) * -Resolve 4 | 5 | foreach($analyzersPath in $analyzersPaths) 6 | { 7 | # Uninstall the language agnostic analyzers. 8 | if (Test-Path $analyzersPath) 9 | { 10 | foreach ($analyzerFilePath in Get-ChildItem $analyzersPath -Filter *.dll) 11 | { 12 | if($project.Object.AnalyzerReferences) 13 | { 14 | $project.Object.AnalyzerReferences.Remove($analyzerFilePath.FullName) 15 | } 16 | } 17 | } 18 | } 19 | 20 | # $project.Type gives the language name like (C# or VB.NET) 21 | $languageFolder = "" 22 | if($project.Type -eq "C#") 23 | { 24 | $languageFolder = "cs" 25 | } 26 | if($project.Type -eq "VB.NET") 27 | { 28 | $languageFolder = "vb" 29 | } 30 | if($languageFolder -eq "") 31 | { 32 | return 33 | } 34 | 35 | foreach($analyzersPath in $analyzersPaths) 36 | { 37 | # Uninstall language specific analyzers. 38 | $languageAnalyzersPath = join-path $analyzersPath $languageFolder 39 | if (Test-Path $languageAnalyzersPath) 40 | { 41 | foreach ($analyzerFilePath in Get-ChildItem $languageAnalyzersPath -Filter *.dll) 42 | { 43 | if($project.Object.AnalyzerReferences) 44 | { 45 | try 46 | { 47 | $project.Object.AnalyzerReferences.Remove($analyzerFilePath.FullName) 48 | } 49 | catch 50 | { 51 | 52 | } 53 | } 54 | } 55 | } 56 | } 57 | # SIG # Begin signature block 58 | # MIIapQYJKoZIhvcNAQcCoIIaljCCGpICAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB 59 | # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR 60 | # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUA+iPf06reDGl8vnuLnqw/MIv 61 | # 3FmgghWCMIIEwzCCA6ugAwIBAgITMwAAAHQNgGQOfWd9owAAAAAAdDANBgkqhkiG 62 | # 9w0BAQUFADB3MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G 63 | # A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSEw 64 | # HwYDVQQDExhNaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EwHhcNMTUwMzIwMTczMjA1 65 | # WhcNMTYwNjIwMTczMjA1WjCBszELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp 66 | # bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw 67 | # b3JhdGlvbjENMAsGA1UECxMETU9QUjEnMCUGA1UECxMebkNpcGhlciBEU0UgRVNO 68 | # OjdEMkUtMzc4Mi1CMEY3MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBT 69 | # ZXJ2aWNlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4NFrifjVvo5Y 70 | # gN/jD+4M6zszXn3GnmZHP9AerBSCDRiftpwnIvG2hpREQXSJkW8X9t+Y5jbLX3iS 71 | # 6XJ+S7kExWIUc3HGf2NBW+tk8r1cVWJGzA9ewQnEr9nxvyV94BegUO4lqkXl48Z+ 72 | # vxBZqcGPPtn77GQbY1u1p7jq681X6xtD9WWRv1D1+cEGvH2qzDfnBqmgzLH1M8wN 73 | # ssh1ZgDRbTCTR8+OomdEXhoTf/McHucPncG8SPyBgW1UauJpE8bO9ZdnMmxIyhHC 74 | # VjrW3Dpi9PwQl2RIC4pc8RbClfDLYBukA5sMyfe7kr8Ac2czHKJ673VKGUZaDH6a 75 | # W6A6HVQ16wIDAQABo4IBCTCCAQUwHQYDVR0OBBYEFCUsOGYFtEU5DmC29u69PuDd 76 | # r4wNMB8GA1UdIwQYMBaAFCM0+NlSRnAK7UD7dvuzK7DDNbMPMFQGA1UdHwRNMEsw 77 | # SaBHoEWGQ2h0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3Rz 78 | # L01pY3Jvc29mdFRpbWVTdGFtcFBDQS5jcmwwWAYIKwYBBQUHAQEETDBKMEgGCCsG 79 | # AQUFBzAChjxodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY3Jv 80 | # c29mdFRpbWVTdGFtcFBDQS5jcnQwEwYDVR0lBAwwCgYIKwYBBQUHAwgwDQYJKoZI 81 | # hvcNAQEFBQADggEBAEEG50j6xJHcMBMNInjC0iPTszPL+yYh1978CncY+4Nyzu/U 82 | # LIaP4xXj1RICZ1xbN9MDe02RW0FTZgn9457fLHgJORo2HYqBocllfJx7kbIPSptB 83 | # 3cdEC2EFyUwu8rRrKKoIR+4IrGZUF1aQiMbpddAhEDh5yT+7VTDFpjmmU7/NXFbS 84 | # ThcUvGISy+lL8MWR3J2EypjWDttWFGht21OLMM+6J2V1oDFvk6N1EGDqqu7uduvl 85 | # jAup0655zzS+SR8i0MT1o+/zrjDcjohGI4ygqjyXrwfbdug2VN+Ls4mewOospGBr 86 | # 8d/DthI6rzM4elFxNTXm5AjiUZaC+b7hG4N8e2cwggTsMIID1KADAgECAhMzAAAB 87 | # Cix5rtd5e6asAAEAAAEKMA0GCSqGSIb3DQEBBQUAMHkxCzAJBgNVBAYTAlVTMRMw 88 | # EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN 89 | # aWNyb3NvZnQgQ29ycG9yYXRpb24xIzAhBgNVBAMTGk1pY3Jvc29mdCBDb2RlIFNp 90 | # Z25pbmcgUENBMB4XDTE1MDYwNDE3NDI0NVoXDTE2MDkwNDE3NDI0NVowgYMxCzAJ 91 | # BgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25k 92 | # MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xDTALBgNVBAsTBE1PUFIx 93 | # HjAcBgNVBAMTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjCCASIwDQYJKoZIhvcNAQEB 94 | # BQADggEPADCCAQoCggEBAJL8bza74QO5KNZG0aJhuqVG+2MWPi75R9LH7O3HmbEm 95 | # UXW92swPBhQRpGwZnsBfTVSJ5E1Q2I3NoWGldxOaHKftDXT3p1Z56Cj3U9KxemPg 96 | # 9ZSXt+zZR/hsPfMliLO8CsUEp458hUh2HGFGqhnEemKLwcI1qvtYb8VjC5NJMIEb 97 | # e99/fE+0R21feByvtveWE1LvudFNOeVz3khOPBSqlw05zItR4VzRO/COZ+owYKlN 98 | # Wp1DvdsjusAP10sQnZxN8FGihKrknKc91qPvChhIqPqxTqWYDku/8BTzAMiwSNZb 99 | # /jjXiREtBbpDAk8iAJYlrX01boRoqyAYOCj+HKIQsaUCAwEAAaOCAWAwggFcMBMG 100 | # A1UdJQQMMAoGCCsGAQUFBwMDMB0GA1UdDgQWBBSJ/gox6ibN5m3HkZG5lIyiGGE3 101 | # NDBRBgNVHREESjBIpEYwRDENMAsGA1UECxMETU9QUjEzMDEGA1UEBRMqMzE1OTUr 102 | # MDQwNzkzNTAtMTZmYS00YzYwLWI2YmYtOWQyYjFjZDA1OTg0MB8GA1UdIwQYMBaA 103 | # FMsR6MrStBZYAck3LjMWFrlMmgofMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9j 104 | # cmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY0NvZFNpZ1BDQV8w 105 | # OC0zMS0yMDEwLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6 106 | # Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljQ29kU2lnUENBXzA4LTMx 107 | # LTIwMTAuY3J0MA0GCSqGSIb3DQEBBQUAA4IBAQCmqFOR3zsB/mFdBlrrZvAM2PfZ 108 | # hNMAUQ4Q0aTRFyjnjDM4K9hDxgOLdeszkvSp4mf9AtulHU5DRV0bSePgTxbwfo/w 109 | # iBHKgq2k+6apX/WXYMh7xL98m2ntH4LB8c2OeEti9dcNHNdTEtaWUu81vRmOoECT 110 | # oQqlLRacwkZ0COvb9NilSTZUEhFVA7N7FvtH/vto/MBFXOI/Enkzou+Cxd5AGQfu 111 | # FcUKm1kFQanQl56BngNb/ErjGi4FrFBHL4z6edgeIPgF+ylrGBT6cgS3C6eaZOwR 112 | # XU9FSY0pGi370LYJU180lOAWxLnqczXoV+/h6xbDGMcGszvPYYTitkSJlKOGMIIF 113 | # vDCCA6SgAwIBAgIKYTMmGgAAAAAAMTANBgkqhkiG9w0BAQUFADBfMRMwEQYKCZIm 114 | # iZPyLGQBGRYDY29tMRkwFwYKCZImiZPyLGQBGRYJbWljcm9zb2Z0MS0wKwYDVQQD 115 | # EyRNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMTAwODMx 116 | # MjIxOTMyWhcNMjAwODMxMjIyOTMyWjB5MQswCQYDVQQGEwJVUzETMBEGA1UECBMK 117 | # V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 118 | # IENvcnBvcmF0aW9uMSMwIQYDVQQDExpNaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBD 119 | # QTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJyWVwZMGS/HZpgICBC 120 | # mXZTbD4b1m/My/Hqa/6XFhDg3zp0gxq3L6Ay7P/ewkJOI9VyANs1VwqJyq4gSfTw 121 | # aKxNS42lvXlLcZtHB9r9Jd+ddYjPqnNEf9eB2/O98jakyVxF3K+tPeAoaJcap6Vy 122 | # c1bxF5Tk/TWUcqDWdl8ed0WDhTgW0HNbBbpnUo2lsmkv2hkL/pJ0KeJ2L1TdFDBZ 123 | # +NKNYv3LyV9GMVC5JxPkQDDPcikQKCLHN049oDI9kM2hOAaFXE5WgigqBTK3S9dP 124 | # Y+fSLWLxRT3nrAgA9kahntFbjCZT6HqqSvJGzzc8OJ60d1ylF56NyxGPVjzBrAlf 125 | # A9MCAwEAAaOCAV4wggFaMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMsR6MrS 126 | # tBZYAck3LjMWFrlMmgofMAsGA1UdDwQEAwIBhjASBgkrBgEEAYI3FQEEBQIDAQAB 127 | # MCMGCSsGAQQBgjcVAgQWBBT90TFO0yaKleGYYDuoMW+mPLzYLTAZBgkrBgEEAYI3 128 | # FAIEDB4KAFMAdQBiAEMAQTAfBgNVHSMEGDAWgBQOrIJgQFYnl+UlE/wq4QpTlVnk 129 | # pDBQBgNVHR8ESTBHMEWgQ6BBhj9odHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtp 130 | # L2NybC9wcm9kdWN0cy9taWNyb3NvZnRyb290Y2VydC5jcmwwVAYIKwYBBQUHAQEE 131 | # SDBGMEQGCCsGAQUFBzAChjhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2Nl 132 | # cnRzL01pY3Jvc29mdFJvb3RDZXJ0LmNydDANBgkqhkiG9w0BAQUFAAOCAgEAWTk+ 133 | # fyZGr+tvQLEytWrrDi9uqEn361917Uw7LddDrQv+y+ktMaMjzHxQmIAhXaw9L0y6 134 | # oqhWnONwu7i0+Hm1SXL3PupBf8rhDBdpy6WcIC36C1DEVs0t40rSvHDnqA2iA6VW 135 | # 4LiKS1fylUKc8fPv7uOGHzQ8uFaa8FMjhSqkghyT4pQHHfLiTviMocroE6WRTsgb 136 | # 0o9ylSpxbZsa+BzwU9ZnzCL/XB3Nooy9J7J5Y1ZEolHN+emjWFbdmwJFRC9f9Nqu 137 | # 1IIybvyklRPk62nnqaIsvsgrEA5ljpnb9aL6EiYJZTiU8XofSrvR4Vbo0HiWGFzJ 138 | # NRZf3ZMdSY4tvq00RBzuEBUaAF3dNVshzpjHCe6FDoxPbQ4TTj18KUicctHzbMrB 139 | # 7HCjV5JXfZSNoBtIA1r3z6NnCnSlNu0tLxfI5nI3EvRvsTxngvlSso0zFmUeDord 140 | # EN5k9G/ORtTTF+l5xAS00/ss3x+KnqwK+xMnQK3k+eGpf0a7B2BHZWBATrBC7E7t 141 | # s3Z52Ao0CW0cgDEf4g5U3eWh++VHEK1kmP9QFi58vwUheuKVQSdpw5OPlcmN2Jsh 142 | # rg1cnPCiroZogwxqLbt2awAdlq3yFnv2FoMkuYjPaqhHMS+a3ONxPdcAfmJH0c6I 143 | # ybgY+g5yjcGjPa8CQGr/aZuW4hCoELQ3UAjWwz0wggYHMIID76ADAgECAgphFmg0 144 | # AAAAAAAcMA0GCSqGSIb3DQEBBQUAMF8xEzARBgoJkiaJk/IsZAEZFgNjb20xGTAX 145 | # BgoJkiaJk/IsZAEZFgltaWNyb3NvZnQxLTArBgNVBAMTJE1pY3Jvc29mdCBSb290 146 | # IENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0wNzA0MDMxMjUzMDlaFw0yMTA0MDMx 147 | # MzAzMDlaMHcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYD 148 | # VQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xITAf 149 | # BgNVBAMTGE1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQTCCASIwDQYJKoZIhvcNAQEB 150 | # BQADggEPADCCAQoCggEBAJ+hbLHf20iSKnxrLhnhveLjxZlRI1Ctzt0YTiQP7tGn 151 | # 0UytdDAgEesH1VSVFUmUG0KSrphcMCbaAGvoe73siQcP9w4EmPCJzB/LMySHnfL0 152 | # Zxws/HvniB3q506jocEjU8qN+kXPCdBer9CwQgSi+aZsk2fXKNxGU7CG0OUoRi4n 153 | # rIZPVVIM5AMs+2qQkDBuh/NZMJ36ftaXs+ghl3740hPzCLdTbVK0RZCfSABKR2YR 154 | # JylmqJfk0waBSqL5hKcRRxQJgp+E7VV4/gGaHVAIhQAQMEbtt94jRrvELVSfrx54 155 | # QTF3zJvfO4OToWECtR0Nsfz3m7IBziJLVP/5BcPCIAsCAwEAAaOCAaswggGnMA8G 156 | # A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFCM0+NlSRnAK7UD7dvuzK7DDNbMPMAsG 157 | # A1UdDwQEAwIBhjAQBgkrBgEEAYI3FQEEAwIBADCBmAYDVR0jBIGQMIGNgBQOrIJg 158 | # QFYnl+UlE/wq4QpTlVnkpKFjpGEwXzETMBEGCgmSJomT8ixkARkWA2NvbTEZMBcG 159 | # CgmSJomT8ixkARkWCW1pY3Jvc29mdDEtMCsGA1UEAxMkTWljcm9zb2Z0IFJvb3Qg 160 | # Q2VydGlmaWNhdGUgQXV0aG9yaXR5ghB5rRahSqClrUxzWPQHEy5lMFAGA1UdHwRJ 161 | # MEcwRaBDoEGGP2h0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1 162 | # Y3RzL21pY3Jvc29mdHJvb3RjZXJ0LmNybDBUBggrBgEFBQcBAQRIMEYwRAYIKwYB 163 | # BQUHMAKGOGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljcm9z 164 | # b2Z0Um9vdENlcnQuY3J0MBMGA1UdJQQMMAoGCCsGAQUFBwMIMA0GCSqGSIb3DQEB 165 | # BQUAA4ICAQAQl4rDXANENt3ptK132855UU0BsS50cVttDBOrzr57j7gu1BKijG1i 166 | # uFcCy04gE1CZ3XpA4le7r1iaHOEdAYasu3jyi9DsOwHu4r6PCgXIjUji8FMV3U+r 167 | # kuTnjWrVgMHmlPIGL4UD6ZEqJCJw+/b85HiZLg33B+JwvBhOnY5rCnKVuKE5nGct 168 | # xVEO6mJcPxaYiyA/4gcaMvnMMUp2MT0rcgvI6nA9/4UKE9/CCmGO8Ne4F+tOi3/F 169 | # NSteo7/rvH0LQnvUU3Ih7jDKu3hlXFsBFwoUDtLaFJj1PLlmWLMtL+f5hYbMUVbo 170 | # nXCUbKw5TNT2eb+qGHpiKe+imyk0BncaYsk9Hm0fgvALxyy7z0Oz5fnsfbXjpKh0 171 | # NbhOxXEjEiZ2CzxSjHFaRkMUvLOzsE1nyJ9C/4B5IYCeFTBm6EISXhrIniIh0EPp 172 | # K+m79EjMLNTYMoBMJipIJF9a6lbvpt6Znco6b72BJ3QGEe52Ib+bgsEnVLaxaj2J 173 | # oXZhtG6hE6a/qkfwEm/9ijJssv7fUciMI8lmvZ0dhxJkAj0tr1mPuOQh5bWwymO0 174 | # eFQF1EEuUKyUsKV4q7OglnUa2ZKHE3UiLzKoCG6gW4wlv6DvhMoh1useT8ma7kng 175 | # 9wFlb4kLfchpyOZu6qeXzjEp/w7FW1zYTRuh2Povnj8uVRZryROj/TGCBI0wggSJ 176 | # AgEBMIGQMHkxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYD 177 | # VQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xIzAh 178 | # BgNVBAMTGk1pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBAhMzAAABCix5rtd5e6as 179 | # AAEAAAEKMAkGBSsOAwIaBQCggaYwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQw 180 | # HAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwIwYJKoZIhvcNAQkEMRYEFHxv 181 | # QEFmGVD4yYwxkgEb+6vYK1SsMEYGCisGAQQBgjcCAQwxODA2oByAGgB1AG4AaQBu 182 | # AHMAdABhAGwAbAAuAHAAcwAxoRaAFGh0dHA6Ly9taWNyb3NvZnQuY29tMA0GCSqG 183 | # SIb3DQEBAQUABIIBAIQVySydDm+61yXfwvTRQm9YV2k/tk6GJYRhfMIfUkKD3ysR 184 | # wEN+nXa15Lk6exFXkTnD4O0UxJQUXUso3SfpPmUifQ6fMJYxPsXnyHENiWXsdQ8r 185 | # 5DXgbByYcJS0QsHBxv6wNUAa3UOCV4znYZ4DQ+MY/L4QT9hN7kC7kOHuF0F6Kohz 186 | # 22KHpUwnw5tH+EqjP3oUla0NdrMHJEKAUDaAJ5sVuHIJOiOTbv34x7hd2fty8Gv9 187 | # VJ1KW8Kwv/k6PXzf0u9kpDtw5K5IjPwBgrY+Ds/ufuqLdNNfECHO7LhP//fUtmAz 188 | # qv0r+3LC0ro6Fp1nruAvRpXvtrGMDGy8hk5ofK+hggIoMIICJAYJKoZIhvcNAQkG 189 | # MYICFTCCAhECAQEwgY4wdzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0 190 | # b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3Jh 191 | # dGlvbjEhMB8GA1UEAxMYTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBAhMzAAAAdA2A 192 | # ZA59Z32jAAAAAAB0MAkGBSsOAwIaBQCgXTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcN 193 | # AQcBMBwGCSqGSIb3DQEJBTEPFw0xNTA2MTkwMjU5NDhaMCMGCSqGSIb3DQEJBDEW 194 | # BBS3ur37KGDNwz4wbagIdT/bQ0V7eDANBgkqhkiG9w0BAQUFAASCAQCcfMVGV2cj 195 | # D1Zp77HKsBOTX7x6b9fgXDytnkbjr9hT4nydyIh1qCEFpCBjFi59iEAbMDEvv9M8 196 | # YsSesU0pi0PjGX//XEzfYqQ5C+j7D91PSLG8JbWXq0PT20LHBOyiyJ4/dl6flfeN 197 | # BAlw1RS6xyNdSsczoLfq71p4OI3Ob5VFy9cfNqBhiYV9NqpOzUh6toJH2Q4gyn8B 198 | # AeKTws6OU0lgc19mw0ezTMULb3NeOuowY9PMF6GcR4Btu9+zq5T0E89PUXE/wr6x 199 | # LYAAyX43lASSLOB8t6xZP/getsRkA7dCWZsrzQzhsyxdE4Krb1QY1esNMaB0t2zO 200 | # yHkt4p+Ldz8C 201 | # SIG # End signature block 202 | -------------------------------------------------------------------------------- /PublicApiAnalyzer/PublicApiAnalyzer.Internal.ruleset: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /PublicApiAnalyzer/PublicApiAnalyzer.Test/AttributeTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | namespace PublicApiAnalyzer.Test 5 | { 6 | using Xunit; 7 | 8 | public class AttributeTests 9 | { 10 | [Fact] 11 | public void TestNoCodeFixAttributeReason() 12 | { 13 | string reason = "Reason"; 14 | var attribute = new NoCodeFixAttribute(reason); 15 | Assert.Same(reason, attribute.Reason); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /PublicApiAnalyzer/PublicApiAnalyzer.Test/ExportCodeFixProviderAttributeNameTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | namespace PublicApiAnalyzer.Test 5 | { 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Reflection; 10 | using Microsoft.CodeAnalysis; 11 | using Microsoft.CodeAnalysis.CodeFixes; 12 | using PublicApiAnalyzer.ApiDesign; 13 | using Xunit; 14 | 15 | public class ExportCodeFixProviderAttributeNameTest 16 | { 17 | public static IEnumerable CodeFixProviderTypeData 18 | { 19 | get 20 | { 21 | var codeFixProviders = typeof(DeclarePublicAPIFix) 22 | .Assembly 23 | .GetTypes() 24 | .Where(t => typeof(CodeFixProvider).IsAssignableFrom(t)); 25 | 26 | return codeFixProviders.Select(x => new[] { x }); 27 | } 28 | } 29 | 30 | [Theory] 31 | [MemberData(nameof(CodeFixProviderTypeData))] 32 | public void TestExportCodeFixProviderAttribute(Type codeFixProvider) 33 | { 34 | var exportCodeFixProviderAttribute = codeFixProvider.GetCustomAttributes(false).FirstOrDefault(); 35 | var noCodeFixAttribute = codeFixProvider.GetCustomAttributes(false).FirstOrDefault(); 36 | 37 | if (noCodeFixAttribute != null) 38 | { 39 | Assert.Null(exportCodeFixProviderAttribute); 40 | 41 | return; 42 | } 43 | 44 | Assert.NotNull(exportCodeFixProviderAttribute); 45 | Assert.Equal(codeFixProvider.Name, exportCodeFixProviderAttribute.Name); 46 | Assert.Contains(LanguageNames.CSharp, exportCodeFixProviderAttribute.Languages); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /PublicApiAnalyzer/PublicApiAnalyzer.Test/Helpers/CodeFixVerifier.Helper.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | namespace TestHelper 5 | { 6 | using System.Collections.Generic; 7 | using System.Collections.Immutable; 8 | using System.Linq; 9 | using System.Threading; 10 | using System.Threading.Tasks; 11 | using Microsoft.CodeAnalysis; 12 | using Microsoft.CodeAnalysis.CodeActions; 13 | using Microsoft.CodeAnalysis.Formatting; 14 | using Microsoft.CodeAnalysis.Simplification; 15 | using Microsoft.CodeAnalysis.Text; 16 | 17 | /// 18 | /// Diagnostic Producer class with extra methods dealing with applying code fixes. 19 | /// All methods are static 20 | /// 21 | public abstract partial class CodeFixVerifier : DiagnosticVerifier 22 | { 23 | /// 24 | /// Apply the inputted to the inputted document. 25 | /// Meant to be used to apply code fixes. 26 | /// 27 | /// The to apply the fix on 28 | /// A that will be applied to the 29 | /// . 30 | /// The that the task will observe. 31 | /// A with the changes from the . 32 | private static async Task ApplyFixAsync(Project project, CodeAction codeAction, CancellationToken cancellationToken) 33 | { 34 | var operations = await codeAction.GetOperationsAsync(cancellationToken).ConfigureAwait(false); 35 | var solution = operations.OfType().Single().ChangedSolution; 36 | return solution.GetProject(project.Id); 37 | } 38 | 39 | /// 40 | /// Compare two collections of s, and return a list of any new diagnostics that appear 41 | /// only in the second collection. 42 | /// 43 | /// Considers to be the same if they have the same s. 44 | /// In the case of multiple diagnostics with the same in a row, this method may not 45 | /// necessarily return the new one. 46 | /// 47 | /// 48 | /// The s that existed in the code before the code fix was 49 | /// applied. 50 | /// The s that exist in the code after the code fix was 51 | /// applied. 52 | /// A list of s that only surfaced in the code after the code fix was 53 | /// applied. 54 | private static IEnumerable GetNewDiagnostics(IEnumerable diagnostics, IEnumerable newDiagnostics) 55 | { 56 | var oldArray = diagnostics.OrderBy(d => d.Location.SourceSpan.Start).ToArray(); 57 | var newArray = newDiagnostics.OrderBy(d => d.Location.SourceSpan.Start).ToArray(); 58 | 59 | int oldIndex = 0; 60 | int newIndex = 0; 61 | 62 | while (newIndex < newArray.Length) 63 | { 64 | if (oldIndex < oldArray.Length && oldArray[oldIndex].Id == newArray[newIndex].Id) 65 | { 66 | ++oldIndex; 67 | ++newIndex; 68 | } 69 | else 70 | { 71 | yield return newArray[newIndex++]; 72 | } 73 | } 74 | } 75 | 76 | /// 77 | /// Get the existing compiler diagnostics on the input document. 78 | /// 79 | /// The to run the compiler diagnostic analyzers on. 80 | /// The that the task will observe. 81 | /// The compiler diagnostics that were found in the code. 82 | private static async Task> GetCompilerDiagnosticsAsync(Project project, CancellationToken cancellationToken) 83 | { 84 | var allDiagnostics = ImmutableArray.Create(); 85 | 86 | foreach (var document in project.Documents) 87 | { 88 | var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); 89 | allDiagnostics = allDiagnostics.AddRange(semanticModel.GetDiagnostics(cancellationToken: cancellationToken)); 90 | } 91 | 92 | return allDiagnostics; 93 | } 94 | 95 | /// 96 | /// Given a document, turn it into a string based on the syntax root. 97 | /// 98 | /// The to be converted to a string. 99 | /// The that the task will observe. 100 | /// A string containing the syntax of the after formatting. 101 | private static async Task GetStringFromDocumentAsync(Document document, CancellationToken cancellationToken) 102 | { 103 | var simplifiedDoc = await Simplifier.ReduceAsync(document, Simplifier.Annotation, cancellationToken: cancellationToken).ConfigureAwait(false); 104 | var formatted = await Formatter.FormatAsync(simplifiedDoc, Formatter.Annotation, cancellationToken: cancellationToken).ConfigureAwait(false); 105 | var sourceText = await formatted.GetTextAsync(cancellationToken).ConfigureAwait(false); 106 | return sourceText.ToString(); 107 | } 108 | 109 | /// 110 | /// Implements a workaround for issue #936, force re-parsing to get the same sort of syntax tree as the original document. 111 | /// 112 | /// The project to update. 113 | /// The . 114 | /// The updated . 115 | private static async Task RecreateProjectDocumentsAsync(Project project, CancellationToken cancellationToken) 116 | { 117 | foreach (var documentId in project.DocumentIds) 118 | { 119 | var document = project.GetDocument(documentId); 120 | document = await RecreateDocumentAsync(document, cancellationToken).ConfigureAwait(false); 121 | project = document.Project; 122 | } 123 | 124 | return project; 125 | } 126 | 127 | private static async Task RecreateDocumentAsync(Document document, CancellationToken cancellationToken) 128 | { 129 | var newText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); 130 | newText = newText.WithChanges(new TextChange(new TextSpan(0, 0), " ")); 131 | newText = newText.WithChanges(new TextChange(new TextSpan(0, 1), string.Empty)); 132 | return document.WithText(newText); 133 | } 134 | 135 | /// 136 | /// Formats the whitespace in all documents of the specified . 137 | /// 138 | /// The project to update. 139 | /// The . 140 | /// The updated . 141 | private static async Task ReformatProjectDocumentsAsync(Project project, CancellationToken cancellationToken) 142 | { 143 | foreach (var documentId in project.DocumentIds) 144 | { 145 | var document = project.GetDocument(documentId); 146 | document = await Formatter.FormatAsync(document, Formatter.Annotation, cancellationToken: cancellationToken).ConfigureAwait(false); 147 | project = document.Project; 148 | } 149 | 150 | return project; 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /PublicApiAnalyzer/PublicApiAnalyzer.Test/Helpers/DiagnosticResult.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | namespace TestHelper 5 | { 6 | using System; 7 | using Microsoft.CodeAnalysis; 8 | 9 | /// 10 | /// Structure that stores information about a appearing in a source. 11 | /// 12 | public struct DiagnosticResult 13 | { 14 | private static readonly object[] EmptyArguments = new object[0]; 15 | 16 | private DiagnosticResultLocation[] locations; 17 | private string message; 18 | 19 | public DiagnosticResult(DiagnosticDescriptor descriptor) 20 | : this() 21 | { 22 | this.Id = descriptor.Id; 23 | this.Severity = descriptor.DefaultSeverity; 24 | this.MessageFormat = descriptor.MessageFormat; 25 | } 26 | 27 | public DiagnosticResultLocation[] Locations 28 | { 29 | get 30 | { 31 | if (this.locations == null) 32 | { 33 | this.locations = new DiagnosticResultLocation[] { }; 34 | } 35 | 36 | return this.locations; 37 | } 38 | 39 | set 40 | { 41 | this.locations = value; 42 | } 43 | } 44 | 45 | public DiagnosticSeverity Severity 46 | { 47 | get; set; 48 | } 49 | 50 | public string Id 51 | { 52 | get; set; 53 | } 54 | 55 | public string Message 56 | { 57 | get 58 | { 59 | if (this.message != null) 60 | { 61 | return this.message; 62 | } 63 | 64 | if (this.MessageFormat != null) 65 | { 66 | return string.Format(this.MessageFormat.ToString(), this.MessageArguments ?? EmptyArguments); 67 | } 68 | 69 | return null; 70 | } 71 | 72 | set 73 | { 74 | this.message = value; 75 | } 76 | } 77 | 78 | public LocalizableString MessageFormat 79 | { 80 | get; 81 | set; 82 | } 83 | 84 | public object[] MessageArguments 85 | { 86 | get; 87 | set; 88 | } 89 | 90 | public string Path 91 | { 92 | get 93 | { 94 | return this.Locations.Length > 0 ? this.Locations[0].Path : string.Empty; 95 | } 96 | } 97 | 98 | public int Line 99 | { 100 | get 101 | { 102 | return this.Locations.Length > 0 ? this.Locations[0].Line : -1; 103 | } 104 | } 105 | 106 | public int Column 107 | { 108 | get 109 | { 110 | return this.Locations.Length > 0 ? this.Locations[0].Column : -1; 111 | } 112 | } 113 | 114 | public DiagnosticResult WithArguments(params object[] arguments) 115 | { 116 | DiagnosticResult result = this; 117 | result.MessageArguments = arguments; 118 | return result; 119 | } 120 | 121 | public DiagnosticResult WithMessageFormat(LocalizableString messageFormat) 122 | { 123 | DiagnosticResult result = this; 124 | result.MessageFormat = messageFormat; 125 | return result; 126 | } 127 | 128 | public DiagnosticResult WithLocation(int line, int column) 129 | { 130 | return this.WithLocation("Test0.cs", line, column); 131 | } 132 | 133 | public DiagnosticResult WithLocation(string path, int line, int column) 134 | { 135 | DiagnosticResult result = this; 136 | Array.Resize(ref result.locations, (result.locations?.Length ?? 0) + 1); 137 | result.locations[result.locations.Length - 1] = new DiagnosticResultLocation(path, line, column); 138 | return result; 139 | } 140 | 141 | public DiagnosticResult WithLineOffset(int offset) 142 | { 143 | DiagnosticResult result = this; 144 | Array.Resize(ref result.locations, result.locations?.Length ?? 0); 145 | for (int i = 0; i < result.locations.Length; i++) 146 | { 147 | result.locations[i].Line += offset; 148 | } 149 | 150 | return result; 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /PublicApiAnalyzer/PublicApiAnalyzer.Test/Helpers/DiagnosticResultLocation.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | namespace TestHelper 5 | { 6 | using System; 7 | 8 | /// 9 | /// Location where the diagnostic appears, as determined by path, line number, and column number. 10 | /// 11 | public struct DiagnosticResultLocation 12 | { 13 | public string Path; 14 | public int Line; 15 | public int Column; 16 | 17 | public DiagnosticResultLocation(string path, int line, int column) 18 | { 19 | if (line < 0 && column < 0) 20 | { 21 | throw new ArgumentOutOfRangeException("At least one of line and column must be > 0"); 22 | } 23 | 24 | if (line < -1 || column < -1) 25 | { 26 | throw new ArgumentOutOfRangeException("Both line and column must be >= -1"); 27 | } 28 | 29 | this.Path = path; 30 | this.Line = line; 31 | this.Column = column; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /PublicApiAnalyzer/PublicApiAnalyzer.Test/Helpers/DiagnosticVerifier.Helper.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | namespace TestHelper 5 | { 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Collections.Immutable; 9 | using System.Linq; 10 | using System.Threading; 11 | using System.Threading.Tasks; 12 | using Microsoft.CodeAnalysis; 13 | using Microsoft.CodeAnalysis.CSharp; 14 | using Microsoft.CodeAnalysis.Diagnostics; 15 | using Microsoft.CodeAnalysis.Text; 16 | using PublicApiAnalyzer; 17 | using PublicApiAnalyzer.ApiDesign; 18 | using PublicApiAnalyzer.Test.Helpers; 19 | 20 | /// 21 | /// Class for turning strings into documents and getting the diagnostics on them. 22 | /// All methods are static. 23 | /// 24 | public abstract partial class DiagnosticVerifier 25 | { 26 | private static readonly string DefaultFilePathPrefix = "Test"; 27 | private static readonly string CSharpDefaultFileExt = "cs"; 28 | private static readonly string VisualBasicDefaultExt = "vb"; 29 | private static readonly string CSharpDefaultFilePath = DefaultFilePathPrefix + 0 + "." + CSharpDefaultFileExt; 30 | private static readonly string VisualBasicDefaultFilePath = DefaultFilePathPrefix + 0 + "." + VisualBasicDefaultExt; 31 | private static readonly string TestProjectName = "TestProject"; 32 | 33 | /// 34 | /// Given an analyzer and a collection of documents to apply it to, run the analyzer and gather an array of 35 | /// diagnostics found. The returned diagnostics are then ordered by location in the source documents. 36 | /// 37 | /// The analyzer to run on the documents. 38 | /// The s that the analyzer will be run on. 39 | /// The that the task will observe. 40 | /// A collection of s that surfaced in the source code, sorted by 41 | /// . 42 | protected static async Task> GetSortedDiagnosticsFromDocumentsAsync(ImmutableArray analyzers, Document[] documents, CancellationToken cancellationToken) 43 | { 44 | var projects = new HashSet(); 45 | foreach (var document in documents) 46 | { 47 | projects.Add(document.Project); 48 | } 49 | 50 | var diagnostics = ImmutableArray.CreateBuilder(); 51 | foreach (var project in projects) 52 | { 53 | var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); 54 | var compilationWithAnalyzers = compilation.WithAnalyzers(analyzers, project.AnalyzerOptions, cancellationToken); 55 | var compilerDiagnostics = compilation.GetDiagnostics(cancellationToken); 56 | var compilerErrors = compilerDiagnostics.Where(i => i.Severity == DiagnosticSeverity.Error); 57 | var diags = await compilationWithAnalyzers.GetAnalyzerDiagnosticsAsync().ConfigureAwait(false); 58 | var allDiagnostics = await compilationWithAnalyzers.GetAllDiagnosticsAsync().ConfigureAwait(false); 59 | var failureDiagnostics = allDiagnostics.Where(diagnostic => diagnostic.Id == "AD0001"); 60 | foreach (var diag in diags.Concat(compilerErrors).Concat(failureDiagnostics)) 61 | { 62 | if (diag.Location == Location.None || !diag.Location.IsInSource) 63 | { 64 | diagnostics.Add(diag); 65 | } 66 | else 67 | { 68 | for (int i = 0; i < documents.Length; i++) 69 | { 70 | var document = documents[i]; 71 | var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); 72 | if (tree == diag.Location.SourceTree) 73 | { 74 | diagnostics.Add(diag); 75 | break; 76 | } 77 | } 78 | } 79 | } 80 | } 81 | 82 | var results = SortDistinctDiagnostics(diagnostics); 83 | return results.ToImmutableArray(); 84 | } 85 | 86 | /// 87 | /// Create a from a string through creating a project that contains it. 88 | /// 89 | /// Classes in the form of a string. 90 | /// The language the source classes are in. Values may be taken from the 91 | /// class. 92 | /// The file name for the document, or to generate a default 93 | /// filename according to the specified . 94 | /// A created from the source string. 95 | protected Document CreateDocument(string source, string language = LanguageNames.CSharp, string fileName = null) 96 | { 97 | string[] filenames = null; 98 | if (fileName != null) 99 | { 100 | filenames = new[] { fileName }; 101 | } 102 | 103 | return this.CreateProject(new[] { source }, language, filenames).Documents.Single(); 104 | } 105 | 106 | /// 107 | /// Creates a solution that will be used as parent for the sources that need to be checked. 108 | /// 109 | /// The project identifier to use. 110 | /// The language for which the solution is being created. 111 | /// The created solution. 112 | protected virtual Solution CreateSolution(ProjectId projectId, string language) 113 | { 114 | var compilationOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, allowUnsafe: true); 115 | 116 | Solution solution = new AdhocWorkspace() 117 | .CurrentSolution 118 | .AddProject(projectId, TestProjectName, TestProjectName, language) 119 | .WithProjectCompilationOptions(projectId, compilationOptions) 120 | .AddMetadataReference(projectId, MetadataReferences.CorlibReference) 121 | .AddMetadataReference(projectId, MetadataReferences.SystemReference) 122 | .AddMetadataReference(projectId, MetadataReferences.SystemCoreReference) 123 | .AddMetadataReference(projectId, MetadataReferences.CSharpSymbolsReference) 124 | .AddMetadataReference(projectId, MetadataReferences.CodeAnalysisReference); 125 | 126 | var publicApi = this.GetUnshippedPublicApi(); 127 | if (publicApi != null) 128 | { 129 | var documentId = DocumentId.CreateNewId(projectId); 130 | solution = solution.AddAdditionalDocument(documentId, DeclarePublicAPIAnalyzer.UnshippedFileName, publicApi, filePath: this.GetUnshippedPublicApiFilePath()); 131 | } 132 | 133 | publicApi = this.GetShippedPublicApi(); 134 | if (publicApi != null) 135 | { 136 | var documentId = DocumentId.CreateNewId(projectId); 137 | solution = solution.AddAdditionalDocument(documentId, DeclarePublicAPIAnalyzer.ShippedFileName, publicApi, filePath: this.GetShippedPublicApiFilePath()); 138 | } 139 | 140 | ParseOptions parseOptions = solution.GetProject(projectId).ParseOptions; 141 | return solution.WithProjectParseOptions(projectId, parseOptions.WithDocumentationMode(DocumentationMode.Diagnose)); 142 | } 143 | 144 | /// 145 | /// Gets the diagnostics that will be suppressed. 146 | /// 147 | /// A collection of diagnostic identifiers. 148 | protected virtual IEnumerable GetDisabledDiagnostics() 149 | { 150 | return Enumerable.Empty(); 151 | } 152 | 153 | /// 154 | /// Gets the content of the settings file to use. 155 | /// 156 | /// The contents of the settings file to use. 157 | protected virtual string GetUnshippedPublicApi() 158 | { 159 | return null; 160 | } 161 | 162 | protected virtual string GetUnshippedPublicApiFilePath() 163 | { 164 | return null; 165 | } 166 | 167 | /// 168 | /// Gets the content of the settings file to use. 169 | /// 170 | /// The contents of the settings file to use. 171 | protected virtual string GetShippedPublicApi() 172 | { 173 | return null; 174 | } 175 | 176 | protected virtual string GetShippedPublicApiFilePath() 177 | { 178 | return null; 179 | } 180 | 181 | protected DiagnosticResult CSharpDiagnostic(string diagnosticId = null) 182 | { 183 | var analyzers = this.GetCSharpDiagnosticAnalyzers(); 184 | var supportedDiagnostics = analyzers.SelectMany(analyzer => analyzer.SupportedDiagnostics); 185 | if (diagnosticId == null) 186 | { 187 | return this.CSharpDiagnostic(supportedDiagnostics.Single()); 188 | } 189 | else 190 | { 191 | return this.CSharpDiagnostic(supportedDiagnostics.Single(i => i.Id == diagnosticId)); 192 | } 193 | } 194 | 195 | protected DiagnosticResult CSharpDiagnostic(DiagnosticDescriptor descriptor) 196 | { 197 | return new DiagnosticResult(descriptor); 198 | } 199 | 200 | /// 201 | /// Create a project using the input strings as sources. 202 | /// 203 | /// 204 | /// This method first creates a by calling , and then 205 | /// applies compilation options to the project by calling . 206 | /// 207 | /// Classes in the form of strings. 208 | /// The language the source classes are in. Values may be taken from the 209 | /// class. 210 | /// The filenames or null if the default filename should be used 211 | /// A created out of the s created from the source 212 | /// strings. 213 | protected Project CreateProject(string[] sources, string language = LanguageNames.CSharp, string[] filenames = null) 214 | { 215 | Project project = this.CreateProjectImpl(sources, language, filenames); 216 | return this.ApplyCompilationOptions(project); 217 | } 218 | 219 | /// 220 | /// Create a project using the input strings as sources. 221 | /// 222 | /// Classes in the form of strings. 223 | /// The language the source classes are in. Values may be taken from the 224 | /// class. 225 | /// The filenames or null if the default filename should be used 226 | /// A created out of the s created from the source 227 | /// strings. 228 | protected virtual Project CreateProjectImpl(string[] sources, string language, string[] filenames) 229 | { 230 | string fileNamePrefix = DefaultFilePathPrefix; 231 | string fileExt = language == LanguageNames.CSharp ? CSharpDefaultFileExt : VisualBasicDefaultExt; 232 | 233 | var projectId = ProjectId.CreateNewId(debugName: TestProjectName); 234 | var solution = this.CreateSolution(projectId, language); 235 | 236 | int count = 0; 237 | for (int i = 0; i < sources.Length; i++) 238 | { 239 | string source = sources[i]; 240 | var newFileName = filenames?[i] ?? fileNamePrefix + count + "." + fileExt; 241 | var documentId = DocumentId.CreateNewId(projectId, debugName: newFileName); 242 | solution = solution.AddDocument(documentId, newFileName, SourceText.From(source)); 243 | count++; 244 | } 245 | 246 | return solution.GetProject(projectId); 247 | } 248 | 249 | /// 250 | /// Applies compilation options to a project. 251 | /// 252 | /// 253 | /// The default implementation configures the project by enabling all supported diagnostics of analyzers 254 | /// included in as well as AD0001. After configuring these 255 | /// diagnostics, any diagnostic IDs indicated in are explictly supressed 256 | /// using . 257 | /// 258 | /// The project. 259 | /// The modified project. 260 | protected virtual Project ApplyCompilationOptions(Project project) 261 | { 262 | var analyzers = this.GetCSharpDiagnosticAnalyzers(); 263 | 264 | var supportedDiagnosticsSpecificOptions = new Dictionary(); 265 | foreach (var analyzer in analyzers) 266 | { 267 | foreach (var diagnostic in analyzer.SupportedDiagnostics) 268 | { 269 | // make sure the analyzers we are testing are enabled 270 | supportedDiagnosticsSpecificOptions[diagnostic.Id] = ReportDiagnostic.Default; 271 | } 272 | } 273 | 274 | // Report exceptions during the analysis process as errors 275 | supportedDiagnosticsSpecificOptions.Add("AD0001", ReportDiagnostic.Error); 276 | 277 | foreach (var id in this.GetDisabledDiagnostics()) 278 | { 279 | supportedDiagnosticsSpecificOptions[id] = ReportDiagnostic.Suppress; 280 | } 281 | 282 | // update the project compilation options 283 | var modifiedSpecificDiagnosticOptions = supportedDiagnosticsSpecificOptions.ToImmutableDictionary().SetItems(project.CompilationOptions.SpecificDiagnosticOptions); 284 | var modifiedCompilationOptions = project.CompilationOptions.WithSpecificDiagnosticOptions(modifiedSpecificDiagnosticOptions); 285 | 286 | Solution solution = project.Solution.WithProjectCompilationOptions(project.Id, modifiedCompilationOptions); 287 | return solution.GetProject(project.Id); 288 | } 289 | 290 | /// 291 | /// Sort s by location in source document. 292 | /// 293 | /// A collection of s to be sorted. 294 | /// A collection containing the input , sorted by 295 | /// and . 296 | private static Diagnostic[] SortDistinctDiagnostics(IEnumerable diagnostics) 297 | { 298 | return diagnostics.OrderBy(d => d.Location.SourceSpan.Start).ThenBy(d => d.Id).ToArray(); 299 | } 300 | 301 | /// 302 | /// Given classes in the form of strings, their language, and an to apply to 303 | /// it, return the s found in the string after converting it to a 304 | /// . 305 | /// 306 | /// Classes in the form of strings. 307 | /// The language the source classes are in. Values may be taken from the 308 | /// class. 309 | /// The analyzers to be run on the sources. 310 | /// The that the task will observe. 311 | /// The filenames or null if the default filename should be used 312 | /// A collection of s that surfaced in the source code, sorted by 313 | /// . 314 | private Task> GetSortedDiagnosticsAsync(string[] sources, string language, ImmutableArray analyzers, CancellationToken cancellationToken, string[] filenames) 315 | { 316 | return GetSortedDiagnosticsFromDocumentsAsync(analyzers, this.GetDocuments(sources, language, filenames), cancellationToken); 317 | } 318 | 319 | /// 320 | /// Given an array of strings as sources and a language, turn them into a and return the 321 | /// documents and spans of it. 322 | /// 323 | /// Classes in the form of strings. 324 | /// The language the source classes are in. Values may be taken from the 325 | /// class. 326 | /// The filenames or null if the default filename should be used 327 | /// A collection of s representing the sources. 328 | private Document[] GetDocuments(string[] sources, string language, string[] filenames) 329 | { 330 | if (language != LanguageNames.CSharp && language != LanguageNames.VisualBasic) 331 | { 332 | throw new ArgumentException("Unsupported Language"); 333 | } 334 | 335 | var project = this.CreateProject(sources, language, filenames); 336 | var documents = project.Documents.ToArray(); 337 | 338 | if (sources.Length != documents.Length) 339 | { 340 | throw new SystemException("Amount of sources did not match amount of Documents created"); 341 | } 342 | 343 | return documents; 344 | } 345 | } 346 | } 347 | -------------------------------------------------------------------------------- /PublicApiAnalyzer/PublicApiAnalyzer.Test/Helpers/MetadataReferences.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | namespace PublicApiAnalyzer.Test.Helpers 5 | { 6 | using System.Collections.Immutable; 7 | using System.Linq; 8 | using Microsoft.CodeAnalysis; 9 | using Microsoft.CodeAnalysis.CSharp; 10 | 11 | /// 12 | /// Metadata references used to create test projects. 13 | /// 14 | internal static class MetadataReferences 15 | { 16 | internal static readonly MetadataReference CorlibReference = MetadataReference.CreateFromFile(typeof(object).Assembly.Location).WithAliases(ImmutableArray.Create("global", "corlib")); 17 | internal static readonly MetadataReference SystemReference = MetadataReference.CreateFromFile(typeof(System.Diagnostics.Debug).Assembly.Location).WithAliases(ImmutableArray.Create("global", "system")); 18 | internal static readonly MetadataReference SystemCoreReference = MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location); 19 | internal static readonly MetadataReference CSharpSymbolsReference = MetadataReference.CreateFromFile(typeof(CSharpCompilation).Assembly.Location); 20 | internal static readonly MetadataReference CodeAnalysisReference = MetadataReference.CreateFromFile(typeof(Compilation).Assembly.Location); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /PublicApiAnalyzer/PublicApiAnalyzer.Test/Helpers/TestDiagnosticProvider.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | namespace PublicApiAnalyzer.Test.Helpers 5 | { 6 | using System.Collections.Generic; 7 | using System.Collections.Immutable; 8 | using System.Linq; 9 | using System.Threading; 10 | using System.Threading.Tasks; 11 | using Microsoft.CodeAnalysis; 12 | using Microsoft.CodeAnalysis.CodeFixes; 13 | 14 | internal sealed class TestDiagnosticProvider : FixAllContext.DiagnosticProvider 15 | { 16 | private ImmutableArray diagnostics; 17 | 18 | private TestDiagnosticProvider(ImmutableArray diagnostics) 19 | { 20 | this.diagnostics = diagnostics; 21 | } 22 | 23 | public override Task> GetAllDiagnosticsAsync(Project project, CancellationToken cancellationToken) 24 | { 25 | return Task.FromResult>(this.diagnostics); 26 | } 27 | 28 | public override Task> GetDocumentDiagnosticsAsync(Document document, CancellationToken cancellationToken) 29 | { 30 | return Task.FromResult(this.diagnostics.Where(i => i.Location.GetLineSpan().Path == document.Name)); 31 | } 32 | 33 | public override Task> GetProjectDiagnosticsAsync(Project project, CancellationToken cancellationToken) 34 | { 35 | return Task.FromResult(this.diagnostics.Where(i => !i.Location.IsInSource)); 36 | } 37 | 38 | internal static TestDiagnosticProvider Create(ImmutableArray diagnostics) 39 | { 40 | return new TestDiagnosticProvider(diagnostics); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /PublicApiAnalyzer/PublicApiAnalyzer.Test/Helpers/TestXmlReferenceResolver.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | namespace PublicApiAnalyzer.Test.Helpers 5 | { 6 | using System.Collections.Generic; 7 | using System.IO; 8 | using System.Runtime.CompilerServices; 9 | using System.Text; 10 | using Microsoft.CodeAnalysis; 11 | 12 | internal class TestXmlReferenceResolver : XmlReferenceResolver 13 | { 14 | public Dictionary XmlReferences { get; } = 15 | new Dictionary(); 16 | 17 | public override bool Equals(object other) 18 | { 19 | return ReferenceEquals(this, other); 20 | } 21 | 22 | public override int GetHashCode() 23 | { 24 | return RuntimeHelpers.GetHashCode(this); 25 | } 26 | 27 | public override Stream OpenRead(string resolvedPath) 28 | { 29 | string content; 30 | if (!this.XmlReferences.TryGetValue(resolvedPath, out content)) 31 | { 32 | return null; 33 | } 34 | 35 | return new MemoryStream(Encoding.UTF8.GetBytes(content)); 36 | } 37 | 38 | public override string ResolveReference(string path, string baseFilePath) 39 | { 40 | return path; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /PublicApiAnalyzer/PublicApiAnalyzer.Test/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | using System; 5 | using System.Reflection; 6 | using System.Runtime.InteropServices; 7 | using Xunit; 8 | 9 | // General Information about an assembly is controlled through the following 10 | // set of attributes. Change these attribute values to modify the information 11 | // associated with an assembly. 12 | [assembly: AssemblyTrademark("")] 13 | [assembly: AssemblyCulture("")] 14 | [assembly: CLSCompliant(false)] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | [assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly, DisableTestParallelization = true)] 22 | -------------------------------------------------------------------------------- /PublicApiAnalyzer/PublicApiAnalyzer.Test/PublicApiAnalyzer.Test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | net452 6 | 7 | 8 | 9 | ..\PublicApiAnalyzer.Internal.ruleset 10 | 11 | 12 | 13 | true 14 | ..\..\build\keys\TestingKey.snk 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /PublicApiAnalyzer/PublicApiAnalyzer.Test/PublicApiTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | namespace PublicApiAnalyzer.Test 5 | { 6 | using System; 7 | using System.Text; 8 | using Xunit; 9 | 10 | /// 11 | /// Unit tests related to the public API surface of PublicApiAnalyzer.dll. 12 | /// 13 | public class PublicApiTests 14 | { 15 | /// 16 | /// This test ensures all types in PublicApiAnalyzer.dll are marked internal. 17 | /// 18 | /// 19 | /// This test will be removed after the first alpha release of the public API analyzer is installed in 20 | /// this project. 21 | /// 22 | [Fact] 23 | public void TestAllTypesAreInternal() 24 | { 25 | StringBuilder publicTypes = new StringBuilder(); 26 | foreach (Type type in typeof(AnalyzerCategory).Assembly.ExportedTypes) 27 | { 28 | if (publicTypes.Length > 0) 29 | { 30 | publicTypes.Append(", "); 31 | } 32 | 33 | publicTypes.Append(type.Name); 34 | } 35 | 36 | Assert.Equal(string.Empty, publicTypes.ToString()); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /PublicApiAnalyzer/PublicApiAnalyzer.Test/Verifiers/DiagnosticVerifier.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | namespace TestHelper 5 | { 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Collections.Immutable; 9 | using System.Linq; 10 | using System.Text; 11 | using System.Threading; 12 | using System.Threading.Tasks; 13 | using Microsoft.CodeAnalysis; 14 | using Microsoft.CodeAnalysis.Diagnostics; 15 | using Xunit; 16 | 17 | /// 18 | /// Superclass of all unit tests for s. 19 | /// 20 | public abstract partial class DiagnosticVerifier 21 | { 22 | protected static DiagnosticResult[] EmptyDiagnosticResults { get; } = { }; 23 | 24 | /// 25 | /// Verifies that the analyzer will properly handle an empty source. 26 | /// 27 | /// A representing the asynchronous unit test. 28 | [Fact] 29 | public async Task TestEmptySourceAsync() 30 | { 31 | var testCode = string.Empty; 32 | await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); 33 | } 34 | 35 | /// 36 | /// Verifies that each diagnostics contains a in the expected 37 | /// format. 38 | /// 39 | [Fact] 40 | public void TestHelpLink() 41 | { 42 | foreach (var diagnosticAnalyzer in this.GetCSharpDiagnosticAnalyzers()) 43 | { 44 | foreach (var diagnostic in diagnosticAnalyzer.SupportedDiagnostics) 45 | { 46 | if (diagnostic.DefaultSeverity == DiagnosticSeverity.Hidden && diagnostic.CustomTags.Contains(WellKnownDiagnosticTags.NotConfigurable)) 47 | { 48 | // This diagnostic will never appear in the UI. 49 | continue; 50 | } 51 | 52 | string expected = $"https://github.com/DotNetAnalyzers/PublicApiAnalyzer/blob/master/docs/{diagnostic.Id}.md"; 53 | Assert.Equal(expected, diagnostic.HelpLinkUri); 54 | } 55 | } 56 | } 57 | 58 | /// 59 | /// Gets the C# analyzers being tested 60 | /// 61 | /// 62 | /// New instances of all the C# analyzers being tested. 63 | /// 64 | protected abstract IEnumerable GetCSharpDiagnosticAnalyzers(); 65 | 66 | /// 67 | /// Called to test a C# when applied on the single input source as a string. 68 | /// 69 | /// Input a for the expected . 70 | /// 71 | /// 72 | /// A class in the form of a string to run the analyzer on. 73 | /// A s describing the that should 74 | /// be reported by the analyzer for the specified source. 75 | /// The that the task will observe. 76 | /// The filename or null if the default filename should be used 77 | /// A representing the asynchronous operation. 78 | protected Task VerifyCSharpDiagnosticAsync(string source, DiagnosticResult expected, CancellationToken cancellationToken, string filename = null) 79 | { 80 | return this.VerifyCSharpDiagnosticAsync(source, new[] { expected }, cancellationToken, filename); 81 | } 82 | 83 | /// 84 | /// Called to test a C# when applied on the single input source as a string. 85 | /// 86 | /// Input a for each expected. 87 | /// 88 | /// 89 | /// A class in the form of a string to run the analyzer on. 90 | /// A collection of s describing the 91 | /// s that should be reported by the analyzer for the specified source. 92 | /// The that the task will observe. 93 | /// The filename or null if the default filename should be used 94 | /// A representing the asynchronous operation. 95 | protected Task VerifyCSharpDiagnosticAsync(string source, DiagnosticResult[] expected, CancellationToken cancellationToken, string filename = null) 96 | { 97 | return this.VerifyDiagnosticsAsync(new[] { source }, LanguageNames.CSharp, this.GetCSharpDiagnosticAnalyzers().ToImmutableArray(), expected, cancellationToken, filename != null ? new[] { filename } : null); 98 | } 99 | 100 | /// 101 | /// Called to test a C# when applied on the input strings as sources. 102 | /// 103 | /// Input a for each expected. 104 | /// 105 | /// 106 | /// A collection of strings to create source documents from to run the analyzers 107 | /// on. 108 | /// A collection of s describing the 109 | /// s that should be reported by the analyzer for the specified sources. 110 | /// The that the task will observe. 111 | /// The filenames or null if the default filename should be used 112 | /// A representing the asynchronous operation. 113 | protected Task VerifyCSharpDiagnosticAsync(string[] sources, DiagnosticResult[] expected, CancellationToken cancellationToken, string[] filenames = null) 114 | { 115 | return this.VerifyDiagnosticsAsync(sources, LanguageNames.CSharp, this.GetCSharpDiagnosticAnalyzers().ToImmutableArray(), expected, cancellationToken, filenames); 116 | } 117 | 118 | /// 119 | /// Checks each of the actual s found and compares them with the corresponding 120 | /// in the array of expected results. s are considered 121 | /// equal only if the , , 122 | /// , and of the 123 | /// match the actual . 124 | /// 125 | /// The s found by the compiler after running the analyzer 126 | /// on the source code. 127 | /// The analyzers that have been run on the sources. 128 | /// A collection of s describing the expected 129 | /// diagnostics for the sources. 130 | private static void VerifyDiagnosticResults(IEnumerable actualResults, ImmutableArray analyzers, DiagnosticResult[] expectedResults) 131 | { 132 | int expectedCount = expectedResults.Length; 133 | int actualCount = actualResults.Count(); 134 | 135 | if (expectedCount != actualCount) 136 | { 137 | string diagnosticsOutput = actualResults.Any() ? FormatDiagnostics(analyzers, actualResults.ToArray()) : " NONE."; 138 | 139 | Assert.True( 140 | false, 141 | string.Format("Mismatch between number of diagnostics returned, expected \"{0}\" actual \"{1}\"\r\n\r\nDiagnostics:\r\n{2}\r\n", expectedCount, actualCount, diagnosticsOutput)); 142 | } 143 | 144 | for (int i = 0; i < expectedResults.Length; i++) 145 | { 146 | var actual = actualResults.ElementAt(i); 147 | var expected = expectedResults[i]; 148 | 149 | if (expected.Line == -1 && expected.Column == -1) 150 | { 151 | if (actual.Location != Location.None) 152 | { 153 | string message = 154 | string.Format( 155 | "Expected:\nA project diagnostic with No location\nActual:\n{0}", 156 | FormatDiagnostics(analyzers, actual)); 157 | Assert.True(false, message); 158 | } 159 | } 160 | else 161 | { 162 | VerifyDiagnosticLocation(analyzers, actual, actual.Location, expected.Locations.First()); 163 | var additionalLocations = actual.AdditionalLocations.ToArray(); 164 | 165 | if (additionalLocations.Length != expected.Locations.Length - 1) 166 | { 167 | string message = 168 | string.Format( 169 | "Expected {0} additional locations but got {1} for Diagnostic:\r\n {2}\r\n", 170 | expected.Locations.Length - 1, 171 | additionalLocations.Length, 172 | FormatDiagnostics(analyzers, actual)); 173 | Assert.True(false, message); 174 | } 175 | 176 | for (int j = 0; j < additionalLocations.Length; ++j) 177 | { 178 | VerifyDiagnosticLocation(analyzers, actual, additionalLocations[j], expected.Locations[j + 1]); 179 | } 180 | } 181 | 182 | if (actual.Id != expected.Id) 183 | { 184 | string message = 185 | string.Format( 186 | "Expected diagnostic id to be \"{0}\" was \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n", 187 | expected.Id, 188 | actual.Id, 189 | FormatDiagnostics(analyzers, actual)); 190 | Assert.True(false, message); 191 | } 192 | 193 | if (actual.Severity != expected.Severity) 194 | { 195 | string message = 196 | string.Format( 197 | "Expected diagnostic severity to be \"{0}\" was \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n", 198 | expected.Severity, 199 | actual.Severity, 200 | FormatDiagnostics(analyzers, actual)); 201 | Assert.True(false, message); 202 | } 203 | 204 | if (actual.GetMessage() != expected.Message) 205 | { 206 | string message = 207 | string.Format( 208 | "Expected diagnostic message to be \"{0}\" was \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n", 209 | expected.Message, 210 | actual.GetMessage(), 211 | FormatDiagnostics(analyzers, actual)); 212 | Assert.True(false, message); 213 | } 214 | } 215 | } 216 | 217 | /// 218 | /// Helper method to that checks the location of a 219 | /// and compares it with the location described by a 220 | /// . 221 | /// 222 | /// The analyzer that have been run on the sources. 223 | /// The diagnostic that was found in the code. 224 | /// The location of the diagnostic found in the code. 225 | /// The describing the expected location of the 226 | /// diagnostic. 227 | private static void VerifyDiagnosticLocation(ImmutableArray analyzers, Diagnostic diagnostic, Location actual, DiagnosticResultLocation expected) 228 | { 229 | var actualSpan = actual.GetLineSpan(); 230 | 231 | string message = 232 | string.Format( 233 | "Expected diagnostic to be in file \"{0}\" was actually in file \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n", 234 | expected.Path, 235 | actualSpan.Path, 236 | FormatDiagnostics(analyzers, diagnostic)); 237 | Assert.True( 238 | actualSpan.Path == expected.Path || (actualSpan.Path != null && actualSpan.Path.Contains("Test0.") && expected.Path.Contains("Test.")), 239 | message); 240 | 241 | var actualLinePosition = actualSpan.StartLinePosition; 242 | 243 | // Only check line position if it matters 244 | if (expected.Line > 0) 245 | { 246 | if (actualLinePosition.Line + 1 != expected.Line) 247 | { 248 | string message2 = 249 | string.Format( 250 | "Expected diagnostic to be on line \"{0}\" was actually on line \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n", 251 | expected.Line, 252 | actualLinePosition.Line + 1, 253 | FormatDiagnostics(analyzers, diagnostic)); 254 | Assert.True(false, message2); 255 | } 256 | } 257 | 258 | // Only check column position if it matters 259 | if (expected.Column > 0) 260 | { 261 | if (actualLinePosition.Character + 1 != expected.Column) 262 | { 263 | string message2 = 264 | string.Format( 265 | "Expected diagnostic to start at column \"{0}\" was actually at column \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n", 266 | expected.Column, 267 | actualLinePosition.Character + 1, 268 | FormatDiagnostics(analyzers, diagnostic)); 269 | Assert.True(false, message2); 270 | } 271 | } 272 | } 273 | 274 | /// 275 | /// Helper method to format a into an easily readable string. 276 | /// 277 | /// The analyzers that this verifier tests. 278 | /// A collection of s to be formatted. 279 | /// The formatted as a string. 280 | private static string FormatDiagnostics(ImmutableArray analyzers, params Diagnostic[] diagnostics) 281 | { 282 | var builder = new StringBuilder(); 283 | for (int i = 0; i < diagnostics.Length; ++i) 284 | { 285 | var diagnosticsId = diagnostics[i].Id; 286 | 287 | builder.Append("// ").AppendLine(diagnostics[i].ToString()); 288 | 289 | var applicableAnalyzer = analyzers.FirstOrDefault(a => a.SupportedDiagnostics.Any(dd => dd.Id == diagnosticsId)); 290 | if (applicableAnalyzer != null) 291 | { 292 | var analyzerType = applicableAnalyzer.GetType(); 293 | 294 | var location = diagnostics[i].Location; 295 | if (location == Location.None) 296 | { 297 | builder.AppendFormat("GetGlobalResult({0}.{1})", analyzerType.Name, diagnosticsId); 298 | } 299 | else if (!location.IsInSource) 300 | { 301 | builder.AppendFormat("GetMetadataResult({0})", diagnostics[i]); 302 | } 303 | else 304 | { 305 | string resultMethodName = diagnostics[i].Location.SourceTree.FilePath.EndsWith(".cs") ? "GetCSharpResultAt" : "GetBasicResultAt"; 306 | var linePosition = diagnostics[i].Location.GetLineSpan().StartLinePosition; 307 | 308 | builder.AppendFormat( 309 | "{0}({1}, {2}, {3}.{4})", 310 | resultMethodName, 311 | linePosition.Line + 1, 312 | linePosition.Character + 1, 313 | analyzerType.Name, 314 | diagnosticsId); 315 | } 316 | 317 | if (i != diagnostics.Length - 1) 318 | { 319 | builder.Append(','); 320 | } 321 | 322 | builder.AppendLine(); 323 | } 324 | } 325 | 326 | return builder.ToString(); 327 | } 328 | 329 | /// 330 | /// General method that gets a collection of actual s found in the source after the 331 | /// analyzer is run, then verifies each of them. 332 | /// 333 | /// An array of strings to create source documents from to run the analyzers on. 334 | /// The language of the classes represented by the source strings. 335 | /// The analyzers to be run on the source code. 336 | /// A collection of s that should appear after the analyzer 337 | /// is run on the sources. 338 | /// The that the task will observe. 339 | /// The filenames or null if the default filename should be used 340 | /// A representing the asynchronous operation. 341 | private async Task VerifyDiagnosticsAsync(string[] sources, string language, ImmutableArray analyzers, DiagnosticResult[] expected, CancellationToken cancellationToken, string[] filenames) 342 | { 343 | VerifyDiagnosticResults(await this.GetSortedDiagnosticsAsync(sources, language, analyzers, cancellationToken, filenames).ConfigureAwait(false), analyzers, expected); 344 | } 345 | } 346 | } 347 | -------------------------------------------------------------------------------- /PublicApiAnalyzer/PublicApiAnalyzer.Vsix/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Visual Studio Extension": { 4 | "executablePath": "$(DevEnvDir)devenv.exe", 5 | "commandLineArgs": "/rootsuffix $(VSSDKTargetPlatformRegRootSuffix) /log" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /PublicApiAnalyzer/PublicApiAnalyzer.Vsix/PublicApiAnalyzer.Vsix.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | net452 7 | PublicApiAnalyzer 8 | PublicApiAnalyzer.Vsix 9 | 10 | 11 | 12 | false 13 | false 14 | false 15 | false 16 | false 17 | false 18 | Roslyn 19 | 20 | 21 | 22 | 23 | False 24 | 25 | 26 | 27 | ..\PublicApiAnalyzer.Internal.ruleset 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /PublicApiAnalyzer/PublicApiAnalyzer.Vsix/source.extension.vsixmanifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Public API Analyzer 6 | An analyzer for public APIs. 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /PublicApiAnalyzer/PublicApiAnalyzer.ruleset: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /PublicApiAnalyzer/PublicApiAnalyzer/AnalyzerCategory.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | namespace PublicApiAnalyzer 5 | { 6 | /// 7 | /// Class defining the analyzer category constants. 8 | /// 9 | internal static class AnalyzerCategory 10 | { 11 | /// 12 | /// Category definition for API design rules. 13 | /// 14 | internal const string ApiDesign = nameof(ApiDesign); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /PublicApiAnalyzer/PublicApiAnalyzer/AnalyzerConstants.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | namespace PublicApiAnalyzer 5 | { 6 | using System.Diagnostics.CodeAnalysis; 7 | using Microsoft.CodeAnalysis; 8 | 9 | internal static class AnalyzerConstants 10 | { 11 | static AnalyzerConstants() 12 | { 13 | #if DEBUG 14 | // In DEBUG builds, the tests are enabled to simplify development and testing. 15 | DisabledNoTests = true; 16 | #else 17 | DisabledNoTests = false; 18 | #endif 19 | } 20 | 21 | /// 22 | /// Gets a reference value which can be passed to 23 | /// 24 | /// to disable a diagnostic which is currently untested. 25 | /// 26 | /// 27 | /// A reference value which can be passed to 28 | /// 29 | /// to disable a diagnostic which is currently untested. 30 | /// 31 | [ExcludeFromCodeCoverage] 32 | [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1623:Property summary documentation must match accessors.", Justification = "This property behaves more like an opaque value than a Boolean.")] 33 | internal static bool DisabledNoTests { get; } 34 | 35 | /// 36 | /// Gets a reference value which can be passed to 37 | /// 38 | /// to indicate that the diagnostic should be enabled by default. 39 | /// 40 | /// 41 | /// A reference value which can be passed to 42 | /// 43 | /// to indicate that the diagnostic should be enabled by default. 44 | /// 45 | [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1623:Property summary documentation must match accessors.", Justification = "This property behaves more like an opaque value than a Boolean.")] 46 | internal static bool EnabledByDefault => true; 47 | 48 | /// 49 | /// Gets a reference value which can be passed to 50 | /// 51 | /// to indicate that the diagnostic should be disabled by default. 52 | /// 53 | /// 54 | /// A reference value which can be passed to 55 | /// 56 | /// to indicate that the diagnostic should be disabled by default. 57 | /// 58 | [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1623:Property summary documentation must match accessors.", Justification = "This property behaves more like an opaque value than a Boolean.")] 59 | internal static bool DisabledByDefault => false; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /PublicApiAnalyzer/PublicApiAnalyzer/ApiDesign/DeclarePublicAPIAnalyzer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | namespace PublicApiAnalyzer.ApiDesign 5 | { 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Collections.Immutable; 9 | using System.IO; 10 | using System.Threading; 11 | using Microsoft.CodeAnalysis; 12 | using Microsoft.CodeAnalysis.Diagnostics; 13 | using Microsoft.CodeAnalysis.Text; 14 | 15 | /// 16 | /// This file contains the descriptors and driver for the Public API analyzer diagnostics. 17 | /// 18 | [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] 19 | internal sealed partial class DeclarePublicAPIAnalyzer : DiagnosticAnalyzer 20 | { 21 | internal const string ShippedFileName = "PublicAPI.Shipped.txt"; 22 | internal const string UnshippedFileName = "PublicAPI.Unshipped.txt"; 23 | internal const string PublicApiNamePropertyBagKey = "PublicAPIName"; 24 | internal const string MinimalNamePropertyBagKey = "MinimalName"; 25 | internal const string PublicApiNamesOfSiblingsToRemovePropertyBagKey = "PublicApiNamesOfSiblingsToRemove"; 26 | internal const string PublicApiNamesOfSiblingsToRemovePropertyBagValueSeparator = ";;"; 27 | internal const string RemovedApiPrefix = "*REMOVED*"; 28 | internal const string InvalidReasonShippedCantHaveRemoved = "The shipped API file can't have removed members"; 29 | 30 | internal static readonly DiagnosticDescriptor DeclareNewApiRule = new DiagnosticDescriptor( 31 | id: RoslynDiagnosticIds.DeclarePublicApiRuleId, 32 | title: new LocalizableResourceString(nameof(RoslynDiagnosticsResources.DeclarePublicApiTitle), RoslynDiagnosticsResources.ResourceManager, typeof(RoslynDiagnosticsResources)), 33 | messageFormat: new LocalizableResourceString(nameof(RoslynDiagnosticsResources.DeclarePublicApiMessage), RoslynDiagnosticsResources.ResourceManager, typeof(RoslynDiagnosticsResources)), 34 | category: AnalyzerCategory.ApiDesign, 35 | defaultSeverity: DiagnosticSeverity.Warning, 36 | isEnabledByDefault: AnalyzerConstants.EnabledByDefault, 37 | description: new LocalizableResourceString(nameof(RoslynDiagnosticsResources.DeclarePublicApiDescription), RoslynDiagnosticsResources.ResourceManager, typeof(RoslynDiagnosticsResources)), 38 | helpLinkUri: $"https://github.com/DotNetAnalyzers/PublicApiAnalyzer/blob/master/docs/{RoslynDiagnosticIds.DeclarePublicApiRuleId}.md", 39 | customTags: new string[0]); 40 | 41 | internal static readonly DiagnosticDescriptor RemoveDeletedApiRule = new DiagnosticDescriptor( 42 | id: RoslynDiagnosticIds.RemoveDeletedApiRuleId, 43 | title: new LocalizableResourceString(nameof(RoslynDiagnosticsResources.RemoveDeletedApiTitle), RoslynDiagnosticsResources.ResourceManager, typeof(RoslynDiagnosticsResources)), 44 | messageFormat: new LocalizableResourceString(nameof(RoslynDiagnosticsResources.RemoveDeletedApiMessage), RoslynDiagnosticsResources.ResourceManager, typeof(RoslynDiagnosticsResources)), 45 | category: AnalyzerCategory.ApiDesign, 46 | defaultSeverity: DiagnosticSeverity.Warning, 47 | isEnabledByDefault: AnalyzerConstants.EnabledByDefault, 48 | description: new LocalizableResourceString(nameof(RoslynDiagnosticsResources.RemoveDeletedApiDescription), RoslynDiagnosticsResources.ResourceManager, typeof(RoslynDiagnosticsResources)), 49 | helpLinkUri: $"https://github.com/DotNetAnalyzers/PublicApiAnalyzer/blob/master/docs/{RoslynDiagnosticIds.RemoveDeletedApiRuleId}.md", 50 | customTags: new string[0]); 51 | 52 | internal static readonly DiagnosticDescriptor ExposedNoninstantiableType = new DiagnosticDescriptor( 53 | id: RoslynDiagnosticIds.ExposedNoninstantiableTypeRuleId, 54 | title: new LocalizableResourceString(nameof(RoslynDiagnosticsResources.ExposedNoninstantiableTypeTitle), RoslynDiagnosticsResources.ResourceManager, typeof(RoslynDiagnosticsResources)), 55 | messageFormat: new LocalizableResourceString(nameof(RoslynDiagnosticsResources.ExposedNoninstantiableTypeMessage), RoslynDiagnosticsResources.ResourceManager, typeof(RoslynDiagnosticsResources)), 56 | category: AnalyzerCategory.ApiDesign, 57 | defaultSeverity: DiagnosticSeverity.Warning, 58 | isEnabledByDefault: AnalyzerConstants.EnabledByDefault, 59 | description: new LocalizableResourceString(nameof(RoslynDiagnosticsResources.ExposedNoninstantiableTypeDescription), RoslynDiagnosticsResources.ResourceManager, typeof(RoslynDiagnosticsResources)), 60 | helpLinkUri: $"https://github.com/DotNetAnalyzers/PublicApiAnalyzer/blob/master/docs/{RoslynDiagnosticIds.ExposedNoninstantiableTypeRuleId}.md", 61 | customTags: new string[0]); 62 | 63 | internal static readonly DiagnosticDescriptor PublicApiFilesInvalid = new DiagnosticDescriptor( 64 | id: RoslynDiagnosticIds.PublicApiFilesInvalid, 65 | title: new LocalizableResourceString(nameof(RoslynDiagnosticsResources.PublicApiFilesInvalidTitle), RoslynDiagnosticsResources.ResourceManager, typeof(RoslynDiagnosticsResources)), 66 | messageFormat: new LocalizableResourceString(nameof(RoslynDiagnosticsResources.PublicApiFilesInvalidMessage), RoslynDiagnosticsResources.ResourceManager, typeof(RoslynDiagnosticsResources)), 67 | category: AnalyzerCategory.ApiDesign, 68 | defaultSeverity: DiagnosticSeverity.Warning, 69 | isEnabledByDefault: AnalyzerConstants.EnabledByDefault, 70 | helpLinkUri: $"https://github.com/DotNetAnalyzers/PublicApiAnalyzer/blob/master/docs/{RoslynDiagnosticIds.PublicApiFilesInvalid}.md", 71 | customTags: new string[0]); 72 | 73 | internal static readonly DiagnosticDescriptor DuplicateSymbolInApiFiles = new DiagnosticDescriptor( 74 | id: RoslynDiagnosticIds.DuplicatedSymbolInPublicApiFiles, 75 | title: new LocalizableResourceString(nameof(RoslynDiagnosticsResources.DuplicateSymbolsInPublicApiFilesTitle), RoslynDiagnosticsResources.ResourceManager, typeof(RoslynDiagnosticsResources)), 76 | messageFormat: new LocalizableResourceString(nameof(RoslynDiagnosticsResources.DuplicateSymbolsInPublicApiFilesMessage), RoslynDiagnosticsResources.ResourceManager, typeof(RoslynDiagnosticsResources)), 77 | category: AnalyzerCategory.ApiDesign, 78 | defaultSeverity: DiagnosticSeverity.Warning, 79 | isEnabledByDefault: AnalyzerConstants.EnabledByDefault, 80 | helpLinkUri: $"https://github.com/DotNetAnalyzers/PublicApiAnalyzer/blob/master/docs/{RoslynDiagnosticIds.DuplicatedSymbolInPublicApiFiles}.md", 81 | customTags: new string[0]); 82 | 83 | internal static readonly DiagnosticDescriptor AvoidMultipleOverloadsWithOptionalParameters = new DiagnosticDescriptor( 84 | id: RoslynDiagnosticIds.AvoidMultipleOverloadsWithOptionalParameters, 85 | title: new LocalizableResourceString(nameof(RoslynDiagnosticsResources.AvoidMultipleOverloadsWithOptionalParametersTitle), RoslynDiagnosticsResources.ResourceManager, typeof(RoslynDiagnosticsResources)), 86 | messageFormat: new LocalizableResourceString(nameof(RoslynDiagnosticsResources.AvoidMultipleOverloadsWithOptionalParametersMessage), RoslynDiagnosticsResources.ResourceManager, typeof(RoslynDiagnosticsResources)), 87 | category: AnalyzerCategory.ApiDesign, 88 | defaultSeverity: DiagnosticSeverity.Warning, 89 | isEnabledByDefault: AnalyzerConstants.EnabledByDefault, 90 | helpLinkUri: $"https://github.com/DotNetAnalyzers/PublicApiAnalyzer/blob/master/docs/{RoslynDiagnosticIds.AvoidMultipleOverloadsWithOptionalParameters}.md", 91 | customTags: new string[0]); 92 | 93 | internal static readonly DiagnosticDescriptor OverloadWithOptionalParametersShouldHaveMostParameters = new DiagnosticDescriptor( 94 | id: RoslynDiagnosticIds.OverloadWithOptionalParametersShouldHaveMostParameters, 95 | title: new LocalizableResourceString(nameof(RoslynDiagnosticsResources.OverloadWithOptionalParametersShouldHaveMostParametersTitle), RoslynDiagnosticsResources.ResourceManager, typeof(RoslynDiagnosticsResources)), 96 | messageFormat: new LocalizableResourceString(nameof(RoslynDiagnosticsResources.OverloadWithOptionalParametersShouldHaveMostParametersMessage), RoslynDiagnosticsResources.ResourceManager, typeof(RoslynDiagnosticsResources)), 97 | category: AnalyzerCategory.ApiDesign, 98 | defaultSeverity: DiagnosticSeverity.Warning, 99 | isEnabledByDefault: AnalyzerConstants.EnabledByDefault, 100 | helpLinkUri: $"https://github.com/DotNetAnalyzers/PublicApiAnalyzer/blob/master/docs/{RoslynDiagnosticIds.OverloadWithOptionalParametersShouldHaveMostParameters}.md", 101 | customTags: new string[0]); 102 | 103 | internal static readonly SymbolDisplayFormat ShortSymbolNameFormat = 104 | new SymbolDisplayFormat( 105 | globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.OmittedAsContaining, 106 | typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameOnly, 107 | propertyStyle: SymbolDisplayPropertyStyle.NameOnly, 108 | genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters, 109 | memberOptions: SymbolDisplayMemberOptions.None, 110 | parameterOptions: SymbolDisplayParameterOptions.None, 111 | miscellaneousOptions: SymbolDisplayMiscellaneousOptions.None); 112 | 113 | private static readonly SymbolDisplayFormat PublicApiFormat = 114 | new SymbolDisplayFormat( 115 | globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.OmittedAsContaining, 116 | typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, 117 | propertyStyle: SymbolDisplayPropertyStyle.ShowReadWriteDescriptor, 118 | genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters, 119 | memberOptions: SymbolDisplayMemberOptions.IncludeParameters | SymbolDisplayMemberOptions.IncludeContainingType | SymbolDisplayMemberOptions.IncludeExplicitInterface | SymbolDisplayMemberOptions.IncludeModifiers | SymbolDisplayMemberOptions.IncludeConstantValue, 120 | parameterOptions: SymbolDisplayParameterOptions.IncludeExtensionThis | SymbolDisplayParameterOptions.IncludeParamsRefOut | SymbolDisplayParameterOptions.IncludeType | SymbolDisplayParameterOptions.IncludeName | SymbolDisplayParameterOptions.IncludeDefaultValue, 121 | miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes); 122 | 123 | public override ImmutableArray SupportedDiagnostics => 124 | ImmutableArray.Create( 125 | DeclareNewApiRule, 126 | RemoveDeletedApiRule, 127 | ExposedNoninstantiableType, 128 | PublicApiFilesInvalid, 129 | DuplicateSymbolInApiFiles, 130 | AvoidMultipleOverloadsWithOptionalParameters, 131 | OverloadWithOptionalParametersShouldHaveMostParameters); 132 | 133 | public override void Initialize(AnalysisContext context) 134 | { 135 | // TODO: Make the analyzer thread-safe. 136 | ////context.EnableConcurrentExecution(); 137 | 138 | // Analyzer needs to get callbacks for generated code, and might report diagnostics in generated code. 139 | context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); 140 | 141 | context.RegisterCompilationStartAction(this.OnCompilationStart); 142 | } 143 | 144 | private static ApiData ReadApiData(string path, SourceText sourceText, bool isShippedApi) 145 | { 146 | var apiBuilder = ImmutableArray.CreateBuilder(); 147 | var removedBuilder = ImmutableArray.CreateBuilder(); 148 | 149 | foreach (var line in sourceText.Lines) 150 | { 151 | string text = line.ToString(); 152 | if (string.IsNullOrWhiteSpace(text)) 153 | { 154 | continue; 155 | } 156 | 157 | var apiLine = new ApiLine(text, line.Span, sourceText, path, isShippedApi); 158 | if (text.StartsWith(RemovedApiPrefix, StringComparison.Ordinal)) 159 | { 160 | string removedtext = text.Substring(RemovedApiPrefix.Length); 161 | removedBuilder.Add(new RemovedApiLine(removedtext, apiLine)); 162 | } 163 | else 164 | { 165 | apiBuilder.Add(apiLine); 166 | } 167 | } 168 | 169 | return new ApiData(apiBuilder.ToImmutable(), removedBuilder.ToImmutable()); 170 | } 171 | 172 | private static bool TryGetApiData(ImmutableArray additionalTexts, CancellationToken cancellationToken, out ApiData shippedData, out ApiData unshippedData) 173 | { 174 | if (!TryGetApiText(additionalTexts, cancellationToken, out AdditionalText shippedText, out AdditionalText unshippedText)) 175 | { 176 | shippedData = default; 177 | unshippedData = default; 178 | return false; 179 | } 180 | 181 | shippedData = ReadApiData(shippedText.Path, shippedText.GetText(cancellationToken), isShippedApi: true); 182 | unshippedData = ReadApiData(unshippedText.Path, unshippedText.GetText(cancellationToken), isShippedApi: false); 183 | return true; 184 | } 185 | 186 | private static bool TryGetApiText(ImmutableArray additionalTexts, CancellationToken cancellationToken, out AdditionalText shippedText, out AdditionalText unshippedText) 187 | { 188 | shippedText = null; 189 | unshippedText = null; 190 | 191 | var comparer = StringComparer.Ordinal; 192 | foreach (var text in additionalTexts) 193 | { 194 | cancellationToken.ThrowIfCancellationRequested(); 195 | 196 | string fileName = Path.GetFileName(text.Path); 197 | if (comparer.Equals(fileName, ShippedFileName)) 198 | { 199 | shippedText = text; 200 | continue; 201 | } 202 | 203 | if (comparer.Equals(fileName, UnshippedFileName)) 204 | { 205 | unshippedText = text; 206 | continue; 207 | } 208 | } 209 | 210 | return shippedText != null && unshippedText != null; 211 | } 212 | 213 | private static void ValidateApiList(Dictionary publicApiMap, ImmutableArray apiList, List errors) 214 | { 215 | foreach (var cur in apiList) 216 | { 217 | if (publicApiMap.TryGetValue(cur.Text, out ApiLine existingLine)) 218 | { 219 | var existingLinePositionSpan = existingLine.SourceText.Lines.GetLinePositionSpan(existingLine.Span); 220 | var existingLocation = Location.Create(existingLine.Path, existingLine.Span, existingLinePositionSpan); 221 | 222 | var duplicateLinePositionSpan = cur.SourceText.Lines.GetLinePositionSpan(cur.Span); 223 | var duplicateLocation = Location.Create(cur.Path, cur.Span, duplicateLinePositionSpan); 224 | errors.Add(Diagnostic.Create(DuplicateSymbolInApiFiles, duplicateLocation, new[] { existingLocation }, cur.Text)); 225 | } 226 | else 227 | { 228 | publicApiMap.Add(cur.Text, cur); 229 | } 230 | } 231 | } 232 | 233 | private void OnCompilationStart(CompilationStartAnalysisContext compilationContext) 234 | { 235 | var additionalFiles = compilationContext.Options.AdditionalFiles; 236 | 237 | if (!TryGetApiData(additionalFiles, compilationContext.CancellationToken, out ApiData shippedData, out ApiData unshippedData)) 238 | { 239 | return; 240 | } 241 | 242 | if (!this.ValidateApiFiles(shippedData, unshippedData, out List errors)) 243 | { 244 | compilationContext.RegisterCompilationEndAction(context => 245 | { 246 | foreach (var cur in errors) 247 | { 248 | context.ReportDiagnostic(cur); 249 | } 250 | }); 251 | 252 | return; 253 | } 254 | 255 | var impl = new Impl(compilationContext.Compilation, shippedData, unshippedData); 256 | compilationContext.RegisterSymbolAction( 257 | impl.OnSymbolAction, 258 | SymbolKind.NamedType, 259 | SymbolKind.Event, 260 | SymbolKind.Field, 261 | SymbolKind.Method); 262 | compilationContext.RegisterCompilationEndAction(impl.OnCompilationEnd); 263 | } 264 | 265 | private bool ValidateApiFiles(ApiData shippedData, ApiData unshippedData, out List errors) 266 | { 267 | errors = new List(); 268 | if (shippedData.RemovedApiList.Length > 0) 269 | { 270 | errors.Add(Diagnostic.Create(PublicApiFilesInvalid, Location.None, InvalidReasonShippedCantHaveRemoved)); 271 | } 272 | 273 | var publicApiMap = new Dictionary(StringComparer.Ordinal); 274 | ValidateApiList(publicApiMap, shippedData.ApiList, errors); 275 | ValidateApiList(publicApiMap, unshippedData.ApiList, errors); 276 | 277 | return errors.Count == 0; 278 | } 279 | } 280 | } 281 | -------------------------------------------------------------------------------- /PublicApiAnalyzer/PublicApiAnalyzer/ExcludeFromCodeCoverageAttribute.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | #if NETSTANDARD1_1 5 | 6 | namespace System.Diagnostics.CodeAnalysis 7 | { 8 | [AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = false)] 9 | internal sealed class ExcludeFromCodeCoverageAttribute : Attribute 10 | { 11 | } 12 | } 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /PublicApiAnalyzer/PublicApiAnalyzer/Helpers/IMethodSymbolExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | namespace PublicApiAnalyzer.Helpers 5 | { 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using Microsoft.CodeAnalysis; 9 | 10 | internal static class IMethodSymbolExtensions 11 | { 12 | public static bool HasOptionalParameters(this IMethodSymbol methodSymbol) 13 | { 14 | return methodSymbol.Parameters.Any(p => p.IsOptional); 15 | } 16 | 17 | public static IEnumerable GetOverloads(this IMethodSymbol method) 18 | { 19 | foreach (var member in method?.ContainingType?.GetMembers(method.Name).OfType()) 20 | { 21 | if (!member.Equals(method)) 22 | { 23 | yield return member; 24 | } 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /PublicApiAnalyzer/PublicApiAnalyzer/Helpers/ISymbolExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | namespace PublicApiAnalyzer.Helpers 5 | { 6 | using System.Collections.Immutable; 7 | using Microsoft.CodeAnalysis; 8 | 9 | internal static class ISymbolExtensions 10 | { 11 | public static bool IsDefaultConstructor(this ISymbol symbol) 12 | { 13 | return symbol.IsConstructor() && symbol.GetParameters().Length == 0; 14 | } 15 | 16 | public static bool IsConstructor(this ISymbol symbol) 17 | { 18 | return (symbol as IMethodSymbol)?.MethodKind == MethodKind.Constructor; 19 | } 20 | 21 | public static ImmutableArray GetParameters(this ISymbol symbol) 22 | { 23 | return symbol.TypeSwitch( 24 | (IMethodSymbol m) => m.Parameters, 25 | (IPropertySymbol p) => p.Parameters, 26 | _ => ImmutableArray.Create()); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /PublicApiAnalyzer/PublicApiAnalyzer/Helpers/ObjectExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | namespace PublicApiAnalyzer.Helpers 5 | { 6 | using System; 7 | 8 | internal static class ObjectExtensions 9 | { 10 | public static TResult TypeSwitch(this TBaseType obj, Func matchFunc1, Func matchFunc2, Func defaultFunc = null) 11 | where TDerivedType1 : TBaseType 12 | where TDerivedType2 : TBaseType 13 | { 14 | if (obj is TDerivedType1 derived1) 15 | { 16 | return matchFunc1(derived1); 17 | } 18 | else if (obj is TDerivedType2 derived2) 19 | { 20 | return matchFunc2(derived2); 21 | } 22 | else if (defaultFunc != null) 23 | { 24 | return defaultFunc(obj); 25 | } 26 | else 27 | { 28 | return default; 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /PublicApiAnalyzer/PublicApiAnalyzer/NoCodeFixAttribute.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | namespace PublicApiAnalyzer 5 | { 6 | using System; 7 | using Microsoft.CodeAnalysis.Diagnostics; 8 | 9 | /// 10 | /// This attribute is applied to s for which no code fix is currently planned. 11 | /// 12 | /// 13 | /// There are several reasons an analyzer does not have a code fix, including but not limited to the 14 | /// following: 15 | /// 16 | /// Visual Studio provides a built-in code fix. 17 | /// A code fix could not provide a useful solution. 18 | /// 19 | /// The should be provided. 20 | /// 21 | [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] 22 | internal sealed class NoCodeFixAttribute : System.Attribute 23 | { 24 | /// 25 | /// Initializes a new instance of the class. 26 | /// 27 | /// The reason why the does not have a code fix. 28 | public NoCodeFixAttribute(string reason) 29 | { 30 | this.Reason = reason; 31 | } 32 | 33 | /// 34 | /// Gets the reason why the does not have a code fix. 35 | /// 36 | /// 37 | /// The reason why the does not have a code fix. 38 | /// 39 | public string Reason 40 | { 41 | get; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /PublicApiAnalyzer/PublicApiAnalyzer/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | using System; 5 | using System.Reflection; 6 | using System.Runtime.CompilerServices; 7 | using System.Runtime.InteropServices; 8 | 9 | // General Information about an assembly is controlled through the following 10 | // set of attributes. Change these attribute values to modify the information 11 | // associated with an assembly. 12 | [assembly: AssemblyTrademark("")] 13 | [assembly: AssemblyCulture("")] 14 | [assembly: CLSCompliant(false)] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | #if DEVELOPMENT_KEY 22 | [assembly: InternalsVisibleTo("PublicApiAnalyzer.CodeFixes, PublicKey=00240000048000009400000006020000002400005253413100040000010001006b4f7413340c619a8d40711b6d977d6dbfb1ab7d7c41aed30d385f4e4c40fd52319b9150eca37207c6e9dfe5bed9876aec9d6b592c17529e56b0ca9119efe4a14797fed920955a01186229599f1374f493c6bdee1d9a8f37f09f564f44a35bb953d36e5846b88c2b8a2818cf0fa9f465be73a191293d56c4f4c05732d4d0f6a5")] 23 | #else 24 | [assembly: InternalsVisibleTo("PublicApiAnalyzer.CodeFixes, PublicKey=0024000004800000940000000602000000240000525341310004000001000100ab5523b35a0e5a54a36c50292857295b245020ce1e7b338804b0c52be7843656fba45dd678b227e250cc126b4c7644151b18cd8e7e3db9876eaa20cc1e108b7679bcd0b655e5cb0c5e2c30ae1d17d82e88cd1c8f68b11f9e91663466d37b606675fa66bf2f00df32d3116aa275e3ce7a909af473b9b48b69b14d3f883e99eccf")] 25 | #endif 26 | [assembly: InternalsVisibleTo("PublicApiAnalyzer.Test, PublicKey=00240000048000009400000006020000002400005253413100040000010001008d7949d002a66db66875775e2b20a3bbf6589ea56624495d375c3d1f15d2517d2baa654575f5384b91edf0e3951c0c85a7a0228391d6a92134b14d8720e3926338e4f5b349f8066f2f98a8a83263bb54ba74a41a91ca51e02f4a3feb666a578bb38bd275397051ef4532b03256a159a9fa54102ce3d5718e5afbd794ee15df92")] 27 | -------------------------------------------------------------------------------- /PublicApiAnalyzer/PublicApiAnalyzer/PublicAPI.Shipped.txt: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /PublicApiAnalyzer/PublicApiAnalyzer/PublicAPI.Unshipped.txt: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /PublicApiAnalyzer/PublicApiAnalyzer/PublicApiAnalyzer.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | netstandard1.1;net452 6 | 7 | 8 | 9 | 10 | 11 | portable-net45+win8 12 | 13 | 14 | 15 | 16 | 17 | ..\PublicApiAnalyzer.ruleset 18 | 19 | 20 | 21 | true 22 | ..\..\build\keys\PublicApiAnalyzer.snk 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /PublicApiAnalyzer/PublicApiAnalyzer/RoslynDiagnosticIds.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. 2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 3 | 4 | namespace PublicApiAnalyzer 5 | { 6 | internal static class RoslynDiagnosticIds 7 | { 8 | public const string DeclarePublicApiRuleId = "RS0016"; 9 | public const string RemoveDeletedApiRuleId = "RS0017"; 10 | public const string ExposedNoninstantiableTypeRuleId = "RS0022"; 11 | public const string PublicApiFilesInvalid = "RS0024"; 12 | public const string DuplicatedSymbolInPublicApiFiles = "RS0025"; 13 | public const string AvoidMultipleOverloadsWithOptionalParameters = "RS0026"; 14 | public const string OverloadWithOptionalParametersShouldHaveMostParameters = "RS0027"; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /PublicApiAnalyzer/PublicApiAnalyzer/RoslynDiagnosticsResources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace PublicApiAnalyzer { 12 | using System; 13 | using System.Reflection; 14 | 15 | 16 | /// 17 | /// A strongly-typed resource class, for looking up localized strings, etc. 18 | /// 19 | // This class was auto-generated by the StronglyTypedResourceBuilder 20 | // class via a tool like ResGen or Visual Studio. 21 | // To add or remove a member, edit your .ResX file then rerun ResGen 22 | // with the /str option, or rebuild your VS project. 23 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] 24 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 25 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 26 | internal class RoslynDiagnosticsResources { 27 | 28 | private static global::System.Resources.ResourceManager resourceMan; 29 | 30 | private static global::System.Globalization.CultureInfo resourceCulture; 31 | 32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 33 | internal RoslynDiagnosticsResources() { 34 | } 35 | 36 | /// 37 | /// Returns the cached ResourceManager instance used by this class. 38 | /// 39 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 40 | internal static global::System.Resources.ResourceManager ResourceManager { 41 | get { 42 | if (object.ReferenceEquals(resourceMan, null)) { 43 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PublicApiAnalyzer.RoslynDiagnosticsResources", typeof(RoslynDiagnosticsResources).GetTypeInfo().Assembly); 44 | resourceMan = temp; 45 | } 46 | return resourceMan; 47 | } 48 | } 49 | 50 | /// 51 | /// Overrides the current thread's CurrentUICulture property for all 52 | /// resource lookups using this strongly typed resource class. 53 | /// 54 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 55 | internal static global::System.Globalization.CultureInfo Culture { 56 | get { 57 | return resourceCulture; 58 | } 59 | set { 60 | resourceCulture = value; 61 | } 62 | } 63 | 64 | /// 65 | /// Looks up a localized string similar to Symbol '{0}' violates the backcompat requirement: 'Do not add multiple overloads with optional parameters'. See '{1}' for details.. 66 | /// 67 | internal static string AvoidMultipleOverloadsWithOptionalParametersMessage { 68 | get { 69 | return ResourceManager.GetString("AvoidMultipleOverloadsWithOptionalParametersMessage", resourceCulture); 70 | } 71 | } 72 | 73 | /// 74 | /// Looks up a localized string similar to Do not add multiple public overloads with optional parameters. 75 | /// 76 | internal static string AvoidMultipleOverloadsWithOptionalParametersTitle { 77 | get { 78 | return ResourceManager.GetString("AvoidMultipleOverloadsWithOptionalParametersTitle", resourceCulture); 79 | } 80 | } 81 | 82 | /// 83 | /// Looks up a localized string similar to All public types and members should be declared in PublicAPI.txt. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes.. 84 | /// 85 | internal static string DeclarePublicApiDescription { 86 | get { 87 | return ResourceManager.GetString("DeclarePublicApiDescription", resourceCulture); 88 | } 89 | } 90 | 91 | /// 92 | /// Looks up a localized string similar to Symbol '{0}' is not part of the declared API.. 93 | /// 94 | internal static string DeclarePublicApiMessage { 95 | get { 96 | return ResourceManager.GetString("DeclarePublicApiMessage", resourceCulture); 97 | } 98 | } 99 | 100 | /// 101 | /// Looks up a localized string similar to Add public types and members to the declared API. 102 | /// 103 | internal static string DeclarePublicApiTitle { 104 | get { 105 | return ResourceManager.GetString("DeclarePublicApiTitle", resourceCulture); 106 | } 107 | } 108 | 109 | /// 110 | /// Looks up a localized string similar to The symbol '{0}' appears more than once in the public API files.. 111 | /// 112 | internal static string DuplicateSymbolsInPublicApiFilesMessage { 113 | get { 114 | return ResourceManager.GetString("DuplicateSymbolsInPublicApiFilesMessage", resourceCulture); 115 | } 116 | } 117 | 118 | /// 119 | /// Looks up a localized string similar to Do not duplicate symbols in public API files. 120 | /// 121 | internal static string DuplicateSymbolsInPublicApiFilesTitle { 122 | get { 123 | return ResourceManager.GetString("DuplicateSymbolsInPublicApiFilesTitle", resourceCulture); 124 | } 125 | } 126 | 127 | /// 128 | /// Looks up a localized string similar to When a base class is noninheritable because its constructor is internal, a derived class should not make it inheritable by having a public or protected constructor.. 129 | /// 130 | internal static string ExposedNoninstantiableTypeDescription { 131 | get { 132 | return ResourceManager.GetString("ExposedNoninstantiableTypeDescription", resourceCulture); 133 | } 134 | } 135 | 136 | /// 137 | /// Looks up a localized string similar to Constructor makes its noninheritable base class inheritable, thereby exposing its protected members.. 138 | /// 139 | internal static string ExposedNoninstantiableTypeMessage { 140 | get { 141 | return ResourceManager.GetString("ExposedNoninstantiableTypeMessage", resourceCulture); 142 | } 143 | } 144 | 145 | /// 146 | /// Looks up a localized string similar to Constructor make noninheritable base class inheritable. 147 | /// 148 | internal static string ExposedNoninstantiableTypeTitle { 149 | get { 150 | return ResourceManager.GetString("ExposedNoninstantiableTypeTitle", resourceCulture); 151 | } 152 | } 153 | 154 | /// 155 | /// Looks up a localized string similar to Symbol '{0}' violates the backcompat requirement: 'Public API with optional parameter(s) should have the most parameters amongst its public overloads'. See '{1}' for details.. 156 | /// 157 | internal static string OverloadWithOptionalParametersShouldHaveMostParametersMessage { 158 | get { 159 | return ResourceManager.GetString("OverloadWithOptionalParametersShouldHaveMostParametersMessage", resourceCulture); 160 | } 161 | } 162 | 163 | /// 164 | /// Looks up a localized string similar to Public API with optional parameter(s) should have the most parameters amongst its public overloads.. 165 | /// 166 | internal static string OverloadWithOptionalParametersShouldHaveMostParametersTitle { 167 | get { 168 | return ResourceManager.GetString("OverloadWithOptionalParametersShouldHaveMostParametersTitle", resourceCulture); 169 | } 170 | } 171 | 172 | /// 173 | /// Looks up a localized string similar to The contents of the public API files are invalid: {0}. 174 | /// 175 | internal static string PublicApiFilesInvalidMessage { 176 | get { 177 | return ResourceManager.GetString("PublicApiFilesInvalidMessage", resourceCulture); 178 | } 179 | } 180 | 181 | /// 182 | /// Looks up a localized string similar to The contents of the public API files are invalid. 183 | /// 184 | internal static string PublicApiFilesInvalidTitle { 185 | get { 186 | return ResourceManager.GetString("PublicApiFilesInvalidTitle", resourceCulture); 187 | } 188 | } 189 | 190 | /// 191 | /// Looks up a localized string similar to implicit constructor for {0}. 192 | /// 193 | internal static string PublicImplicitConstructorErrorMessageName { 194 | get { 195 | return ResourceManager.GetString("PublicImplicitConstructorErrorMessageName", resourceCulture); 196 | } 197 | } 198 | 199 | /// 200 | /// Looks up a localized string similar to When removing a public type or member the corresponding entry in PublicAPI.txt should also be removed. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes.. 201 | /// 202 | internal static string RemoveDeletedApiDescription { 203 | get { 204 | return ResourceManager.GetString("RemoveDeletedApiDescription", resourceCulture); 205 | } 206 | } 207 | 208 | /// 209 | /// Looks up a localized string similar to Symbol '{0}' is part of the declared API, but is either not public or could not be found. 210 | /// 211 | internal static string RemoveDeletedApiMessage { 212 | get { 213 | return ResourceManager.GetString("RemoveDeletedApiMessage", resourceCulture); 214 | } 215 | } 216 | 217 | /// 218 | /// Looks up a localized string similar to Remove deleted types and members from the declared API. 219 | /// 220 | internal static string RemoveDeletedApiTitle { 221 | get { 222 | return ResourceManager.GetString("RemoveDeletedApiTitle", resourceCulture); 223 | } 224 | } 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /PublicApiAnalyzer/PublicApiAnalyzer/RoslynDiagnosticsResources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | Add public types and members to the declared API 122 | 123 | 124 | Symbol '{0}' is not part of the declared API. 125 | 126 | 127 | All public types and members should be declared in PublicAPI.txt. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. 128 | 129 | 130 | Remove deleted types and members from the declared API 131 | 132 | 133 | Symbol '{0}' is part of the declared API, but is either not public or could not be found 134 | 135 | 136 | When removing a public type or member the corresponding entry in PublicAPI.txt should also be removed. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. 137 | 138 | 139 | When a base class is noninheritable because its constructor is internal, a derived class should not make it inheritable by having a public or protected constructor. 140 | 141 | 142 | Constructor makes its noninheritable base class inheritable, thereby exposing its protected members. 143 | 144 | 145 | Constructor make noninheritable base class inheritable 146 | 147 | 148 | The contents of the public API files are invalid 149 | 150 | 151 | The contents of the public API files are invalid: {0} 152 | 153 | 154 | Do not duplicate symbols in public API files 155 | 156 | 157 | The symbol '{0}' appears more than once in the public API files. 158 | 159 | 160 | implicit constructor for {0} 161 | 162 | 163 | Symbol '{0}' violates the backcompat requirement: 'Do not add multiple overloads with optional parameters'. See '{1}' for details. 164 | 165 | 166 | Do not add multiple public overloads with optional parameters 167 | 168 | 169 | Symbol '{0}' violates the backcompat requirement: 'Public API with optional parameter(s) should have the most parameters amongst its public overloads'. See '{1}' for details. 170 | 171 | 172 | Public API with optional parameter(s) should have the most parameters amongst its public overloads. 173 | 174 | -------------------------------------------------------------------------------- /PublicApiAnalyzer/stylecop.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", 3 | "settings": { 4 | "documentationRules": { 5 | "companyName": "Tunnel Vision Laboratories, LLC", 6 | "copyrightText": "Copyright (c) {companyName}. All Rights Reserved.\r\nLicensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.", 7 | "xmlHeader": false, 8 | "fileNamingConvention": "metadata" 9 | }, 10 | "layoutRules": { 11 | "newlineAtEndOfFile": "require" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /PublicApiAnalyzer/version.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/AArnott/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", 3 | "version": "1.0.0-beta.{height}", 4 | "assemblyVersion": { 5 | "precision": "revision" 6 | }, 7 | "publicReleaseRefSpec": [ 8 | "^refs/heads/master$" 9 | ], 10 | "nugetPackageVersion": { 11 | "semVer": 2 12 | }, 13 | "cloudBuild": { 14 | "buildNumber": { 15 | "enabled": true 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Public API Analyzer 2 | 3 | [![NuGet](https://img.shields.io/nuget/v/DotNetAnalyzers.PublicApiAnalyzer.svg)](https://www.nuget.org/packages/DotNetAnalyzers.PublicApiAnalyzer) [![NuGet Beta](https://img.shields.io/nuget/vpre/DotNetAnalyzers.PublicApiAnalyzer.svg)](https://www.nuget.org/packages/DotNetAnalyzers.PublicApiAnalyzer) 4 | 5 | [![Join the chat at https://gitter.im/DotNetAnalyzers/PublicApiAnalyzer](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/DotNetAnalyzers/PublicApiAnalyzer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 6 | 7 | [![Build status](https://ci.appveyor.com/api/projects/status/27963rsy48aseywm/branch/master?svg=true)](https://ci.appveyor.com/project/sharwell/publicapianalyzer/branch/master) 8 | 9 | [![codecov.io](http://codecov.io/github/DotNetAnalyzers/PublicApiAnalyzer/coverage.svg?branch=master)](http://codecov.io/github/DotNetAnalyzers/PublicApiAnalyzer?branch=master) 10 | 11 | ## Using Public API Analyzer 12 | 13 | The preferable way to use this package is to add the NuGet package [DotNetAnalyzers.PublicApiAnalyzer](http://www.nuget.org/packages/DotNetAnalyzers.PublicApiAnalyzer/) 14 | to the project where you want to enforce rules. 15 | 16 | The severity of individual rules may be configured using [rule set files](https://msdn.microsoft.com/en-us/library/dd264996.aspx) 17 | in Visual Studio 2015. 18 | 19 | ## Team Considerations 20 | 21 | If you use older versions of Visual Studio in addition to Visual Studio 2015, you may still install these analyzers. They will be automatically disabled when you open the project back up in Visual Studio 2013 or earlier. 22 | 23 | ## Contributing 24 | 25 | See [Contributing](CONTRIBUTING.md) 26 | -------------------------------------------------------------------------------- /THIRD-PARTY-NOTICES.txt: -------------------------------------------------------------------------------- 1 | PublicApiAnalyzer uses third-party libraries or other resources that may be 2 | distributed under licenses different than the PublicApiAnalyzer software. 3 | 4 | In the event that we accidentally failed to list a required notice, please 5 | bring it to our attention by posting an issue. 6 | 7 | The attached notices are provided for information only. 8 | 9 | 10 | License notice for .NET Compiler Platform ("Roslyn") 11 | ---------------------------------------------------- 12 | 13 | Copyright Microsoft. 14 | 15 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 16 | these files except in compliance with the License. You may obtain a copy of the 17 | License at 18 | 19 | http://www.apache.org/licenses/LICENSE-2.0 20 | 21 | Unless required by applicable law or agreed to in writing, software distributed 22 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 23 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 24 | specific language governing permissions and limitations under the License. 25 | 26 | 27 | License notice for StyleCop.Analyzers 28 | ------------------------------------- 29 | 30 | Copyright (c) Tunnel Vision Laboratories, LLC. All rights reserved. 31 | 32 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 33 | these files except in compliance with the License. You may obtain a copy of the 34 | License at 35 | 36 | http://www.apache.org/licenses/LICENSE-2.0 37 | 38 | Unless required by applicable law or agreed to in writing, software distributed 39 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 40 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 41 | specific language governing permissions and limitations under the License. 42 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 1.0.{build} 2 | os: Visual Studio 2017 3 | init: 4 | - git config --global core.autocrlf true 5 | configuration: 6 | - Debug 7 | - Release 8 | before_build: 9 | - nuget restore 10 | skip_tags: true 11 | build: 12 | project: PublicApiAnalyzer.sln 13 | verbosity: minimal 14 | test_script: 15 | - cd build 16 | - ps: | 17 | if ($env:Configuration -eq 'Debug') { 18 | .\opencover-report.ps1 -Debug -NoBuild -NoReport -AppVeyor 19 | if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } 20 | $packageConfig = [xml](Get-Content ..\.nuget\packages.config) 21 | $codecov_version = $packageConfig.SelectSingleNode('/packages/package[@id="Codecov"]').version 22 | $codecov = "..\packages\Codecov.$codecov_version\tools\codecov.exe" 23 | &$codecov -f '..\build\OpenCover.Reports\OpenCover.PublicApiAnalyzer.xml' 24 | } else { 25 | .\opencover-report.ps1 -NoBuild -NoReport -AppVeyor 26 | if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } 27 | } 28 | # preserve "packages" directory in the root of build folder but will reset it if packages.config is modified 29 | cache: 30 | - packages -> **\packages.config 31 | 32 | # 'Release' is hard-coded to ensure VSIX and NuGet artifacts are only published for release configuration builds 33 | artifacts: 34 | - path: 'PublicApiAnalyzer\PublicApiAnalyzer.Vsix\bin\Release\net452\*.vsix' 35 | - path: 'PublicApiAnalyzer\PublicApiAnalyzer.CodeFixes\bin\Release\*.nupkg' 36 | -------------------------------------------------------------------------------- /build/build.ps1: -------------------------------------------------------------------------------- 1 | param ( 2 | [switch]$Debug, 3 | [string]$VisualStudioVersion = '15.0', 4 | [string]$Verbosity = 'minimal', 5 | [string]$Logger, 6 | [switch]$Incremental 7 | ) 8 | 9 | # build the solution 10 | $SolutionPath = "..\PublicApiAnalyzer.sln" 11 | 12 | # make sure the script was run from the expected path 13 | if (!(Test-Path $SolutionPath)) { 14 | $host.ui.WriteErrorLine('The script was run from an invalid working directory.') 15 | exit 1 16 | } 17 | 18 | If ($Debug) { 19 | $BuildConfig = 'Debug' 20 | } Else { 21 | $BuildConfig = 'Release' 22 | } 23 | 24 | # download NuGet.exe if necessary 25 | $nuget = '..\.nuget\NuGet.exe' 26 | If (-not (Test-Path $nuget)) { 27 | If (-not (Test-Path '..\.nuget')) { 28 | mkdir '..\.nuget' 29 | } 30 | 31 | $nugetSource = 'https://dist.nuget.org/win-x86-commandline/latest/nuget.exe' 32 | Invoke-WebRequest $nugetSource -OutFile $nuget 33 | If (-not $?) { 34 | $host.ui.WriteErrorLine('Unable to download NuGet executable, aborting!') 35 | exit $LASTEXITCODE 36 | } 37 | } 38 | 39 | # build the main project 40 | $visualStudio = (Get-ItemProperty 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\SxS\VS7')."$VisualStudioVersion" 41 | $msbuild = "$visualStudio\MSBuild\$VisualStudioVersion\Bin\MSBuild.exe" 42 | If (-not (Test-Path $msbuild)) { 43 | $host.UI.WriteErrorLine("Couldn't find MSBuild.exe") 44 | exit 1 45 | } 46 | 47 | # Attempt to restore packages up to 3 times, to improve resiliency to connection timeouts and access denied errors. 48 | $maxAttempts = 3 49 | For ($attempt = 0; $attempt -lt $maxAttempts; $attempt++) { 50 | &$nuget 'restore' $SolutionPath 51 | If ($?) { 52 | Break 53 | } ElseIf (($attempt + 1) -eq $maxAttempts) { 54 | $host.ui.WriteErrorLine('Failed to restore required NuGet packages, aborting!') 55 | exit $LASTEXITCODE 56 | } 57 | } 58 | 59 | If ($Logger) { 60 | $LoggerArgument = "/logger:$Logger" 61 | } 62 | 63 | If ($Incremental) { 64 | $Target = 'build' 65 | } Else { 66 | $Target = 'rebuild' 67 | } 68 | 69 | &$msbuild '/nologo' '/m' '/nr:false' "/t:$Target" $LoggerArgument "/verbosity:$Verbosity" "/p:Configuration=$BuildConfig" "/p:VisualStudioVersion=$VisualStudioVersion" "/p:KeyConfiguration=$KeyConfiguration" $SolutionPath 70 | If (-not $?) { 71 | $host.ui.WriteErrorLine('Build failed, aborting!') 72 | exit $LASTEXITCODE 73 | } 74 | -------------------------------------------------------------------------------- /build/keys/PublicApiAnalyzer.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotNetAnalyzers/PublicApiAnalyzer/b5d6b3dc6e42f676f8764a2d1ebe6916baf766cc/build/keys/PublicApiAnalyzer.snk -------------------------------------------------------------------------------- /build/keys/TestingKey.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotNetAnalyzers/PublicApiAnalyzer/b5d6b3dc6e42f676f8764a2d1ebe6916baf766cc/build/keys/TestingKey.snk -------------------------------------------------------------------------------- /build/opencover-report.ps1: -------------------------------------------------------------------------------- 1 | param ( 2 | [switch]$Debug, 3 | [switch]$NoBuild, 4 | [switch]$NoReport, 5 | [switch]$AppVeyor 6 | ) 7 | 8 | If (-not $NoBuild) { 9 | # Run a build to ensure everything is up-to-date 10 | If ($Debug) { 11 | .\build.ps1 -Debug -Incremental 12 | } Else { 13 | .\build.ps1 -Incremental 14 | } 15 | 16 | If (-not $?) { 17 | $host.UI.WriteErrorLine('Build failed; coverage analysis aborted.') 18 | Exit $LASTEXITCODE 19 | } 20 | } 21 | 22 | If ($Debug) { 23 | $Configuration = 'Debug' 24 | } Else { 25 | $Configuration = 'Release' 26 | } 27 | 28 | $packageConfig = [xml](Get-Content ..\.nuget\packages.config) 29 | $opencover_version = $packageConfig.SelectSingleNode('/packages/package[@id="OpenCover"]').version 30 | $reportgenerator_version = $packageConfig.SelectSingleNode('/packages/package[@id="ReportGenerator"]').version 31 | $xunitrunner_version = $packageConfig.SelectSingleNode('/packages/package[@id="xunit.runner.console"]').version 32 | $pdb2pdb_version = $packageConfig.SelectSingleNode('/packages/package[@id="Microsoft.DiaSymReader.Pdb2Pdb"]').version 33 | 34 | $packages_folder = '..\packages' 35 | $opencover_console = "$packages_folder\OpenCover.$opencover_version\tools\OpenCover.Console.exe" 36 | $xunit_runner_console = "$packages_folder\xunit.runner.console.$xunitrunner_version\tools\xunit.console.x86.exe" 37 | $report_generator = "$packages_folder\ReportGenerator.$reportgenerator_version\tools\ReportGenerator.exe" 38 | $pdb2pdb = "$packages_folder\Microsoft.DiaSymReader.Pdb2Pdb.$pdb2pdb_version\tools\Pdb2Pdb.exe" 39 | $report_folder = '.\OpenCover.Reports' 40 | $symbols_folder = '.\OpenCover.Symbols' 41 | $target_dll = "..\PublicApiAnalyzer\PublicApiAnalyzer.Test\bin\$Configuration\net452\PublicApiAnalyzer.Test.dll" 42 | 43 | If (Test-Path $symbols_folder) { 44 | Remove-Item -Recurse -Force $symbols_folder 45 | } 46 | 47 | mkdir $symbols_folder | Out-Null 48 | 49 | function Convert-Coverage-Pdb() { 50 | param ( 51 | [string]$assembly, 52 | [string]$outputDir 53 | ) 54 | $sourceDir = [IO.Path]::GetDirectoryName($assembly) 55 | $outputName = [IO.Path]::ChangeExtension([IO.Path]::GetFileName($assembly), 'pdb') 56 | $sourcePdb = Join-Path $sourceDir $outputName 57 | $output = Join-Path $outputDir $outputName 58 | if (Test-Path $sourcePdb) { 59 | &$pdb2pdb $assembly /out $output 60 | 61 | # Workaround for https://github.com/OpenCover/opencover/issues/800 62 | Remove-Item $sourcePdb 63 | Copy-Item $assembly $outputDir 64 | } 65 | } 66 | 67 | function Extract-Coverage-Pdb() { 68 | param ( 69 | [string]$assembly, 70 | [string]$outputDir 71 | ) 72 | $sourceDir = [IO.Path]::GetDirectoryName($assembly) 73 | $outputName = [IO.Path]::ChangeExtension([IO.Path]::GetFileName($assembly), 'pdb') 74 | $intermediatePdb = Join-Path $sourceDir $outputName 75 | $output = Join-Path $outputDir $outputName 76 | if (-not (Test-Path $output)) { 77 | &$pdb2pdb $assembly /out $intermediatePdb /extract 78 | if (Test-Path $intermediatePdb) { 79 | &$pdb2pdb $assembly /pdb $intermediatePdb /out $output 80 | Remove-Item $intermediatePdb 81 | 82 | # Workaround for https://github.com/OpenCover/opencover/issues/800 83 | Copy-Item $assembly $outputDir 84 | } 85 | } 86 | } 87 | 88 | $target_dir = [IO.Path]::GetDirectoryName($target_dll) 89 | Get-ChildItem $target_dir -Filter *.dll | Foreach-Object { Convert-Coverage-Pdb -assembly $_.FullName -outputDir $symbols_folder } 90 | 91 | If (Test-Path $report_folder) { 92 | Remove-Item -Recurse -Force $report_folder 93 | } 94 | 95 | mkdir $report_folder | Out-Null 96 | 97 | If ($AppVeyor) { 98 | $AppVeyorArg = '-appveyor' 99 | } 100 | 101 | &$opencover_console ` 102 | -register:user ` 103 | -returntargetcode ` 104 | -hideskipped:All ` 105 | -filter:"+[PublicApi*]*" ` 106 | -excludebyattribute:*.ExcludeFromCodeCoverage* ` 107 | -excludebyfile:*\*Designer.cs ` 108 | -searchdirs:"$symbols_folder" ` 109 | -output:"$report_folder\OpenCover.PublicApiAnalyzer.xml" ` 110 | -target:"$xunit_runner_console" ` 111 | -targetargs:"$target_dll -noshadow $AppVeyorArg" 112 | 113 | If ($AppVeyor -and -not $?) { 114 | $host.UI.WriteErrorLine('Build failed; coverage analysis aborted.') 115 | Exit $LASTEXITCODE 116 | } 117 | 118 | If (-not $NoReport) { 119 | &$report_generator -targetdir:$report_folder -reports:$report_folder\OpenCover.*.xml 120 | $host.UI.WriteLine("Open $report_folder\index.htm to see code coverage results.") 121 | } 122 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | # https://docs.codecov.io/docs/codecov-yaml 2 | # https://github.com/codecov/support/wiki/Codecov-Yaml 3 | 4 | coverage: 5 | status: 6 | project: 7 | default: false 8 | patch: 9 | default: false 10 | fixes: 11 | - "build/PublicApiAnalyzer/::PublicApiAnalyzer/" 12 | 13 | comment: 14 | layout: "diff" 15 | 16 | flags: 17 | production: 18 | paths: 19 | - PublicApiAnalyzer/PublicApiAnalyzer/ 20 | - PublicApiAnalyzer/PublicApiAnalyzer.CodeFixes/ 21 | test: 22 | paths: 23 | - PublicApiAnalyzer/PublicApiAnalyzer.Test/ 24 | -------------------------------------------------------------------------------- /docs/RS0016.md: -------------------------------------------------------------------------------- 1 | # RS0016 2 | 3 | 🚧 See https://github.com/DotNetAnalyzers/PublicApiAnalyzer/issues/26 4 | -------------------------------------------------------------------------------- /docs/RS0017.md: -------------------------------------------------------------------------------- 1 | # RS0017 2 | 3 | 🚧 See https://github.com/DotNetAnalyzers/PublicApiAnalyzer/issues/26 4 | -------------------------------------------------------------------------------- /docs/RS0022.md: -------------------------------------------------------------------------------- 1 | # RS0022 2 | 3 | 🚧 See https://github.com/DotNetAnalyzers/PublicApiAnalyzer/issues/26 4 | -------------------------------------------------------------------------------- /docs/RS0024.md: -------------------------------------------------------------------------------- 1 | # RS0024 2 | 3 | 🚧 See https://github.com/DotNetAnalyzers/PublicApiAnalyzer/issues/26 4 | -------------------------------------------------------------------------------- /docs/RS0025.md: -------------------------------------------------------------------------------- 1 | # RS0025 2 | 3 | 🚧 See https://github.com/DotNetAnalyzers/PublicApiAnalyzer/issues/26 4 | -------------------------------------------------------------------------------- /docs/RS0026.md: -------------------------------------------------------------------------------- 1 | # RS0026 2 | 3 | 🚧 See https://github.com/DotNetAnalyzers/PublicApiAnalyzer/issues/26 4 | -------------------------------------------------------------------------------- /docs/RS0027.md: -------------------------------------------------------------------------------- 1 | # RS0027 2 | 3 | 🚧 See https://github.com/DotNetAnalyzers/PublicApiAnalyzer/issues/26 4 | -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "2.2.101" 4 | } 5 | } 6 | --------------------------------------------------------------------------------