Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/clang/lib/Sema/SemaAvailability.cpp
35233 views
1
//===--- SemaAvailability.cpp - Availability attribute handling -----------===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
//
9
// This file processes the availability attribute.
10
//
11
//===----------------------------------------------------------------------===//
12
13
#include "clang/AST/Attr.h"
14
#include "clang/AST/Decl.h"
15
#include "clang/AST/DeclTemplate.h"
16
#include "clang/AST/RecursiveASTVisitor.h"
17
#include "clang/Basic/DiagnosticSema.h"
18
#include "clang/Basic/IdentifierTable.h"
19
#include "clang/Basic/LangOptions.h"
20
#include "clang/Basic/TargetInfo.h"
21
#include "clang/Lex/Preprocessor.h"
22
#include "clang/Sema/DelayedDiagnostic.h"
23
#include "clang/Sema/ScopeInfo.h"
24
#include "clang/Sema/Sema.h"
25
#include "clang/Sema/SemaObjC.h"
26
#include "llvm/ADT/StringRef.h"
27
#include <optional>
28
29
using namespace clang;
30
using namespace sema;
31
32
static bool hasMatchingEnvironmentOrNone(const ASTContext &Context,
33
const AvailabilityAttr *AA) {
34
IdentifierInfo *IIEnvironment = AA->getEnvironment();
35
auto Environment = Context.getTargetInfo().getTriple().getEnvironment();
36
if (!IIEnvironment || Environment == llvm::Triple::UnknownEnvironment)
37
return true;
38
39
llvm::Triple::EnvironmentType ET =
40
AvailabilityAttr::getEnvironmentType(IIEnvironment->getName());
41
return Environment == ET;
42
}
43
44
static const AvailabilityAttr *getAttrForPlatform(ASTContext &Context,
45
const Decl *D) {
46
AvailabilityAttr const *PartialMatch = nullptr;
47
// Check each AvailabilityAttr to find the one for this platform.
48
// For multiple attributes with the same platform try to find one for this
49
// environment.
50
// The attribute is always on the FunctionDecl, not on the
51
// FunctionTemplateDecl.
52
if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(D))
53
D = FTD->getTemplatedDecl();
54
for (const auto *A : D->attrs()) {
55
if (const auto *Avail = dyn_cast<AvailabilityAttr>(A)) {
56
// FIXME: this is copied from CheckAvailability. We should try to
57
// de-duplicate.
58
59
// Check if this is an App Extension "platform", and if so chop off
60
// the suffix for matching with the actual platform.
61
StringRef ActualPlatform = Avail->getPlatform()->getName();
62
StringRef RealizedPlatform = ActualPlatform;
63
if (Context.getLangOpts().AppExt) {
64
size_t suffix = RealizedPlatform.rfind("_app_extension");
65
if (suffix != StringRef::npos)
66
RealizedPlatform = RealizedPlatform.slice(0, suffix);
67
}
68
69
StringRef TargetPlatform = Context.getTargetInfo().getPlatformName();
70
71
// Match the platform name.
72
if (RealizedPlatform == TargetPlatform) {
73
// Find the best matching attribute for this environment
74
if (hasMatchingEnvironmentOrNone(Context, Avail))
75
return Avail;
76
PartialMatch = Avail;
77
}
78
}
79
}
80
return PartialMatch;
81
}
82
83
/// The diagnostic we should emit for \c D, and the declaration that
84
/// originated it, or \c AR_Available.
85
///
86
/// \param D The declaration to check.
87
/// \param Message If non-null, this will be populated with the message from
88
/// the availability attribute that is selected.
89
/// \param ClassReceiver If we're checking the method of a class message
90
/// send, the class. Otherwise nullptr.
91
static std::pair<AvailabilityResult, const NamedDecl *>
92
ShouldDiagnoseAvailabilityOfDecl(Sema &S, const NamedDecl *D,
93
std::string *Message,
94
ObjCInterfaceDecl *ClassReceiver) {
95
AvailabilityResult Result = D->getAvailability(Message);
96
97
// For typedefs, if the typedef declaration appears available look
98
// to the underlying type to see if it is more restrictive.
99
while (const auto *TD = dyn_cast<TypedefNameDecl>(D)) {
100
if (Result == AR_Available) {
101
if (const auto *TT = TD->getUnderlyingType()->getAs<TagType>()) {
102
D = TT->getDecl();
103
Result = D->getAvailability(Message);
104
continue;
105
}
106
}
107
break;
108
}
109
110
// For alias templates, get the underlying declaration.
111
if (const auto *ADecl = dyn_cast<TypeAliasTemplateDecl>(D)) {
112
D = ADecl->getTemplatedDecl();
113
Result = D->getAvailability(Message);
114
}
115
116
// Forward class declarations get their attributes from their definition.
117
if (const auto *IDecl = dyn_cast<ObjCInterfaceDecl>(D)) {
118
if (IDecl->getDefinition()) {
119
D = IDecl->getDefinition();
120
Result = D->getAvailability(Message);
121
}
122
}
123
124
if (const auto *ECD = dyn_cast<EnumConstantDecl>(D))
125
if (Result == AR_Available) {
126
const DeclContext *DC = ECD->getDeclContext();
127
if (const auto *TheEnumDecl = dyn_cast<EnumDecl>(DC)) {
128
Result = TheEnumDecl->getAvailability(Message);
129
D = TheEnumDecl;
130
}
131
}
132
133
// For +new, infer availability from -init.
134
if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) {
135
if (S.ObjC().NSAPIObj && ClassReceiver) {
136
ObjCMethodDecl *Init = ClassReceiver->lookupInstanceMethod(
137
S.ObjC().NSAPIObj->getInitSelector());
138
if (Init && Result == AR_Available && MD->isClassMethod() &&
139
MD->getSelector() == S.ObjC().NSAPIObj->getNewSelector() &&
140
MD->definedInNSObject(S.getASTContext())) {
141
Result = Init->getAvailability(Message);
142
D = Init;
143
}
144
}
145
}
146
147
return {Result, D};
148
}
149
150
151
/// whether we should emit a diagnostic for \c K and \c DeclVersion in
152
/// the context of \c Ctx. For example, we should emit an unavailable diagnostic
153
/// in a deprecated context, but not the other way around.
154
static bool ShouldDiagnoseAvailabilityInContext(
155
Sema &S, AvailabilityResult K, VersionTuple DeclVersion,
156
const IdentifierInfo *DeclEnv, Decl *Ctx, const NamedDecl *OffendingDecl) {
157
assert(K != AR_Available && "Expected an unavailable declaration here!");
158
159
// If this was defined using CF_OPTIONS, etc. then ignore the diagnostic.
160
auto DeclLoc = Ctx->getBeginLoc();
161
// This is only a problem in Foundation's C++ implementation for CF_OPTIONS.
162
if (DeclLoc.isMacroID() && S.getLangOpts().CPlusPlus &&
163
isa<TypedefDecl>(OffendingDecl)) {
164
StringRef MacroName = S.getPreprocessor().getImmediateMacroName(DeclLoc);
165
if (MacroName == "CF_OPTIONS" || MacroName == "OBJC_OPTIONS" ||
166
MacroName == "SWIFT_OPTIONS" || MacroName == "NS_OPTIONS") {
167
return false;
168
}
169
}
170
171
// In HLSL, skip emitting diagnostic if the diagnostic mode is not set to
172
// strict (-fhlsl-strict-availability), or if the target is library and the
173
// availability is restricted to a specific environment/shader stage.
174
// For libraries the availability will be checked later in
175
// DiagnoseHLSLAvailability class once where the specific environment/shader
176
// stage of the caller is known.
177
if (S.getLangOpts().HLSL) {
178
if (!S.getLangOpts().HLSLStrictAvailability ||
179
(DeclEnv != nullptr &&
180
S.getASTContext().getTargetInfo().getTriple().getEnvironment() ==
181
llvm::Triple::EnvironmentType::Library))
182
return false;
183
}
184
185
// Checks if we should emit the availability diagnostic in the context of C.
186
auto CheckContext = [&](const Decl *C) {
187
if (K == AR_NotYetIntroduced) {
188
if (const AvailabilityAttr *AA = getAttrForPlatform(S.Context, C))
189
if (AA->getIntroduced() >= DeclVersion &&
190
AA->getEnvironment() == DeclEnv)
191
return true;
192
} else if (K == AR_Deprecated) {
193
if (C->isDeprecated())
194
return true;
195
} else if (K == AR_Unavailable) {
196
// It is perfectly fine to refer to an 'unavailable' Objective-C method
197
// when it is referenced from within the @implementation itself. In this
198
// context, we interpret unavailable as a form of access control.
199
if (const auto *MD = dyn_cast<ObjCMethodDecl>(OffendingDecl)) {
200
if (const auto *Impl = dyn_cast<ObjCImplDecl>(C)) {
201
if (MD->getClassInterface() == Impl->getClassInterface())
202
return true;
203
}
204
}
205
}
206
207
if (C->isUnavailable())
208
return true;
209
return false;
210
};
211
212
do {
213
if (CheckContext(Ctx))
214
return false;
215
216
// An implementation implicitly has the availability of the interface.
217
// Unless it is "+load" method.
218
if (const auto *MethodD = dyn_cast<ObjCMethodDecl>(Ctx))
219
if (MethodD->isClassMethod() &&
220
MethodD->getSelector().getAsString() == "load")
221
return true;
222
223
if (const auto *CatOrImpl = dyn_cast<ObjCImplDecl>(Ctx)) {
224
if (const ObjCInterfaceDecl *Interface = CatOrImpl->getClassInterface())
225
if (CheckContext(Interface))
226
return false;
227
}
228
// A category implicitly has the availability of the interface.
229
else if (const auto *CatD = dyn_cast<ObjCCategoryDecl>(Ctx))
230
if (const ObjCInterfaceDecl *Interface = CatD->getClassInterface())
231
if (CheckContext(Interface))
232
return false;
233
} while ((Ctx = cast_or_null<Decl>(Ctx->getDeclContext())));
234
235
return true;
236
}
237
238
static unsigned getAvailabilityDiagnosticKind(
239
const ASTContext &Context, const VersionTuple &DeploymentVersion,
240
const VersionTuple &DeclVersion, bool HasMatchingEnv) {
241
const auto &Triple = Context.getTargetInfo().getTriple();
242
VersionTuple ForceAvailabilityFromVersion;
243
switch (Triple.getOS()) {
244
// For iOS, emit the diagnostic even if -Wunguarded-availability is
245
// not specified for deployment targets >= to iOS 11 or equivalent or
246
// for declarations that were introduced in iOS 11 (macOS 10.13, ...) or
247
// later.
248
case llvm::Triple::IOS:
249
case llvm::Triple::TvOS:
250
ForceAvailabilityFromVersion = VersionTuple(/*Major=*/11);
251
break;
252
case llvm::Triple::WatchOS:
253
ForceAvailabilityFromVersion = VersionTuple(/*Major=*/4);
254
break;
255
case llvm::Triple::Darwin:
256
case llvm::Triple::MacOSX:
257
ForceAvailabilityFromVersion = VersionTuple(/*Major=*/10, /*Minor=*/13);
258
break;
259
// For HLSL, use diagnostic from HLSLAvailability group which
260
// are reported as errors by default and in strict diagnostic mode
261
// (-fhlsl-strict-availability) and as warnings in relaxed diagnostic
262
// mode (-Wno-error=hlsl-availability)
263
case llvm::Triple::ShaderModel:
264
return HasMatchingEnv ? diag::warn_hlsl_availability
265
: diag::warn_hlsl_availability_unavailable;
266
default:
267
// New Apple targets should always warn about availability.
268
ForceAvailabilityFromVersion =
269
(Triple.getVendor() == llvm::Triple::Apple)
270
? VersionTuple(/*Major=*/0, 0)
271
: VersionTuple(/*Major=*/(unsigned)-1, (unsigned)-1);
272
}
273
if (DeploymentVersion >= ForceAvailabilityFromVersion ||
274
DeclVersion >= ForceAvailabilityFromVersion)
275
return HasMatchingEnv ? diag::warn_unguarded_availability_new
276
: diag::warn_unguarded_availability_unavailable_new;
277
return HasMatchingEnv ? diag::warn_unguarded_availability
278
: diag::warn_unguarded_availability_unavailable;
279
}
280
281
static NamedDecl *findEnclosingDeclToAnnotate(Decl *OrigCtx) {
282
for (Decl *Ctx = OrigCtx; Ctx;
283
Ctx = cast_or_null<Decl>(Ctx->getDeclContext())) {
284
if (isa<TagDecl>(Ctx) || isa<FunctionDecl>(Ctx) || isa<ObjCMethodDecl>(Ctx))
285
return cast<NamedDecl>(Ctx);
286
if (auto *CD = dyn_cast<ObjCContainerDecl>(Ctx)) {
287
if (auto *Imp = dyn_cast<ObjCImplDecl>(Ctx))
288
return Imp->getClassInterface();
289
return CD;
290
}
291
}
292
293
return dyn_cast<NamedDecl>(OrigCtx);
294
}
295
296
namespace {
297
298
struct AttributeInsertion {
299
StringRef Prefix;
300
SourceLocation Loc;
301
StringRef Suffix;
302
303
static AttributeInsertion createInsertionAfter(const NamedDecl *D) {
304
return {" ", D->getEndLoc(), ""};
305
}
306
static AttributeInsertion createInsertionAfter(SourceLocation Loc) {
307
return {" ", Loc, ""};
308
}
309
static AttributeInsertion createInsertionBefore(const NamedDecl *D) {
310
return {"", D->getBeginLoc(), "\n"};
311
}
312
};
313
314
} // end anonymous namespace
315
316
/// Tries to parse a string as ObjC method name.
317
///
318
/// \param Name The string to parse. Expected to originate from availability
319
/// attribute argument.
320
/// \param SlotNames The vector that will be populated with slot names. In case
321
/// of unsuccessful parsing can contain invalid data.
322
/// \returns A number of method parameters if parsing was successful,
323
/// std::nullopt otherwise.
324
static std::optional<unsigned>
325
tryParseObjCMethodName(StringRef Name, SmallVectorImpl<StringRef> &SlotNames,
326
const LangOptions &LangOpts) {
327
// Accept replacements starting with - or + as valid ObjC method names.
328
if (!Name.empty() && (Name.front() == '-' || Name.front() == '+'))
329
Name = Name.drop_front(1);
330
if (Name.empty())
331
return std::nullopt;
332
Name.split(SlotNames, ':');
333
unsigned NumParams;
334
if (Name.back() == ':') {
335
// Remove an empty string at the end that doesn't represent any slot.
336
SlotNames.pop_back();
337
NumParams = SlotNames.size();
338
} else {
339
if (SlotNames.size() != 1)
340
// Not a valid method name, just a colon-separated string.
341
return std::nullopt;
342
NumParams = 0;
343
}
344
// Verify all slot names are valid.
345
bool AllowDollar = LangOpts.DollarIdents;
346
for (StringRef S : SlotNames) {
347
if (S.empty())
348
continue;
349
if (!isValidAsciiIdentifier(S, AllowDollar))
350
return std::nullopt;
351
}
352
return NumParams;
353
}
354
355
/// Returns a source location in which it's appropriate to insert a new
356
/// attribute for the given declaration \D.
357
static std::optional<AttributeInsertion>
358
createAttributeInsertion(const NamedDecl *D, const SourceManager &SM,
359
const LangOptions &LangOpts) {
360
if (isa<ObjCPropertyDecl>(D))
361
return AttributeInsertion::createInsertionAfter(D);
362
if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) {
363
if (MD->hasBody())
364
return std::nullopt;
365
return AttributeInsertion::createInsertionAfter(D);
366
}
367
if (const auto *TD = dyn_cast<TagDecl>(D)) {
368
SourceLocation Loc =
369
Lexer::getLocForEndOfToken(TD->getInnerLocStart(), 0, SM, LangOpts);
370
if (Loc.isInvalid())
371
return std::nullopt;
372
// Insert after the 'struct'/whatever keyword.
373
return AttributeInsertion::createInsertionAfter(Loc);
374
}
375
return AttributeInsertion::createInsertionBefore(D);
376
}
377
378
/// Actually emit an availability diagnostic for a reference to an unavailable
379
/// decl.
380
///
381
/// \param Ctx The context that the reference occurred in
382
/// \param ReferringDecl The exact declaration that was referenced.
383
/// \param OffendingDecl A related decl to \c ReferringDecl that has an
384
/// availability attribute corresponding to \c K attached to it. Note that this
385
/// may not be the same as ReferringDecl, i.e. if an EnumDecl is annotated and
386
/// we refer to a member EnumConstantDecl, ReferringDecl is the EnumConstantDecl
387
/// and OffendingDecl is the EnumDecl.
388
static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K,
389
Decl *Ctx, const NamedDecl *ReferringDecl,
390
const NamedDecl *OffendingDecl,
391
StringRef Message,
392
ArrayRef<SourceLocation> Locs,
393
const ObjCInterfaceDecl *UnknownObjCClass,
394
const ObjCPropertyDecl *ObjCProperty,
395
bool ObjCPropertyAccess) {
396
// Diagnostics for deprecated or unavailable.
397
unsigned diag, diag_message, diag_fwdclass_message;
398
unsigned diag_available_here = diag::note_availability_specified_here;
399
SourceLocation NoteLocation = OffendingDecl->getLocation();
400
401
// Matches 'diag::note_property_attribute' options.
402
unsigned property_note_select;
403
404
// Matches diag::note_availability_specified_here.
405
unsigned available_here_select_kind;
406
407
VersionTuple DeclVersion;
408
const AvailabilityAttr *AA = getAttrForPlatform(S.Context, OffendingDecl);
409
const IdentifierInfo *IIEnv = nullptr;
410
if (AA) {
411
DeclVersion = AA->getIntroduced();
412
IIEnv = AA->getEnvironment();
413
}
414
415
if (!ShouldDiagnoseAvailabilityInContext(S, K, DeclVersion, IIEnv, Ctx,
416
OffendingDecl))
417
return;
418
419
SourceLocation Loc = Locs.front();
420
421
// The declaration can have multiple availability attributes, we are looking
422
// at one of them.
423
if (AA && AA->isInherited()) {
424
for (const Decl *Redecl = OffendingDecl->getMostRecentDecl(); Redecl;
425
Redecl = Redecl->getPreviousDecl()) {
426
const AvailabilityAttr *AForRedecl =
427
getAttrForPlatform(S.Context, Redecl);
428
if (AForRedecl && !AForRedecl->isInherited()) {
429
// If D is a declaration with inherited attributes, the note should
430
// point to the declaration with actual attributes.
431
NoteLocation = Redecl->getLocation();
432
break;
433
}
434
}
435
}
436
437
switch (K) {
438
case AR_NotYetIntroduced: {
439
// We would like to emit the diagnostic even if -Wunguarded-availability is
440
// not specified for deployment targets >= to iOS 11 or equivalent or
441
// for declarations that were introduced in iOS 11 (macOS 10.13, ...) or
442
// later.
443
assert(AA != nullptr && "expecting valid availability attribute");
444
VersionTuple Introduced = AA->getIntroduced();
445
bool EnvironmentMatchesOrNone =
446
hasMatchingEnvironmentOrNone(S.getASTContext(), AA);
447
448
const TargetInfo &TI = S.getASTContext().getTargetInfo();
449
std::string PlatformName(
450
AvailabilityAttr::getPrettyPlatformName(TI.getPlatformName()));
451
llvm::StringRef TargetEnvironment(
452
llvm::Triple::getEnvironmentTypeName(TI.getTriple().getEnvironment()));
453
llvm::StringRef AttrEnvironment =
454
AA->getEnvironment() ? AA->getEnvironment()->getName() : "";
455
bool UseEnvironment =
456
(!AttrEnvironment.empty() && !TargetEnvironment.empty());
457
458
unsigned DiagKind = getAvailabilityDiagnosticKind(
459
S.Context, S.Context.getTargetInfo().getPlatformMinVersion(),
460
Introduced, EnvironmentMatchesOrNone);
461
462
S.Diag(Loc, DiagKind) << OffendingDecl << PlatformName
463
<< Introduced.getAsString() << UseEnvironment
464
<< TargetEnvironment;
465
466
S.Diag(OffendingDecl->getLocation(),
467
diag::note_partial_availability_specified_here)
468
<< OffendingDecl << PlatformName << Introduced.getAsString()
469
<< S.Context.getTargetInfo().getPlatformMinVersion().getAsString()
470
<< UseEnvironment << AttrEnvironment << TargetEnvironment;
471
472
// Do not offer to silence the warning or fixits for HLSL
473
if (S.getLangOpts().HLSL)
474
return;
475
476
if (const auto *Enclosing = findEnclosingDeclToAnnotate(Ctx)) {
477
if (const auto *TD = dyn_cast<TagDecl>(Enclosing))
478
if (TD->getDeclName().isEmpty()) {
479
S.Diag(TD->getLocation(),
480
diag::note_decl_unguarded_availability_silence)
481
<< /*Anonymous*/ 1 << TD->getKindName();
482
return;
483
}
484
auto FixitNoteDiag =
485
S.Diag(Enclosing->getLocation(),
486
diag::note_decl_unguarded_availability_silence)
487
<< /*Named*/ 0 << Enclosing;
488
// Don't offer a fixit for declarations with availability attributes.
489
if (Enclosing->hasAttr<AvailabilityAttr>())
490
return;
491
if (!S.getPreprocessor().isMacroDefined("API_AVAILABLE"))
492
return;
493
std::optional<AttributeInsertion> Insertion = createAttributeInsertion(
494
Enclosing, S.getSourceManager(), S.getLangOpts());
495
if (!Insertion)
496
return;
497
std::string PlatformName =
498
AvailabilityAttr::getPlatformNameSourceSpelling(
499
S.getASTContext().getTargetInfo().getPlatformName())
500
.lower();
501
std::string Introduced =
502
OffendingDecl->getVersionIntroduced().getAsString();
503
FixitNoteDiag << FixItHint::CreateInsertion(
504
Insertion->Loc,
505
(llvm::Twine(Insertion->Prefix) + "API_AVAILABLE(" + PlatformName +
506
"(" + Introduced + "))" + Insertion->Suffix)
507
.str());
508
}
509
return;
510
}
511
case AR_Deprecated:
512
diag = !ObjCPropertyAccess ? diag::warn_deprecated
513
: diag::warn_property_method_deprecated;
514
diag_message = diag::warn_deprecated_message;
515
diag_fwdclass_message = diag::warn_deprecated_fwdclass_message;
516
property_note_select = /* deprecated */ 0;
517
available_here_select_kind = /* deprecated */ 2;
518
if (const auto *AL = OffendingDecl->getAttr<DeprecatedAttr>())
519
NoteLocation = AL->getLocation();
520
break;
521
522
case AR_Unavailable:
523
diag = !ObjCPropertyAccess ? diag::err_unavailable
524
: diag::err_property_method_unavailable;
525
diag_message = diag::err_unavailable_message;
526
diag_fwdclass_message = diag::warn_unavailable_fwdclass_message;
527
property_note_select = /* unavailable */ 1;
528
available_here_select_kind = /* unavailable */ 0;
529
530
if (auto AL = OffendingDecl->getAttr<UnavailableAttr>()) {
531
if (AL->isImplicit() && AL->getImplicitReason()) {
532
// Most of these failures are due to extra restrictions in ARC;
533
// reflect that in the primary diagnostic when applicable.
534
auto flagARCError = [&] {
535
if (S.getLangOpts().ObjCAutoRefCount &&
536
S.getSourceManager().isInSystemHeader(
537
OffendingDecl->getLocation()))
538
diag = diag::err_unavailable_in_arc;
539
};
540
541
switch (AL->getImplicitReason()) {
542
case UnavailableAttr::IR_None: break;
543
544
case UnavailableAttr::IR_ARCForbiddenType:
545
flagARCError();
546
diag_available_here = diag::note_arc_forbidden_type;
547
break;
548
549
case UnavailableAttr::IR_ForbiddenWeak:
550
if (S.getLangOpts().ObjCWeakRuntime)
551
diag_available_here = diag::note_arc_weak_disabled;
552
else
553
diag_available_here = diag::note_arc_weak_no_runtime;
554
break;
555
556
case UnavailableAttr::IR_ARCForbiddenConversion:
557
flagARCError();
558
diag_available_here = diag::note_performs_forbidden_arc_conversion;
559
break;
560
561
case UnavailableAttr::IR_ARCInitReturnsUnrelated:
562
flagARCError();
563
diag_available_here = diag::note_arc_init_returns_unrelated;
564
break;
565
566
case UnavailableAttr::IR_ARCFieldWithOwnership:
567
flagARCError();
568
diag_available_here = diag::note_arc_field_with_ownership;
569
break;
570
}
571
}
572
}
573
break;
574
575
case AR_Available:
576
llvm_unreachable("Warning for availability of available declaration?");
577
}
578
579
SmallVector<FixItHint, 12> FixIts;
580
if (K == AR_Deprecated) {
581
StringRef Replacement;
582
if (auto AL = OffendingDecl->getAttr<DeprecatedAttr>())
583
Replacement = AL->getReplacement();
584
if (auto AL = getAttrForPlatform(S.Context, OffendingDecl))
585
Replacement = AL->getReplacement();
586
587
CharSourceRange UseRange;
588
if (!Replacement.empty())
589
UseRange =
590
CharSourceRange::getCharRange(Loc, S.getLocForEndOfToken(Loc));
591
if (UseRange.isValid()) {
592
if (const auto *MethodDecl = dyn_cast<ObjCMethodDecl>(ReferringDecl)) {
593
Selector Sel = MethodDecl->getSelector();
594
SmallVector<StringRef, 12> SelectorSlotNames;
595
std::optional<unsigned> NumParams = tryParseObjCMethodName(
596
Replacement, SelectorSlotNames, S.getLangOpts());
597
if (NumParams && *NumParams == Sel.getNumArgs()) {
598
assert(SelectorSlotNames.size() == Locs.size());
599
for (unsigned I = 0; I < Locs.size(); ++I) {
600
if (!Sel.getNameForSlot(I).empty()) {
601
CharSourceRange NameRange = CharSourceRange::getCharRange(
602
Locs[I], S.getLocForEndOfToken(Locs[I]));
603
FixIts.push_back(FixItHint::CreateReplacement(
604
NameRange, SelectorSlotNames[I]));
605
} else
606
FixIts.push_back(
607
FixItHint::CreateInsertion(Locs[I], SelectorSlotNames[I]));
608
}
609
} else
610
FixIts.push_back(FixItHint::CreateReplacement(UseRange, Replacement));
611
} else
612
FixIts.push_back(FixItHint::CreateReplacement(UseRange, Replacement));
613
}
614
}
615
616
// We emit deprecation warning for deprecated specializations
617
// when their instantiation stacks originate outside
618
// of a system header, even if the diagnostics is suppresed at the
619
// point of definition.
620
SourceLocation InstantiationLoc =
621
S.getTopMostPointOfInstantiation(ReferringDecl);
622
bool ShouldAllowWarningInSystemHeader =
623
InstantiationLoc != Loc &&
624
!S.getSourceManager().isInSystemHeader(InstantiationLoc);
625
struct AllowWarningInSystemHeaders {
626
AllowWarningInSystemHeaders(DiagnosticsEngine &E,
627
bool AllowWarningInSystemHeaders)
628
: Engine(E), Prev(E.getSuppressSystemWarnings()) {
629
E.setSuppressSystemWarnings(!AllowWarningInSystemHeaders);
630
}
631
~AllowWarningInSystemHeaders() { Engine.setSuppressSystemWarnings(Prev); }
632
633
private:
634
DiagnosticsEngine &Engine;
635
bool Prev;
636
} SystemWarningOverrideRAII(S.getDiagnostics(),
637
ShouldAllowWarningInSystemHeader);
638
639
if (!Message.empty()) {
640
S.Diag(Loc, diag_message) << ReferringDecl << Message << FixIts;
641
if (ObjCProperty)
642
S.Diag(ObjCProperty->getLocation(), diag::note_property_attribute)
643
<< ObjCProperty->getDeclName() << property_note_select;
644
} else if (!UnknownObjCClass) {
645
S.Diag(Loc, diag) << ReferringDecl << FixIts;
646
if (ObjCProperty)
647
S.Diag(ObjCProperty->getLocation(), diag::note_property_attribute)
648
<< ObjCProperty->getDeclName() << property_note_select;
649
} else {
650
S.Diag(Loc, diag_fwdclass_message) << ReferringDecl << FixIts;
651
S.Diag(UnknownObjCClass->getLocation(), diag::note_forward_class);
652
}
653
654
S.Diag(NoteLocation, diag_available_here)
655
<< OffendingDecl << available_here_select_kind;
656
}
657
658
void Sema::handleDelayedAvailabilityCheck(DelayedDiagnostic &DD, Decl *Ctx) {
659
assert(DD.Kind == DelayedDiagnostic::Availability &&
660
"Expected an availability diagnostic here");
661
662
DD.Triggered = true;
663
DoEmitAvailabilityWarning(
664
*this, DD.getAvailabilityResult(), Ctx, DD.getAvailabilityReferringDecl(),
665
DD.getAvailabilityOffendingDecl(), DD.getAvailabilityMessage(),
666
DD.getAvailabilitySelectorLocs(), DD.getUnknownObjCClass(),
667
DD.getObjCProperty(), false);
668
}
669
670
static void EmitAvailabilityWarning(Sema &S, AvailabilityResult AR,
671
const NamedDecl *ReferringDecl,
672
const NamedDecl *OffendingDecl,
673
StringRef Message,
674
ArrayRef<SourceLocation> Locs,
675
const ObjCInterfaceDecl *UnknownObjCClass,
676
const ObjCPropertyDecl *ObjCProperty,
677
bool ObjCPropertyAccess) {
678
// Delay if we're currently parsing a declaration.
679
if (S.DelayedDiagnostics.shouldDelayDiagnostics()) {
680
S.DelayedDiagnostics.add(
681
DelayedDiagnostic::makeAvailability(
682
AR, Locs, ReferringDecl, OffendingDecl, UnknownObjCClass,
683
ObjCProperty, Message, ObjCPropertyAccess));
684
return;
685
}
686
687
Decl *Ctx = cast<Decl>(S.getCurLexicalContext());
688
DoEmitAvailabilityWarning(S, AR, Ctx, ReferringDecl, OffendingDecl,
689
Message, Locs, UnknownObjCClass, ObjCProperty,
690
ObjCPropertyAccess);
691
}
692
693
namespace {
694
695
/// Returns true if the given statement can be a body-like child of \p Parent.
696
bool isBodyLikeChildStmt(const Stmt *S, const Stmt *Parent) {
697
switch (Parent->getStmtClass()) {
698
case Stmt::IfStmtClass:
699
return cast<IfStmt>(Parent)->getThen() == S ||
700
cast<IfStmt>(Parent)->getElse() == S;
701
case Stmt::WhileStmtClass:
702
return cast<WhileStmt>(Parent)->getBody() == S;
703
case Stmt::DoStmtClass:
704
return cast<DoStmt>(Parent)->getBody() == S;
705
case Stmt::ForStmtClass:
706
return cast<ForStmt>(Parent)->getBody() == S;
707
case Stmt::CXXForRangeStmtClass:
708
return cast<CXXForRangeStmt>(Parent)->getBody() == S;
709
case Stmt::ObjCForCollectionStmtClass:
710
return cast<ObjCForCollectionStmt>(Parent)->getBody() == S;
711
case Stmt::CaseStmtClass:
712
case Stmt::DefaultStmtClass:
713
return cast<SwitchCase>(Parent)->getSubStmt() == S;
714
default:
715
return false;
716
}
717
}
718
719
class StmtUSEFinder : public RecursiveASTVisitor<StmtUSEFinder> {
720
const Stmt *Target;
721
722
public:
723
bool VisitStmt(Stmt *S) { return S != Target; }
724
725
/// Returns true if the given statement is present in the given declaration.
726
static bool isContained(const Stmt *Target, const Decl *D) {
727
StmtUSEFinder Visitor;
728
Visitor.Target = Target;
729
return !Visitor.TraverseDecl(const_cast<Decl *>(D));
730
}
731
};
732
733
/// Traverses the AST and finds the last statement that used a given
734
/// declaration.
735
class LastDeclUSEFinder : public RecursiveASTVisitor<LastDeclUSEFinder> {
736
const Decl *D;
737
738
public:
739
bool VisitDeclRefExpr(DeclRefExpr *DRE) {
740
if (DRE->getDecl() == D)
741
return false;
742
return true;
743
}
744
745
static const Stmt *findLastStmtThatUsesDecl(const Decl *D,
746
const CompoundStmt *Scope) {
747
LastDeclUSEFinder Visitor;
748
Visitor.D = D;
749
for (const Stmt *S : llvm::reverse(Scope->body())) {
750
if (!Visitor.TraverseStmt(const_cast<Stmt *>(S)))
751
return S;
752
}
753
return nullptr;
754
}
755
};
756
757
/// This class implements -Wunguarded-availability.
758
///
759
/// This is done with a traversal of the AST of a function that makes reference
760
/// to a partially available declaration. Whenever we encounter an \c if of the
761
/// form: \c if(@available(...)), we use the version from the condition to visit
762
/// the then statement.
763
class DiagnoseUnguardedAvailability
764
: public RecursiveASTVisitor<DiagnoseUnguardedAvailability> {
765
typedef RecursiveASTVisitor<DiagnoseUnguardedAvailability> Base;
766
767
Sema &SemaRef;
768
Decl *Ctx;
769
770
/// Stack of potentially nested 'if (@available(...))'s.
771
SmallVector<VersionTuple, 8> AvailabilityStack;
772
SmallVector<const Stmt *, 16> StmtStack;
773
774
void DiagnoseDeclAvailability(NamedDecl *D, SourceRange Range,
775
ObjCInterfaceDecl *ClassReceiver = nullptr);
776
777
public:
778
DiagnoseUnguardedAvailability(Sema &SemaRef, Decl *Ctx)
779
: SemaRef(SemaRef), Ctx(Ctx) {
780
AvailabilityStack.push_back(
781
SemaRef.Context.getTargetInfo().getPlatformMinVersion());
782
}
783
784
bool TraverseStmt(Stmt *S) {
785
if (!S)
786
return true;
787
StmtStack.push_back(S);
788
bool Result = Base::TraverseStmt(S);
789
StmtStack.pop_back();
790
return Result;
791
}
792
793
void IssueDiagnostics(Stmt *S) { TraverseStmt(S); }
794
795
bool TraverseIfStmt(IfStmt *If);
796
797
// for 'case X:' statements, don't bother looking at the 'X'; it can't lead
798
// to any useful diagnostics.
799
bool TraverseCaseStmt(CaseStmt *CS) { return TraverseStmt(CS->getSubStmt()); }
800
801
bool VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *PRE) { return true; }
802
803
bool VisitObjCMessageExpr(ObjCMessageExpr *Msg) {
804
if (ObjCMethodDecl *D = Msg->getMethodDecl()) {
805
ObjCInterfaceDecl *ID = nullptr;
806
QualType ReceiverTy = Msg->getClassReceiver();
807
if (!ReceiverTy.isNull() && ReceiverTy->getAsObjCInterfaceType())
808
ID = ReceiverTy->getAsObjCInterfaceType()->getInterface();
809
810
DiagnoseDeclAvailability(
811
D, SourceRange(Msg->getSelectorStartLoc(), Msg->getEndLoc()), ID);
812
}
813
return true;
814
}
815
816
bool VisitDeclRefExpr(DeclRefExpr *DRE) {
817
DiagnoseDeclAvailability(DRE->getDecl(),
818
SourceRange(DRE->getBeginLoc(), DRE->getEndLoc()));
819
return true;
820
}
821
822
bool VisitMemberExpr(MemberExpr *ME) {
823
DiagnoseDeclAvailability(ME->getMemberDecl(),
824
SourceRange(ME->getBeginLoc(), ME->getEndLoc()));
825
return true;
826
}
827
828
bool VisitObjCAvailabilityCheckExpr(ObjCAvailabilityCheckExpr *E) {
829
SemaRef.Diag(E->getBeginLoc(), diag::warn_at_available_unchecked_use)
830
<< (!SemaRef.getLangOpts().ObjC);
831
return true;
832
}
833
834
bool VisitTypeLoc(TypeLoc Ty);
835
};
836
837
void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability(
838
NamedDecl *D, SourceRange Range, ObjCInterfaceDecl *ReceiverClass) {
839
AvailabilityResult Result;
840
const NamedDecl *OffendingDecl;
841
std::tie(Result, OffendingDecl) =
842
ShouldDiagnoseAvailabilityOfDecl(SemaRef, D, nullptr, ReceiverClass);
843
if (Result != AR_Available) {
844
// All other diagnostic kinds have already been handled in
845
// DiagnoseAvailabilityOfDecl.
846
if (Result != AR_NotYetIntroduced)
847
return;
848
849
const AvailabilityAttr *AA =
850
getAttrForPlatform(SemaRef.getASTContext(), OffendingDecl);
851
assert(AA != nullptr && "expecting valid availability attribute");
852
bool EnvironmentMatchesOrNone =
853
hasMatchingEnvironmentOrNone(SemaRef.getASTContext(), AA);
854
VersionTuple Introduced = AA->getIntroduced();
855
856
if (EnvironmentMatchesOrNone && AvailabilityStack.back() >= Introduced)
857
return;
858
859
// If the context of this function is less available than D, we should not
860
// emit a diagnostic.
861
if (!ShouldDiagnoseAvailabilityInContext(SemaRef, Result, Introduced,
862
AA->getEnvironment(), Ctx,
863
OffendingDecl))
864
return;
865
866
const TargetInfo &TI = SemaRef.getASTContext().getTargetInfo();
867
std::string PlatformName(
868
AvailabilityAttr::getPrettyPlatformName(TI.getPlatformName()));
869
llvm::StringRef TargetEnvironment(TI.getTriple().getEnvironmentName());
870
llvm::StringRef AttrEnvironment =
871
AA->getEnvironment() ? AA->getEnvironment()->getName() : "";
872
bool UseEnvironment =
873
(!AttrEnvironment.empty() && !TargetEnvironment.empty());
874
875
unsigned DiagKind = getAvailabilityDiagnosticKind(
876
SemaRef.Context,
877
SemaRef.Context.getTargetInfo().getPlatformMinVersion(), Introduced,
878
EnvironmentMatchesOrNone);
879
880
SemaRef.Diag(Range.getBegin(), DiagKind)
881
<< Range << D << PlatformName << Introduced.getAsString()
882
<< UseEnvironment << TargetEnvironment;
883
884
SemaRef.Diag(OffendingDecl->getLocation(),
885
diag::note_partial_availability_specified_here)
886
<< OffendingDecl << PlatformName << Introduced.getAsString()
887
<< SemaRef.Context.getTargetInfo().getPlatformMinVersion().getAsString()
888
<< UseEnvironment << AttrEnvironment << TargetEnvironment;
889
890
// Do not offer to silence the warning or fixits for HLSL
891
if (SemaRef.getLangOpts().HLSL)
892
return;
893
894
auto FixitDiag =
895
SemaRef.Diag(Range.getBegin(), diag::note_unguarded_available_silence)
896
<< Range << D
897
<< (SemaRef.getLangOpts().ObjC ? /*@available*/ 0
898
: /*__builtin_available*/ 1);
899
900
// Find the statement which should be enclosed in the if @available check.
901
if (StmtStack.empty())
902
return;
903
const Stmt *StmtOfUse = StmtStack.back();
904
const CompoundStmt *Scope = nullptr;
905
for (const Stmt *S : llvm::reverse(StmtStack)) {
906
if (const auto *CS = dyn_cast<CompoundStmt>(S)) {
907
Scope = CS;
908
break;
909
}
910
if (isBodyLikeChildStmt(StmtOfUse, S)) {
911
// The declaration won't be seen outside of the statement, so we don't
912
// have to wrap the uses of any declared variables in if (@available).
913
// Therefore we can avoid setting Scope here.
914
break;
915
}
916
StmtOfUse = S;
917
}
918
const Stmt *LastStmtOfUse = nullptr;
919
if (isa<DeclStmt>(StmtOfUse) && Scope) {
920
for (const Decl *D : cast<DeclStmt>(StmtOfUse)->decls()) {
921
if (StmtUSEFinder::isContained(StmtStack.back(), D)) {
922
LastStmtOfUse = LastDeclUSEFinder::findLastStmtThatUsesDecl(D, Scope);
923
break;
924
}
925
}
926
}
927
928
const SourceManager &SM = SemaRef.getSourceManager();
929
SourceLocation IfInsertionLoc =
930
SM.getExpansionLoc(StmtOfUse->getBeginLoc());
931
SourceLocation StmtEndLoc =
932
SM.getExpansionRange(
933
(LastStmtOfUse ? LastStmtOfUse : StmtOfUse)->getEndLoc())
934
.getEnd();
935
if (SM.getFileID(IfInsertionLoc) != SM.getFileID(StmtEndLoc))
936
return;
937
938
StringRef Indentation = Lexer::getIndentationForLine(IfInsertionLoc, SM);
939
const char *ExtraIndentation = " ";
940
std::string FixItString;
941
llvm::raw_string_ostream FixItOS(FixItString);
942
FixItOS << "if (" << (SemaRef.getLangOpts().ObjC ? "@available"
943
: "__builtin_available")
944
<< "("
945
<< AvailabilityAttr::getPlatformNameSourceSpelling(
946
SemaRef.getASTContext().getTargetInfo().getPlatformName())
947
<< " " << Introduced.getAsString() << ", *)) {\n"
948
<< Indentation << ExtraIndentation;
949
FixitDiag << FixItHint::CreateInsertion(IfInsertionLoc, FixItOS.str());
950
SourceLocation ElseInsertionLoc = Lexer::findLocationAfterToken(
951
StmtEndLoc, tok::semi, SM, SemaRef.getLangOpts(),
952
/*SkipTrailingWhitespaceAndNewLine=*/false);
953
if (ElseInsertionLoc.isInvalid())
954
ElseInsertionLoc =
955
Lexer::getLocForEndOfToken(StmtEndLoc, 0, SM, SemaRef.getLangOpts());
956
FixItOS.str().clear();
957
FixItOS << "\n"
958
<< Indentation << "} else {\n"
959
<< Indentation << ExtraIndentation
960
<< "// Fallback on earlier versions\n"
961
<< Indentation << "}";
962
FixitDiag << FixItHint::CreateInsertion(ElseInsertionLoc, FixItOS.str());
963
}
964
}
965
966
bool DiagnoseUnguardedAvailability::VisitTypeLoc(TypeLoc Ty) {
967
const Type *TyPtr = Ty.getTypePtr();
968
SourceRange Range{Ty.getBeginLoc(), Ty.getEndLoc()};
969
970
if (Range.isInvalid())
971
return true;
972
973
if (const auto *TT = dyn_cast<TagType>(TyPtr)) {
974
TagDecl *TD = TT->getDecl();
975
DiagnoseDeclAvailability(TD, Range);
976
977
} else if (const auto *TD = dyn_cast<TypedefType>(TyPtr)) {
978
TypedefNameDecl *D = TD->getDecl();
979
DiagnoseDeclAvailability(D, Range);
980
981
} else if (const auto *ObjCO = dyn_cast<ObjCObjectType>(TyPtr)) {
982
if (NamedDecl *D = ObjCO->getInterface())
983
DiagnoseDeclAvailability(D, Range);
984
}
985
986
return true;
987
}
988
989
bool DiagnoseUnguardedAvailability::TraverseIfStmt(IfStmt *If) {
990
VersionTuple CondVersion;
991
if (auto *E = dyn_cast<ObjCAvailabilityCheckExpr>(If->getCond())) {
992
CondVersion = E->getVersion();
993
994
// If we're using the '*' case here or if this check is redundant, then we
995
// use the enclosing version to check both branches.
996
if (CondVersion.empty() || CondVersion <= AvailabilityStack.back())
997
return TraverseStmt(If->getThen()) && TraverseStmt(If->getElse());
998
} else {
999
// This isn't an availability checking 'if', we can just continue.
1000
return Base::TraverseIfStmt(If);
1001
}
1002
1003
AvailabilityStack.push_back(CondVersion);
1004
bool ShouldContinue = TraverseStmt(If->getThen());
1005
AvailabilityStack.pop_back();
1006
1007
return ShouldContinue && TraverseStmt(If->getElse());
1008
}
1009
1010
} // end anonymous namespace
1011
1012
void Sema::DiagnoseUnguardedAvailabilityViolations(Decl *D) {
1013
Stmt *Body = nullptr;
1014
1015
if (auto *FD = D->getAsFunction()) {
1016
Body = FD->getBody();
1017
1018
if (auto *CD = dyn_cast<CXXConstructorDecl>(FD))
1019
for (const CXXCtorInitializer *CI : CD->inits())
1020
DiagnoseUnguardedAvailability(*this, D).IssueDiagnostics(CI->getInit());
1021
1022
} else if (auto *MD = dyn_cast<ObjCMethodDecl>(D))
1023
Body = MD->getBody();
1024
else if (auto *BD = dyn_cast<BlockDecl>(D))
1025
Body = BD->getBody();
1026
1027
assert(Body && "Need a body here!");
1028
1029
DiagnoseUnguardedAvailability(*this, D).IssueDiagnostics(Body);
1030
}
1031
1032
FunctionScopeInfo *Sema::getCurFunctionAvailabilityContext() {
1033
if (FunctionScopes.empty())
1034
return nullptr;
1035
1036
// Conservatively search the entire current function scope context for
1037
// availability violations. This ensures we always correctly analyze nested
1038
// classes, blocks, lambdas, etc. that may or may not be inside if(@available)
1039
// checks themselves.
1040
return FunctionScopes.front();
1041
}
1042
1043
void Sema::DiagnoseAvailabilityOfDecl(NamedDecl *D,
1044
ArrayRef<SourceLocation> Locs,
1045
const ObjCInterfaceDecl *UnknownObjCClass,
1046
bool ObjCPropertyAccess,
1047
bool AvoidPartialAvailabilityChecks,
1048
ObjCInterfaceDecl *ClassReceiver) {
1049
std::string Message;
1050
AvailabilityResult Result;
1051
const NamedDecl* OffendingDecl;
1052
// See if this declaration is unavailable, deprecated, or partial.
1053
std::tie(Result, OffendingDecl) =
1054
ShouldDiagnoseAvailabilityOfDecl(*this, D, &Message, ClassReceiver);
1055
if (Result == AR_Available)
1056
return;
1057
1058
if (Result == AR_NotYetIntroduced) {
1059
if (AvoidPartialAvailabilityChecks)
1060
return;
1061
1062
// We need to know the @available context in the current function to
1063
// diagnose this use, let DiagnoseUnguardedAvailabilityViolations do that
1064
// when we're done parsing the current function.
1065
if (FunctionScopeInfo *Context = getCurFunctionAvailabilityContext()) {
1066
Context->HasPotentialAvailabilityViolations = true;
1067
return;
1068
}
1069
}
1070
1071
const ObjCPropertyDecl *ObjCPDecl = nullptr;
1072
if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) {
1073
if (const ObjCPropertyDecl *PD = MD->findPropertyDecl()) {
1074
AvailabilityResult PDeclResult = PD->getAvailability(nullptr);
1075
if (PDeclResult == Result)
1076
ObjCPDecl = PD;
1077
}
1078
}
1079
1080
EmitAvailabilityWarning(*this, Result, D, OffendingDecl, Message, Locs,
1081
UnknownObjCClass, ObjCPDecl, ObjCPropertyAccess);
1082
}
1083
1084