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-2021 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 5
    const p = smember.toParent();
46 5
    if (p && p.isTemplateInstance())
47
    {
48 5
        return false; // for backward compatibility
49
    }
50

51 5
    if (!symbolIsVisible(sc, smember) && (!(sc.flags & SCOPE.onlysafeaccess) || sc.func.setUnsafe()))
52
    {
53 3
        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, vis = %d, semanticRun = %d\n",
55
        //        smember.kind(), smember.toPrettyChars(), smember.visible() smember.semanticRun);
56 3
        return true;
57
    }
58 5
    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.visibility.pkg = '%s')\n", s.toChars(), mod.toChars(), s.visible().pkg ? s.visible().pkg.toChars() : "NULL");
74
    }
75 5
    Package pkg = null;
76 5
    if (s.visible().pkg)
77 5
        pkg = s.visible().pkg;
78
    else
79
    {
80
        // no explicit package for visibility, inferring most qualified one
81 5
        for (; s; s = s.parent)
82
        {
83 5
            if (auto m = s.isModule())
84
            {
85 5
                DsymbolTable dst = Package.resolve(m.md ? m.md.packages : null, null, null);
86 5
                assert(dst);
87 5
                Dsymbol s2 = dst.lookup(m.ident);
88 5
                assert(s2);
89 5
                Package p = s2.isPackage();
90 5
                if (p && p.isPackageMod())
91
                {
92 5
                    pkg = p;
93 5
                    break;
94
                }
95
            }
96 5
            else if ((pkg = s.isPackage()) !is null)
97 5
                break;
98
        }
99
    }
100
    static if (LOG)
101
    {
102
        if (pkg)
103
            printf("\tsymbol access binds to package '%s'\n", pkg.toChars());
104
    }
105 5
    if (pkg)
106
    {
107 5
        if (pkg == mod.parent)
108
        {
109
            static if (LOG)
110
            {
111
                printf("\tsc is in permitted package for s\n");
112
            }
113 5
            return true;
114
        }
115 5
        if (pkg.isPackageMod() == mod)
116
        {
117
            static if (LOG)
118
            {
119
                printf("\ts is in same package.d module as sc\n");
120
            }
121 5
            return true;
122
        }
123 5
        Dsymbol ancestor = mod.parent;
124 5
        for (; ancestor; ancestor = ancestor.parent)
125
        {
126 5
            if (ancestor == pkg)
127
            {
128
                static if (LOG)
129
                {
130
                    printf("\tsc is in permitted ancestor package for s\n");
131
                }
132 5
                return true;
133
            }
134
        }
135
    }
136
    static if (LOG)
137
    {
138
        printf("\tno package access\n");
139
    }
140 5
    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 5
    if (auto cd = s.isClassMember()) // also includes interfaces
149
    {
150 5
        for (auto scx = sc; scx; scx = scx.enclosing)
151
        {
152 5
            if (!scx.scopesym)
153 5
                continue;
154 5
            auto cd2 = scx.scopesym.isClassDeclaration();
155 5
            if (cd2 && cd.isBaseOf(cd2, null))
156 5
                return true;
157
        }
158
    }
159 5
    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, Dsymbol d)
167
{
168 5
    if (sc.flags & SCOPE.noaccesscheck)
169 5
        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 5
    if (d.isUnitTestDeclaration())
183
    {
184
        // Unittests are always accessible.
185 5
        return false;
186
    }
187

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

191 5
    if (e.type.ty == Tclass)
192
    {
193
        // Do access check
194 5
        ClassDeclaration cd = (cast(TypeClass)e.type).sym;
195 5
        if (e.op == TOK.super_)
196
        {
197 5
            if (ClassDeclaration cd2 = sc.func.toParent().isClassDeclaration())
198 5
                cd = cd2;
199
        }
200 5
        return checkAccess(cd, loc, sc, d);
201
    }
202 5
    else if (e.type.ty == Tstruct)
203
    {
204
        // Do access check
205 5
        StructDeclaration cd = (cast(TypeStruct)e.type).sym;
206 5
        return checkAccess(cd, loc, sc, d);
207
    }
208 5
    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 5
    if (sc._module == p)
227 5
        return false;
228 5
    for (; sc; sc = sc.enclosing)
229
    {
230 5
        if (sc.scopesym && sc.scopesym.isPackageAccessible(p, Visibility(Visibility.Kind.private_)))
231 5
            return false;
232
    }
233

234 5
    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 visibility instead of iterating here
248 5
    s = mostVisibleOverload(s);
249 5
    final switch (s.visible().kind)
250
    {
251 5
    case Visibility.Kind.undefined: return true;
252 0
    case Visibility.Kind.none: return false; // no access
253 5
    case Visibility.Kind.private_: return s.getAccessModule() == mod;
254 5
    case Visibility.Kind.package_: return s.getAccessModule() == mod || hasPackageAccess(mod, s);
255 0
    case Visibility.Kind.protected_: return s.getAccessModule() == mod;
256 5
    case Visibility.Kind.public_, Visibility.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 5
    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 5
    s = mostVisibleOverload(s);
280 5
    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 5
    final switch (s.visible().kind)
295
    {
296 5
    case Visibility.Kind.undefined: return true;
297 0
    case Visibility.Kind.none: return false; // no access
298 5
    case Visibility.Kind.private_: return sc._module == s.getAccessModule();
299 5
    case Visibility.Kind.package_: return sc._module == s.getAccessModule() || hasPackageAccess(sc._module, s);
300 5
    case Visibility.Kind.protected_: return hasProtectedAccess(sc, s);
301 5
    case Visibility.Kind.public_, Visibility.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 visibility is an
309
 * attribute of the alias not the aliasee.
310
 */
311
public Dsymbol mostVisibleOverload(Dsymbol s, Module mod = null)
312
{
313 5
    if (!s.isOverloadable())
314 5
        return s;
315

316 5
    Dsymbol next, fstart = s, mostVisible = s;
317 5
    for (; s; s = next)
318
    {
319
        // void func() {}
320
        // private void func(int) {}
321 5
        if (auto fd = s.isFuncDeclaration())
322 5
            next = fd.overnext;
323
        // template temp(T) {}
324
        // private template temp(T:int) {}
325 5
        else if (auto td = s.isTemplateDeclaration())
326 5
            next = td.overnext;
327
        // alias common = mod1.func1;
328
        // alias common = mod2.func2;
329 5
        else if (auto fa = s.isFuncAliasDeclaration())
330 0
            next = fa.overnext;
331
        // alias common = mod1.templ1;
332
        // alias common = mod2.templ2;
333 5
        else if (auto od = s.isOverDeclaration())
334 5
            next = od.overnext;
335
        // alias name = sym;
336
        // private void name(int) {}
337 5
        else if (auto ad = s.isAliasDeclaration())
338
        {
339 5
            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 5
            if (ad.semanticRun < PASS.semanticdone)
343 5
                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 its own Aliasee, leaving a shell that we peel away here.
359
                 */
360 5
                auto aliasee = ad.toAlias();
361 5
                if (aliasee.isFuncAliasDeclaration || aliasee.isOverDeclaration)
362 5
                    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 5
                    assert(ad.overnext is null, "Unresolved overload of alias");
370 5
                    break;
371
                }
372
            }
373
            // handled by dmd.func.overloadApply for unknown reason
374 5
            assert(next !is ad); // should not alias itself
375 5
            assert(next !is fstart); // should not alias the overload list itself
376
        }
377
        else
378 0
            break;
379

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

396 5
        if (next &&
397 5
            visibilitySeenFromModule(mostVisible, mod) < visibilitySeenFromModule(next, mod))
398 5
            mostVisible = next;
399
    }
400 5
    return mostVisible;
401
}

Read our documentation on viewing source code .

Loading