1 /**
2  * Copyright: Copyright (c) 2011 Jacob Carlborg. All rights reserved.
3  * Authors: Jacob Carlborg, Joakim Brännström (joakim.brannstrom dottli gmx.com)
4  * Version: 1.1
5  * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
6  * History:
7  *  1.0 initial release. 2012-01-29 $(BR)
8  *    Jacob Carlborg
9  *
10  *  1.1 additional features missing compared to cindex.py. 2015-03-07 $(BR)
11  *    Joakim Brännström
12  */
13 module clang.SourceLocation;
14 
15 import std.typecons;
16 
17 import clang.c.Index;
18 
19 import clang.File;
20 import clang.TranslationUnit;
21 import clang.Util;
22 
23 /// A SourceLocation represents a particular location within a source file.
24 struct SourceLocation {
25     import std.format : FormatSpec, format, formattedWrite, formatValue;
26 
27     mixin CX;
28 
29     struct Location {
30         File file;
31         uint line;
32         uint column;
33         uint offset;
34 
35         string toString() @safe const {
36             import std.exception : assumeUnique;
37             import std.format : FormatSpec;
38 
39             char[] buf;
40             buf.reserve(100);
41             auto fmt = FormatSpec!char("%s");
42             toString((const(char)[] s) { buf ~= s; }, fmt);
43             auto trustedUnique(T)(T t) @trusted {
44                 return assumeUnique(t);
45             }
46 
47             return trustedUnique(buf);
48         }
49 
50         void toString(Writer, Char)(scope Writer w, FormatSpec!Char fmt) const {
51             formattedWrite(w, "[%s line=%d column=%d offset=%d]", file.name, line, column, offset);
52         }
53     }
54 
55     // ugly hack. Must fix to something that works for both File and string.
56     struct Location2 {
57         string file;
58         uint line;
59         uint column;
60         uint offset;
61 
62         string toString() @safe const {
63             import std.exception : assumeUnique;
64             import std.format : FormatSpec;
65 
66             char[] buf;
67             buf.reserve(100);
68             auto fmt = FormatSpec!char("%s");
69             toString((const(char)[] s) { buf ~= s; }, fmt);
70             auto trustedUnique(T)(T t) @trusted {
71                 return assumeUnique(t);
72             }
73 
74             return trustedUnique(buf);
75         }
76 
77         void toString(Writer, Char)(scope Writer w, FormatSpec!Char fmt) const {
78             formattedWrite(w, "[%s line=%d column=%d offset=%d]", file, line, column, offset);
79         }
80     }
81 
82     /// Retrieve a NULL (invalid) source location.
83     static SourceLocation empty() {
84         auto r = clang_getNullLocation();
85         return SourceLocation(r);
86     }
87 
88     /** Retrieves the source location associated with a given file/line/column
89      * in a particular translation unit.
90      * TODO consider moving to TranslationUnit instead
91      *
92      * Params:
93      *  tu = translation unit to derive location from.
94      *  file = a file in tu.
95      *  line = text line. Starting at 1.
96      *  offset = offset into the line. Starting at 1.
97      */
98     static Nullable!SourceLocation fromPosition(ref TranslationUnit tu,
99             ref File file, uint line, uint offset) {
100 
101         auto rval = Nullable!SourceLocation();
102         auto r = SourceLocation(clang_getLocation(tu, file, line, offset));
103         if (r.file !is null) {
104             rval = SourceLocation(r);
105         }
106 
107         return rval;
108     }
109 
110     /** Retrieves the source location associated with a given character offset
111      * in a particular translation unit.
112      * TODO consider moving to TranslationUnit instead
113      */
114     static SourceLocation fromOffset(ref TranslationUnit tu, ref File file, uint offset) {
115         auto r = clang_getLocationForOffset(tu, file, offset);
116         return SourceLocation(r);
117     }
118 
119     /// Get the file represented by this source location.
120     /// TODO implement with a cache, this is inefficient.
121     @property File file() const @safe {
122         return spelling.file;
123     }
124 
125     /// Get the line represented by this source location.
126     @property uint line() const @trusted {
127         uint result;
128         clang_getSpellingLocation(cx, null, &result, null, null);
129         return result;
130     }
131 
132     /// Get the column represented by this source location.
133     @property uint column() const @trusted {
134         uint result;
135         clang_getSpellingLocation(cx, null, null, &result, null);
136         return result;
137     }
138 
139     /// Get the file offset represented by this source location.
140     @property uint offset() const @trusted {
141         uint result;
142         clang_getSpellingLocation(cx, null, null, null, &result);
143         return result;
144     }
145 
146     /// The path the SourceLocation point to.
147     @property string path() const @trusted {
148         File file;
149         clang_getSpellingLocation(cx, &file.cx, null, null, null);
150         return file.name;
151     }
152 
153     /** Returns if the given source location is in the main file of the
154      * corresponding translation unit.
155      */
156     @property bool isFromMainFile() const @trusted {
157         return clang_Location_isFromMainFile(cx) != 0;
158     }
159 
160     /** Retrieve the file, line, column, and offset represented by
161      * the given source location.
162      *
163      * If the location refers into a macro expansion, retrieves the
164      * location of the macro expansion.
165      *
166      * Location within a source file that will be decomposed into its parts.
167      *
168      * file [out] if non-NULL, will be set to the file to which the given
169      * source location points.
170      *
171      * line [out] if non-NULL, will be set to the line to which the given
172      * source location points.
173      *
174      * column [out] if non-NULL, will be set to the column to which the given
175      * source location points.
176      *
177      * offset [out] if non-NULL, will be set to the offset into the
178      * buffer to which the given source location points.
179      */
180     @property Location expansion() const @trusted {
181         Location data;
182 
183         clang_getExpansionLocation(cx, &data.file.cx, &data.line, &data.column, &data.offset);
184 
185         return data;
186     }
187 
188     /** Retrieve the file, line, column, and offset represented by
189      * the given source location, as specified in a # line directive.
190      *
191      * Example: given the following source code in a file somefile.c
192      * ---
193      * #123 "dummy.c" 1
194      *
195      * static int func()
196      * {
197      *     return 0;
198      * }
199      * ---
200      * the location information returned by this function would be
201      * ---
202      * File: dummy.c Line: 124 Column: 12
203      * ---
204      * whereas clang_getExpansionLocation would have returned
205      * ---
206      * File: somefile.c Line: 3 Column: 12
207      * ---
208      *
209      *  filename [out] if non-NULL, will be set to the filename of the
210      * source location. Note that filenames returned will be for "virtual" files,
211      * which don't necessarily exist on the machine running clang - e.g. when
212      * parsing preprocessed output obtained from a different environment. If
213      * a non-NULL value is passed in, remember to dispose of the returned value
214      * using \c clang_disposeString() once you've finished with it. For an invalid
215      * source location, an empty string is returned.
216      *
217      *  line [out] if non-NULL, will be set to the line number of the
218      * source location. For an invalid source location, zero is returned.
219      *
220      *  column [out] if non-NULL, will be set to the column number of the
221      * source location. For an invalid source location, zero is returned.
222      */
223     auto presumed() const @trusted {
224         Location2 data;
225         CXString cxstring;
226 
227         clang_getPresumedLocation(cx, &cxstring, &data.line, &data.column);
228         data.file = toD(cxstring);
229 
230         return data;
231     }
232 
233     /** Retrieve the file, line, column, and offset represented by
234      * the given source location.
235      *
236      * If the location refers into a macro instantiation, return where the
237      * location was originally spelled in the source file.
238      *
239      * The location within a source file that will be decomposed into its
240      * parts.
241      *
242      * file [out] if non-NULL, will be set to the file to which the given
243      * source location points.
244      *
245      * line [out] if non-NULL, will be set to the line to which the given
246      * source location points.
247      *
248      * column [out] if non-NULL, will be set to the column to which the given
249      * source location points.
250      *
251      * offset [out] if non-NULL, will be set to the offset into the
252      * buffer to which the given source location points.
253      */
254     @property Location spelling() const @trusted {
255         Location data;
256 
257         clang_getSpellingLocation(cx, &data.file.cx, &data.line, &data.column, &data.offset);
258 
259         return data;
260     }
261 
262     string toString() @safe const {
263         import std.exception : assumeUnique;
264 
265         char[] buf;
266         buf.reserve(100);
267         auto fmt = FormatSpec!char("%s");
268         toString((const(char)[] s) { buf ~= s; }, fmt);
269         auto trustedUnique(T)(T t) @trusted {
270             return assumeUnique(t);
271         }
272 
273         return trustedUnique(buf);
274     }
275 
276     void toString(Writer, Char)(scope Writer w, FormatSpec!Char fmt) const {
277         if (isValid)
278             formatValue(w, spelling, fmt);
279     }
280 }