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 }