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         } catch (Exception ex) {
36         }
37 
38         return true;
39     }
40 }
41 
42 /** Analyze the scope the declaration/definition reside in by backtracking to
43  * the root.
44  */
45 auto backtrackScopeRange(NodeT)(const(NodeT) node) {
46     static if (is(NodeT == Cursor)) {
47         Cursor c = node;
48     } else {
49         // a Declaration class
50         // TODO add a constraint
51         Cursor c = node.cursor;
52     }
53 
54     import std.algorithm : among, filter;
55     import clang.c.Index : CXCursorKind;
56 
57     return BacktrackResult(c).filter!(a => a.kind.among(CXCursorKind.unionDecl,
58             CXCursorKind.structDecl, CXCursorKind.classDecl, CXCursorKind.namespace));
59 }
60 
61 /// Backtrack a cursor until the top cursor is reached.
62 auto backtrack(NodeT)(const(NodeT) node) {
63     static if (is(NodeT == Cursor)) {
64         Cursor c = node;
65     } else {
66         // a Declaration class
67         // TODO add a constraint
68         Cursor c = node.cursor;
69     }
70 
71     return BacktrackResult(c);
72 }
73 
74 /// Determine if a kind creates a local scope.
75 bool isLocalScope(CXCursorKind kind) @safe pure nothrow @nogc {
76     switch (kind) with (CXCursorKind) {
77     case classTemplate:
78     case structDecl:
79     case unionDecl:
80     case classDecl:
81     case cxxMethod:
82     case functionDecl:
83     case constructor:
84     case destructor:
85         return true;
86     default:
87         return false;
88     }
89 }
90 
91 /// Determine if a cursor is in the global or namespace scope.
92 bool isGlobalOrNamespaceScope(const(Cursor) c) @safe {
93     import std.algorithm : among;
94     import clang.c.Index : CXCursorKind;
95 
96     // if the loop is never ran it is in the global namespace
97     foreach (bt; c.backtrack) {
98         if (bt.kind.among(CXCursorKind.namespace, CXCursorKind.translationUnit)) {
99             return true;
100         } else if (bt.kind.isLocalScope) {
101             return false;
102         }
103     }
104 
105     return false;
106 }