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 dmd.common.outbuffer;
15 import dmd.root.filename;
16 
17 version (DMDLIB)
18 {
19     version = LocOffset;
20 }
21 
22 /// How code locations are formatted for diagnostic reporting
23 enum MessageStyle : ubyte
24 {
25     digitalmars,  /// filename.d(line): message
26     gnu,          /// filename.d:line: message, see https://www.gnu.org/prep/standards/html_node/Errors.html
27 }
28 
29 /**
30 A source code location
31 
32 Used for error messages, `__FILE__` and `__LINE__` tokens, `__traits(getLocation, XXX)`,
33 debug info etc.
34 */
35 struct Loc
36 {
37     /// zero-terminated filename string, either absolute or relative to cwd
38     const(char)* filename;
39     uint linnum; /// line number, starting from 1
40     uint charnum; /// utf8 code unit index relative to start of line, starting from 1
41     version (LocOffset)
42         uint fileOffset; /// utf8 code unit index relative to start of file, starting from 0
43 
44     static immutable Loc initial; /// use for default initialization of const ref Loc's
45 
46     extern (C++) __gshared bool showColumns;
47     extern (C++) __gshared MessageStyle messageStyle;
48 
49 nothrow:
50 
51     /*******************************
52      * Configure how display is done
53      * Params:
54      *  showColumns = when to display columns
55      *  messageStyle = digitalmars or gnu style messages
56      */
57     extern (C++) static void set(bool showColumns, MessageStyle messageStyle)
58     {
59         this.showColumns = showColumns;
60         this.messageStyle = messageStyle;
61     }
62 
63     extern (D) this(const(char)* filename, uint linnum, uint charnum) pure
64     {
65         this.linnum = linnum;
66         this.charnum = charnum;
67         this.filename = filename;
68     }
69 
70     extern (C++) const(char)* toChars(
71         bool showColumns = Loc.showColumns,
72         MessageStyle messageStyle = Loc.messageStyle) const pure nothrow
73     {
74         OutBuffer buf;
75         if (filename)
76         {
77             buf.writestring(filename);
78         }
79         if (linnum)
80         {
81             final switch (messageStyle)
82             {
83                 case MessageStyle.digitalmars:
84                     buf.writeByte('(');
85                     buf.print(linnum);
86                     if (showColumns && charnum)
87                     {
88                         buf.writeByte(',');
89                         buf.print(charnum);
90                     }
91                     buf.writeByte(')');
92                     break;
93                 case MessageStyle.gnu: // https://www.gnu.org/prep/standards/html_node/Errors.html
94                     buf.writeByte(':');
95                     buf.print(linnum);
96                     if (showColumns && charnum)
97                     {
98                         buf.writeByte(':');
99                         buf.print(charnum);
100                     }
101                     break;
102             }
103         }
104         return buf.extractChars();
105     }
106 
107     /**
108      * Checks for equivalence by comparing the filename contents (not the pointer) and character location.
109      *
110      * Note:
111      *  - Uses case-insensitive comparison on Windows
112      *  - Ignores `charnum` if `Columns` is false.
113      */
114     extern (C++) bool equals(ref const(Loc) loc) const
115     {
116         return (!showColumns || charnum == loc.charnum) &&
117                linnum == loc.linnum &&
118                FileName.equals(filename, loc.filename);
119     }
120 
121     /**
122      * `opEquals()` / `toHash()` for AA key usage
123      *
124      * Compare filename contents (case-sensitively on Windows too), not
125      * the pointer - a static foreach loop repeatedly mixing in a mixin
126      * may lead to multiple equivalent filenames (`foo.d-mixin-<line>`),
127      * e.g., for test/runnable/test18880.d.
128      */
129     extern (D) bool opEquals(ref const(Loc) loc) const @trusted pure nothrow @nogc
130     {
131         import core.stdc.string : strcmp;
132 
133         return charnum == loc.charnum &&
134                linnum == loc.linnum &&
135                (filename == loc.filename ||
136                 (filename && loc.filename && strcmp(filename, loc.filename) == 0));
137     }
138 
139     /// ditto
140     extern (D) size_t toHash() const @trusted pure nothrow
141     {
142         import dmd.root.string : toDString;
143 
144         auto hash = hashOf(linnum);
145         hash = hashOf(charnum, hash);
146         hash = hashOf(filename.toDString, hash);
147         return hash;
148     }
149 
150     /******************
151      * Returns:
152      *   true if Loc has been set to other than the default initialization
153      */
154     bool isValid() const pure
155     {
156         return filename !is null;
157     }
158 }