1/* Compiler implementation of the D programming language 2 * Copyright (C) 1999-2019 by The D Language Foundation, All Rights Reserved 3 * written by Walter Bright 4 * http://www.digitalmars.com 5 * Distributed under the Boost Software License, Version 1.0. 6 * http://www.boost.org/LICENSE_1_0.txt 7 * https://github.com/D-Programming-Language/dmd/blob/master/src/access.c 8 */ 9 10#include "root/dsystem.h" 11#include "root/root.h" 12#include "root/rmem.h" 13 14#include "errors.h" 15#include "enum.h" 16#include "aggregate.h" 17#include "init.h" 18#include "attrib.h" 19#include "scope.h" 20#include "id.h" 21#include "mtype.h" 22#include "declaration.h" 23#include "aggregate.h" 24#include "expression.h" 25#include "module.h" 26#include "template.h" 27 28/* Code to do access checks 29 */ 30 31bool hasPackageAccess(Scope *sc, Dsymbol *s); 32bool hasPackageAccess(Module *mod, Dsymbol *s); 33bool hasPrivateAccess(AggregateDeclaration *ad, Dsymbol *smember); 34bool isFriendOf(AggregateDeclaration *ad, AggregateDeclaration *cd); 35static Dsymbol *mostVisibleOverload(Dsymbol *s); 36 37/**************************************** 38 * Return Prot access for Dsymbol smember in this declaration. 39 */ 40Prot getAccess(AggregateDeclaration *ad, Dsymbol *smember) 41{ 42 Prot access_ret = Prot(PROTnone); 43 44 assert(ad->isStructDeclaration() || ad->isClassDeclaration()); 45 if (smember->toParent() == ad) 46 { 47 access_ret = smember->prot(); 48 } 49 else if (smember->isDeclaration()->isStatic()) 50 { 51 access_ret = smember->prot(); 52 } 53 if (ClassDeclaration *cd = ad->isClassDeclaration()) 54 { 55 for (size_t i = 0; i < cd->baseclasses->dim; i++) 56 { 57 BaseClass *b = (*cd->baseclasses)[i]; 58 59 Prot access = getAccess(b->sym, smember); 60 switch (access.kind) 61 { 62 case PROTnone: 63 break; 64 65 case PROTprivate: 66 access_ret = Prot(PROTnone); // private members of base class not accessible 67 break; 68 69 case PROTpackage: 70 case PROTprotected: 71 case PROTpublic: 72 case PROTexport: 73 // If access is to be tightened 74 if (PROTpublic < access.kind) 75 access = Prot(PROTpublic); 76 77 // Pick path with loosest access 78 if (access_ret.isMoreRestrictiveThan(access)) 79 access_ret = access; 80 break; 81 82 default: 83 assert(0); 84 } 85 } 86 } 87 88 return access_ret; 89} 90 91/******************************************************** 92 * Helper function for checkAccess() 93 * Returns: 94 * false is not accessible 95 * true is accessible 96 */ 97static bool isAccessible( 98 Dsymbol *smember, 99 Dsymbol *sfunc, 100 AggregateDeclaration *dthis, 101 AggregateDeclaration *cdscope) 102{ 103 assert(dthis); 104 105 if (hasPrivateAccess(dthis, sfunc) || 106 isFriendOf(dthis, cdscope)) 107 { 108 if (smember->toParent() == dthis) 109 return true; 110 111 if (ClassDeclaration *cdthis = dthis->isClassDeclaration()) 112 { 113 for (size_t i = 0; i < cdthis->baseclasses->dim; i++) 114 { 115 BaseClass *b = (*cdthis->baseclasses)[i]; 116 Prot access = getAccess(b->sym, smember); 117 if (access.kind >= PROTprotected || 118 isAccessible(smember, sfunc, b->sym, cdscope)) 119 { 120 return true; 121 } 122 } 123 } 124 } 125 else 126 { 127 if (smember->toParent() != dthis) 128 { 129 if (ClassDeclaration *cdthis = dthis->isClassDeclaration()) 130 { 131 for (size_t i = 0; i < cdthis->baseclasses->dim; i++) 132 { 133 BaseClass *b = (*cdthis->baseclasses)[i]; 134 if (isAccessible(smember, sfunc, b->sym, cdscope)) 135 return true; 136 } 137 } 138 } 139 } 140 return false; 141} 142 143/******************************* 144 * Do access check for member of this class, this class being the 145 * type of the 'this' pointer used to access smember. 146 * Returns true if the member is not accessible. 147 */ 148bool checkAccess(AggregateDeclaration *ad, Loc loc, Scope *sc, Dsymbol *smember) 149{ 150 FuncDeclaration *f = sc->func; 151 AggregateDeclaration *cdscope = sc->getStructClassScope(); 152 153 Dsymbol *smemberparent = smember->toParent(); 154 if (!smemberparent || !smemberparent->isAggregateDeclaration()) 155 { 156 return false; // then it is accessible 157 } 158 159 // BUG: should enable this check 160 //assert(smember->parent->isBaseOf(this, NULL)); 161 162 bool result; 163 Prot access; 164 if (smemberparent == ad) 165 { 166 access = smember->prot(); 167 result = access.kind >= PROTpublic || 168 hasPrivateAccess(ad, f) || 169 isFriendOf(ad, cdscope) || 170 (access.kind == PROTpackage && hasPackageAccess(sc, smember)) || 171 ad->getAccessModule() == sc->_module; 172 } 173 else if ((access = getAccess(ad, smember)).kind >= PROTpublic) 174 { 175 result = true; 176 } 177 else if (access.kind == PROTpackage && hasPackageAccess(sc, ad)) 178 { 179 result = true; 180 } 181 else 182 { 183 result = isAccessible(smember, f, ad, cdscope); 184 } 185 if (!result) 186 { 187 ad->error(loc, "member %s is not accessible", smember->toChars()); 188 //printf("smember = %s %s, prot = %d, semanticRun = %d\n", 189 // smember->kind(), smember->toPrettyChars(), smember->prot(), smember->semanticRun); 190 return true; 191 } 192 return false; 193} 194 195/**************************************** 196 * Determine if this is the same or friend of cd. 197 */ 198bool isFriendOf(AggregateDeclaration *ad, AggregateDeclaration *cd) 199{ 200 if (ad == cd) 201 return true; 202 203 // Friends if both are in the same module 204 //if (toParent() == cd->toParent()) 205 if (cd && ad->getAccessModule() == cd->getAccessModule()) 206 { 207 return true; 208 } 209 210 return false; 211} 212 213/**************************************** 214 * Determine if scope sc has package level access to s. 215 */ 216bool hasPackageAccess(Scope *sc, Dsymbol *s) 217{ 218 return hasPackageAccess(sc->_module, s); 219} 220 221bool hasPackageAccess(Module *mod, Dsymbol *s) 222{ 223 Package *pkg = NULL; 224 225 if (s->prot().pkg) 226 pkg = s->prot().pkg; 227 else 228 { 229 // no explicit package for protection, inferring most qualified one 230 for (; s; s = s->parent) 231 { 232 if (Module *m = s->isModule()) 233 { 234 DsymbolTable *dst = Package::resolve(m->md ? m->md->packages : NULL, NULL, NULL); 235 assert(dst); 236 Dsymbol *s2 = dst->lookup(m->ident); 237 assert(s2); 238 Package *p = s2->isPackage(); 239 if (p && p->isPackageMod()) 240 { 241 pkg = p; 242 break; 243 } 244 } 245 else if ((pkg = s->isPackage()) != NULL) 246 break; 247 } 248 } 249 250 if (pkg) 251 { 252 if (pkg == mod->parent) 253 { 254 return true; 255 } 256 if (pkg->isPackageMod() == mod) 257 { 258 return true; 259 } 260 Dsymbol* ancestor = mod->parent; 261 for (; ancestor; ancestor = ancestor->parent) 262 { 263 if (ancestor == pkg) 264 { 265 return true; 266 } 267 } 268 } 269 270 return false; 271} 272 273/**************************************** 274 * Determine if scope sc has protected level access to cd. 275 */ 276bool hasProtectedAccess(Scope *sc, Dsymbol *s) 277{ 278 if (ClassDeclaration *cd = s->isClassMember()) // also includes interfaces 279 { 280 for (Scope *scx = sc; scx; scx = scx->enclosing) 281 { 282 if (!scx->scopesym) 283 continue; 284 ClassDeclaration *cd2 = scx->scopesym->isClassDeclaration(); 285 if (cd2 && cd->isBaseOf(cd2, NULL)) 286 return true; 287 } 288 } 289 return sc->_module == s->getAccessModule(); 290} 291 292/********************************** 293 * Determine if smember has access to private members of this declaration. 294 */ 295bool hasPrivateAccess(AggregateDeclaration *ad, Dsymbol *smember) 296{ 297 if (smember) 298 { 299 AggregateDeclaration *cd = NULL; 300 Dsymbol *smemberparent = smember->toParent(); 301 if (smemberparent) 302 cd = smemberparent->isAggregateDeclaration(); 303 304 if (ad == cd) // smember is a member of this class 305 { 306 return true; // so we get private access 307 } 308 309 // If both are members of the same module, grant access 310 while (1) 311 { 312 Dsymbol *sp = smember->toParent(); 313 if (sp->isFuncDeclaration() && smember->isFuncDeclaration()) 314 smember = sp; 315 else 316 break; 317 } 318 if (!cd && ad->toParent() == smember->toParent()) 319 { 320 return true; 321 } 322 if (!cd && ad->getAccessModule() == smember->getAccessModule()) 323 { 324 return true; 325 } 326 } 327 return false; 328} 329 330/**************************************** 331 * Check access to d for expression e.d 332 * Returns true if the declaration is not accessible. 333 */ 334bool checkAccess(Loc loc, Scope *sc, Expression *e, Declaration *d) 335{ 336 if (sc->flags & SCOPEnoaccesscheck) 337 return false; 338 339 if (d->isUnitTestDeclaration()) 340 { 341 // Unittests are always accessible. 342 return false; 343 } 344 if (!e) 345 { 346 if ((d->prot().kind == PROTprivate && d->getAccessModule() != sc->_module) || 347 (d->prot().kind == PROTpackage && !hasPackageAccess(sc, d))) 348 { 349 error(loc, "%s %s is not accessible from module %s", 350 d->kind(), d->toPrettyChars(), sc->_module->toChars()); 351 return true; 352 } 353 } 354 else if (e->type->ty == Tclass) 355 { 356 // Do access check 357 ClassDeclaration *cd = (ClassDeclaration *)(((TypeClass *)e->type)->sym); 358 if (e->op == TOKsuper) 359 { 360 ClassDeclaration *cd2 = sc->func->toParent()->isClassDeclaration(); 361 if (cd2) 362 cd = cd2; 363 } 364 return checkAccess(cd, loc, sc, d); 365 } 366 else if (e->type->ty == Tstruct) 367 { 368 // Do access check 369 StructDeclaration *cd = (StructDeclaration *)(((TypeStruct *)e->type)->sym); 370 return checkAccess(cd, loc, sc, d); 371 } 372 return false; 373} 374 375/**************************************** 376 * Check access to package/module `p` from scope `sc`. 377 * 378 * Params: 379 * loc = source location for issued error message 380 * sc = scope from which to access to a fully qualified package name 381 * p = the package/module to check access for 382 * Returns: true if the package is not accessible. 383 * 384 * Because a global symbol table tree is used for imported packages/modules, 385 * access to them needs to be checked based on the imports in the scope chain 386 * (see Bugzilla 313). 387 * 388 */ 389bool checkAccess(Loc loc, Scope *sc, Package *p) 390{ 391 if (sc->_module == p) 392 return false; 393 for (; sc; sc = sc->enclosing) 394 { 395 if (sc->scopesym && sc->scopesym->isPackageAccessible(p, Prot(PROTprivate))) 396 return false; 397 } 398 const char *name = p->toPrettyChars(); 399 if (p->isPkgMod == PKGmodule || p->isModule()) 400 deprecation(loc, "%s %s is not accessible here, perhaps add 'static import %s;'", p->kind(), name, name); 401 else 402 deprecation(loc, "%s %s is not accessible here", p->kind(), name); 403 return true; 404} 405 406/** 407 * Check whether symbols `s` is visible in `mod`. 408 * 409 * Params: 410 * mod = lookup origin 411 * s = symbol to check for visibility 412 * Returns: true if s is visible in mod 413 */ 414bool symbolIsVisible(Module *mod, Dsymbol *s) 415{ 416 // should sort overloads by ascending protection instead of iterating here 417 s = mostVisibleOverload(s); 418 419 switch (s->prot().kind) 420 { 421 case PROTundefined: 422 return true; 423 case PROTnone: 424 return false; // no access 425 case PROTprivate: 426 return s->getAccessModule() == mod; 427 case PROTpackage: 428 return s->getAccessModule() == mod || hasPackageAccess(mod, s); 429 case PROTprotected: 430 return s->getAccessModule() == mod; 431 case PROTpublic: 432 case PROTexport: 433 return true; 434 default: 435 assert(0); 436 } 437} 438 439/** 440 * Same as above, but determines the lookup module from symbols `origin`. 441 */ 442bool symbolIsVisible(Dsymbol *origin, Dsymbol *s) 443{ 444 return symbolIsVisible(origin->getAccessModule(), s); 445} 446 447/** 448 * Same as above but also checks for protected symbols visible from scope `sc`. 449 * Used for qualified name lookup. 450 * 451 * Params: 452 * sc = lookup scope 453 * s = symbol to check for visibility 454 * Returns: true if s is visible by origin 455 */ 456bool symbolIsVisible(Scope *sc, Dsymbol *s) 457{ 458 s = mostVisibleOverload(s); 459 460 switch (s->prot().kind) 461 { 462 case PROTundefined: 463 return true; 464 case PROTnone: 465 return false; // no access 466 case PROTprivate: 467 return sc->_module == s->getAccessModule(); 468 case PROTpackage: 469 return sc->_module == s->getAccessModule() || hasPackageAccess(sc->_module, s); 470 case PROTprotected: 471 return hasProtectedAccess(sc, s); 472 case PROTpublic: 473 case PROTexport: 474 return true; 475 default: 476 assert(0); 477 } 478} 479 480/** 481 * Use the most visible overload to check visibility. Later perform an access 482 * check on the resolved overload. This function is similar to overloadApply, 483 * but doesn't recurse nor resolve aliases because protection/visibility is an 484 * attribute of the alias not the aliasee. 485 */ 486static Dsymbol *mostVisibleOverload(Dsymbol *s) 487{ 488 if (!s->isOverloadable()) 489 return s; 490 491 Dsymbol *next = NULL; 492 Dsymbol *fstart = s; 493 Dsymbol *mostVisible = s; 494 for (; s; s = next) 495 { 496 // void func() {} 497 // private void func(int) {} 498 if (FuncDeclaration *fd = s->isFuncDeclaration()) 499 next = fd->overnext; 500 // template temp(T) {} 501 // private template temp(T:int) {} 502 else if (TemplateDeclaration *td = s->isTemplateDeclaration()) 503 next = td->overnext; 504 // alias common = mod1.func1; 505 // alias common = mod2.func2; 506 else if (FuncAliasDeclaration *fa = s->isFuncAliasDeclaration()) 507 next = fa->overnext; 508 // alias common = mod1.templ1; 509 // alias common = mod2.templ2; 510 else if (OverDeclaration *od = s->isOverDeclaration()) 511 next = od->overnext; 512 // alias name = sym; 513 // private void name(int) {} 514 else if (AliasDeclaration *ad = s->isAliasDeclaration()) 515 { 516 if (!ad->isOverloadable()) 517 { 518 //printf("Non overloadable Aliasee in overload list\n"); 519 assert(0); 520 } 521 // Yet unresolved aliases store overloads in overnext. 522 if (ad->semanticRun < PASSsemanticdone) 523 next = ad->overnext; 524 else 525 { 526 /* This is a bit messy due to the complicated implementation of 527 * alias. Aliases aren't overloadable themselves, but if their 528 * Aliasee is overloadable they can be converted to an overloadable 529 * alias. 530 * 531 * This is done by replacing the Aliasee w/ FuncAliasDeclaration 532 * (for functions) or OverDeclaration (for templates) which are 533 * simply overloadable aliases w/ weird names. 534 * 535 * Usually aliases should not be resolved for visibility checking 536 * b/c public aliases to private symbols are public. But for the 537 * overloadable alias situation, the Alias (_ad_) has been moved 538 * into it's own Aliasee, leaving a shell that we peel away here. 539 */ 540 Dsymbol *aliasee = ad->toAlias(); 541 if (aliasee->isFuncAliasDeclaration() || aliasee->isOverDeclaration()) 542 next = aliasee; 543 else 544 { 545 /* A simple alias can be at the end of a function or template overload chain. 546 * It can't have further overloads b/c it would have been 547 * converted to an overloadable alias. 548 */ 549 if (ad->overnext) 550 { 551 //printf("Unresolved overload of alias\n"); 552 assert(0); 553 } 554 break; 555 } 556 } 557 558 // handled by overloadApply for unknown reason 559 assert(next != ad); // should not alias itself 560 assert(next != fstart); // should not alias the overload list itself 561 } 562 else 563 break; 564 565 if (next && mostVisible->prot().isMoreRestrictiveThan(next->prot())) 566 mostVisible = next; 567 } 568 return mostVisible; 569} 570