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 }