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