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