1
/**
2
 * Enforce visibility contrains such as `public` and `private`.
3
 *
4
 * Specification: $(LINK2 https://dlang.org/spec/attribute.html#visibility_attributes, Visibility Attributes)
5
 *
6
 * Copyright:   Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved
7
 * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
8
 * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
9
 * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/access.d, _access.d)
10
 * Documentation:  https://dlang.org/phobos/dmd_access.html
11
 * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/access.d
12
 */
13

14
module dmd.access;
15

16
import dmd.aggregate;
17
import dmd.dclass;
18
import dmd.declaration;
19
import dmd.dmodule;
20
import dmd.dscope;
21
import dmd.dstruct;
22
import dmd.dsymbol;
23
import dmd.errors;
24
import dmd.expression;
25
import dmd.func;
26
import dmd.globals;
27
import dmd.mtype;
28
import dmd.tokens;
29

30
private enum LOG = false;
31

32

33
/*******************************
34
 * Do access check for member of this class, this class being the
35
 * type of the 'this' pointer used to access smember.
36
 * Returns true if the member is not accessible.
37
 */
38
bool checkAccess(AggregateDeclaration ad, Loc loc, Scope* sc, Dsymbol smember)
39
{
40
    static if (LOG)
41
    {
42
        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);
43
    }
44

45 1
    const p = smember.toParent();
46 1
    if (p && p.isTemplateInstance())
47
    {
48 1
        return false; // for backward compatibility
49
    }
50

51 1
    if (!symbolIsVisible(sc, smember) && (!(sc.flags & SCOPE.onlysafeaccess) || sc.func.setUnsafe()))
52
    {
53 1
        ad.error(loc, "member `%s` is not accessible%s", smember.toChars(), (sc.flags & SCOPE.onlysafeaccess) ? " from `@safe` code".ptr : "".ptr);
54
        //printf("smember = %s %s, prot = %d, semanticRun = %d\n",
55
        //        smember.kind(), smember.toPrettyChars(), smember.prot(), smember.semanticRun);
56 1
        return true;
57
    }
58 1
    return false;
59
}
60

61
/****************************************
62
 * Determine if scope sc has package level access to s.
63
 */
64
private bool hasPackageAccess(Scope* sc, Dsymbol s)
65
{
66 0
    return hasPackageAccess(sc._module, s);
67
}
68

69
private bool hasPackageAccess(Module mod, Dsymbol s)
70
{
71
    static if (LOG)
72
    {
73
        printf("hasPackageAccess(s = '%s', mod = '%s', s.protection.pkg = '%s')\n", s.toChars(), mod.toChars(), s.prot().pkg ? s.prot().pkg.toChars() : "NULL");
74
    }
75 1
    Package pkg = null;
76 1
    if (s.prot().pkg)
77 1
        pkg = s.prot().pkg;
78
    else
79
    {
80
        // no explicit package for protection, inferring most qualified one
81 1
        for (; s; s = s.parent)
82
        {
83 1
            if (auto m = s.isModule())
84
            {
85 1
                DsymbolTable dst = Package.resolve(m.md ? m.md.packages : null, null, null);
86 1
                assert(dst);
87 1
                Dsymbol s2 = dst.lookup(m.ident);
88 1
                assert(s2);
89 1
                Package p = s2.isPackage();
90 1
                if (p && p.isPackageMod())
91
                {
92 1
                    pkg = p;
93 1
                    break;
94
                }
95
            }
96 1
            else if ((pkg = s.isPackage()) !is null)
97 1
                break;
98
        }
99
    }
100
    static if (LOG)
101
    {
102
        if (pkg)
103
            printf("\tsymbol access binds to package '%s'\n", pkg.toChars());
104
    }
105 1
    if (pkg)
106
    {
107 1
        if (pkg == mod.parent)
108
        {
109
            static if (LOG)
110
            {
111
                printf("\tsc is in permitted package for s\n");
112
            }
113 1
            return true;
114
        }
115 1
        if (pkg.isPackageMod() == mod)
116
        {
117
            static if (LOG)
118
            {
119
                printf("\ts is in same package.d module as sc\n");
120
            }
121 1
            return true;
122
        }
123 1
        Dsymbol ancestor = mod.parent;
124 1
        for (; ancestor; ancestor = ancestor.parent)
125
        {
126 1
            if (ancestor == pkg)
127
            {
128
                static if (LOG)
129
                {
130
                    printf("\tsc is in permitted ancestor package for s\n");
131
                }
132 1
                return true;
133
            }
134
        }
135
    }
136
    static if (LOG)
137
    {
138
        printf("\tno package access\n");
139
    }
140 1
    return false;
141
}
142

143
/****************************************
144
 * Determine if scope sc has protected level access to cd.
145
 */
146
private bool hasProtectedAccess(Scope *sc, Dsymbol s)
147
{
148 1
    if (auto cd = s.isClassMember()) // also includes interfaces
149
    {
150 1
        for (auto scx = sc; scx; scx = scx.enclosing)
151
        {
152 1
            if (!scx.scopesym)
153 1
                continue;
154 1
            auto cd2 = scx.scopesym.isClassDeclaration();
155 1
            if (cd2 && cd.isBaseOf(cd2, null))
156 1
                return true;
157
        }
158
    }
159 1
    return sc._module == s.getAccessModule();
160
}
161

162
/****************************************
163
 * Check access to d for expression e.d
164
 * Returns true if the declaration is not accessible.
165
 */
166
bool checkAccess(Loc loc, Scope* sc, Expression e, Declaration d)
167
{
168 1
    if (sc.flags & SCOPE.noaccesscheck)
169 1
        return false;
170
    static if (LOG)
171
    {
172
        if (e)
173
        {
174
            printf("checkAccess(%s . %s)\n", e.toChars(), d.toChars());
175
            printf("\te.type = %s\n", e.type.toChars());
176
        }
177
        else
178
        {
179
            printf("checkAccess(%s)\n", d.toPrettyChars());
180
        }
181
    }
182 1
    if (d.isUnitTestDeclaration())
183
    {
184
        // Unittests are always accessible.
185 1
        return false;
186
    }
187

188 1
    if (!e)
189 1
        return false;
190

191 1
    if (e.type.ty == Tclass)
192
    {
193
        // Do access check
194 1
        ClassDeclaration cd = (cast(TypeClass)e.type).sym;
195 1
        if (e.op == TOK.super_)
196
        {
197 1
            if (ClassDeclaration cd2 = sc.func.toParent().isClassDeclaration())
198 1
                cd = cd2;
199
        }
200 1
        return checkAccess(cd, loc, sc, d);
201
    }
202 1
    else if (e.type.ty == Tstruct)
203
    {
204
        // Do access check
205 1
        StructDeclaration cd = (cast(TypeStruct)e.type).sym;
206 1
        return checkAccess(cd, loc, sc, d);
207
    }
208 1
    return false;
209
}
210

211
/****************************************
212
 * Check access to package/module `p` from scope `sc`.
213
 *
214
 * Params:
215
 *   sc = scope from which to access to a fully qualified package name
216
 *   p = the package/module to check access for
217
 * Returns: true if the package is not accessible.
218
 *
219
 * Because a global symbol table tree is used for imported packages/modules,
220
 * access to them needs to be checked based on the imports in the scope chain
221
 * (see https://issues.dlang.org/show_bug.cgi?id=313).
222
 *
223
 */
224
bool checkAccess(Scope* sc, Package p)
225
{
226 1
    if (sc._module == p)
227 1
        return false;
228 1
    for (; sc; sc = sc.enclosing)
229
    {
230 1
        if (sc.scopesym && sc.scopesym.isPackageAccessible(p, Prot(Prot.Kind.private_)))
231 1
            return false;
232
    }
233

234 1
    return true;
235
}
236

237
/**
238
 * Check whether symbols `s` is visible in `mod`.
239
 *
240
 * Params:
241
 *  mod = lookup origin
242
 *  s = symbol to check for visibility
243
 * Returns: true if s is visible in mod
244
 */
245
bool symbolIsVisible(Module mod, Dsymbol s)
246
{
247
    // should sort overloads by ascending protection instead of iterating here
248 1
    s = mostVisibleOverload(s);
249 1
    final switch (s.prot().kind)
250
    {
251 1
    case Prot.Kind.undefined: return true;
252 0
    case Prot.Kind.none: return false; // no access
253 1
    case Prot.Kind.private_: return s.getAccessModule() == mod;
254 1
    case Prot.Kind.package_: return s.getAccessModule() == mod || hasPackageAccess(mod, s);
255 0
    case Prot.Kind.protected_: return s.getAccessModule() == mod;
256 1
    case Prot.Kind.public_, Prot.Kind.export_: return true;
257
    }
258
}
259

260
/**
261
 * Same as above, but determines the lookup module from symbols `origin`.
262
 */
263
bool symbolIsVisible(Dsymbol origin, Dsymbol s)
264
{
265 1
    return symbolIsVisible(origin.getAccessModule(), s);
266
}
267

268
/**
269
 * Same as above but also checks for protected symbols visible from scope `sc`.
270
 * Used for qualified name lookup.
271
 *
272
 * Params:
273
 *  sc = lookup scope
274
 *  s = symbol to check for visibility
275
 * Returns: true if s is visible by origin
276
 */
277
bool symbolIsVisible(Scope *sc, Dsymbol s)
278
{
279 1
    s = mostVisibleOverload(s);
280 1
    return checkSymbolAccess(sc, s);
281
}
282

283
/**
284
 * Check if a symbol is visible from a given scope without taking
285
 * into account the most visible overload.
286
 *
287
 * Params:
288
 *  sc = lookup scope
289
 *  s = symbol to check for visibility
290
 * Returns: true if s is visible by origin
291
 */
292
bool checkSymbolAccess(Scope *sc, Dsymbol s)
293
{
294 1
    final switch (s.prot().kind)
295
    {
296 1
    case Prot.Kind.undefined: return true;
297 0
    case Prot.Kind.none: return false; // no access
298 1
    case Prot.Kind.private_: return sc._module == s.getAccessModule();
299 1
    case Prot.Kind.package_: return sc._module == s.getAccessModule() || hasPackageAccess(sc._module, s);
300 1
    case Prot.Kind.protected_: return hasProtectedAccess(sc, s);
301 1
    case Prot.Kind.public_, Prot.Kind.export_: return true;
302
    }
303
}
304

305
/**
306
 * Use the most visible overload to check visibility. Later perform an access
307
 * check on the resolved overload.  This function is similar to overloadApply,
308
 * but doesn't recurse nor resolve aliases because protection/visibility is an
309
 * attribute of the alias not the aliasee.
310
 */
311
public Dsymbol mostVisibleOverload(Dsymbol s, Module mod = null)
312
{
313 1
    if (!s.isOverloadable())
314 1
        return s;
315

316 1
    Dsymbol next, fstart = s, mostVisible = s;
317 1
    for (; s; s = next)
318
    {
319
        // void func() {}
320
        // private void func(int) {}
321 1
        if (auto fd = s.isFuncDeclaration())
322 1
            next = fd.overnext;
323
        // template temp(T) {}
324
        // private template temp(T:int) {}
325 1
        else if (auto td = s.isTemplateDeclaration())
326 1
            next = td.overnext;
327
        // alias common = mod1.func1;
328
        // alias common = mod2.func2;
329 1
        else if (auto fa = s.isFuncAliasDeclaration())
330 0
            next = fa.overnext;
331
        // alias common = mod1.templ1;
332
        // alias common = mod2.templ2;
333 1
        else if (auto od = s.isOverDeclaration())
334 1
            next = od.overnext;
335
        // alias name = sym;
336
        // private void name(int) {}
337 1
        else if (auto ad = s.isAliasDeclaration())
338
        {
339 1
            assert(ad.isOverloadable || ad.type && ad.type.ty == Terror,
340
                "Non overloadable Aliasee in overload list");
341
            // Yet unresolved aliases store overloads in overnext.
342 1
            if (ad.semanticRun < PASS.semanticdone)
343 1
                next = ad.overnext;
344
            else
345
            {
346
                /* This is a bit messy due to the complicated implementation of
347
                 * alias.  Aliases aren't overloadable themselves, but if their
348
                 * Aliasee is overloadable they can be converted to an overloadable
349
                 * alias.
350
                 *
351
                 * This is done by replacing the Aliasee w/ FuncAliasDeclaration
352
                 * (for functions) or OverDeclaration (for templates) which are
353
                 * simply overloadable aliases w/ weird names.
354
                 *
355
                 * Usually aliases should not be resolved for visibility checking
356
                 * b/c public aliases to private symbols are public. But for the
357
                 * overloadable alias situation, the Alias (_ad_) has been moved
358
                 * into it's own Aliasee, leaving a shell that we peel away here.
359
                 */
360 1
                auto aliasee = ad.toAlias();
361 1
                if (aliasee.isFuncAliasDeclaration || aliasee.isOverDeclaration)
362 1
                    next = aliasee;
363
                else
364
                {
365
                    /* A simple alias can be at the end of a function or template overload chain.
366
                     * It can't have further overloads b/c it would have been
367
                     * converted to an overloadable alias.
368
                     */
369 1
                    assert(ad.overnext is null, "Unresolved overload of alias");
370 1
                    break;
371
                }
372
            }
373
            // handled by dmd.func.overloadApply for unknown reason
374 1
            assert(next !is ad); // should not alias itself
375 1
            assert(next !is fstart); // should not alias the overload list itself
376
        }
377
        else
378 0
            break;
379

380
        /**
381
        * Return the "effective" protection attribute of a symbol when accessed in a module.
382
        * The effective protection attribute is the same as the regular protection attribute,
383
        * except package() is "private" if the module is outside the package;
384
        * otherwise, "public".
385
        */
386
        static Prot protectionSeenFromModule(Dsymbol d, Module mod = null)
387
        {
388 1
            Prot prot = d.prot();
389 1
            if (mod && prot.kind == Prot.Kind.package_)
390
            {
391 1
                return hasPackageAccess(mod, d) ? Prot(Prot.Kind.public_) : Prot(Prot.Kind.private_);
392
            }
393 1
            return prot;
394
        }
395

396 1
        if (next &&
397 1
            protectionSeenFromModule(mostVisible, mod).isMoreRestrictiveThan(protectionSeenFromModule(next, mod)))
398 1
            mostVisible = next;
399
    }
400 1
    return mostVisible;
401
}

Read our documentation on viewing source code .

Loading