Roslyn: Proposal: Expand supported Caller Info Attributes

Created on 10 Feb 2015  路  8Comments  路  Source: dotnet/roslyn

Problem:

The currently supported caller info attributes can only provide the name of the method, the name of the source file and the line number of the call in the source file. This is fine for scenarios such as simplifying implementations of INotifyPropertyChanged. However, if using these attributed for logging the amount of information available is quite limited.

Solution:

Expand the number of supported caller info attributes to allow embedding additional diagnostic information. The following list is quite expansive to discuss/argue over the potential possibilities.

CallerColumnNumberAttribute: The column number of where the method is invoked.
CallerTypeNameAttribute: The simple name of the declaring type of the calling method.
CallerNamespaceAttribute: The namespace of the declaring type of the calling method.
CallerFullTypeNameAttribute: The full name of the declaring type of the calling method.
CallerTypeAttribute: The declaring type of the calling method. This is replaced by the compiler by ldtoken of the type followed by a call to Type.GetTypeFromHandle.
CallerMethodAttribute: The MethodBase of the calling method. This is replaced by the compiler by ldtoken of the method reference followed by a call to MethodBase.GetMethodFromHandle.

Example usage:

using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;

namespace Project1 {
    public static class Program {
        private static void Foo([CallerMemberName] string memberName = null,
                                [CallerTypeName] string typeName = null,
                                [CallerNamespaceName] string namespaceName = null,
                                [CallerFullTypeName] string fullTypeName = null,
                                [CallerFilePath] string filePath = null,
                                [CallerLineNumber] int lineNumber = 0,
                                [CallerColumnNumber] int columnNumber = 0,
                                [CallerType] Type type,
                                [CallerMethod] MethodBase method)
        {
            Debug.Assert(memberName == "Main");
            Debug.Assert(typeName == "Program");
            Debug.Assert(namespaceName == "Project1");
            Debug.Assert(fullTypeName == "Project1.Program");
            Debug.Assert(filePath == "c:\\foo\\bar\\Program.cs");
            Debug.Assert(lineNumber == 29);
            Debug.Assert(columnNumber == 12);
            Debug.Assert(type == typeof(Program));
            Debug.Assert(method == typeof(Program).GetMethod("Main"));
        }

        public static void Main() {
            Foo();
        }
    }
}
0 - Backlog Area-Language Design Feature Request Language-C#

Most helpful comment

That's a lot of optional parameters, maybe time for an object?

Public Foo( [CallerInfo] As CallerInfo = null);

CallerInfo

Class CallerInfo
  Public ReadOnly MemberName   As String
  Public ReadOnly TypeName     As String
  Public ReadOnly Namespace    As String
  Public ReadOnly FullTypeName As String
  Public ReadOnly FilePath     As String
  Public ReadOnly LineNumber   As Integer
  Public ReadOnly ColumnNumber As Integer
  Public ReadOnly Type         As Type
  Public ReadOnly Method       As MethodBase
End Class

All 8 comments

That's a lot of optional parameters, maybe time for an object?

Public Foo( [CallerInfo] As CallerInfo = null);

CallerInfo

Class CallerInfo
  Public ReadOnly MemberName   As String
  Public ReadOnly TypeName     As String
  Public ReadOnly Namespace    As String
  Public ReadOnly FullTypeName As String
  Public ReadOnly FilePath     As String
  Public ReadOnly LineNumber   As Integer
  Public ReadOnly ColumnNumber As Integer
  Public ReadOnly Type         As Type
  Public ReadOnly Method       As MethodBase
End Class

Maybe. I wouldn't imagine that they would all be used in conjunction, but that could be useful as a convenience.

@HaloFour Some attributes seem redundant:

| Attribute | Equvalent |
| --- | --- |
| [CallerTypeName] | CallerMethod.ReflectedType.Name |
| [CallerNamespace] | CallerMethod.ReflectedType.Namespace |
| [CallerFullTypeName] | CallerMethod.ReflectedType.FullName |
| [CallerType] | CallerMethod.ReflectedType |

@ashmind

That they are. Technically you wouldn't need anything beyond [CallerMethod], [CallerFilePath], [CallerLineNumber] and [CallerColumnNumber] in order to derive all of that information, and if those were the only attributes that existed then I would be completely happy. It is the existing attribute [CallerMemberName] which set the precedent for attributes only resulting in portions of the metadata and so I simply completed the story.

This would be very useful in log libraries, like NLog. We now use stacktrace for that, but those are not implemented in the new framework (yet?) https://github.com/dotnet/corefx/issues/1797

I think one object/struct would be better then a those optional parameters.

If this will be implemented with a new type (e.g. Foo([CallerInfo] ICallerInfo)), it would be also nice if that call has preference over Foo(). AFAIK this is now not the case, so a change in the caller attributes is a breaking change (not binary backwards-compatible).

I suppose CallerTypeName & CallerType etc. should be named CallerOwnerTypeName & CallerOwnerType and so on...

And CallerType etc. or additional CallerMemberType etc. should be of the method return type (in case of property accessor, it has to be the property type).

Was this page helpful?
0 / 5 - 0 ratings

Related issues

davidroth picture davidroth  路  158Comments

alrz picture alrz  路  125Comments

MadsTorgersen picture MadsTorgersen  路  158Comments

mattwar picture mattwar  路  190Comments

MgSam picture MgSam  路  152Comments