1 /**
2  * Copyright: Copyright (c) 2016 Wojciech Szęszoł. All rights reserved.
3  * Authors: Wojciech Szęszoł
4  * Version: Initial created: Mar 08, 2016
5  * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
6  */
7 module dstep.translator.MacroIndex;
8 
9 import std.typecons;
10 import std.algorithm;
11 import std.range;
12 
13 import clang.c.Index;
14 import clang.Cursor;
15 import clang.SourceLocation;
16 import clang.SourceRange;
17 import clang.Token;
18 import clang.TranslationUnit;
19 
20 import dstep.translator.Preprocessor;
21 
22 class MacroIndex
23 {
24     private TranslationUnit translUnit;
25     private bool delegate (Cursor, Cursor) lessOp;
26     private Cursor[] expansions;
27     private Cursor[string] globalCursors_;
28     public Directive[] directives;
29 
30     this(TranslationUnit translUnit)
31     {
32         this.translUnit = translUnit;
33 
34         auto expansionsAppender = appender!(Cursor[])();
35 
36         foreach (cursor, parent; translUnit.cursor.all)
37         {
38             if (cursor.kind == CXCursorKind.macroExpansion)
39                 expansionsAppender.put(cursor);
40             else if (!cursor.spelling.empty)
41                 globalCursors_[extendedSpelling(cursor)] = cursor;
42         }
43 
44         lessOp = translUnit.relativeCursorLocationLessOp();
45         expansions = expansionsAppender.data.sort!((a, b) => lessOp(a, b)).array;
46         directives = dstep.translator.Preprocessor.directives(translUnit);
47     }
48 
49     static string extendedSpelling(Cursor cursor)
50     {
51         import std.format : format;
52 
53         switch (cursor.kind)
54         {
55             case CXCursorKind.structDecl:
56                 return format("struct %s", cursor.spelling);
57 
58             case CXCursorKind.unionDecl:
59                 return format("union %s", cursor.spelling);
60 
61             case CXCursorKind.enumDecl:
62                 return format("enum %s", cursor.spelling);
63 
64             default:
65                 return cursor.spelling;
66         }
67     }
68 
69     Cursor[] queryExpansion(Cursor cursor) const
70     {
71         import std.array;
72         import std.algorithm.searching;
73 
74         auto expansionsSorted = expansions.assumeSorted!((a, b) => lessOp(a, b));
75 
76         auto equal = expansionsSorted.equalRange(cursor);
77         auto greater = expansionsSorted.upperBound(cursor);
78 
79         auto result = appender!(Cursor[])();
80 
81         if (!equal.empty)
82             result ~= equal.array;
83 
84         result ~= until
85             !(itr => itr.file != cursor.file ||
86             itr.location.offset >= cursor.extent.end.offset)
87             (greater, OpenRight.yes);
88 
89         return result.data;
90     }
91 
92 
93     Tuple!(bool, SourceLocation) includeGuardLocation()
94     {
95         import std.range.primitives : empty;
96 
97         static bool checkIfndef(ConditionalDirective directives, string identifier)
98         {
99             auto negation = directives.condition.peek!UnaryExpr;
100 
101             if (negation !is null && negation.operator == "!")
102             {
103                 auto defined = negation.subexpr.peek!DefinedExpr;
104                 return defined !is null && defined.identifier == identifier;
105             }
106 
107             return false;
108         }
109 
110         if (!directives.empty)
111         {
112             if (directives[0].kind == DirectiveKind.pragmaOnce)
113             {
114                 return Tuple!(bool, SourceLocation)(true, directives[0].extent.start);
115             }
116             else if (2 <= directives.length)
117             {
118                 auto ifndef = cast (ConditionalDirective) directives[0];
119                 auto define = cast (DefineDirective) directives[1];
120                 auto endif = directives[$ - 1];
121 
122                 if (ifndef && define &&
123                     ifndef.endif == endif &&
124                     checkIfndef(ifndef, define.spelling))
125                     return Tuple!(bool, SourceLocation)(true, ifndef.location);
126             }
127         }
128 
129         return Tuple!(bool, SourceLocation)(false, SourceLocation.empty);
130     }
131 
132     Cursor[string] globalCursors()
133     {
134         return globalCursors_;
135     }
136 }