Skip to content

Commit

Permalink
Add more warning suppression scenarios (#1232)
Browse files Browse the repository at this point in the history
Co-authored-by: Vitek Karas <[email protected]>
  • Loading branch information
mateoatr and vitek-karas authored Jun 4, 2020
1 parent 9edcb49 commit ba6d3da
Show file tree
Hide file tree
Showing 5 changed files with 241 additions and 28 deletions.
4 changes: 2 additions & 2 deletions src/linker/Linker.Steps/MarkStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1435,8 +1435,8 @@ bool MarkSpecialCustomAttributeDependencies (CustomAttribute ca, ICustomAttribut
l.Parameters.Count == 1 && l.Parameters[0].ParameterType.IsTypeOf ("System", "Type"),
provider);
return true;
} else if (dt.Name == "UnconditionalSuppressMessageAttribute" && dt.Namespace == "System.Diagnostics.CodeAnalysis") {
_context.Suppressions.AddLocalSuppression (ca, provider);
} else if (UnconditionalSuppressMessageAttributeState.TypeRefHasUnconditionalSuppressions (dt)) {
_context.Suppressions.AddSuppression (ca, provider);
}

return false;
Expand Down
129 changes: 103 additions & 26 deletions src/linker/Linker/UnconditionalSuppressMessageAttributeState.cs
Original file line number Diff line number Diff line change
@@ -1,35 +1,36 @@
using System.Collections.Generic;
using System.Linq;
using Mono.Cecil;

namespace Mono.Linker
{
public class UnconditionalSuppressMessageAttributeState
{
private readonly LinkContext _context;
private readonly Dictionary<ICustomAttributeProvider, Dictionary<int, SuppressMessageInfo>> _localSuppressions;

private bool HasLocalSuppressions {
get {
return _localSuppressions.Count != 0;
}
}
private readonly Dictionary<ICustomAttributeProvider, Dictionary<int, SuppressMessageInfo>> _suppressions;
private HashSet<AssemblyDefinition> InitializedAssemblies { get; }

public UnconditionalSuppressMessageAttributeState (LinkContext context)
{
_context = context;
_localSuppressions = new Dictionary<ICustomAttributeProvider, Dictionary<int, SuppressMessageInfo>> ();
_suppressions = new Dictionary<ICustomAttributeProvider, Dictionary<int, SuppressMessageInfo>> ();
InitializedAssemblies = new HashSet<AssemblyDefinition> ();
}

public void AddLocalSuppression (CustomAttribute ca, ICustomAttributeProvider provider)
public void AddSuppression (CustomAttribute ca, ICustomAttributeProvider provider)
{
SuppressMessageInfo info;
if (!TryDecodeSuppressMessageAttributeData (ca, out info)) {
if (!TryDecodeSuppressMessageAttributeData (ca, out info))
return;
}

if (!_localSuppressions.TryGetValue (provider, out var suppressions)) {
AddSuppression (info, provider);
}

private void AddSuppression (SuppressMessageInfo info, ICustomAttributeProvider provider)
{
if (!_suppressions.TryGetValue (provider, out var suppressions)) {
suppressions = new Dictionary<int, SuppressMessageInfo> ();
_localSuppressions.Add (provider, suppressions);
_suppressions.Add (provider, suppressions);
}

if (suppressions.ContainsKey (info.Id))
Expand All @@ -40,20 +41,38 @@ public void AddLocalSuppression (CustomAttribute ca, ICustomAttributeProvider pr

public bool IsSuppressed (int id, MessageOrigin warningOrigin, out SuppressMessageInfo info)
{
if (HasLocalSuppressions && warningOrigin.MemberDefinition != null) {
IMemberDefinition memberDefinition = warningOrigin.MemberDefinition;
while (memberDefinition != null) {
if (IsLocallySuppressed (id, memberDefinition, out info))
return true;
info = default;
if (warningOrigin.MemberDefinition == null)
return false;

memberDefinition = memberDefinition.DeclaringType;
}
IMemberDefinition elementContainingWarning = warningOrigin.MemberDefinition;
ModuleDefinition module = GetModuleFromProvider (warningOrigin.MemberDefinition);
DecodeModuleLevelAndGlobalSuppressMessageAttributes (module);
while (elementContainingWarning != null) {
if (IsSuppressed (id, elementContainingWarning, out info))
return true;

elementContainingWarning = elementContainingWarning.DeclaringType;
}

info = default;
// Check if there's an assembly or module level suppression.
if (IsSuppressed (id, module, out info) ||
IsSuppressed (id, module.Assembly, out info))
return true;

return false;
}

private bool IsSuppressed (int id, ICustomAttributeProvider provider, out SuppressMessageInfo info)
{
info = default;
if (provider == null)
return false;

return (_suppressions.TryGetValue (provider, out var suppressions) &&
suppressions.TryGetValue (id, out info));
}

private static bool TryDecodeSuppressMessageAttributeData (CustomAttribute attribute, out SuppressMessageInfo info)
{
info = default;
Expand Down Expand Up @@ -96,12 +115,70 @@ private static bool TryDecodeSuppressMessageAttributeData (CustomAttribute attri
return true;
}

private bool IsLocallySuppressed (int id, ICustomAttributeProvider provider, out SuppressMessageInfo info)
public ModuleDefinition GetModuleFromProvider (ICustomAttributeProvider provider)
{
Dictionary<int, SuppressMessageInfo> suppressions;
info = default;
return _localSuppressions.TryGetValue (provider, out suppressions) &&
suppressions.TryGetValue (id, out info);
switch (provider.MetadataToken.TokenType) {
case TokenType.Module:
return provider as ModuleDefinition;
case TokenType.Assembly:
return (provider as AssemblyDefinition).MainModule;
case TokenType.TypeDef:
return (provider as TypeDefinition).Module;
case TokenType.Method:
case TokenType.Property:
case TokenType.Field:
case TokenType.Event:
return (provider as IMemberDefinition).DeclaringType.Module;
default:
return null;
}
}

private void DecodeModuleLevelAndGlobalSuppressMessageAttributes (ModuleDefinition module)
{
AssemblyDefinition assembly = module.Assembly;
if (!InitializedAssemblies.Contains (assembly)) {
LookForModuleLevelAndGlobalSuppressions (module, assembly);
foreach (var _module in assembly.Modules)
LookForModuleLevelAndGlobalSuppressions (_module, _module);
}
}

public void LookForModuleLevelAndGlobalSuppressions (ModuleDefinition module, ICustomAttributeProvider provider)
{
var attributes = provider.CustomAttributes.
Where (a => TypeRefHasUnconditionalSuppressions (a.AttributeType));
foreach (var instance in attributes) {
SuppressMessageInfo info;
if (!TryDecodeSuppressMessageAttributeData (instance, out info))
continue;

if (info.Target == null && (info.Scope == "module" || info.Scope == null)) {
AddSuppression (info, provider);
continue;
}

if (info.Target == null || info.Scope == null)
continue;

switch (info.Scope) {
case "type":
case "member":
foreach (var result in DocumentationSignatureParser.GetMembersForDocumentationSignature (info.Target, module))
AddSuppression (info, provider);

break;
default:
_context.LogMessage (MessageContainer.CreateInfoMessage ($"Scope `{info.Scope}` used in `UnconditionalSuppressMessage` is currently not supported."));
break;
}
}
}

public static bool TypeRefHasUnconditionalSuppressions (TypeReference typeRef)
{
return typeRef.Name == "UnconditionalSuppressMessageAttribute" &&
typeRef.Namespace == "System.Diagnostics.CodeAnalysis";
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using Mono.Linker.Tests.Cases.Expectations.Assertions;
using Mono.Linker.Tests.Cases.Expectations.Metadata;
using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;

[assembly: UnconditionalSuppressMessage ("Test", "IL2006:Suppress unrecognized reflection pattern warnings in this assembly")]

namespace Mono.Linker.Tests.Cases.WarningSuppression
{
[SkipKeptItemsValidation]
[LogDoesNotContain ("TriggerUnrecognizedPattern()")]
public class SuppressWarningsInAssembly
{
public static void Main ()
{
Expression.Call (TriggerUnrecognizedPattern (), "", Type.EmptyTypes);
}

public static Type TriggerUnrecognizedPattern ()
{
return typeof (SuppressWarningsInAssembly);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
using Mono.Linker.Tests.Cases.Expectations.Assertions;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;
using System.Text;

[module: UnconditionalSuppressMessage ("Test", "IL2006", Scope = "type", Target = "T:Mono.Linker.Tests.Cases.WarningSuppression.WarningsInType")]
[module: UnconditionalSuppressMessage ("Test", "IL2006", Scope = "member", Target = "M:Mono.Linker.Tests.Cases.WarningSuppression.WarningsInMembers.Method")]
[module: UnconditionalSuppressMessage ("Test", "IL2006", Scope = "member", Target = "M:Mono.Linker.Tests.Cases.WarningSuppression.WarningsInMembers.get_Property")]

namespace Mono.Linker.Tests.Cases.WarningSuppression
{
[SkipKeptItemsValidation]
[LogDoesNotContain ("TriggerUnrecognizedPattern()")]
public class SuppressWarningsInMembersAndTypesUsingTarget
{
public static void Main ()
{
var warningsInType = new WarningsInType ();
warningsInType.Warning1 ();
warningsInType.Warning2 ();
var warningInNestedType = new WarningsInType.NestedType ();
warningInNestedType.Warning3 ();

var warningsInMembers = new WarningsInMembers ();
warningsInMembers.Method ();
int propertyThatTriggersWarning = warningsInMembers.Property;
}

public static Type TriggerUnrecognizedPattern ()
{
return typeof (SuppressWarningsInMembersAndTypesUsingTarget);
}

public class NestedType
{
public static void Warning ()
{
Expression.Call (TriggerUnrecognizedPattern (), "", Type.EmptyTypes);
}
}
}

public class WarningsInType
{
public void Warning1 ()
{
Expression.Call (SuppressWarningsInMembersAndTypesUsingTarget.TriggerUnrecognizedPattern (), "", Type.EmptyTypes);
}

public void Warning2 ()
{
Expression.Call (SuppressWarningsInMembersAndTypesUsingTarget.TriggerUnrecognizedPattern (), "", Type.EmptyTypes);
}

public class NestedType
{
public void Warning3 ()
{
void Warning4 ()
{
Expression.Call (SuppressWarningsInMembersAndTypesUsingTarget.TriggerUnrecognizedPattern (), "", Type.EmptyTypes);
}

SuppressWarningsInMembersAndTypesUsingTarget.TriggerUnrecognizedPattern ();
Warning4 ();
}
}
}

public class WarningsInMembers
{
public void Method ()
{
Expression.Call (SuppressWarningsInMembersAndTypesUsingTarget.TriggerUnrecognizedPattern (), "", Type.EmptyTypes);
}

public int Property {
get {
Expression.Call (SuppressWarningsInMembersAndTypesUsingTarget.TriggerUnrecognizedPattern (), "", Type.EmptyTypes);
return 0;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using Mono.Linker.Tests.Cases.Expectations.Assertions;
using Mono.Linker.Tests.Cases.Expectations.Metadata;
using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;

[module: UnconditionalSuppressMessage ("Test", "IL2006:Suppress unrecognized reflection pattern warnings in this module")]

namespace Mono.Linker.Tests.Cases.WarningSuppression
{
[SkipKeptItemsValidation]
[LogDoesNotContain ("TriggerUnrecognizedPattern()")]
public class SuppressWarningsInModule
{
public static void Main ()
{
Expression.Call (TriggerUnrecognizedPattern (), "", Type.EmptyTypes);
}

public static Type TriggerUnrecognizedPattern ()
{
return typeof (SuppressWarningsInModule);
}
}
}

0 comments on commit ba6d3da

Please sign in to comment.