Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add more warning suppression scenarios #1232

Merged
merged 6 commits into from
Jun 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/linker/Linker.Steps/MarkStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1445,8 +1445,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 @@ -41,20 +42,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 @@ -97,12 +116,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);
}
}
}