Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -919,6 +919,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
FeatureAvailKind Kind = FeatureAvailKind::None;
clang::Decl *Decl = nullptr;
ImplicitCastExpr *Call = nullptr;
bool IsDeprecated = false;
bool isInvalid() const { return Kind == FeatureAvailKind::None; }
};

Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/DiagnosticGroups.td
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,8 @@ def Deprecated : DiagGroup<"deprecated", [DeprecatedAnonEnumEnumConversion,
]>,
DiagCategory<"Deprecations">;

def PermanentlyAvailableDomain : DiagGroup<"permanently-available-domain">;

def CXX20Designator : DiagGroup<"c++20-designator">;
// Allow -Wno-c99-designator to be used to turn off all warnings on valid C99
// designators (including the warning controlled by -Wc++20-designator).
Expand Down
8 changes: 8 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -4136,6 +4136,14 @@ def err_features_invalid_for_decl : Error<
"feature attributes cannot be applied to %0">;
def err_features_invalid_name : Error<
"cannot find definition of feature attribute '%0'">;
def warn_deprecated_availability_domain : Warning<
"availability domain '%0' is deprecated">, InGroup<DiagGroup<"deprecated-availability-domain">>;
def warn_permanently_available_domain_expr : Warning<
"unnecessary check for '%0'; this expression will always evaluate to true">,
InGroup<PermanentlyAvailableDomain>;
def warn_permanently_available_domain_decl : Warning<
"%select{attribute has no effect because '%0' is always available|declaration is permanently unavailable because '%0' is always available}1">,
InGroup<PermanentlyAvailableDomain>;
def err_features_invalid__arg : Error<
"invalid argument %0: must evaluate to 0 or 1">;
def err_feature_invalid_for_decl : Error<
Expand Down
10 changes: 10 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ class InitializedEntity;
enum class LangAS : unsigned int;
class LocalInstantiationScope;
class LookupResult;
class MacroInfo;
class MangleNumberingContext;
typedef ArrayRef<IdentifierLoc> ModuleIdPath;
class ModuleLoader;
Expand Down Expand Up @@ -2670,6 +2671,15 @@ class Sema final : public SemaBase {
void DiagnoseFeatureAvailabilityOfDecl(NamedDecl *D,
ArrayRef<SourceLocation> Locs);

void diagnoseDeprecatedAvailabilityDomain(StringRef DomainName,
SourceLocation AvailLoc,
SourceLocation DomainLoc,
bool IsUnavailable = false,
const ParsedAttr *PA = nullptr);

/// A map that indicates whether a macro is simple.
llvm::SmallDenseMap<clang::MacroInfo *, bool> SimpleFeatureAvailabiltyMacros;

/// Retrieve the current function, if any, that should be analyzed for
/// potential availability violations.
sema::FunctionScopeInfo *getCurFunctionAvailabilityContext();
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -997,7 +997,8 @@ ASTContext::getFeatureAvailInfo(Decl *D) const {
llvm_unreachable("invalid feature kind");
}

ASTContext::AvailabilityDomainInfo Info{Kind, D, nullptr};
ASTContext::AvailabilityDomainInfo Info{Kind, D, nullptr,
D->hasAttr<DeprecatedAttr>()};

if (Kind == FeatureAvailKind::Dynamic) {
Expr *FnExpr = Init->getInit(1);
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2492,6 +2492,10 @@ static void handleFeatureAvailabilityAttr(Sema &S, Decl *D,
int IsUnavailable =
AL.getArgAsExpr(1)->getIntegerConstantExpr(S.Context)->getExtValue();

S.diagnoseDeprecatedAvailabilityDomain(II->getName(), AL.getLoc(),
AL.getArgAsIdent(0)->getLoc(),
IsUnavailable, &AL);

if (IsUnavailable != 0 && IsUnavailable != 1) {
S.Diag(AL.getArgAsExpr(1)->getExprLoc(), diag::err_features_invalid__arg)
<< IsUnavailable;
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Sema/SemaExprObjC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5162,6 +5162,10 @@ ExprResult SemaObjC::ActOnObjCAvailabilityCheckExpr(
if (FunctionScopeInfo *Info = SemaRef.getCurFunctionAvailabilityContext())
Info->HasPotentialFeatureAvailabilityViolations = true;
auto Spec = AvailSpecs.front();

SemaRef.diagnoseDeprecatedAvailabilityDomain(Spec.getDomainName(), AtLoc,
Spec.getBeginLoc());

return ObjCAvailabilityCheckExpr::CreateAvailabilityFeatureCheck(
AtLoc, RParen, Context.BoolTy, Spec.getDomainName(), Context);
}
Expand Down
156 changes: 156 additions & 0 deletions clang/lib/Sema/SemaFeatureAvailability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
#include "clang/AST/Attr.h"
#include "clang/AST/Decl.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/Lexer.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/DelayedDiagnostic.h"
#include "clang/Sema/ScopeInfo.h"
#include "clang/Sema/Sema.h"
Expand Down Expand Up @@ -240,3 +243,156 @@ void Sema::DiagnoseFeatureAvailabilityOfDecl(NamedDecl *D,
Decl *Ctx = cast<Decl>(getCurLexicalContext());
diagnoseDeclFeatureAvailability(D, Locs.front(), Ctx, *this);
}

static bool isSimpleFeatureAvailabiltyMacro(MacroInfo *Info) {
// Must match:
// __attribute__((availability(domain : id, id/numeric_constant)))
if (Info->getNumTokens() != 13)
return false;

if (!Info->getReplacementToken(0).is(tok::kw___attribute) ||
!Info->getReplacementToken(1).is(tok::l_paren) ||
!Info->getReplacementToken(2).is(tok::l_paren))
return false;

if (const Token &Tk = Info->getReplacementToken(3);
!Tk.is(tok::identifier) ||
Tk.getIdentifierInfo()->getName() != "availability")
return false;

if (!Info->getReplacementToken(4).is(tok::l_paren))
return false;

if (const Token &Tk = Info->getReplacementToken(5);
!Tk.is(tok::identifier) || Tk.getIdentifierInfo()->getName() != "domain")
return false;

if (!Info->getReplacementToken(6).is(tok::colon))
return false;

if (const Token &Tk = Info->getReplacementToken(7); !Tk.is(tok::identifier))
return false;

if (!Info->getReplacementToken(8).is(tok::comma))
return false;

if (const Token &Tk = Info->getReplacementToken(9);
!Tk.is(tok::identifier) && !Tk.is(tok::numeric_constant))
return false;

if (!Info->getReplacementToken(10).is(tok::r_paren) ||
!Info->getReplacementToken(11).is(tok::r_paren) ||
!Info->getReplacementToken(12).is(tok::r_paren))
return false;

return true;
}

void Sema::diagnoseDeprecatedAvailabilityDomain(StringRef DomainName,
SourceLocation AvailLoc,
SourceLocation DomainLoc,
bool IsUnavailable,
const ParsedAttr *PA) {
auto CreateFixIt = [&]() {
if (PA->getRange().getBegin().isMacroID()) {
auto *MacroII = PA->getMacroIdentifier();

// Macro identifier isn't always set.
if (!MacroII)
return FixItHint{};

MacroDefinition MD = PP.getMacroDefinition(MacroII);
MacroInfo *Info = MD.getMacroInfo();

bool IsSimple;
auto It = SimpleFeatureAvailabiltyMacros.find(Info);

if (It == SimpleFeatureAvailabiltyMacros.end()) {
IsSimple = isSimpleFeatureAvailabiltyMacro(Info);
SimpleFeatureAvailabiltyMacros[Info] = IsSimple;
} else {
IsSimple = It->second;
}

if (!IsSimple)
return FixItHint{};

FileID FID = SourceMgr.getFileID(AvailLoc);
const SrcMgr::ExpansionInfo *EI =
&SourceMgr.getSLocEntry(FID).getExpansion();
if (IsUnavailable)
return FixItHint::CreateReplacement(EI->getExpansionLocRange(),
"__attribute__((unavailable))");
return FixItHint::CreateRemoval(EI->getExpansionLocRange());
}

if (PA->getSyntax() != AttributeCommonInfo::Syntax::AS_GNU)
return FixItHint{};

SourceRange AttrRange = PA->getRange();

// Replace the availability attribute with "unavailable".
if (IsUnavailable)
return FixItHint::CreateReplacement(AttrRange, "unavailable");

// Remove the availability attribute.

// If there is a leading comma, there's another operand that precedes the
// availability attribute. In that case, remove the availability attribute
// and the comma.
Token PrevTok = *Lexer::findPreviousToken(AttrRange.getBegin(), SourceMgr,
getLangOpts(), false);
if (PrevTok.is(tok::comma))
return FixItHint::CreateRemoval(
SourceRange(PrevTok.getLocation(), AttrRange.getEnd()));

// If there is a trailing comma, there's another operand that follows the
// availability attribute. In that case, remove the availability attribute
// and the comma.
Token NextTok = *Lexer::findNextToken(AttrRange.getEnd(), SourceMgr,
getLangOpts(), false);
if (NextTok.is(tok::comma))
return FixItHint::CreateRemoval(
SourceRange(AttrRange.getBegin(), NextTok.getLocation()));

// If no leading or trailing commas are found, the availability attribute is
// the only operand. Remove the entire attribute construct.

// Look for '__attribute'.
for (int i = 0; i < 2; ++i)
PrevTok = *Lexer::findPreviousToken(PrevTok.getLocation(), SourceMgr,
getLangOpts(), false);
if (!PrevTok.is(tok::raw_identifier) ||
PrevTok.getRawIdentifier() != "__attribute__")
return FixItHint{};

// Look for the closing ')'.
NextTok = *Lexer::findNextToken(NextTok.getLocation(), SourceMgr,
getLangOpts(), false);
if (!NextTok.is(tok::r_paren))
return FixItHint{};

return FixItHint::CreateRemoval(
SourceRange(PrevTok.getLocation(), NextTok.getLocation()));
};

ASTContext::AvailabilityDomainInfo Info =
Context.getFeatureAvailInfo(DomainName);

if (Info.IsDeprecated) {
Diag(DomainLoc, diag::warn_deprecated_availability_domain) << DomainName;
if (Info.Kind == FeatureAvailKind::AlwaysAvailable) {
if (PA) {
auto FixitDiag =
Diag(AvailLoc, diag::warn_permanently_available_domain_decl)
<< DomainName << IsUnavailable;

FixItHint Hint = CreateFixIt();
if (!Hint.isNull())
FixitDiag << Hint;
} else
Diag(AvailLoc, diag::warn_permanently_available_domain_expr)
<< DomainName;
}
}
}
Loading