1 /** 2 * This file is part of DCD, a development tool for the D programming language. 3 * Copyright (C) 2014 Brian Schott 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 3 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 */ 18 19 module dsymbol.scope_; 20 21 import dsymbol.symbol; 22 import dsymbol.import_; 23 import dsymbol.builtin.names; 24 import containers.ttree; 25 import containers.unrolledlist; 26 import std.experimental.logger; 27 import stdx.allocator.mallocator : Mallocator; 28 29 /** 30 * Contains symbols and supports lookup of symbols by cursor position. 31 */ 32 struct Scope 33 { 34 @disable this(this); 35 @disable this(); 36 37 /** 38 * Params: 39 * begin = the beginning byte index 40 * end = the ending byte index 41 */ 42 this (uint begin, uint end) 43 { 44 this.startLocation = begin; 45 this.endLocation = end; 46 } 47 48 ~this() 49 { 50 foreach (child; children[]) 51 typeid(Scope).destroy(child); 52 foreach (symbol; _symbols) 53 { 54 if (symbol.owned) 55 typeid(DSymbol).destroy(symbol.ptr); 56 } 57 } 58 59 /** 60 * Params: 61 * cursorPosition = the cursor position in bytes 62 * Returns: 63 * the innermost scope that contains the given cursor position 64 */ 65 Scope* getScopeByCursor(size_t cursorPosition) pure @nogc 66 { 67 if (cursorPosition < startLocation) return null; 68 if (cursorPosition > endLocation) return null; 69 foreach (child; children[]) 70 { 71 auto childScope = child.getScopeByCursor(cursorPosition); 72 if (childScope !is null) 73 return childScope; 74 } 75 return cast(typeof(return)) &this; 76 } 77 78 /** 79 * Params: 80 * cursorPosition = the cursor position in bytes 81 * Returns: 82 * all symbols in the scope containing the cursor position, as well as 83 * the symbols in parent scopes of that scope. 84 */ 85 DSymbol*[] getSymbolsInCursorScope(size_t cursorPosition) 86 { 87 import std.array : array; 88 import std.algorithm.iteration : map; 89 90 auto s = getScopeByCursor(cursorPosition); 91 92 if (s is null) 93 return []; 94 95 UnrolledList!(DSymbol*) retVal; 96 Scope* sc = s; 97 while (sc !is null) 98 { 99 foreach (item; sc._symbols[]) 100 { 101 if (item.ptr.kind == CompletionKind.withSymbol) 102 { 103 if (item.ptr.type !is null) 104 foreach (i; item.ptr.type.opSlice()) 105 retVal.insert(i); 106 } 107 else if (item.ptr.type !is null && item.ptr.kind == CompletionKind.importSymbol) 108 { 109 if (item.ptr.qualifier != SymbolQualifier.selectiveImport) 110 { 111 foreach (i; item.ptr.type.opSlice()) 112 retVal.insert(i); 113 } 114 else if (item.ptr !is null) 115 retVal.insert(item.ptr.type); 116 } 117 else 118 retVal.insert(item.ptr); 119 } 120 sc = sc.parent; 121 } 122 return array(retVal[]); 123 } 124 125 /** 126 * Params: 127 * name = the symbol name to search for 128 * Returns: 129 * all symbols in this scope or parent scopes with the given name 130 */ 131 inout(DSymbol)*[] getSymbolsByName(istring name) inout 132 { 133 import std.array : array, appender; 134 import std.algorithm.iteration : map; 135 136 DSymbol s = DSymbol(name); 137 auto er = _symbols.equalRange(SymbolOwnership(&s)); 138 if (!er.empty) 139 return cast(typeof(return)) array(er.map!(a => a.ptr)); 140 141 // Check symbols from "with" statement 142 DSymbol ir2 = DSymbol(WITH_SYMBOL_NAME); 143 auto r2 = _symbols.equalRange(SymbolOwnership(&ir2)); 144 if (!r2.empty) 145 { 146 auto app = appender!(DSymbol*[])(); 147 foreach (e; r2) 148 { 149 if (e.type is null) 150 continue; 151 foreach (withSymbol; e.type.getPartsByName(s.name)) 152 app.put(cast(DSymbol*) withSymbol); 153 } 154 if (app.data.length > 0) 155 return cast(typeof(return)) app.data; 156 } 157 158 if (name.ptr != CONSTRUCTOR_SYMBOL_NAME.ptr 159 && name.ptr != DESTRUCTOR_SYMBOL_NAME.ptr 160 && name.ptr != UNITTEST_SYMBOL_NAME.ptr 161 && name.ptr != THIS_SYMBOL_NAME.ptr) 162 { 163 // Check imported symbols 164 DSymbol ir = DSymbol(IMPORT_SYMBOL_NAME); 165 166 auto app = appender!(DSymbol*[])(); 167 foreach (e; _symbols.equalRange(SymbolOwnership(&ir))) 168 { 169 if (e.type is null) 170 continue; 171 if (e.qualifier == SymbolQualifier.selectiveImport && 172 e.type.name.ptr == name.ptr) 173 app.put(cast(DSymbol*) e.type); 174 else 175 foreach (importedSymbol; e.type.getPartsByName(s.name)) 176 app.put(cast(DSymbol*) importedSymbol); 177 } 178 if (app.data.length > 0) 179 return cast(typeof(return)) app.data; 180 } 181 if (parent is null) 182 return []; 183 return parent.getSymbolsByName(name); 184 } 185 186 /** 187 * Params: 188 * name = the symbol name to search for 189 * cursorPosition = the cursor position in bytes 190 * Returns: 191 * all symbols with the given name in the scope containing the cursor 192 * and its parent scopes 193 */ 194 DSymbol*[] getSymbolsByNameAndCursor(istring name, size_t cursorPosition) 195 { 196 auto s = getScopeByCursor(cursorPosition); 197 if (s is null) 198 return []; 199 return s.getSymbolsByName(name); 200 } 201 202 DSymbol* getFirstSymbolByNameAndCursor(istring name, size_t cursorPosition) 203 { 204 auto s = getSymbolsByNameAndCursor(name, cursorPosition); 205 return s.length > 0 ? s[0] : null; 206 } 207 208 /** 209 * Returns an array of symbols that are present at global scope 210 */ 211 inout(DSymbol)*[] getSymbolsAtGlobalScope(istring name) inout 212 { 213 if (parent !is null) 214 return parent.getSymbolsAtGlobalScope(name); 215 return getSymbolsByName(name); 216 } 217 218 /// The scope that contains this one 219 Scope* parent; 220 221 /// Child scopes 222 UnrolledList!(Scope*, Mallocator, false) children; 223 224 /// Start location of this scope in bytes 225 uint startLocation; 226 227 /// End location of this scope in bytes 228 uint endLocation; 229 230 auto symbols() @property 231 { 232 return _symbols[]; 233 } 234 235 /** 236 * Adds the given symbol to this scope. 237 * Params: 238 * symbol = the symbol to add 239 * owns = if true, the symbol's destructor will be called when this 240 * scope's destructor is called. 241 */ 242 void addSymbol(DSymbol* symbol, bool owns) 243 { 244 assert(symbol !is null); 245 _symbols.insert(SymbolOwnership(symbol, owns)); 246 } 247 248 private: 249 /// Symbols contained in this scope 250 TTree!(SymbolOwnership, Mallocator, true, "a.opCmp(b) < 0", false) _symbols; 251 }