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