1 /**
2 Copyright: Copyright (c) 2017, Joakim Brännström. All rights reserved.
3 License: MPL-2
4 Author: Joakim Brännström (joakim.brannstrom@gmx.com)
5 
6 This Source Code Form is subject to the terms of the Mozilla Public License,
7 v.2.0. If a copy of the MPL was not distributed with this file, You can obtain
8 one at http://mozilla.org/MPL/2.0/.
9 */
10 module cpptooling.analyzer.clang.cursor_backtrack;
11 
12 import clang.Cursor : Cursor;
13 
14 // TODO remove this, shouldn't be needed.
15 import clang.c.Index : CXCursorKind;
16 
17 private struct BacktrackResult {
18     private Cursor cursor;
19 
20     Cursor front() @safe nothrow const {
21         assert(!empty, "Can't get front of an empty range");
22 
23         return cursor;
24     }
25 
26     void popFront() @safe {
27         assert(!empty, "Can't pop front of an empty range");
28 
29         cursor = cursor.semanticParent;
30     }
31 
32     bool empty() @safe nothrow const {
33         try {
34             return !cursor.isValid;
35         }
36         catch (Exception ex) {
37         }
38 
39         return true;
40     }
41 }
42 
43 /** Analyze the scope the declaration/definition reside in by backtracking to
44  * the root.
45  */
46 auto backtrackScopeRange(NodeT)(const(NodeT) node) {
47     static if (is(NodeT == Cursor)) {
48         Cursor c = node;
49     } else {
50         // a Declaration class
51         // TODO add a constraint
52         Cursor c = node.cursor;
53     }
54 
55     import std.algorithm : among, filter;
56     import clang.c.Index : CXCursorKind;
57 
58     return BacktrackResult(c).filter!(a => a.kind.among(CXCursorKind.unionDecl,
59             CXCursorKind.structDecl, CXCursorKind.classDecl, CXCursorKind.namespace));
60 }
61 
62 /// Backtrack a cursor until the top cursor is reached.
63 auto backtrack(NodeT)(const(NodeT) node) {
64     static if (is(NodeT == Cursor)) {
65         Cursor c = node;
66     } else {
67         // a Declaration class
68         // TODO add a constraint
69         Cursor c = node.cursor;
70     }
71 
72     return BacktrackResult(c);
73 }
74 
75 /// Determine if a kind creates a local scope.
76 bool isLocalScope(CXCursorKind kind) @safe pure nothrow @nogc {
77     switch (kind) with (CXCursorKind) {
78     case classTemplate:
79     case structDecl:
80     case unionDecl:
81     case classDecl:
82     case cxxMethod:
83     case functionDecl:
84     case constructor:
85     case destructor:
86         return true;
87     default:
88         return false;
89     }
90 }
91 
92 /// Determine if a cursor is in the global or namespace scope.
93 bool isGlobalOrNamespaceScope(const(Cursor) c) @safe {
94     import std.algorithm : among;
95     import clang.c.Index : CXCursorKind;
96 
97     // if the loop is never ran it is in the global namespace
98     foreach (bt; c.backtrack) {
99         if (bt.kind.among(CXCursorKind.namespace, CXCursorKind.translationUnit)) {
100             return true;
101         } else if (bt.kind.isLocalScope) {
102             return false;
103         }
104     }
105 
106     return false;
107 }