Skip to content

Commit

Permalink
Add compiler support for UnconditionalSuppressMessage (#51792)
Browse files Browse the repository at this point in the history
Fixes #48885
  • Loading branch information
agocke authored Mar 19, 2021
1 parent 150ee54 commit 8cdc029
Show file tree
Hide file tree
Showing 4 changed files with 419 additions and 257 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Runtime.ExceptionServices;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp;
Expand All @@ -30,10 +32,41 @@ protected override Task VerifyAsync(string source, string language, DiagnosticAn

protected override bool ConsiderArgumentsForComparingDiagnostics => true;

private static readonly Lazy<ImmutableArray<MetadataReference>> s_references = new Lazy<ImmutableArray<MetadataReference>>(() =>
{
const string unconditionalSuppressMessageDef = @"
namespace System.Diagnostics.CodeAnalysis
{
[System.AttributeUsage(System.AttributeTargets.All, AllowMultiple=true, Inherited=false)]
public sealed class UnconditionalSuppressMessageAttribute : System.Attribute
{
public UnconditionalSuppressMessageAttribute(string category, string checkId)
{
Category = category;
CheckId = checkId;
}
public string Category { get; }
public string CheckId { get; }
public string Scope { get; set; }
public string Target { get; set; }
public string MessageId { get; set; }
public string Justification { get; set; }
}
}";
var compRef = CSharpCompilation.Create("unconditionalsuppress",
options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary),
syntaxTrees: new[] { CSharpSyntaxTree.ParseText(unconditionalSuppressMessageDef) },
references: new[] { TestBase.MscorlibRef }).EmitToImageReference();

return ImmutableArray.Create(TestBase.MscorlibRef, compRef, TestBase.ValueTupleRef);

}, System.Threading.LazyThreadSafetyMode.PublicationOnly);

private static Compilation CreateCompilation(string source, string language, string rootNamespace)
{
string fileName = language == LanguageNames.CSharp ? "Test.cs" : "Test.vb";
string projectName = "TestProject";
var references = s_references.Value;

var syntaxTree = language == LanguageNames.CSharp ?
CSharpSyntaxTree.ParseText(source, path: fileName) :
Expand All @@ -43,15 +76,15 @@ private static Compilation CreateCompilation(string source, string language, str
{
return CSharpCompilation.Create(
projectName,
syntaxTrees: new[] { syntaxTree },
references: new[] { TestBase.MscorlibRef, TestBase.ValueTupleRef });
syntaxTrees: new[] { syntaxTree, },
references: references);
}
else
{
return VisualBasicCompilation.Create(
projectName,
syntaxTrees: new[] { syntaxTree },
references: new[] { TestBase.MscorlibRef },
references: references,
options: new VisualBasicCompilationOptions(
OutputKind.DynamicallyLinkedLibrary,
rootNamespace: rootNamespace));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#nullable disable

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using Roslyn.Utilities;

Expand All @@ -32,9 +32,10 @@ private static bool TryGetTargetScope(SuppressMessageInfo info, out TargetScope
=> s_suppressMessageScopeTypes.TryGetValue(info.Scope ?? string.Empty, out scope);

private readonly Compilation _compilation;
private GlobalSuppressions _lazyGlobalSuppressions;
private GlobalSuppressions? _lazyGlobalSuppressions;
private readonly ConcurrentDictionary<ISymbol, ImmutableDictionary<string, SuppressMessageInfo>> _localSuppressionsBySymbol;
private ISymbol _lazySuppressMessageAttribute;
private StrongBox<ISymbol?>? _lazySuppressMessageAttribute;
private StrongBox<ISymbol?>? _lazyUnconditionalSuppressMessageAttribute;

private class GlobalSuppressions
{
Expand All @@ -48,7 +49,7 @@ public void AddCompilationWideSuppression(SuppressMessageInfo info)

public void AddGlobalSymbolSuppression(ISymbol symbol, SuppressMessageInfo info)
{
Dictionary<string, SuppressMessageInfo> suppressions;
Dictionary<string, SuppressMessageInfo>? suppressions;
if (_globalSymbolSuppressions.TryGetValue(symbol, out suppressions))
{
AddOrUpdate(info, suppressions);
Expand All @@ -68,7 +69,7 @@ public bool HasCompilationWideSuppression(string id, out SuppressMessageInfo inf
public bool HasGlobalSymbolSuppression(ISymbol symbol, string id, bool isImmediatelyContainingSymbol, out SuppressMessageInfo info)
{
Debug.Assert(symbol != null);
Dictionary<string, SuppressMessageInfo> suppressions;
Dictionary<string, SuppressMessageInfo>? suppressions;
if (_globalSymbolSuppressions.TryGetValue(symbol, out suppressions) &&
suppressions.TryGetValue(id, out info))
{
Expand Down Expand Up @@ -121,7 +122,7 @@ public Diagnostic ApplySourceSuppressions(Diagnostic diagnostic)
return diagnostic;
}

public bool IsDiagnosticSuppressed(Diagnostic diagnostic, out AttributeData suppressingAttribute)
public bool IsDiagnosticSuppressed(Diagnostic diagnostic, [NotNullWhen(true)] out AttributeData? suppressingAttribute)
{
SuppressMessageInfo info;
if (IsDiagnosticSuppressed(diagnostic, out info))
Expand Down Expand Up @@ -204,11 +205,11 @@ bool hasNamespaceSuppression(INamespaceSymbol namespaceSymbol, bool inImmediatel
}
}

private bool IsDiagnosticGloballySuppressed(string id, ISymbol symbolOpt, bool isImmediatelyContainingSymbol, out SuppressMessageInfo info)
private bool IsDiagnosticGloballySuppressed(string id, ISymbol? symbolOpt, bool isImmediatelyContainingSymbol, out SuppressMessageInfo info)
{
this.DecodeGlobalSuppressMessageAttributes();
return _lazyGlobalSuppressions.HasCompilationWideSuppression(id, out info) ||
symbolOpt != null && _lazyGlobalSuppressions.HasGlobalSymbolSuppression(symbolOpt, id, isImmediatelyContainingSymbol, out info);
var globalSuppressions = this.DecodeGlobalSuppressMessageAttributes();
return globalSuppressions.HasCompilationWideSuppression(id, out info) ||
symbolOpt != null && globalSuppressions.HasGlobalSymbolSuppression(symbolOpt, id, isImmediatelyContainingSymbol, out info);
}

private bool IsDiagnosticLocallySuppressed(string id, ISymbol symbol, out SuppressMessageInfo info)
Expand All @@ -217,20 +218,39 @@ private bool IsDiagnosticLocallySuppressed(string id, ISymbol symbol, out Suppre
return suppressions.TryGetValue(id, out info);
}

private ISymbol SuppressMessageAttribute
private ISymbol? SuppressMessageAttribute
{
get
{
if (_lazySuppressMessageAttribute == null)
if (_lazySuppressMessageAttribute is null)
{
_lazySuppressMessageAttribute = _compilation.GetTypeByMetadataName("System.Diagnostics.CodeAnalysis.SuppressMessageAttribute");
Interlocked.CompareExchange(
ref _lazySuppressMessageAttribute,
new StrongBox<ISymbol?>(_compilation.GetTypeByMetadataName("System.Diagnostics.CodeAnalysis.SuppressMessageAttribute")),
null);
}

return _lazySuppressMessageAttribute;
return _lazySuppressMessageAttribute.Value;
}
}

private void DecodeGlobalSuppressMessageAttributes()
private ISymbol? UnconditionalSuppressMessageAttribute
{
get
{
if (_lazyUnconditionalSuppressMessageAttribute is null)
{
Interlocked.CompareExchange(
ref _lazyUnconditionalSuppressMessageAttribute,
new StrongBox<ISymbol?>(_compilation.GetTypeByMetadataName("System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute")),
null);
}

return _lazyUnconditionalSuppressMessageAttribute.Value;
}
}

private GlobalSuppressions DecodeGlobalSuppressMessageAttributes()
{
if (_lazyGlobalSuppressions == null)
{
Expand All @@ -244,11 +264,15 @@ private void DecodeGlobalSuppressMessageAttributes()

Interlocked.CompareExchange(ref _lazyGlobalSuppressions, suppressions, null);
}
return _lazyGlobalSuppressions;
}

private bool IsSuppressionAttribute(AttributeData a)
=> a.AttributeClass == SuppressMessageAttribute || a.AttributeClass == UnconditionalSuppressMessageAttribute;

private ImmutableDictionary<string, SuppressMessageInfo> DecodeLocalSuppressMessageAttributes(ISymbol symbol)
{
var attributes = symbol.GetAttributes().Where(a => a.AttributeClass == this.SuppressMessageAttribute);
var attributes = symbol.GetAttributes().Where(a => IsSuppressionAttribute(a));
return DecodeLocalSuppressMessageAttributes(symbol, attributes);
}

Expand Down Expand Up @@ -284,7 +308,7 @@ private void DecodeGlobalSuppressMessageAttributes(Compilation compilation, ISym
{
Debug.Assert(symbol is IAssemblySymbol || symbol is IModuleSymbol);

var attributes = symbol.GetAttributes().Where(a => a.AttributeClass == this.SuppressMessageAttribute);
var attributes = symbol.GetAttributes().Where(a => IsSuppressionAttribute(a));
DecodeGlobalSuppressMessageAttributes(compilation, symbol, globalSuppressions, attributes);
}

Expand Down
Loading

0 comments on commit 8cdc029

Please sign in to comment.