I'm writing the roslyn analyzer, that should check if method's parameter declared with [NotNull] attribute in current method declaration or in one of interfaces and makes some checks it method body. I registered the CodeBlockAction with RegisterCodeBlockAction, but when I trying to get attributes from parameter declaration in interfaces/base classes, that has it, sometimes it returns empty array.
I discovered, that is happens in case, if interface/base class is located in other assembly, and actualy analyzer works fine, when Intelisense runs it, but there is no warnings\errors in build output. I think this happens because of sematic analysis of referenced assembly is not completly finished (but it's a bit strange).
I wrote some logs
6/6/2019 13:59:47 Analize method symbol "ClassLibrary1.Program.Foo(string)" with 1 interfaces
6/6/2019 13:59:47 declaration ClassLibrary2.IFoo.Foo(string): [0 attributes] string s
6/6/2019 13:59:47 declaration ClassLibrary1.Program.Foo(string): [0 attributes] string s
6/6/2019 13:59:59 Analize method symbol "ClassLibrary1.Program.Foo(string)" with 1 interfaces
6/6/2019 13:59:59 declaration ClassLibrary2.IFoo.Foo(string): [1 attributes] string s
6/6/2019 13:59:59 declaration ClassLibrary1.Program.Foo(string): [0 attributes] string s
So you can see, that at 13:59:47 (msbuild run) there are no attributes, but at 13:59:59 (I opened document in Visual studio) there is one attribute.
There is how I get the iterfaces and parameters:
var allMethodDeclarations = //some code using methodSymbol.ContainingType.Interfaces
for (var i = 0; i < methodSymbol.Parameters.Length; ++i)
{
var currentParameter = methodSymbol.Parameters[i];
//parameters can be renamed, the only way is to use the order
var hasNotNull = allMethodDeclarations
.Select(d => d.Parameters[i])
.SelectMany(p => p.GetAttributes())
.Any(a => a.AttributeClass.Name == nameof(NotNullAttribute));
if (hasNotNull)
{
//do something
}
}
Example code on which bug is reproduced:
In assembly 1
public interface IFoo
{
void Foo([NotNull] string s);
}
In assembly 2, that references assembly 1
public class Program : IFoo
{
public void Foo(string s)
{
}
}
Okay, I got it. It was because of conditional compilation attribute on NotNullAttribute from Jetbrains.Annotations, so the compiler shows data from the actual referenced symbols (where it skips [NotNull] because JETBRAINS_ANNOTATIONS was not defined), and Visual Studio provides the real attributes list from code, and conditional compilation is not considered this way.
It looks inconsistent, but I had to define JETBRAINS_ANNOTATIONS in my project to make analyzer work.
Related
Consider this code:
using System.Linq;
namespace ExtensionMethodIssue
{
static class Program
{
static void Main(string[] args)
{
var a = new[] { 1 };
var b = new[] { 1, 2 }.Where(a.Contains).ToList();
var c = new[] { 1, 2 }.Where(i => a.Contains(i)).ToList();
}
}
}
The code compiles successfully.
Then I'm adding the nuget package "itext7 7.0.4", and now the compilation fails because of:
//Error CS0122: 'KernelExtensions.Contains<TKey, TValue>(IDictionary<TKey, TValue>, TKey)' is inaccessible due to its protection level
var b = new[] { 1, 2, 3 }.Where(a.Contains).ToList();
// This is still ok.
var c = new[] { 1, 2, 3 }.Where(i => a.Contains(i)).ToList();
The reason is that the itext7 library has an internal class with extension methods in the global namespace (here it is).
internal static class KernelExtensions {
public static bool Contains<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key) {
return dictionary.ContainsKey(key);
}
}
For some reason the compiler chooses an inaccessible extension method with an incompatible signature from the global namespace instead of the accessible extension method with a compatible signature from the LINQ namespace.
The question is: is this behavior expected in terms of the language specification or is it a bug in the compiler? And why does it fail only in the case with a "method group" and still work with i => a.Contains(i)?
That is a compiler bug, an inaccessible function should not affect overload resolution.
As a general workaround use lambdas as an argument instead of a method groups because overload resolution seems to work fine with them. It is not obvious which is faster/more efficient in your particular scenario, micro-optimize as needed using relevant performance metrics.
In this particular case you can also use other extension methods like Enumerable.Intersect() if you are working with sets, and aren't concerned about duplicates, Enumerable.Join() or a simple loop.
For more information check:
C# compiler chooses inaccessible extension method over accessible one Roslyn issue on GitHub
Is there any overhead in the use of anonymous methods? discussion on StackOverflow
Is a LINQ statement faster than a 'foreach' loop? discussion on StackOverflow
For vs. Linq - Performance vs. Future discussion on StackOverflow
class Program
{
static void Main(string[] args)
{
var x = new Program();
Console.Write(x.Text);
Console.Write(x.Num);
//Console.Write(x.Num);//line A
}
private string Text_;
public string Text
{
get
{
return Text_ ?? (Text_ = "hello");//line B
}
}
private int? Num_;
public int Num
{
get
{
return (int)(Num_ ?? (Num_ = 42));//line C
}
}
}
I'm using Visual Studio 2010 to get code coverage results. It shows that line B is totally covered while line C is partially covered. I would expect that line B is partially covered as well instead of being totally covered. Why is it the case that the code coverage results shows line B as being totally covered?
To demonstrate that it's working "correctly" for Num property, uncomment line A and run the coverage. It should show line C as totally being covered.
When I rewrite the code to a more verbose form (see below), it works correctly and reports that Text_ is partially covered. I prefer to use the former for its brevity, and would like to know if the two forms are equivalent too. Thanks in advance.
if (Text_ != null)
{
return Text_;
}
else
{
return Text_ = "hello";
}
The ?? on Nullable<T> expands to something more complex at the compiler, i.e.
a ?? b
is really
a.HasValue ? a.GetValueOrDefault() : b
Now, since a was null/empty for the only time it was executed, the a.GetValueOrDefault() has never been called. The underlying code when using a string (or any flat reference) is simpler.
In reality, just call it twice to make this go away:
Console.Write(x.Text); // first call; performs init
Console.Write(x.Text); // test once initialized
Console.Write(x.Num); // first call; performs init
Console.Write(x.Num); // test once initialized
Without going into the IL generated by this line I'd suspect that the compiler has done some internal optimisation that causes your "line B" to be regarded as a single statement.
The principal difference between the two statements is that line B refers to an object, whereas line C uses a shorthand reference to Num_.Value.
In general, it's definitely not worth getting stressed about "100% code coverage" -- as this example indicates, sometimes the instrumentation is wrong, anyway. What's important is that most of the code is covered, at least the principal execution paths through business logic.
Coverage is no replacement for code reviews by another human.
I have to complete a project in C# to find number of methods per java class.
I could find all methods in the .java file using c# regular expression, but what I want is to find the number of methods per each and every class, including inner classes. Can any one help me.
string[] lines = File.ReadAllLines(file);
int countLine = 0;
int AllCount = 0;
foreach (string line in lines)
{
countLine = MethodsCount(line);
AllCount = AllCount + countLine;
}
label5.Text = AllCount.ToString();
Here's the method-counting method.
private int MethodsCount (string LineOperator)
{
int count = 0;
string[] words = LineOperator.Split('{');
foreach (string word in words)
{
if (Regex.IsMatch(word, #"(static\s|final\s)?(public|private|internal)(\sstatic|\sfinal)?\s(int|boolean|void|double|String|long|String\[\]|String\[\]\[\])?\s([a-z]|[A-Z]+[a-z]+|[a-z]+[A-Z]+)+(\s)*\((\s|\n|\r)*"))
{
count = count + 1;
}
}
return count;
}
if we consider a class
public class vehicle {
public method1() {
---------
}
public method2() {
------
}
public class car extends vehicle {
public method3() {
-------
}
}
}
I want to get the output there are this number of methods in vehicle class,this number of methods in car class like wise.
Parsing a Java source file with just regex is flaky at best. There are so many different ways to format or annotate the code that you'll easily miss valid method declarations. The regex in your code sample does not handle line breaks, generic methods, the throws clause, arbitrary return types, the synchronized modifier or annotated arguments, two method declarations on the same line...
You'd have to drop regex and build (or reuse) a full parser to be sure you get everything. Fortunately, if you have access to a JDK you can take a shortcut.
First, compile the Java source code. This will give you an output directory full of .class files (inner classes get their own file). Scan the output directory to collect all the class names you need to analyze.
Then, for each class, run the javap command, which will give you this kind of output:
barend#TURMINDER-XUSS /tmp$ javap java.util.Iterator
Compiled from "Iterator.java"
public interface java.util.Iterator{
public abstract boolean hasNext();
public abstract java.lang.Object next();
public abstract void remove();
}
This is much easier to parse than a full Java source file. You can just count all lines containing \s[a-zA-Z][a-zA-Z0-9_]*\( and you have your method count. Use the 'compiled from' information to get to the method count per source file.
(edit) NOTE: javap doesn't print private methods by default. pass the -private argument to include these in the output.
What is this Type in .NET? I am using reflection to get a list of all the classes and this one turns up.
What is it? where does it come from? How is the name DisplayClass1 chosen? I search the sources and didnt see anything. What does the <> mean? what does the c__ mean? is there reference?
It's almost certainly a class generated by the compiler due to a lambda expression or anonymous method. For example, consider this code:
using System;
class Test
{
static void Main()
{
int x = 10;
Func<int, int> foo = y => y + x;
Console.WriteLine(foo(x));
}
}
That gets compiled into:
using System;
class Test
{
static void Main()
{
ExtraClass extra = new ExtraClass();
extra.x = 10;
Func<int, int> foo = extra.DelegateMethod;
Console.WriteLine(foo(x));
}
private class ExtraClass
{
public int x;
public int DelegateMethod(int y)
{
return y + x;
}
}
}
... except using <>c_displayClass1 as the name instead of ExtraClass. This is an unspeakable name in that it isn't valid C# - which means the C# compiler knows for sure that it won't appear in your own code and clash with its choice.
The exact manner of compiling anonymous functions is implementation-specific, of course - as is the choice of name for the extra class.
The compiler also generates extra classes for iterator blocks and (in C# 5) async methods and delegates.
Jon is of course correct. I've provided a "decoder ring" for figuring out what the various compiler-generate type names mean here:
Where to learn about VS debugger 'magic names'
The names are quite long and we sometimes get complaints that we're bulking up the size of metadata as a result. We might change the name generation rules to address this concern at any time in the future. It is therefore very important that you not write code that takes advantage of knowledge of this compiler implementation detail.
I have encountered some unforeseen consequences of porting to mono 2.8.1. Problem can be boiled down to a sample program (I have been unable to reduce it further, after cutting several classes and ~1000 lines of code to the file quoted below)
public class Person
{
public Person(int age, string name = null){}
public Person(double income, string name = null){}
public Person(double income, int age, string name = null){}
}
class Program
{
static void Main()
{
Person p = new Person(1.0, name: "John Doe");
}
}
Compilation of above code with mcs gives output:
test.cs(22,24): error CS0584: Internal compiler error: Internal error
test.cs(22,20): error CS0266: Cannot implicitly convert type `object' to `NamedParams.Person'.
An explicit conversion exists (are you missing a cast?)
Compilation failed: 2 error(s), 0 warnings
Removing use of optional/named parameter (i.e. calling new Person(1.0, null, "John Doe") or new Person(1.0, null, name:"John Doe"), or new Person(1.0, "John Doe") ) leads to flawless compilation. Also, under VS2010 the file (and whole solution with which I started with) compiles fine. Casting removes error CS0266, but not CS0584 -- so no surprise there.
My question: is it me doing something wrong, or mcs (i.e. bug in mcs is obvious to me -- what else ,,internal error'' would mean, but perhaps it's ok such program won't compile), or maybe Microsoft compiler in VS2010 should not let such code to compile?
I bet it's mcs who's wrong (unable to guess right constructor), but perhaps it's otherwise and I shouldn't know better?
PS. I tried searching for a known bug like this in both Google and Novell's Bugzilla, but was unable to find anything relevant. Again, I may be blind ;)
Okay, here goes. The crash is indeed due to the third overload, Person(double income, int age, string name = null).
The compiler sees you are trying to pass less arguments than what are listed in the signature so it goes looking for optional arguments. It happily notices that name is optional and assumes you are not passing that argument. It does this by appending a placeholder at the end of the supplied arguments. Next, it goes to reorder the named arguments in the list so they end up in their right position. This means John Doe is now correctly at the last position for name, but the placeholder gets into the age position. The compiler then tries to fill in the default values, but is shocked to find a placeholder at a location which has no default value. It thinks this can not happen, since the placeholder was only added for an optional argument and now suddenly it is not optional anymore. Not knowing what to do, it throws an exception.
The following patch seems to fix the issue (however it may break something else, so no warranty):
--- mono-2.6.7.orig/mcs/mcs/ecore.cs 2009-10-02 12:51:12.000000000 +0200
+++ mono-2.6.7/mcs/mcs/ecore.cs 2010-12-21 02:26:44.000000000 +0100
## -3803,6 +3803,15 ##
int args_gap = Math.Abs (arg_count - param_count);
if (optional_count != 0) {
+ // readjust for optional arguments passed as named arguments
+ for (int i = 0; i < arguments.Count; i++) {
+ NamedArgument na = arguments[i] as NamedArgument;
+ if (na == null)
+ continue;
+ int index = pd.GetParameterIndexByName (na.Name.Value);
+ if (pd.FixedParameters[index].HasDefaultValue)
+ optional_count--;
+ }
if (args_gap > optional_count)
return int.MaxValue - 10000 + args_gap - optional_count;
For others coming to this thread. The bug is still there in the latest versions of Mono Compiler as of April 2013. I created a work around without needing to modify the compiler using C# overloaded functions.
Foo(int a, bool b = true) {
Foo(a, b, "Default String");
}
Foo(int a, bool b, string c)