1 /**
2  * Defines an identifier, which is the name of a `Dsymbol`.
3  *
4  * Copyright:   Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
5  * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
6  * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/identifier.d, _identifier.d)
8  * Documentation:  https://dlang.org/phobos/dmd_identifier.html
9  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/identifier.d
10  */
11 
12 module dmd.identifier;
13 
14 import core.stdc.ctype;
15 import core.stdc.stdio;
16 import core.stdc.string;
17 import dmd.globals;
18 import dmd.id;
19 import dmd.root.outbuffer;
20 import dmd.root.rootobject;
21 import dmd.root.string;
22 import dmd.root.stringtable;
23 import dmd.tokens;
24 import dmd.utf;
25 
26 
27 /***********************************************************
28  */
29 extern (C++) final class Identifier : RootObject
30 {
31     private const int value;
32 
33     // Indicates if this is an identifier used for an anonymous symbol.
34     private const bool isAnonymous_ = false;
35 
36     private const char[] name;
37 
38 nothrow:
39 
40     /// Construct an identifier from the given name.
41     extern (D) this(const(char)* name)
42     {
43         //printf("Identifier('%s', %d)\n", name, value);
44         this(name.toDString(), TOK.identifier);
45     }
46 
47     /**
48        Construct an identifier from the given name.
49 
50        Params:
51          name = the identifier name. There must be `'\0'` at `name[length]`.
52          length = the length of `name`, excluding the terminating `'\0'`
53          value = Identifier value (e.g. `Id.unitTest`) or `TOK.identifier`
54      */
55     extern (D) this(const(char)* name, size_t length, int value)
56     in
57     {
58         assert(name[length] == '\0');
59     }
60     do
61     {
62         //printf("Identifier('%s', %d)\n", name, value);
63         this(name[0 .. length], value);
64     }
65 
66     /// ditto
67     extern (D) this(const(char)[] name, int value)
68     {
69         //printf("Identifier('%.*s', %d)\n", cast(int)name.length, name.ptr, value);
70         this(name, value, false);
71     }
72 
73     extern (D) private this(const(char)[] name, int value, bool isAnonymous)
74     {
75         //printf("Identifier('%.*s', %d, %d)\n", cast(int)name.length, name.ptr, value, isAnonymous);
76         this.name = name;
77         this.value = value;
78         isAnonymous_ = isAnonymous;
79     }
80 
81     static Identifier create(const(char)* name)
82     {
83         return new Identifier(name);
84     }
85 
86     override const(char)* toChars() const pure
87     {
88         return name.ptr;
89     }
90 
91     extern (D) override const(char)[] toString() const pure
92     {
93         return name;
94     }
95 
96     int getValue() const pure
97     {
98         return value;
99     }
100 
101     bool isAnonymous() const pure @nogc @safe
102     {
103         return isAnonymous_;
104     }
105 
106     const(char)* toHChars2() const
107     {
108         const(char)* p = null;
109         if (this == Id.ctor)
110             p = "this";
111         else if (this == Id.dtor)
112             p = "~this";
113         else if (this == Id.unitTest)
114             p = "unittest";
115         else if (this == Id.dollar)
116             p = "$";
117         else if (this == Id.withSym)
118             p = "with";
119         else if (this == Id.result)
120             p = "result";
121         else if (this == Id.returnLabel)
122             p = "return";
123         else
124         {
125             p = toChars();
126             if (*p == '_')
127             {
128                 if (strncmp(p, "_staticCtor", 11) == 0)
129                     p = "static this";
130                 else if (strncmp(p, "_staticDtor", 11) == 0)
131                     p = "static ~this";
132                 else if (strncmp(p, "__invariant", 11) == 0)
133                     p = "invariant";
134             }
135         }
136         return p;
137     }
138 
139     override DYNCAST dyncast() const
140     {
141         return DYNCAST.identifier;
142     }
143 
144     private extern (D) __gshared StringTable!Identifier stringtable;
145 
146     /**
147      * Generates a new identifier.
148      *
149      * Params:
150      *  prefix = this will be the prefix of the name of the identifier. For debugging
151      *      purpose.
152      */
153     extern(D) static Identifier generateId(const(char)[] prefix)
154     {
155         return generateId(prefix, newSuffix, false);
156     }
157 
158     /**
159      * Generates a new anonymous identifier.
160      *
161      * Params:
162      *  name = this will be part of the name of the identifier. For debugging
163      *      purpose.
164      */
165     extern(D) static Identifier generateAnonymousId(const(char)[] name)
166     {
167         return generateId("__anon" ~ name, newSuffix, true);
168     }
169 
170     /**
171      * Generates a new identifier.
172      *
173      * Params:
174      *  prefix = this will be the prefix of the name of the identifier. For debugging
175      *      purpose.
176      *  suffix = this will be the suffix of the name of the identifier. This is
177      *      what makes the identifier unique
178      */
179     extern(D) static Identifier generateId(const(char)[] prefix, size_t suffix)
180     {
181         return generateId(prefix, suffix, false);
182     }
183 
184     /// ditto
185     static Identifier generateId(const(char)* prefix, size_t length, size_t suffix)
186     {
187         return generateId(prefix[0 .. length], suffix);
188     }
189 
190     // Generates a new, unique, suffix for an identifier.
191     extern (D) private static size_t newSuffix()
192     {
193         __gshared size_t i;
194         return ++i;
195     }
196 
197     extern(D) private static Identifier generateId(const(char)[] prefix, size_t suffix, bool isAnonymous)
198     {
199         OutBuffer buf;
200         buf.write(prefix);
201         buf.print(suffix);
202         return idPool(buf[], isAnonymous);
203     }
204 
205     /***************************************
206      * Generate deterministic named identifier based on a source location,
207      * such that the name is consistent across multiple compilations.
208      * A new unique name is generated. If the prefix+location is already in
209      * the stringtable, an extra suffix is added (starting the count at "_1").
210      *
211      * Params:
212      *      prefix      = first part of the identifier name.
213      *      loc         = source location to use in the identifier name.
214      * Returns:
215      *      Identifier (inside Identifier.idPool) with deterministic name based
216      *      on the source location.
217      */
218     extern (D) static Identifier generateIdWithLoc(string prefix, const ref Loc loc)
219     {
220         // generate `<prefix>_L<line>_C<col>`
221         OutBuffer idBuf;
222         idBuf.writestring(prefix);
223         idBuf.writestring("_L");
224         idBuf.print(loc.linnum);
225         idBuf.writestring("_C");
226         idBuf.print(loc.charnum);
227 
228         /**
229          * Make sure the identifiers are unique per filename, i.e., per module/mixin
230          * (`path/to/foo.d` and `path/to/foo.d-mixin-<line>`). See issues
231          * https://issues.dlang.org/show_bug.cgi?id=16995
232          * https://issues.dlang.org/show_bug.cgi?id=18097
233          * https://issues.dlang.org/show_bug.cgi?id=18111
234          * https://issues.dlang.org/show_bug.cgi?id=18880
235          * https://issues.dlang.org/show_bug.cgi?id=18868
236          * https://issues.dlang.org/show_bug.cgi?id=19058
237          */
238         static struct Key { Loc loc; string prefix; }
239         __gshared uint[Key] counters;
240 
241         static if (__traits(compiles, counters.update(Key.init, () => 0u, (ref uint a) => 0u)))
242         {
243             // 2.082+
244             counters.update(Key(loc, prefix),
245                 () => 1u,          // insertion
246                 (ref uint counter) // update
247                 {
248                     idBuf.writestring("_");
249                     idBuf.print(counter);
250                     return counter + 1;
251                 }
252             );
253         }
254         else
255         {
256             const key = Key(loc, prefix);
257             if (auto pCounter = key in counters)
258             {
259                 idBuf.writestring("_");
260                 idBuf.print((*pCounter)++);
261             }
262             else
263                 counters[key] = 1;
264         }
265 
266         return idPool(idBuf[]);
267     }
268 
269     /********************************************
270      * Create an identifier in the string table.
271      */
272     static Identifier idPool(const(char)* s, uint len)
273     {
274         return idPool(s[0 .. len]);
275     }
276 
277     extern (D) static Identifier idPool(const(char)[] s)
278     {
279         return idPool(s, false);
280     }
281 
282     extern (D) private static Identifier idPool(const(char)[] s, bool isAnonymous)
283     {
284         auto sv = stringtable.update(s);
285         auto id = sv.value;
286         if (!id)
287         {
288             id = new Identifier(sv.toString(), TOK.identifier, isAnonymous);
289             sv.value = id;
290         }
291         return id;
292     }
293 
294     extern (D) static Identifier idPool(const(char)* s, size_t len, int value)
295     {
296         return idPool(s[0 .. len], value);
297     }
298 
299     extern (D) static Identifier idPool(const(char)[] s, int value)
300     {
301         auto sv = stringtable.insert(s, null);
302         assert(sv);
303         auto id = new Identifier(sv.toString(), value);
304         sv.value = id;
305         return id;
306     }
307 
308     /**********************************
309      * Determine if string is a valid Identifier.
310      * Params:
311      *      str = string to check
312      * Returns:
313      *      false for invalid
314      */
315     static bool isValidIdentifier(const(char)* str)
316     {
317         return str && isValidIdentifier(str.toDString);
318     }
319 
320     /**********************************
321      * ditto
322      */
323     extern (D) static bool isValidIdentifier(const(char)[] str)
324     {
325         if (str.length == 0 ||
326             (str[0] >= '0' && str[0] <= '9')) // beware of isdigit() on signed chars
327         {
328             return false;
329         }
330 
331         size_t idx = 0;
332         while (idx < str.length)
333         {
334             dchar dc;
335             const s = utf_decodeChar(str, idx, dc);
336             if (s ||
337                 !((dc >= 0x80 && isUniAlpha(dc)) || isalnum(dc) || dc == '_'))
338             {
339                 return false;
340             }
341         }
342         return true;
343     }
344 
345     extern (D) static Identifier lookup(const(char)* s, size_t len)
346     {
347         return lookup(s[0 .. len]);
348     }
349 
350     extern (D) static Identifier lookup(const(char)[] s)
351     {
352         auto sv = stringtable.lookup(s);
353         if (!sv)
354             return null;
355         return sv.value;
356     }
357 
358     extern (D) static void initTable()
359     {
360         stringtable._init(28_000);
361     }
362 }