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