1 /**
2  * Defines an identifier, which is the name of a `Dsymbol`.
3  *
4  * Copyright:   Copyright (C) 1999-2020 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:
32     const int value;
33     const char[] name;
34 
35 public:
36     /**
37        Construct an identifier from a D slice
38 
39        Note: Since `name` needs to be `\0` terminated for `toChars`,
40        no slice overload is provided yet.
41 
42        Params:
43          name = the identifier name
44                 There must be `'\0'` at `name[length]`.
45          length = the length of `name`, excluding the terminating `'\0'`
46          value = Identifier value (e.g. `Id.unitTest`) or `TOK.identifier`
47      */
48     extern (D) this(const(char)* name, size_t length, int value) nothrow
49     {
50         //printf("Identifier('%s', %d)\n", name, value);
51         this.name = name[0 .. length];
52         this.value = value;
53     }
54 
55     extern (D) this(const(char)[] name, int value) nothrow
56     {
57         //printf("Identifier('%.*s', %d)\n", cast(int)name.length, name.ptr, value);
58         this.name = name;
59         this.value = value;
60     }
61 
62     extern (D) this(const(char)* name) nothrow
63     {
64         //printf("Identifier('%s', %d)\n", name, value);
65         this(name.toDString(), TOK.identifier);
66     }
67 
68     /// Sentinel for an anonymous identifier.
69     static Identifier anonymous() nothrow
70     {
71         __gshared Identifier anonymous;
72 
73         if (anonymous)
74             return anonymous;
75 
76         return anonymous = new Identifier("__anonymous", TOK.identifier);
77     }
78 
79     static Identifier create(const(char)* name) nothrow
80     {
81         return new Identifier(name);
82     }
83 
84 nothrow:
85     override const(char)* toChars() const pure
86     {
87         return name.ptr;
88     }
89 
90     extern (D) override const(char)[] toString() const pure
91     {
92         return name;
93     }
94 
95     int getValue() const pure
96     {
97         return value;
98     }
99 
100     const(char)* toHChars2() const
101     {
102         const(char)* p = null;
103         if (this == Id.ctor)
104             p = "this";
105         else if (this == Id.dtor)
106             p = "~this";
107         else if (this == Id.unitTest)
108             p = "unittest";
109         else if (this == Id.dollar)
110             p = "$";
111         else if (this == Id.withSym)
112             p = "with";
113         else if (this == Id.result)
114             p = "result";
115         else if (this == Id.returnLabel)
116             p = "return";
117         else
118         {
119             p = toChars();
120             if (*p == '_')
121             {
122                 if (strncmp(p, "_staticCtor", 11) == 0)
123                     p = "static this";
124                 else if (strncmp(p, "_staticDtor", 11) == 0)
125                     p = "static ~this";
126                 else if (strncmp(p, "__invariant", 11) == 0)
127                     p = "invariant";
128             }
129         }
130         return p;
131     }
132 
133     override DYNCAST dyncast() const
134     {
135         return DYNCAST.identifier;
136     }
137 
138     private extern (D) __gshared StringTable!Identifier stringtable;
139 
140     extern(D) static Identifier generateId(const(char)[] prefix)
141     {
142         __gshared size_t i;
143         return generateId(prefix, ++i);
144     }
145 
146     extern(D) static Identifier generateId(const(char)[] prefix, size_t i)
147     {
148         OutBuffer buf;
149         buf.write(prefix);
150         buf.print(i);
151         return idPool(buf[]);
152     }
153 
154     /***************************************
155      * Generate deterministic named identifier based on a source location,
156      * such that the name is consistent across multiple compilations.
157      * A new unique name is generated. If the prefix+location is already in
158      * the stringtable, an extra suffix is added (starting the count at "_1").
159      *
160      * Params:
161      *      prefix      = first part of the identifier name.
162      *      loc         = source location to use in the identifier name.
163      * Returns:
164      *      Identifier (inside Identifier.idPool) with deterministic name based
165      *      on the source location.
166      */
167     extern (D) static Identifier generateIdWithLoc(string prefix, const ref Loc loc)
168     {
169         // generate `<prefix>_L<line>_C<col>`
170         OutBuffer idBuf;
171         idBuf.writestring(prefix);
172         idBuf.writestring("_L");
173         idBuf.print(loc.linnum);
174         idBuf.writestring("_C");
175         idBuf.print(loc.charnum);
176 
177         /**
178          * Make sure the identifiers are unique per filename, i.e., per module/mixin
179          * (`path/to/foo.d` and `path/to/foo.d-mixin-<line>`). See issues
180          * https://issues.dlang.org/show_bug.cgi?id=16995
181          * https://issues.dlang.org/show_bug.cgi?id=18097
182          * https://issues.dlang.org/show_bug.cgi?id=18111
183          * https://issues.dlang.org/show_bug.cgi?id=18880
184          * https://issues.dlang.org/show_bug.cgi?id=18868
185          * https://issues.dlang.org/show_bug.cgi?id=19058
186          */
187         static struct Key { Loc loc; string prefix; }
188         __gshared uint[Key] counters;
189 
190         static if (__traits(compiles, counters.update(Key.init, () => 0u, (ref uint a) => 0u)))
191         {
192             // 2.082+
193             counters.update(Key(loc, prefix),
194                 () => 1u,          // insertion
195                 (ref uint counter) // update
196                 {
197                     idBuf.writestring("_");
198                     idBuf.print(counter);
199                     return counter + 1;
200                 }
201             );
202         }
203         else
204         {
205             const key = Key(loc, prefix);
206             if (auto pCounter = key in counters)
207             {
208                 idBuf.writestring("_");
209                 idBuf.print((*pCounter)++);
210             }
211             else
212                 counters[key] = 1;
213         }
214 
215         return idPool(idBuf[]);
216     }
217 
218     /********************************************
219      * Create an identifier in the string table.
220      */
221     static Identifier idPool(const(char)* s, uint len)
222     {
223         return idPool(s[0 .. len]);
224     }
225 
226     extern (D) static Identifier idPool(const(char)[] s)
227     {
228         auto sv = stringtable.update(s);
229         auto id = sv.value;
230         if (!id)
231         {
232             id = new Identifier(sv.toString(), TOK.identifier);
233             sv.value = id;
234         }
235         return id;
236     }
237 
238     extern (D) static Identifier idPool(const(char)* s, size_t len, int value)
239     {
240         return idPool(s[0 .. len], value);
241     }
242 
243     extern (D) static Identifier idPool(const(char)[] s, int value)
244     {
245         auto sv = stringtable.insert(s, null);
246         assert(sv);
247         auto id = new Identifier(sv.toString(), value);
248         sv.value = id;
249         return id;
250     }
251 
252     /**********************************
253      * Determine if string is a valid Identifier.
254      * Params:
255      *      str = string to check
256      * Returns:
257      *      false for invalid
258      */
259     static bool isValidIdentifier(const(char)* str)
260     {
261         return str && isValidIdentifier(str.toDString);
262     }
263 
264     /**********************************
265      * ditto
266      */
267     extern (D) static bool isValidIdentifier(const(char)[] str)
268     {
269         if (str.length == 0 ||
270             (str[0] >= '0' && str[0] <= '9')) // beware of isdigit() on signed chars
271         {
272             return false;
273         }
274 
275         size_t idx = 0;
276         while (idx < str.length)
277         {
278             dchar dc;
279             const s = utf_decodeChar(str, idx, dc);
280             if (s ||
281                 !((dc >= 0x80 && isUniAlpha(dc)) || isalnum(dc) || dc == '_'))
282             {
283                 return false;
284             }
285         }
286         return true;
287     }
288 
289     extern (D) static Identifier lookup(const(char)* s, size_t len)
290     {
291         return lookup(s[0 .. len]);
292     }
293 
294     extern (D) static Identifier lookup(const(char)[] s)
295     {
296         auto sv = stringtable.lookup(s);
297         if (!sv)
298             return null;
299         return sv.value;
300     }
301 
302     extern (D) static void initTable()
303     {
304         stringtable._init(28_000);
305     }
306 }