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.algorithm : canFind, any; 27 import std.experimental.logger; 28 import stdx.allocator.mallocator : Mallocator; 29 30 /** 31 * Contains symbols and supports lookup of symbols by cursor position. 32 */ 33 struct Scope 34 { 35 @disable this(this); 36 @disable this(); 37 38 /** 39 * Params: 40 * begin = the beginning byte index 41 * end = the ending byte index 42 */ 43 this (uint begin, uint end) 44 { 45 this.startLocation = begin; 46 this.endLocation = end; 47 } 48 49 ~this() 50 { 51 foreach (child; children[]) 52 typeid(Scope).destroy(child); 53 foreach (symbol; _symbols) 54 { 55 if (symbol.owned) 56 typeid(DSymbol).destroy(symbol.ptr); 57 } 58 } 59 60 /** 61 * Params: 62 * cursorPosition = the cursor position in bytes 63 * Returns: 64 * the innermost scope that contains the given cursor position 65 */ 66 Scope* getScopeByCursor(size_t cursorPosition) return pure @nogc 67 { 68 if (cursorPosition < startLocation) return null; 69 if (cursorPosition > endLocation) return null; 70 foreach (child; children[]) 71 { 72 auto childScope = child.getScopeByCursor(cursorPosition); 73 if (childScope !is null) 74 return childScope; 75 } 76 return cast(typeof(return)) &this; 77 } 78 79 /** 80 * Params: 81 * cursorPosition = the cursor position in bytes 82 * Returns: 83 * all symbols in the scope containing the cursor position, as well as 84 * the symbols in parent scopes of that scope. 85 */ 86 DSymbol*[] getSymbolsInCursorScope(size_t cursorPosition) 87 { 88 import std.array : array; 89 import std.algorithm.iteration : map; 90 91 auto s = getScopeByCursor(cursorPosition); 92 if (s is null) 93 return null; 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 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 != CONSTRUCTOR_SYMBOL_NAME && 159 name != DESTRUCTOR_SYMBOL_NAME && 160 name != UNITTEST_SYMBOL_NAME && 161 name != THIS_SYMBOL_NAME) 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 && e.type.name == name) 172 app.put(cast(DSymbol*) e.type); 173 else 174 foreach (importedSymbol; e.type.getPartsByName(s.name)) 175 app.put(cast(DSymbol*) importedSymbol); 176 } 177 if (app.data.length > 0) 178 return cast(typeof(return)) app.data; 179 } 180 if (parent is null) 181 return []; 182 return parent.getSymbolsByName(name); 183 } 184 185 /** 186 * Params: 187 * name = the symbol name to search for 188 * cursorPosition = the cursor position in bytes 189 * Returns: 190 * all symbols with the given name in the scope containing the cursor 191 * and its parent scopes 192 */ 193 DSymbol*[] getSymbolsByNameAndCursor(istring name, size_t cursorPosition) 194 { 195 auto s = getScopeByCursor(cursorPosition); 196 if (s is null) 197 return []; 198 return s.getSymbolsByName(name); 199 } 200 201 DSymbol* getFirstSymbolByNameAndCursor(istring name, size_t cursorPosition) 202 { 203 auto s = getSymbolsByNameAndCursor(name, cursorPosition); 204 return s.length > 0 ? s[0] : null; 205 } 206 207 /** 208 * Returns an array of symbols that are present at global scope 209 */ 210 inout(DSymbol)*[] getSymbolsAtGlobalScope(istring name) inout 211 { 212 if (parent !is null) 213 return parent.getSymbolsAtGlobalScope(name); 214 return getSymbolsByName(name); 215 } 216 217 bool hasSymbolRecursive(const(DSymbol)* symbol) const 218 { 219 return _symbols[].canFind!(a => a == symbol) || children[].any!(a => a.hasSymbolRecursive(symbol)); 220 } 221 222 /// The scope that contains this one 223 Scope* parent; 224 225 /// Child scopes 226 UnrolledList!(Scope*, Mallocator, false) children; 227 228 /// Start location of this scope in bytes 229 uint startLocation; 230 231 /// End location of this scope in bytes 232 uint endLocation; 233 234 auto symbols() @property 235 { 236 return _symbols[]; 237 } 238 239 /** 240 * Adds the given symbol to this scope. 241 * Params: 242 * symbol = the symbol to add 243 * owns = if true, the symbol's destructor will be called when this 244 * scope's destructor is called. 245 */ 246 void addSymbol(DSymbol* symbol, bool owns) 247 { 248 assert(symbol !is null); 249 _symbols.insert(SymbolOwnership(symbol, owns)); 250 } 251 252 private: 253 /// Symbols contained in this scope 254 TTree!(SymbolOwnership, Mallocator, true, "a.opCmp(b) < 0", false) _symbols; 255 }