|
13 | 13 | #include "clang/AST/Attr.h"
|
14 | 14 | #include "clang/AST/Decl.h"
|
15 | 15 | #include "clang/AST/RecursiveASTVisitor.h"
|
| 16 | +#include "clang/Basic/SourceManager.h" |
| 17 | +#include "clang/Lex/Lexer.h" |
| 18 | +#include "clang/Lex/Preprocessor.h" |
16 | 19 | #include "clang/Sema/DelayedDiagnostic.h"
|
17 | 20 | #include "clang/Sema/ScopeInfo.h"
|
18 | 21 | #include "clang/Sema/Sema.h"
|
@@ -240,3 +243,156 @@ void Sema::DiagnoseFeatureAvailabilityOfDecl(NamedDecl *D,
|
240 | 243 | Decl *Ctx = cast<Decl>(getCurLexicalContext());
|
241 | 244 | diagnoseDeclFeatureAvailability(D, Locs.front(), Ctx, *this);
|
242 | 245 | }
|
| 246 | + |
| 247 | +static bool isSimpleFeatureAvailabiltyMacro(MacroInfo *Info) { |
| 248 | + // Must match: |
| 249 | + // __attribute__((availability(domain : id, id/numeric_constant))) |
| 250 | + if (Info->getNumTokens() != 13) |
| 251 | + return false; |
| 252 | + |
| 253 | + if (!Info->getReplacementToken(0).is(tok::kw___attribute) || |
| 254 | + !Info->getReplacementToken(1).is(tok::l_paren) || |
| 255 | + !Info->getReplacementToken(2).is(tok::l_paren)) |
| 256 | + return false; |
| 257 | + |
| 258 | + if (const Token &Tk = Info->getReplacementToken(3); |
| 259 | + !Tk.is(tok::identifier) || |
| 260 | + Tk.getIdentifierInfo()->getName() != "availability") |
| 261 | + return false; |
| 262 | + |
| 263 | + if (!Info->getReplacementToken(4).is(tok::l_paren)) |
| 264 | + return false; |
| 265 | + |
| 266 | + if (const Token &Tk = Info->getReplacementToken(5); |
| 267 | + !Tk.is(tok::identifier) || Tk.getIdentifierInfo()->getName() != "domain") |
| 268 | + return false; |
| 269 | + |
| 270 | + if (!Info->getReplacementToken(6).is(tok::colon)) |
| 271 | + return false; |
| 272 | + |
| 273 | + if (const Token &Tk = Info->getReplacementToken(7); !Tk.is(tok::identifier)) |
| 274 | + return false; |
| 275 | + |
| 276 | + if (!Info->getReplacementToken(8).is(tok::comma)) |
| 277 | + return false; |
| 278 | + |
| 279 | + if (const Token &Tk = Info->getReplacementToken(9); |
| 280 | + !Tk.is(tok::identifier) && !Tk.is(tok::numeric_constant)) |
| 281 | + return false; |
| 282 | + |
| 283 | + if (!Info->getReplacementToken(10).is(tok::r_paren) || |
| 284 | + !Info->getReplacementToken(11).is(tok::r_paren) || |
| 285 | + !Info->getReplacementToken(12).is(tok::r_paren)) |
| 286 | + return false; |
| 287 | + |
| 288 | + return true; |
| 289 | +} |
| 290 | + |
| 291 | +void Sema::diagnoseDeprecatedAvailabilityDomain(StringRef DomainName, |
| 292 | + SourceLocation AvailLoc, |
| 293 | + SourceLocation DomainLoc, |
| 294 | + bool IsUnavailable, |
| 295 | + const ParsedAttr *PA) { |
| 296 | + auto CreateFixIt = [&]() { |
| 297 | + if (PA->getRange().getBegin().isMacroID()) { |
| 298 | + auto *MacroII = PA->getMacroIdentifier(); |
| 299 | + |
| 300 | + // Macro identifier isn't always set. |
| 301 | + if (!MacroII) |
| 302 | + return FixItHint{}; |
| 303 | + |
| 304 | + MacroDefinition MD = PP.getMacroDefinition(MacroII); |
| 305 | + MacroInfo *Info = MD.getMacroInfo(); |
| 306 | + |
| 307 | + bool IsSimple; |
| 308 | + auto It = SimpleFeatureAvailabiltyMacros.find(Info); |
| 309 | + |
| 310 | + if (It == SimpleFeatureAvailabiltyMacros.end()) { |
| 311 | + IsSimple = isSimpleFeatureAvailabiltyMacro(Info); |
| 312 | + SimpleFeatureAvailabiltyMacros[Info] = IsSimple; |
| 313 | + } else { |
| 314 | + IsSimple = It->second; |
| 315 | + } |
| 316 | + |
| 317 | + if (!IsSimple) |
| 318 | + return FixItHint{}; |
| 319 | + |
| 320 | + FileID FID = SourceMgr.getFileID(AvailLoc); |
| 321 | + const SrcMgr::ExpansionInfo *EI = |
| 322 | + &SourceMgr.getSLocEntry(FID).getExpansion(); |
| 323 | + if (IsUnavailable) |
| 324 | + return FixItHint::CreateReplacement(EI->getExpansionLocRange(), |
| 325 | + "__attribute__((unavailable))"); |
| 326 | + return FixItHint::CreateRemoval(EI->getExpansionLocRange()); |
| 327 | + } |
| 328 | + |
| 329 | + if (PA->getSyntax() != AttributeCommonInfo::Syntax::AS_GNU) |
| 330 | + return FixItHint{}; |
| 331 | + |
| 332 | + SourceRange AttrRange = PA->getRange(); |
| 333 | + |
| 334 | + // Replace the availability attribute with "unavailable". |
| 335 | + if (IsUnavailable) |
| 336 | + return FixItHint::CreateReplacement(AttrRange, "unavailable"); |
| 337 | + |
| 338 | + // Remove the availability attribute. |
| 339 | + |
| 340 | + // If there is a leading comma, there's another operand that precedes the |
| 341 | + // availability attribute. In that case, remove the availability attribute |
| 342 | + // and the comma. |
| 343 | + Token PrevTok = *Lexer::findPreviousToken(AttrRange.getBegin(), SourceMgr, |
| 344 | + getLangOpts(), false); |
| 345 | + if (PrevTok.is(tok::comma)) |
| 346 | + return FixItHint::CreateRemoval( |
| 347 | + SourceRange(PrevTok.getLocation(), AttrRange.getEnd())); |
| 348 | + |
| 349 | + // If there is a trailing comma, there's another operand that follows the |
| 350 | + // availability attribute. In that case, remove the availability attribute |
| 351 | + // and the comma. |
| 352 | + Token NextTok = *Lexer::findNextToken(AttrRange.getEnd(), SourceMgr, |
| 353 | + getLangOpts(), false); |
| 354 | + if (NextTok.is(tok::comma)) |
| 355 | + return FixItHint::CreateRemoval( |
| 356 | + SourceRange(AttrRange.getBegin(), NextTok.getLocation())); |
| 357 | + |
| 358 | + // If no leading or trailing commas are found, the availability attribute is |
| 359 | + // the only operand. Remove the entire attribute construct. |
| 360 | + |
| 361 | + // Look for '__attribute'. |
| 362 | + for (int i = 0; i < 2; ++i) |
| 363 | + PrevTok = *Lexer::findPreviousToken(PrevTok.getLocation(), SourceMgr, |
| 364 | + getLangOpts(), false); |
| 365 | + if (!PrevTok.is(tok::raw_identifier) || |
| 366 | + PrevTok.getRawIdentifier() != "__attribute__") |
| 367 | + return FixItHint{}; |
| 368 | + |
| 369 | + // Look for the closing ')'. |
| 370 | + NextTok = *Lexer::findNextToken(NextTok.getLocation(), SourceMgr, |
| 371 | + getLangOpts(), false); |
| 372 | + if (!NextTok.is(tok::r_paren)) |
| 373 | + return FixItHint{}; |
| 374 | + |
| 375 | + return FixItHint::CreateRemoval( |
| 376 | + SourceRange(PrevTok.getLocation(), NextTok.getLocation())); |
| 377 | + }; |
| 378 | + |
| 379 | + ASTContext::AvailabilityDomainInfo Info = |
| 380 | + Context.getFeatureAvailInfo(DomainName); |
| 381 | + |
| 382 | + if (Info.IsDeprecated) { |
| 383 | + Diag(DomainLoc, diag::warn_deprecated_availability_domain) << DomainName; |
| 384 | + if (Info.Kind == FeatureAvailKind::AlwaysAvailable) { |
| 385 | + if (PA) { |
| 386 | + auto FixitDiag = |
| 387 | + Diag(AvailLoc, diag::warn_permanently_available_domain_decl) |
| 388 | + << DomainName << IsUnavailable; |
| 389 | + |
| 390 | + FixItHint Hint = CreateFixIt(); |
| 391 | + if (!Hint.isNull()) |
| 392 | + FixitDiag << Hint; |
| 393 | + } else |
| 394 | + Diag(AvailLoc, diag::warn_permanently_available_domain_expr) |
| 395 | + << DomainName; |
| 396 | + } |
| 397 | + } |
| 398 | +} |
0 commit comments