1 /**
2  *  Convert any D symbol or type to a human-readable string, at compile time.
3  *
4  *   Given any D symbol (class, template, function, module name, or non-local variable)
5  *   or any D type, convert it to a compile-time string literal,
6  *   optionally containing the fully qualified and decorated name.
7  *
8  *   Limitations (as of DMD 0.167):
9  *   1. Names of local variables cannot be determined, because they are not permitted
10  *      as template alias parameters. Technically, it's possible to determine the name by using
11  *      a mixin hack, but it's so ugly that it cannot be recommended.
12  *   2. The name mangling for symbols declared inside extern(Windows), extern(C) and extern(Pascal)
13  *      functions is inherently ambiguous, so such inner symbols are not always correctly displayed.
14  *
15  * License:   BSD style: $(LICENSE)
16  * Authors:   Don Clugston
17  * Copyright: Copyright (C) 2005-2006 Don Clugston
18  */
19 module meta.Nameof;
20 
21 import meta.Demangle;
22 
23 private {
24     // --------------------------------------------
25     // Here's the magic...
26     //
27     // Make a unique type for each identifier; but don't actually
28     // use the identifier for anything.
29     // This works because any class always needs to be fully qualified.
30     template inner(alias F)
31     {
32       class inner { }
33     }
34 
35     // If you take the .mangleof an alias parameter, you are only
36     // told that it is an alias.
37     // So, we put the type as a function parameter.
38     template outer(alias B)
39     {
40       void function( inner!(B) ) outer;
41     }
42 
43     // We will get the .mangleof for a pointer to this function pointer.
44     template rawmanglednameof(alias A)
45     {
46       enum string rawmanglednameof  =
47                  typeof(&outer!(A)).mangleof;
48     }
49 
50 // If the identifier is "MyIdentifier" and this module is "QualModule"
51 // The return value will be:
52 //  "PPF"   -- because it's a pointer to a pointer to a function
53 //   "C"     -- because the first parameter is a class
54 //    "10QualModule"  -- the name of this module
55 //      "45" -- the number of characters in the remainder of the mangled name.
56 //         Note that this could be more than 2 characters, but will be at least "10".
57 //      "__T"    -- because it's a class inside a template
58 //       "5inner" -- the name of the template "inner"
59 //       "T" MyIdentifer -- Here's our prize!
60 //       "Z"  -- marks the end of the template parameters for "inner"
61 //    "5inner" -- this is the class "inner"
62 //  "Z"  -- the return value of the function is coming
63 //  "v"  -- the function returns void
64 
65 // The only unknown parts above are:
66 // (1) the name of this source file
67 // (it could move or be renamed). So we do a simple case:
68 //  "C"   -- it's a class
69 //   "10QualModule" -- the name of this module
70 //   "15establishMangle" -- the name of the class
71 // and (2) the number of characters in the remainder of the name
72 
73     class establishMangle {}
74     // Get length of this (fully qualified) module name
75     const int modulemanglelength = establishMangle.mangleof.length - "C15establishMangle".length;
76 
77     // Get the number of chars at the start relating to the pointer
78     const int pointerstartlength = "PPFC".length + modulemanglelength + "__T5inner".length;
79     // And the number of chars at the end
80     const int pointerendlength = "Z5innerZv".length;
81 }
82 
83 // --------------------------------------------------------------
84 // Now, some functions which massage the mangled name to give something more useful.
85 
86 
87 /**
88  * Like .mangleof, except that it works for an alias template parameter instead of a type.
89  */
90 template manglednameof(alias A)
91 {
92     static if (rawmanglednameof!(A).length - pointerstartlength <= 100 + 1) {
93         // the length of the template argument requires 2 characters
94         enum string manglednameof  =
95              rawmanglednameof!(A)[ pointerstartlength + 2 .. $ - pointerendlength];
96     } else
97         enum string manglednameof  =
98              rawmanglednameof!(A)[ pointerstartlength + 3 .. $ - pointerendlength];
99 }
100 
101 /**
102  * The symbol as it was declared, but including full type qualification.
103  *
104  * example: "int mymodule.myclass.myfunc(uint, class otherclass)"
105  */
106 template prettynameof(alias A)
107 {
108   enum string prettynameof = prettyTemplateArg!(manglednameof!(A), MangledNameType.PrettyName);
109 }
110 
111 /** Convert any D type to a human-readable string literal
112  *
113  * example: "int function(double, char[])"
114  */
115 template prettytypeof(A)
116 {
117   enum string prettytypeof = demangleType!(A.mangleof, MangledNameType.PrettyName);
118 }
119 
120 /**
121  * Returns the qualified name of the symbol A.
122  *
123  * This will be a sequence of identifiers, seperated by dots.
124  * eg "mymodule.myclass.myfunc"
125  * This is the same as prettynameof(), except that it doesn't include any type information.
126  */
127 template qualifiednameof(alias A)
128 {
129   enum string qualifiednameof = prettyTemplateArg!(manglednameof!(A), MangledNameType.QualifiedName);
130 }
131 
132 /**
133  * Returns the unqualified name, as a single text string.
134  *
135  * eg. "myfunc"
136  */
137 template symbolnameof(alias A)
138 {
139   enum string symbolnameof = prettyTemplateArg!(manglednameof!(A), MangledNameType.SymbolName);
140 }
141 
142 //----------------------------------------------
143 //                Unit Tests
144 //----------------------------------------------
145 
146 debug (UnitTest)
147 {
148 private {
149 // Declare some structs, classes, enums, functions, and templates.
150 
151 template ClassTemplate(A)
152 {
153    class ClassTemplate {}
154 }
155 
156 struct OuterClass  {
157 class SomeClass {}
158 }
159 
160 alias double delegate (int, OuterClass) SomeDelegate;
161 
162 template IntTemplate(int F)
163 {
164   class IntTemplate { }
165 }
166 
167 template MyInt(int F)
168 {
169     enum int MyIntX = F;
170 }
171 
172 
173 enum SomeEnum { ABC = 2 }
174 SomeEnum SomeInt;
175 
176 // remove the ".d" from the end
177 enum THISFILE = "meta.Nameof";
178 
179 static assert( prettytypeof!(real) == "real");
180 static assert( prettytypeof!(OuterClass.SomeClass) == "class " ~ THISFILE ~".OuterClass.SomeClass");
181 
182 // Test that it works with module names (for example, this module)
183 static assert( qualifiednameof!(meta.Nameof) == "meta.Nameof");
184 static assert( symbolnameof!(meta.Nameof) == "Nameof");
185 
186 static assert( prettynameof!(SomeInt)
187     == "enum " ~ THISFILE ~ ".SomeEnum " ~ THISFILE ~ ".SomeInt");
188 static assert( qualifiednameof!(OuterClass) == THISFILE ~".OuterClass");
189 static assert( symbolnameof!(SomeInt) == "SomeInt");
190 
191 static assert( prettynameof!(inner!( MyInt!(68u) ))
192     ==  "class " ~ THISFILE ~ ".inner!(" ~ THISFILE ~ ".MyInt!(uint = 68)).inner");
193 static assert( symbolnameof!(inner!( MyInt!(68u) )) ==  "inner");
194 static assert( prettynameof!(ClassTemplate!(OuterClass.SomeClass))
195     == "class "~ THISFILE ~ ".ClassTemplate!(class "~ THISFILE ~ ".OuterClass.SomeClass).ClassTemplate");
196 static assert( symbolnameof!(ClassTemplate!(OuterClass.SomeClass))  == "ClassTemplate");
197 
198 // Extern(D) declarations have full type information.
199 extern int pig();
200 extern int pog;
201 static assert( prettynameof!(pig) == "int " ~ THISFILE ~ ".pig()");
202 static assert( prettynameof!(pog) == "int " ~ THISFILE ~ ".pog");
203 static assert( symbolnameof!(pig) == "pig");
204 
205 // Extern(Windows) declarations contain no type information.
206 extern (Windows) {
207     extern int dog();
208     extern int dig;
209 }
210 
211 static assert( prettynameof!(dog) == "dog");
212 static assert( prettynameof!(dig) == "dig");
213 
214 // There are some nasty corner cases involving classes that are inside functions.
215 // Corner case #1: class inside nested function inside template
216 
217 extern (Windows) {
218 template aardvark(X) {
219     int aardvark(short goon) {
220         class wolf {}
221         static assert(prettynameof!(wolf)== "class extern (Windows) int " ~ THISFILE ~ ".aardvark!(struct "
222             ~ THISFILE ~ ".OuterClass).aardvark(short).wolf");
223         static assert(qualifiednameof!(wolf)== THISFILE ~ ".aardvark.aardvark.wolf");
224         static assert( symbolnameof!(wolf) == "wolf");
225         return 3;
226         }
227     }
228 }
229 
230 // This is just to ensure that the static assert actually gets executed.
231 enum test_aardvark = is (aardvark!(OuterClass) == function);
232 
233 // Corner case #2: template inside function. This is currently possible only with mixins.
234 template fox(B, ushort C) {
235     class fox {}
236 }
237 
238 void wolf() {
239         mixin fox!(cfloat, 21);
240         static assert(prettynameof!(fox)== "class void " ~ THISFILE ~ ".wolf().fox!(cfloat, int = 21).fox");
241         static assert(qualifiednameof!(fox)== THISFILE ~ ".wolf.fox.fox");
242         static assert(symbolnameof!(fox)== "fox");
243 }
244 }
245 
246 }