Making extension methods from a third party library obsolete - c#

This question is not about a method I can mark with [System.Obsolete]. The method I wanna ignore is in a dll I don't have control over.
I use a 3rd party library which contains an extension method for objects. This leads to confusion and may cause problems in the future.
Is there any way to mark this extension method (or all the extension methods from a certain dll ) as obsolete externally or prevent this extension method appearing in intellisense. The problematic method is :
public static class ExtensionMethods
{
public static bool IsNumeric(this object obj)
{
if (obj == null)
return false;
return obj.GetType().IsPrimitive || obj is double || (obj is Decimal || obj is DateTime) || obj is TimeSpan;
}
}

You can do this with a Roslyn Code Analyzer. The following code will create a DiagnosticAnalyzer that will give a compiler warning if String.EndsWith() is used.
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ForbiddenMethodsAnalyzer : DiagnosticAnalyzer
{
private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor("Forbidden",
"Don't use this method!",
"Use of the '{0}' method is not allowed",
"Forbidden.Stuff",
DiagnosticSeverity.Warning,
isEnabledByDefault: true,
description: "This method is forbidden");
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } }
public override void Initialize(AnalysisContext context)
{
context.RegisterSyntaxNodeAction(AnalyzeSyntaxNode, SyntaxKind.InvocationExpression);
}
private static void AnalyzeSyntaxNode(SyntaxNodeAnalysisContext context)
{
var invocationExpression = (InvocationExpressionSyntax)context.Node;
var memberAccessExpression = invocationExpression.Expression as MemberAccessExpressionSyntax;
if (memberAccessExpression?.Name.ToString() == "EndsWith")
{
var memberSymbol = context.SemanticModel.GetSymbolInfo(memberAccessExpression).Symbol as IMethodSymbol;
var containingType = memberSymbol.ContainingType;
if (containingType.ContainingNamespace.Name == "System" && containingType.Name == "String")
{
var diagnostic = Diagnostic.Create(Rule, invocationExpression.GetLocation(), memberAccessExpression.ToString());
context.ReportDiagnostic(diagnostic);
}
}
}
}
There are 3 options to use an Analyzer like this:
Add the DiagnosticAnalyzer code directly to your project. It will apply only to that solution.
Create a
class library with the DiagnosticAnalyzer in it, and distribute it
as a Nuget package. It will apply only to solutions that use the package.
Compile a full VSIX extension containing the
class. The analyzer will work on any solution you load.
This is the first project I've done that uses the Roslyn Code Analysis functionality, so unfortunately I don't understand everything that is going on here. I started with the default Analyzer template and tried various methods, stepped through code, and looked at variables with the watch windows until I found the information I needed for this functionality.
The basic process is to register a SyntaxNode Analysis function, filtered to expressions that invoke a method. Within that method I check to see if the Name of the MemberAccessExpressionSyntax being examined is "EndsWith". If it is, I get the ContainingType that the method is on, and check to see if it is on the String class in the System namespace. If it is, I create a Diagnostic instance from a DiagnosticDescriptor to tell the IDE where the problem is, and how much of a problem it represents (A warning in this case, I could make it a full Error if I wanted, which would prevent the code from compiling). It is also possible to present the user with different options to automatically fix the error, but I haven't explored that yet.
A lot of the information came from this tutorial, as well as a whole lot of trial and error.

The best way to handle this would be to use Roslyn and create your own code analyzer, or use an existing tool like FxCop.
However, I found a very non-elegant workaround for this.
In your project you can create a class with the same name as the referenced class, residing in an identical namespace, with the exact same method. Now mark your method as obsolete.
The below code sample has a reference to a library with an ExtensionMethods class which is defined in the External namespace. In the line marked with (*) comment, where the method is called using the static method call syntax, the compiler warns you that the type ExtensionMethods conflicts with an imported type. It also tells you that the method is obsolete (since you have shadowed the imported type, it sees your definition). So when you invoke the method, your code will run. In the line marked with (**) comment, where the method is called using the extension method call syntax, the compiler says that the call is ambiguous and the code won't compile. The only workaround I know of is to turn this call into line (*), which will produce the obsolete warning.
With this solution you will be able to call other extension methods from the referenced type if you use the extension method syntax, provided you don't have the same method defined in your class.
using System;
using External;
namespace Internal
{
class Program
{
static void Main(string[] args)
{
ExtensionMethods.IsNumeric(new object()); // (*)
new object().IsNumeric(); // (**)
}
}
}
namespace External
{
public static class ExtensionMethods
{
[Obsolete]
public static bool IsNumeric(this object o)
{
if (obj == null)
return false;
return obj.GetType().IsPrimitive || obj is double || (obj is Decimal || obj is DateTime) || obj is TimeSpan;
}
}
}

Related

SyntaxNode.ContainsDiagnostics not working with your own diagnostics?

I am trying to write an analyzer that prevents users from providing parameters that are automatically provided (ex: by the compiler with [CallerMemberName]) and I want the analyzer to error you when you have provided a parameter that shouldn't be provided (to tell a parameter shouldn't be provided I created an attribute : DontProvideAttribute).
The thing is such automatically provided parameters must be optional (otherwise the value provided by the user would be written over the one provided automatically) so I've made a second analyze to prevent users from using [DontProvide] on non-optional parameters.
And there comes the problem, I want the error on the method invocation to be there only if the parameter declaration doesn't have the [DontProvide] should only be used on optional parameters error which
foreach (SyntaxReference parameterDefinition in parameter.DeclaringSyntaxReferences)
{
if (parameterDefinition.GetSyntax().ContainsDiagnostics)
{
return;
}
}
should complete this but it seems like it doesn't consider diagnostics you reported yourself.
What i have tried :
-Changing the order of diagnostics to make the declaration being analyzed before the method invocation
-Use .GetDiagnostics().Count() > 0 instead
-Changing the order of the text in the analyzed document to have the method declaration above the method invocation
The analyzer :
public override void Initialize(AnalysisContext context)
{
context.RegisterSymbolAction(AnalyzeParametersDeclaration, SymbolKind.Parameter);
context.RegisterOperationAction(AnalyzeArguments, OperationKind.Argument);
}
private void AnalyzeArguments(OperationAnalysisContext context)
{
IArgumentOperation reference = (IArgumentOperation)context.Operation;
IParameterSymbol parameter = reference.Parameter;
foreach (SyntaxReference parameterDefinition in parameter.DeclaringSyntaxReferences)
{
if (parameterDefinition.GetSyntax().ContainsDiagnostics)
return;
}
foreach (AttributeData attribute in parameter.GetAttributes())
{
if (attribute.AttributeClass.Name == "DontProvideAttribute")
{
context.ReportDiagnostic(Diagnostic.Create(DontProvide, reference.Syntax.GetLocation(), parameter.Name));
}
}
}
private void AnalyzeParametersDeclaration(SymbolAnalysisContext context)
{
IParameterSymbol parameter = (IParameterSymbol)context.Symbol;
if (parameter.GetAttributes().Any(a => a.AttributeClass.Name == "DontProvideAttribute") && !parameter.IsOptional)
{
context.ReportDiagnostic(Diagnostic.Create(DontProvideOnlyForOptional, parameter.Locations[0]))
}
}
Some test code for analyze :
using System;
namespace test
{
internal class Program
{
private static void Main(string[] args)
{
MyClass.MyMethod(null);
}
}
internal class MyClass
{
public static void MyMethod([DontProvide] object parameter)
{
}
}
[AttributeUsage(AttributeTargets.Parameter)]
public class DontProvideAttribute : Attribute
{
}
}
PS : The compiler may tell you that context.RegisterSymbolAction() used with SymbolKind.Parameter isn't supported, which is wrong (see more here)
From the discussion here and #Kris Vandermotten's comment
ContainsDiagnostics is only for syntactic diagnostics (i.e. diagnostics acutally inside the syntax tree)
not for diagnostics reported by later passes (i.e. semantic diagnostics or your own analyzer diagnostics).
Here's why : a specific syntax tree may be contained in many different semantic contexts due to roslyn being able to fork and speculate about things
so in one context, the syntax may be semantically correct, and in another, it won't be
as such, the diagnostics are not stored on the tree itself.
In fact the solution in my case was pretty simple : i just had to remove
foreach (SyntaxReference parameterDefinition in parameter.DeclaringSyntaxReferences)
{
if (parameterDefinition.GetSyntax().ContainsDiagnostics)
return;
}
and add && parameter.IsOptionnal on the if statement there :
foreach (AttributeData attribute in parameter.GetAttributes())
{
if (attribute.AttributeClass.Name == "DontProvideAttribute")
{
context.ReportDiagnostic(Diagnostic.Create(DontProvide, reference.Syntax.GetLocation(), parameter.Name));
}
}

Access Invocations reference using Roslyn

I want to create a Visual Studio Extension(VSIX) using Roslyn. This extension should find all invocations, then looks for their definitions to analyse attributes defined on them. The definition of the method can be any where in the solution.
My first class is like this:
namespace MainProject
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(BusinessProject.Calc.AddNumbers(5, 8));
}
}
}
My second class is something like the following:
namespace BusinessProject
{
public class Calc
{
[CustomAttr("Do something")]
public static long AddNumbers(int a, int b)
{
return a + b;
}
}
}
In above sample, in class Program, I have an invocation of AddNumbers method. I want to 1) analyse the code, 2) find this invocation, 3) get all attributes of the reference method in class Calc, 4) process attributes parameters, 5) and then make a warning/error if needed.
I can analyse current class to find all invocations by RegisterCodeBlockAction, But what I can't do is accessing the entire solution to find definition of the invocation and after that, accessing attributes of the reference.
How can I access the entire solution in RegiserCodeBlockAction?
You're calling the wrong method.
You actually want your analyzer to run on every method invocation, not every code block.
Therefore, you should call RegisterSyntaxNodeAction, and pass SyntaxKind.InvocationExpression.
It will then call you with an InvocationExpressionSyntax which has all the information you need (mostly in the Semantic Model).
As #SLaks mentioned in his comment, by using SemanticModel.Compilation.SyntaxTrees we can access all of the source codes in solution. I found the method and its parameters. But this has a problem which you can't get the TypeInfo of objects using SemanticModel.GetTypeInfo. You have to create a new Semantic Model like as follows:
foreach (var tree in context.SemanticModel.Compilation.SyntaxTrees)
{
var compilation = CSharpCompilation.Create("MyCompilation",
syntaxTrees: new[] { tree }, references: new List<MetadataReference>());
var syntaxRoot = tree.GetRoot();
var model = compilation.GetSemanticModel(tree);
var targetMethod = syntaxRoot.DescendantNodes().OfType<MethodDeclarationSyntax>().FirstOrDefault(f => f.Identifier.ToString() == "Class name to find");
if (targetMethod == null)
continue;
var typeInfo = model.GetTypeInfo(targetMethod.First().ParameterList.Parameters[i].ChildNodes().First());
// Do any thing with typeInfo
}

Disable auto-generation of [Serializable] attribute by VS2015 C# compiler

VS2015 C# compiler emits [Serializable, CompilerGenerated] attributes on all auto-generated helper classes for LINQ queries. Prior versions of Visual Studio used to emit only [CompilerGenerated] attribute on such classes.
Is there a way to disable auto-generation of [Serializable] attribute in VS2015? The reason is that our obfuscator refuses to rename classes marked as [Serializable], although in our case it is 100% safe because we don't use serialization.
Here is the example of auto code generated by older compilers:
[CompilerGenerated]
private sealed class <>c
{
public static readonly Program.<>c <>9 = new Program.<>c();
// ... more stuff here
}
Here is what VS2015 emits:
[Serializable, CompilerGenerated]
private sealed class <>c
{
public static readonly Program.<>c <>9 = new Program.<>c();
// ... more stuff here
}
I want the compiler to stop generating that [Serializable] attribute. Is it possible?
I hesitated to post this... maybe i'm wrong, but i'm sure this can lead to the right direction.
So Roslyn is the compiler which generates the code (that's the default compiler in VS2015), and it's open source (see here). The LabdaRewriter.cs (here) contains the logic which rewrites the lambda into the class and does all the magic. In the GetStaticFrame method there is this line:
_lazyStaticLambdaFrame = new LambdaFrame(_topLevelMethod, scopeSyntaxOpt: null, methodId: methodId, closureId: closureId);
Now LamdaFrame is the class which represents the generated class (see here) (in your case <>c which gets the SerializableAttribute).
Remember the scopeSyntaxOpt: null part from the constructor parameter!
Here is the constructor:
internal LambdaFrame(MethodSymbol topLevelMethod, CSharpSyntaxNode scopeSyntaxOpt, DebugId methodId, DebugId closureId)
: base(MakeName(scopeSyntaxOpt, methodId, closureId), topLevelMethod)
{
_topLevelMethod = topLevelMethod;
_constructor = new LambdaFrameConstructor(this);
this.ClosureOrdinal = closureId.Ordinal;
// static lambdas technically have the class scope so the scope syntax is null
if (scopeSyntaxOpt == null)
{
_staticConstructor = new SynthesizedStaticConstructor(this);
var cacheVariableName = GeneratedNames.MakeCachedFrameInstanceFieldName();
_singletonCache = new SynthesizedLambdaCacheFieldSymbol(this, this, cacheVariableName, topLevelMethod, isReadOnly: true, isStatic: true);
}
...
}
And there is one property called IsSerializable:
// display classes for static lambdas do not have any data and can be serialized.
internal override bool IsSerializable
{
get { return (object)_singletonCache != null; }
}
As you see in the constructor this _singletonCache is basically never null when the instance is created in the GetStaticFrame method (I think this is the case where you do not capture values from the outside in you LINQ expression (FIXME)) , therefore it returns true, and I think based on this property the generated class get's the SerializableAttribute.
So I think at the moment this is hard-coded and you cannot change this, unless you touch the Roslyn source code and improve this....
I ended up writing a simple dnlib-based tool that parses compiled assembly and changes the Serialized property of every class with the [CompilerGenerated] attribute to false. Seems to work well enough for my needs.

Object instantiation fails when using overloaded constructor

I recently stumbled upon an odd issue which I could not explain and I would be glad if someone could clarify why it happens.
The issue I've encountered is as follows:
I have an interface which is implemented, like so:
namespace InterfaceTwo
{
public interface IA { }
}
namespace InterfaceTwo
{
public class A : IA { }
}
And another interface which is implemented in a different project, like so:
namespace InterfaceOne
{
public interface IB { }
}
namespace InterfaceOne
{
public class B : IB { }
}
I have an object which uses those interfaces in it's constructors, like so:
using InterfaceOne;
using InterfaceTwo;
namespace MainObject
{
public class TheMainObject
{
public TheMainObject(IA iaObj) { }
public TheMainObject(IB iaObj) { }
}
}
And finally, I have a class which aggregates the above object, like so:
using InterfaceTwo;
using MainObject;
namespace ReferenceTest
{
public class ReferenceTest
{
public void DoSomething()
{
var a = new A();
var theMainObject = new TheMainObject(a);
}
}
}
Oddly, this code won't compile with the following error:
The type 'InterfaceOne.IB' is defined in an assembly that is not referenced.
You must add a reference to assembly 'InterfaceOne, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
c:\users\harry.baden\documents\visual studio 2013\Projects\ReferenceTest\ReferenceTest\ReferenceTest.cs 11 13 ReferenceTest
I also found that if I change one of the overloads to contain an extra parameter - it does compile... What got me thinking that the problem might be related to some sort of reflection issue which the compiler is running.
Thanks,
Barak.
namespace dependency issue. The error message pretty mush said it: your TheMainObject depends on InterfaceOne and must be properly referenced
this isn't directly related to constructor overloading...
Update:
It is more of a compiler behavior. To determine which overloaded method to use, the compiler has to
check all methods with the same name and same # of parameters to see if all the parameter types are referenced
then pick one method that matches the caller's parameter type (explicitly or implicitly).
We can verify step 1 and step 2 are separated with the following code:
using InterfaceOne;
using InterfaceTwo;
namespace MainObject
{
public class TheMainObject
{
public TheMainObject(IA obj) { }
public TheMainObject(IB obj, int x) { }
}
}
using InterfaceTwo;
using MainObject;
namespace ReferenceTest
{
public class ReferenceTest
{
public static void DoSomething()
{
var a = new A();
var theMainObject = new TheMainObject(a); //no error
}
}
}
The above code compiles because TheMainObject(IB obj, int x) is not a candidate for new TheMainObject(a). However if the constructor is defined as
public TheMainObject(IB obj) { }
or
public TheMainObject(IB obj, int x = 0) { }
a reference to InterfaceTwo.IB is required.
You can avoid this kind of reference check by calling the constructor at run-time, but this is error-prone and you should be cautious. For example:
public static void DoSomething()
{
var a = new A();
TheMainObject theMainObject = null;
var ctor = typeof (TheMainObject).GetConstructor(new[] {typeof (IA)});
if (ctor != null) {
theMainObject = (TheMainObject) ctor.Invoke(new object[] {a});
}
}
I did a little more research and found the following resources. Basically the type widening/narrowing step needs to know about all the types involved. (The VB version is just for reference because the C# spec is for VS.Net 2003).
Overload Resolution C#
Overload Resolution Visual Basic
See this for an explanation of a similar problem that I encountered. To quote the answer from the link:
The C# standard specifies that overload resolution (section 7.5.3) is performed by comparing each matching signature to determine which is a better fit. It doesn't say what happens when a reference is missing, so we must infer that it still needs to compare those unreferenced types.
In your example, it should be evident what overload you're using, but the compiler is not smart enough and will still try to compare both overloads, which is why both references are required.
Perhaps the easiest - but not the prettiest - solution (if you don't want to include the missing reference - which you may have a good reason not to) is to add an additional dummy parameter, effectively making it obvious to the compiler which overload you're calling; or transforming the two TheMainObject constructors into two methods with different names, e.g. TheMainObjectA(IA iaObj) and TheMainObjectB(IB ibObj) - i.e. avoiding overloading altogether.
Another possible solution is to use the dynamic keyword (for .NET 4.0 and above), although some people might discourage this as it can result in runtime errors if you're not careful:
public class TheMainObject
{
public TheMainObject(dynamic obj)
{
if (obj is IA)
{
// work with IA ...
}
else if (obj is IB)
{
// work with IB ...
}
else
{
// exception ...
}
}
}
This way, the compiler doesn't generate an error since the obj parameter is evaluated at runtime - your original code will work. If you choose to use this solution, also consider checking for RuntimeBinderException to avoid accidentally accessing invalid (non-existent) members of the dynamic type.

How to get non-static MethodInfo using method name (not in string, not searching in class type)

Consider following code:
public class Test
{
[System.AttributeUsage(System.AttributeTargets.Method)]
class MethodAttribute : System.Attribute
{
public MethodAttribute(System.Reflection.MethodInfo methodInfo)
{
}
}
public void Foo()
{
}
[Method(Test.Foo)] //< THIS IS THE IMPORTANT LINE
public void Boo()
{
}
}
I want to store MethodInfo instance of Foo in attribute of Boo, but problem is, that I cannot use Foo nor Test.Foo to get instance of MethodInfo. I can NOT use typeof(Test).GetMethod("Foo") (not my decision).
Important information:
Generated error is: error CS0120: An object reference is required to access non-static member `Test.Foo()'
I'm coding in Unity3D which is using Mono, not .Net. (more info http://docs.unity3d.com/410/Documentation/ScriptReference/MonoCompatibility.html )
Windows 7
Absolutely unimportant information:
Why I cannot use typeof(Test).GetMethod("Foo"): I'm not allowed. It's not in my power to change this decision. I can not. (Also, personally, I would like to avoid it anyway as it needs to do some lookup instead of getting statically the data. Also it won't be changed automatically during refactoring and will be checking run-time, not compile-time.)
Why I want to do this: later in code, I want to create a delegate of Boo() (this time normally, with an instance) and use in special even system or something. Before it's called, this attribute allows to setup method to be called in prepare for main event (it's optional, it can be null) and I know how to create a delegate when I have an object and a method info.
Why I cannot just provide both delegates when registering or something: because class containing these methods is not always the one who registers to event source, I need to keep the registration code as simple as possible. In other words, I want to avoid situation when person writing method responsible for connecting forgots to add this preparation delegate.
use expression :
static public class Metadata<T>
{
static public PropertyInfo Property<TProperty>(Expression<Func<T, TProperty>> property)
{
var expression = property.Body as MemberExpression;
return expression.Member as PropertyInfo;
}
}
var foo = Metadata<Test>.Property(test => test.Foo);

Categories

Resources