1 /** 2 * Encapsulates file/line/column locations. 3 * 4 * Copyright: Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved 5 * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) 6 * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 7 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/location.d, _location.d) 8 * Documentation: https://dlang.org/phobos/dmd_location.html 9 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/location.d 10 */ 11 12 module dmd.location; 13 14 import core.stdc.stdio; 15 16 import dmd.common.outbuffer; 17 import dmd.root.array; 18 import dmd.root.filename; 19 20 version (DMDLIB) 21 { 22 version = LocOffset; 23 } 24 25 /// How code locations are formatted for diagnostic reporting 26 enum MessageStyle : ubyte 27 { 28 digitalmars, /// filename.d(line): message 29 gnu, /// filename.d:line: message, see https://www.gnu.org/prep/standards/html_node/Errors.html 30 } 31 32 /** 33 A source code location 34 35 Used for error messages, `__FILE__` and `__LINE__` tokens, `__traits(getLocation, XXX)`, 36 debug info etc. 37 */ 38 struct Loc 39 { 40 private uint _linnum; 41 private uint _charnum; 42 private uint fileIndex; // index into filenames[], starting from 1 (0 means no filename) 43 version (LocOffset) 44 uint fileOffset; /// utf8 code unit index relative to start of file, starting from 0 45 46 static immutable Loc initial; /// use for default initialization of const ref Loc's 47 48 extern (C++) __gshared bool showColumns; 49 extern (C++) __gshared MessageStyle messageStyle; 50 51 __gshared Array!(const(char)*) filenames; 52 53 nothrow: 54 55 /******************************* 56 * Configure how display is done 57 * Params: 58 * showColumns = when to display columns 59 * messageStyle = digitalmars or gnu style messages 60 */ 61 extern (C++) static void set(bool showColumns, MessageStyle messageStyle) 62 { 63 this.showColumns = showColumns; 64 this.messageStyle = messageStyle; 65 } 66 67 extern (C++) this(const(char)* filename, uint linnum, uint charnum) @safe 68 { 69 this._linnum = linnum; 70 this._charnum = charnum; 71 this.filename = filename; 72 } 73 74 /// utf8 code unit index relative to start of line, starting from 1 75 extern (C++) uint charnum() const @nogc @safe 76 { 77 return _charnum; 78 } 79 80 /// ditto 81 extern (C++) uint charnum(uint num) @nogc @safe 82 { 83 return _charnum = num; 84 } 85 86 /// line number, starting from 1 87 extern (C++) uint linnum() const @nogc @safe 88 { 89 return _linnum; 90 } 91 92 /// ditto 93 extern (C++) uint linnum(uint num) @nogc @safe 94 { 95 return _linnum = num; 96 } 97 98 /*** 99 * Returns: filename for this location, null if none 100 */ 101 extern (C++) const(char)* filename() const @nogc 102 { 103 return fileIndex ? filenames[fileIndex - 1] : null; 104 } 105 106 /*** 107 * Set file name for this location 108 * Params: 109 * name = file name for location, null for no file name 110 */ 111 extern (C++) void filename(const(char)* name) @trusted 112 { 113 if (name) 114 { 115 //printf("setting %s\n", name); 116 filenames.push(name); 117 fileIndex = cast(uint)filenames.length; 118 if (!fileIndex) 119 { 120 import dmd.globals : global; 121 import dmd.errors : error, fatal; 122 123 global.gag = 0; // ensure error message gets printed 124 error(Loc.initial, "internal compiler error: file name index overflow!"); 125 fatal(); 126 } 127 } 128 else 129 fileIndex = 0; 130 } 131 132 extern (C++) const(char)* toChars( 133 bool showColumns = Loc.showColumns, 134 MessageStyle messageStyle = Loc.messageStyle) const nothrow 135 { 136 OutBuffer buf; 137 if (fileIndex) 138 { 139 buf.writestring(filename); 140 } 141 if (linnum) 142 { 143 final switch (messageStyle) 144 { 145 case MessageStyle.digitalmars: 146 buf.writeByte('('); 147 buf.print(linnum); 148 if (showColumns && charnum) 149 { 150 buf.writeByte(','); 151 buf.print(charnum); 152 } 153 buf.writeByte(')'); 154 break; 155 case MessageStyle.gnu: // https://www.gnu.org/prep/standards/html_node/Errors.html 156 buf.writeByte(':'); 157 buf.print(linnum); 158 if (showColumns && charnum) 159 { 160 buf.writeByte(':'); 161 buf.print(charnum); 162 } 163 break; 164 } 165 } 166 return buf.extractChars(); 167 } 168 169 /** 170 * Checks for equivalence by comparing the filename contents (not the pointer) and character location. 171 * 172 * Note: 173 * - Uses case-insensitive comparison on Windows 174 * - Ignores `charnum` if `Columns` is false. 175 */ 176 extern (C++) bool equals(ref const(Loc) loc) const 177 { 178 return (!showColumns || charnum == loc.charnum) && 179 linnum == loc.linnum && 180 FileName.equals(filename, loc.filename); 181 } 182 183 /** 184 * `opEquals()` / `toHash()` for AA key usage 185 * 186 * Compare filename contents (case-sensitively on Windows too), not 187 * the pointer - a static foreach loop repeatedly mixing in a mixin 188 * may lead to multiple equivalent filenames (`foo.d-mixin-<line>`), 189 * e.g., for test/runnable/test18880.d. 190 */ 191 extern (D) bool opEquals(ref const(Loc) loc) const @trusted nothrow @nogc 192 { 193 import core.stdc.string : strcmp; 194 195 return charnum == loc.charnum && 196 linnum == loc.linnum && 197 (filename == loc.filename || 198 (filename && loc.filename && strcmp(filename, loc.filename) == 0)); 199 } 200 201 /// ditto 202 extern (D) size_t toHash() const @trusted nothrow 203 { 204 import dmd.root.string : toDString; 205 206 auto hash = hashOf(linnum); 207 hash = hashOf(charnum, hash); 208 hash = hashOf(filename.toDString, hash); 209 return hash; 210 } 211 212 /****************** 213 * Returns: 214 * true if Loc has been set to other than the default initialization 215 */ 216 bool isValid() const pure @safe 217 { 218 return fileIndex != 0; 219 } 220 }