[APINotes] Upstream dependencies of Sema logic to apply API Notes to decls
This upstreams more of the Clang API Notes functionality that is currently implemented in the Apple fork: https://github.com/apple/llvm-project/tree/next/clang/lib/APINotes This is the largest chunk of the API Notes functionality in the upstreaming process. I will soon submit a follow-up patch to actually enable usage of this functionality by having a Clang driver flag that enables API Notes, along with tests.
This commit is contained in:
parent
9791e54149
commit
77d21e758e
@ -306,6 +306,9 @@ class VariadicEnumArgument<string name, string type, list<string> values,
|
||||
bit IsExternalType = isExternalType;
|
||||
}
|
||||
|
||||
// Represents an attribute wrapped by another attribute.
|
||||
class WrappedAttr<string name, bit opt = 0> : Argument<name, opt>;
|
||||
|
||||
// This handles one spelling of an attribute.
|
||||
class Spelling<string name, string variety, int version = 1> {
|
||||
string Name = name;
|
||||
@ -2291,7 +2294,7 @@ def ObjCBridgeRelated : InheritableAttr {
|
||||
def NSErrorDomain : InheritableAttr {
|
||||
let Spellings = [GNU<"ns_error_domain">];
|
||||
let Subjects = SubjectList<[Enum], ErrorDiag>;
|
||||
let Args = [DeclArgument<Var, "ErrorDomain">];
|
||||
let Args = [IdentifierArgument<"ErrorDomain">];
|
||||
let Documentation = [NSErrorDomainDocs];
|
||||
}
|
||||
|
||||
@ -2648,6 +2651,22 @@ def SwiftError : InheritableAttr {
|
||||
let Documentation = [SwiftErrorDocs];
|
||||
}
|
||||
|
||||
def SwiftImportAsNonGeneric : InheritableAttr {
|
||||
// This attribute has no spellings as it is only ever created implicitly
|
||||
// from API notes.
|
||||
let Spellings = [];
|
||||
let SemaHandler = 0;
|
||||
let Documentation = [InternalOnly];
|
||||
}
|
||||
|
||||
def SwiftImportPropertyAsAccessors : InheritableAttr {
|
||||
// This attribute has no spellings as it is only ever created implicitly
|
||||
// from API notes.
|
||||
let Spellings = [];
|
||||
let SemaHandler = 0;
|
||||
let Documentation = [InternalOnly];
|
||||
}
|
||||
|
||||
def SwiftName : InheritableAttr {
|
||||
let Spellings = [GNU<"swift_name">];
|
||||
let Args = [StringArgument<"Name">];
|
||||
@ -2669,6 +2688,31 @@ def SwiftPrivate : InheritableAttr {
|
||||
let SimpleHandler = 1;
|
||||
}
|
||||
|
||||
def SwiftVersionedAddition : Attr {
|
||||
// This attribute has no spellings as it is only ever created implicitly
|
||||
// from API notes.
|
||||
let Spellings = [];
|
||||
let Args = [VersionArgument<"Version">, WrappedAttr<"AdditionalAttr">,
|
||||
BoolArgument<"IsReplacedByActive">];
|
||||
let SemaHandler = 0;
|
||||
let Documentation = [InternalOnly];
|
||||
}
|
||||
|
||||
def SwiftVersionedRemoval : Attr {
|
||||
// This attribute has no spellings as it is only ever created implicitly
|
||||
// from API notes.
|
||||
let Spellings = [];
|
||||
let Args = [VersionArgument<"Version">, UnsignedArgument<"RawKind">,
|
||||
BoolArgument<"IsReplacedByActive">];
|
||||
let SemaHandler = 0;
|
||||
let Documentation = [InternalOnly];
|
||||
let AdditionalMembers = [{
|
||||
attr::Kind getAttrKindToRemove() const {
|
||||
return static_cast<attr::Kind>(getRawKind());
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
def NoDeref : TypeAttr {
|
||||
let Spellings = [Clang<"noderef">];
|
||||
let Documentation = [NoDerefDocs];
|
||||
|
@ -1617,6 +1617,9 @@ def err_pragma_invalid_keyword : Error<
|
||||
def err_pragma_pipeline_invalid_keyword : Error<
|
||||
"invalid argument; expected 'disable'">;
|
||||
|
||||
// API notes.
|
||||
def err_type_unparsed : Error<"unparsed tokens following type">;
|
||||
|
||||
// Pragma unroll support.
|
||||
def warn_pragma_unroll_cuda_value_in_parens : Warning<
|
||||
"argument to '#pragma unroll' should not be in parentheses in CUDA C/C++">,
|
||||
|
@ -198,11 +198,11 @@ public:
|
||||
/// from. Currently this is only used by _Pragma handling.
|
||||
SourceLocation getFileLoc() const { return FileLoc; }
|
||||
|
||||
private:
|
||||
/// Lex - Return the next token in the file. If this is the end of file, it
|
||||
/// return the tok::eof token. This implicitly involves the preprocessor.
|
||||
bool Lex(Token &Result);
|
||||
|
||||
private:
|
||||
/// Called when the preprocessor is in 'dependency scanning lexing mode'.
|
||||
bool LexDependencyDirectiveToken(Token &Result);
|
||||
|
||||
|
@ -3656,6 +3656,18 @@ private:
|
||||
ParseConceptDefinition(const ParsedTemplateInfo &TemplateInfo,
|
||||
SourceLocation &DeclEnd);
|
||||
|
||||
/// Parse the given string as a type.
|
||||
///
|
||||
/// This is a dangerous utility function currently employed only by API notes.
|
||||
/// It is not a general entry-point for safely parsing types from strings.
|
||||
///
|
||||
/// \param TypeStr The string to be parsed as a type.
|
||||
/// \param Context The name of the context in which this string is being
|
||||
/// parsed, which will be used in diagnostics.
|
||||
/// \param IncludeLoc The location at which this parse was triggered.
|
||||
TypeResult ParseTypeFromString(StringRef TypeStr, StringRef Context,
|
||||
SourceLocation IncludeLoc);
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Modules
|
||||
DeclGroupPtrTy ParseModuleDecl(Sema::ModuleImportState &ImportState);
|
||||
|
@ -955,6 +955,10 @@ public:
|
||||
OpaqueParser = P;
|
||||
}
|
||||
|
||||
/// Callback to the parser to parse a type expressed as a string.
|
||||
std::function<TypeResult(StringRef, StringRef, SourceLocation)>
|
||||
ParseTypeFromStringCallback;
|
||||
|
||||
class DelayedDiagnostics;
|
||||
|
||||
class DelayedDiagnosticsState {
|
||||
@ -3017,6 +3021,9 @@ public:
|
||||
ParmVarDecl *BuildParmVarDeclForTypedef(DeclContext *DC,
|
||||
SourceLocation Loc,
|
||||
QualType T);
|
||||
QualType AdjustParameterTypeForObjCAutoRefCount(QualType T,
|
||||
SourceLocation NameLoc,
|
||||
TypeSourceInfo *TSInfo);
|
||||
ParmVarDecl *CheckParameter(DeclContext *DC, SourceLocation StartLoc,
|
||||
SourceLocation NameLoc, IdentifierInfo *Name,
|
||||
QualType T, TypeSourceInfo *TSInfo,
|
||||
@ -4816,6 +4823,29 @@ public:
|
||||
/// Valid types should not have multiple attributes with different CCs.
|
||||
const AttributedType *getCallingConvAttributedType(QualType T) const;
|
||||
|
||||
/// Check whether a nullability type specifier can be added to the given
|
||||
/// type through some means not written in source (e.g. API notes).
|
||||
///
|
||||
/// \param Type The type to which the nullability specifier will be
|
||||
/// added. On success, this type will be updated appropriately.
|
||||
///
|
||||
/// \param Nullability The nullability specifier to add.
|
||||
///
|
||||
/// \param DiagLoc The location to use for diagnostics.
|
||||
///
|
||||
/// \param AllowArrayTypes Whether to accept nullability specifiers on an
|
||||
/// array type (e.g., because it will decay to a pointer).
|
||||
///
|
||||
/// \param OverrideExisting Whether to override an existing, locally-specified
|
||||
/// nullability specifier rather than complaining about the conflict.
|
||||
///
|
||||
/// \returns true if nullability cannot be applied, false otherwise.
|
||||
bool CheckImplicitNullabilityTypeSpecifier(QualType &Type,
|
||||
NullabilityKind Nullability,
|
||||
SourceLocation DiagLoc,
|
||||
bool AllowArrayTypes,
|
||||
bool OverrideExisting);
|
||||
|
||||
/// Process the attributes before creating an attributed statement. Returns
|
||||
/// the semantic attributes that have been processed.
|
||||
void ProcessStmtAttributes(Stmt *Stmt, const ParsedAttributes &InAttrs,
|
||||
|
@ -8051,6 +8051,71 @@ bool Parser::TryAltiVecTokenOutOfLine(DeclSpec &DS, SourceLocation Loc,
|
||||
return false;
|
||||
}
|
||||
|
||||
TypeResult Parser::ParseTypeFromString(StringRef TypeStr, StringRef Context,
|
||||
SourceLocation IncludeLoc) {
|
||||
// Consume (unexpanded) tokens up to the end-of-directive.
|
||||
SmallVector<Token, 4> Tokens;
|
||||
{
|
||||
// Create a new buffer from which we will parse the type.
|
||||
auto &SourceMgr = PP.getSourceManager();
|
||||
FileID FID = SourceMgr.createFileID(
|
||||
llvm::MemoryBuffer::getMemBufferCopy(TypeStr, Context), SrcMgr::C_User,
|
||||
0, 0, IncludeLoc);
|
||||
|
||||
// Form a new lexer that references the buffer.
|
||||
Lexer L(FID, SourceMgr.getBufferOrFake(FID), PP);
|
||||
L.setParsingPreprocessorDirective(true);
|
||||
|
||||
// Lex the tokens from that buffer.
|
||||
Token Tok;
|
||||
do {
|
||||
L.Lex(Tok);
|
||||
Tokens.push_back(Tok);
|
||||
} while (Tok.isNot(tok::eod));
|
||||
}
|
||||
|
||||
// Replace the "eod" token with an "eof" token identifying the end of
|
||||
// the provided string.
|
||||
Token &EndToken = Tokens.back();
|
||||
EndToken.startToken();
|
||||
EndToken.setKind(tok::eof);
|
||||
EndToken.setLocation(Tok.getLocation());
|
||||
EndToken.setEofData(TypeStr.data());
|
||||
|
||||
// Add the current token back.
|
||||
Tokens.push_back(Tok);
|
||||
|
||||
// Enter the tokens into the token stream.
|
||||
PP.EnterTokenStream(Tokens, /*DisableMacroExpansion=*/false,
|
||||
/*IsReinject=*/false);
|
||||
|
||||
// Consume the current token so that we'll start parsing the tokens we
|
||||
// added to the stream.
|
||||
ConsumeAnyToken();
|
||||
|
||||
// Enter a new scope.
|
||||
ParseScope LocalScope(this, 0);
|
||||
|
||||
// Parse the type.
|
||||
TypeResult Result = ParseTypeName(nullptr);
|
||||
|
||||
// Check if we parsed the whole thing.
|
||||
if (Result.isUsable() &&
|
||||
(Tok.isNot(tok::eof) || Tok.getEofData() != TypeStr.data())) {
|
||||
Diag(Tok.getLocation(), diag::err_type_unparsed);
|
||||
}
|
||||
|
||||
// There could be leftover tokens (e.g. because of an error).
|
||||
// Skip through until we reach the 'end of directive' token.
|
||||
while (Tok.isNot(tok::eof))
|
||||
ConsumeAnyToken();
|
||||
|
||||
// Consume the end token.
|
||||
if (Tok.is(tok::eof) && Tok.getEofData() == TypeStr.data())
|
||||
ConsumeAnyToken();
|
||||
return Result;
|
||||
}
|
||||
|
||||
void Parser::DiagnoseBitIntUse(const Token &Tok) {
|
||||
// If the token is for _ExtInt, diagnose it as being deprecated. Otherwise,
|
||||
// the token is about _BitInt and gets (potentially) diagnosed as use of an
|
||||
|
@ -70,6 +70,11 @@ Parser::Parser(Preprocessor &pp, Sema &actions, bool skipFunctionBodies)
|
||||
PP.addCommentHandler(CommentSemaHandler.get());
|
||||
|
||||
PP.setCodeCompletionHandler(*this);
|
||||
|
||||
Actions.ParseTypeFromStringCallback =
|
||||
[this](StringRef TypeStr, StringRef Context, SourceLocation IncludeLoc) {
|
||||
return this->ParseTypeFromString(TypeStr, Context, IncludeLoc);
|
||||
};
|
||||
}
|
||||
|
||||
DiagnosticBuilder Parser::Diag(SourceLocation Loc, unsigned DiagID) {
|
||||
|
@ -15205,6 +15205,37 @@ void Sema::DiagnoseSizeOfParametersAndReturnValue(
|
||||
}
|
||||
}
|
||||
|
||||
QualType Sema::AdjustParameterTypeForObjCAutoRefCount(QualType T,
|
||||
SourceLocation NameLoc,
|
||||
TypeSourceInfo *TSInfo) {
|
||||
// In ARC, infer a lifetime qualifier for appropriate parameter types.
|
||||
if (!getLangOpts().ObjCAutoRefCount ||
|
||||
T.getObjCLifetime() != Qualifiers::OCL_None || !T->isObjCLifetimeType())
|
||||
return T;
|
||||
|
||||
Qualifiers::ObjCLifetime Lifetime;
|
||||
|
||||
// Special cases for arrays:
|
||||
// - if it's const, use __unsafe_unretained
|
||||
// - otherwise, it's an error
|
||||
if (T->isArrayType()) {
|
||||
if (!T.isConstQualified()) {
|
||||
if (DelayedDiagnostics.shouldDelayDiagnostics())
|
||||
DelayedDiagnostics.add(sema::DelayedDiagnostic::makeForbiddenType(
|
||||
NameLoc, diag::err_arc_array_param_no_ownership, T, false));
|
||||
else
|
||||
Diag(NameLoc, diag::err_arc_array_param_no_ownership)
|
||||
<< TSInfo->getTypeLoc().getSourceRange();
|
||||
}
|
||||
Lifetime = Qualifiers::OCL_ExplicitNone;
|
||||
} else {
|
||||
Lifetime = T->getObjCARCImplicitLifetime();
|
||||
}
|
||||
T = Context.getLifetimeQualifiedType(T, Lifetime);
|
||||
|
||||
return T;
|
||||
}
|
||||
|
||||
ParmVarDecl *Sema::CheckParameter(DeclContext *DC, SourceLocation StartLoc,
|
||||
SourceLocation NameLoc, IdentifierInfo *Name,
|
||||
QualType T, TypeSourceInfo *TSInfo,
|
||||
|
@ -6236,29 +6236,35 @@ static void handleObjCRequiresSuperAttr(Sema &S, Decl *D,
|
||||
D->addAttr(::new (S.Context) ObjCRequiresSuperAttr(S.Context, Attrs));
|
||||
}
|
||||
|
||||
static void handleNSErrorDomain(Sema &S, Decl *D, const ParsedAttr &AL) {
|
||||
auto *E = AL.getArgAsExpr(0);
|
||||
auto Loc = E ? E->getBeginLoc() : AL.getLoc();
|
||||
static void handleNSErrorDomain(Sema &S, Decl *D, const ParsedAttr &Attr) {
|
||||
if (!isa<TagDecl>(D)) {
|
||||
S.Diag(D->getBeginLoc(), diag::err_nserrordomain_invalid_decl) << 0;
|
||||
return;
|
||||
}
|
||||
|
||||
IdentifierLoc *IdentLoc =
|
||||
Attr.isArgIdent(0) ? Attr.getArgAsIdent(0) : nullptr;
|
||||
if (!IdentLoc || !IdentLoc->Ident) {
|
||||
// Try to locate the argument directly.
|
||||
SourceLocation Loc = Attr.getLoc();
|
||||
if (Attr.isArgExpr(0) && Attr.getArgAsExpr(0))
|
||||
Loc = Attr.getArgAsExpr(0)->getBeginLoc();
|
||||
|
||||
auto *DRE = dyn_cast<DeclRefExpr>(AL.getArgAsExpr(0));
|
||||
if (!DRE) {
|
||||
S.Diag(Loc, diag::err_nserrordomain_invalid_decl) << 0;
|
||||
return;
|
||||
}
|
||||
|
||||
auto *VD = dyn_cast<VarDecl>(DRE->getDecl());
|
||||
if (!VD) {
|
||||
S.Diag(Loc, diag::err_nserrordomain_invalid_decl) << 1 << DRE->getDecl();
|
||||
// Verify that the identifier is a valid decl in the C decl namespace.
|
||||
LookupResult Result(S, DeclarationName(IdentLoc->Ident), SourceLocation(),
|
||||
Sema::LookupNameKind::LookupOrdinaryName);
|
||||
if (!S.LookupName(Result, S.TUScope) || !Result.getAsSingle<VarDecl>()) {
|
||||
S.Diag(IdentLoc->Loc, diag::err_nserrordomain_invalid_decl)
|
||||
<< 1 << IdentLoc->Ident;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isNSStringType(VD->getType(), S.Context) &&
|
||||
!isCFStringType(VD->getType(), S.Context)) {
|
||||
S.Diag(Loc, diag::err_nserrordomain_wrong_type) << VD;
|
||||
return;
|
||||
}
|
||||
|
||||
D->addAttr(::new (S.Context) NSErrorDomainAttr(S.Context, AL, VD));
|
||||
D->addAttr(::new (S.Context)
|
||||
NSErrorDomainAttr(S.Context, Attr, IdentLoc->Ident));
|
||||
}
|
||||
|
||||
static void handleObjCBridgeAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
|
||||
|
@ -7521,6 +7521,25 @@ static bool HandleWebAssemblyFuncrefAttr(TypeProcessingState &State,
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Rebuild an attributed type without the nullability attribute on it.
|
||||
static QualType rebuildAttributedTypeWithoutNullability(ASTContext &Ctx,
|
||||
QualType Type) {
|
||||
auto Attributed = dyn_cast<AttributedType>(Type.getTypePtr());
|
||||
if (!Attributed)
|
||||
return Type;
|
||||
|
||||
// Skip the nullability attribute; we're done.
|
||||
if (Attributed->getImmediateNullability())
|
||||
return Attributed->getModifiedType();
|
||||
|
||||
// Build the modified type.
|
||||
QualType Modified = rebuildAttributedTypeWithoutNullability(
|
||||
Ctx, Attributed->getModifiedType());
|
||||
assert(Modified.getTypePtr() != Attributed->getModifiedType().getTypePtr());
|
||||
return Ctx.getAttributedType(Attributed->getAttrKind(), Modified,
|
||||
Attributed->getEquivalentType());
|
||||
}
|
||||
|
||||
/// Map a nullability attribute kind to a nullability kind.
|
||||
static NullabilityKind mapNullabilityAttrKind(ParsedAttr::Kind kind) {
|
||||
switch (kind) {
|
||||
@ -7541,74 +7560,65 @@ static NullabilityKind mapNullabilityAttrKind(ParsedAttr::Kind kind) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Applies a nullability type specifier to the given type, if possible.
|
||||
///
|
||||
/// \param state The type processing state.
|
||||
///
|
||||
/// \param type The type to which the nullability specifier will be
|
||||
/// added. On success, this type will be updated appropriately.
|
||||
///
|
||||
/// \param attr The attribute as written on the type.
|
||||
///
|
||||
/// \param allowOnArrayType Whether to accept nullability specifiers on an
|
||||
/// array type (e.g., because it will decay to a pointer).
|
||||
///
|
||||
/// \returns true if a problem has been diagnosed, false on success.
|
||||
static bool checkNullabilityTypeSpecifier(TypeProcessingState &state,
|
||||
QualType &type,
|
||||
ParsedAttr &attr,
|
||||
bool allowOnArrayType) {
|
||||
Sema &S = state.getSema();
|
||||
|
||||
NullabilityKind nullability = mapNullabilityAttrKind(attr.getKind());
|
||||
SourceLocation nullabilityLoc = attr.getLoc();
|
||||
bool isContextSensitive = attr.isContextSensitiveKeywordAttribute();
|
||||
|
||||
recordNullabilitySeen(S, nullabilityLoc);
|
||||
static bool CheckNullabilityTypeSpecifier(
|
||||
Sema &S, TypeProcessingState *State, ParsedAttr *PAttr, QualType &QT,
|
||||
NullabilityKind Nullability, SourceLocation NullabilityLoc,
|
||||
bool IsContextSensitive, bool AllowOnArrayType, bool OverrideExisting) {
|
||||
bool Implicit = (State == nullptr);
|
||||
if (!Implicit)
|
||||
recordNullabilitySeen(S, NullabilityLoc);
|
||||
|
||||
// Check for existing nullability attributes on the type.
|
||||
QualType desugared = type;
|
||||
while (auto attributed = dyn_cast<AttributedType>(desugared.getTypePtr())) {
|
||||
QualType Desugared = QT;
|
||||
while (auto *Attributed = dyn_cast<AttributedType>(Desugared.getTypePtr())) {
|
||||
// Check whether there is already a null
|
||||
if (auto existingNullability = attributed->getImmediateNullability()) {
|
||||
if (auto ExistingNullability = Attributed->getImmediateNullability()) {
|
||||
// Duplicated nullability.
|
||||
if (nullability == *existingNullability) {
|
||||
S.Diag(nullabilityLoc, diag::warn_nullability_duplicate)
|
||||
<< DiagNullabilityKind(nullability, isContextSensitive)
|
||||
<< FixItHint::CreateRemoval(nullabilityLoc);
|
||||
if (Nullability == *ExistingNullability) {
|
||||
if (Implicit)
|
||||
break;
|
||||
|
||||
S.Diag(NullabilityLoc, diag::warn_nullability_duplicate)
|
||||
<< DiagNullabilityKind(Nullability, IsContextSensitive)
|
||||
<< FixItHint::CreateRemoval(NullabilityLoc);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (!OverrideExisting) {
|
||||
// Conflicting nullability.
|
||||
S.Diag(nullabilityLoc, diag::err_nullability_conflicting)
|
||||
<< DiagNullabilityKind(nullability, isContextSensitive)
|
||||
<< DiagNullabilityKind(*existingNullability, false);
|
||||
S.Diag(NullabilityLoc, diag::err_nullability_conflicting)
|
||||
<< DiagNullabilityKind(Nullability, IsContextSensitive)
|
||||
<< DiagNullabilityKind(*ExistingNullability, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
desugared = attributed->getModifiedType();
|
||||
// Rebuild the attributed type, dropping the existing nullability.
|
||||
QT = rebuildAttributedTypeWithoutNullability(S.Context, QT);
|
||||
}
|
||||
|
||||
Desugared = Attributed->getModifiedType();
|
||||
}
|
||||
|
||||
// If there is already a different nullability specifier, complain.
|
||||
// This (unlike the code above) looks through typedefs that might
|
||||
// have nullability specifiers on them, which means we cannot
|
||||
// provide a useful Fix-It.
|
||||
if (auto existingNullability = desugared->getNullability()) {
|
||||
if (nullability != *existingNullability) {
|
||||
S.Diag(nullabilityLoc, diag::err_nullability_conflicting)
|
||||
<< DiagNullabilityKind(nullability, isContextSensitive)
|
||||
<< DiagNullabilityKind(*existingNullability, false);
|
||||
if (auto ExistingNullability = Desugared->getNullability()) {
|
||||
if (Nullability != *ExistingNullability && !Implicit) {
|
||||
S.Diag(NullabilityLoc, diag::err_nullability_conflicting)
|
||||
<< DiagNullabilityKind(Nullability, IsContextSensitive)
|
||||
<< DiagNullabilityKind(*ExistingNullability, false);
|
||||
|
||||
// Try to find the typedef with the existing nullability specifier.
|
||||
if (auto typedefType = desugared->getAs<TypedefType>()) {
|
||||
TypedefNameDecl *typedefDecl = typedefType->getDecl();
|
||||
if (auto TT = Desugared->getAs<TypedefType>()) {
|
||||
TypedefNameDecl *typedefDecl = TT->getDecl();
|
||||
QualType underlyingType = typedefDecl->getUnderlyingType();
|
||||
if (auto typedefNullability
|
||||
= AttributedType::stripOuterNullability(underlyingType)) {
|
||||
if (*typedefNullability == *existingNullability) {
|
||||
if (auto typedefNullability =
|
||||
AttributedType::stripOuterNullability(underlyingType)) {
|
||||
if (*typedefNullability == *ExistingNullability) {
|
||||
S.Diag(typedefDecl->getLocation(), diag::note_nullability_here)
|
||||
<< DiagNullabilityKind(*existingNullability, false);
|
||||
<< DiagNullabilityKind(*ExistingNullability, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -7618,44 +7628,73 @@ static bool checkNullabilityTypeSpecifier(TypeProcessingState &state,
|
||||
}
|
||||
|
||||
// If this definitely isn't a pointer type, reject the specifier.
|
||||
if (!desugared->canHaveNullability() &&
|
||||
!(allowOnArrayType && desugared->isArrayType())) {
|
||||
S.Diag(nullabilityLoc, diag::err_nullability_nonpointer)
|
||||
<< DiagNullabilityKind(nullability, isContextSensitive) << type;
|
||||
if (!Desugared->canHaveNullability() &&
|
||||
!(AllowOnArrayType && Desugared->isArrayType())) {
|
||||
if (!Implicit)
|
||||
S.Diag(NullabilityLoc, diag::err_nullability_nonpointer)
|
||||
<< DiagNullabilityKind(Nullability, IsContextSensitive) << QT;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// For the context-sensitive keywords/Objective-C property
|
||||
// attributes, require that the type be a single-level pointer.
|
||||
if (isContextSensitive) {
|
||||
if (IsContextSensitive) {
|
||||
// Make sure that the pointee isn't itself a pointer type.
|
||||
const Type *pointeeType = nullptr;
|
||||
if (desugared->isArrayType())
|
||||
pointeeType = desugared->getArrayElementTypeNoTypeQual();
|
||||
else if (desugared->isAnyPointerType())
|
||||
pointeeType = desugared->getPointeeType().getTypePtr();
|
||||
if (Desugared->isArrayType())
|
||||
pointeeType = Desugared->getArrayElementTypeNoTypeQual();
|
||||
else if (Desugared->isAnyPointerType())
|
||||
pointeeType = Desugared->getPointeeType().getTypePtr();
|
||||
|
||||
if (pointeeType && (pointeeType->isAnyPointerType() ||
|
||||
pointeeType->isObjCObjectPointerType() ||
|
||||
pointeeType->isMemberPointerType())) {
|
||||
S.Diag(nullabilityLoc, diag::err_nullability_cs_multilevel)
|
||||
<< DiagNullabilityKind(nullability, true)
|
||||
<< type;
|
||||
S.Diag(nullabilityLoc, diag::note_nullability_type_specifier)
|
||||
<< DiagNullabilityKind(nullability, false)
|
||||
<< type
|
||||
<< FixItHint::CreateReplacement(nullabilityLoc,
|
||||
getNullabilitySpelling(nullability));
|
||||
S.Diag(NullabilityLoc, diag::err_nullability_cs_multilevel)
|
||||
<< DiagNullabilityKind(Nullability, true) << QT;
|
||||
S.Diag(NullabilityLoc, diag::note_nullability_type_specifier)
|
||||
<< DiagNullabilityKind(Nullability, false) << QT
|
||||
<< FixItHint::CreateReplacement(NullabilityLoc,
|
||||
getNullabilitySpelling(Nullability));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Form the attributed type.
|
||||
type = state.getAttributedType(
|
||||
createNullabilityAttr(S.Context, attr, nullability), type, type);
|
||||
if (State) {
|
||||
assert(PAttr);
|
||||
Attr *A = createNullabilityAttr(S.Context, *PAttr, Nullability);
|
||||
QT = State->getAttributedType(A, QT, QT);
|
||||
} else {
|
||||
attr::Kind attrKind = AttributedType::getNullabilityAttrKind(Nullability);
|
||||
QT = S.Context.getAttributedType(attrKind, QT, QT);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool CheckNullabilityTypeSpecifier(TypeProcessingState &State,
|
||||
QualType &Type, ParsedAttr &Attr,
|
||||
bool AllowOnArrayType) {
|
||||
NullabilityKind Nullability = mapNullabilityAttrKind(Attr.getKind());
|
||||
SourceLocation NullabilityLoc = Attr.getLoc();
|
||||
bool IsContextSensitive = Attr.isContextSensitiveKeywordAttribute();
|
||||
|
||||
return CheckNullabilityTypeSpecifier(State.getSema(), &State, &Attr, Type,
|
||||
Nullability, NullabilityLoc,
|
||||
IsContextSensitive, AllowOnArrayType,
|
||||
/*overrideExisting*/ false);
|
||||
}
|
||||
|
||||
bool Sema::CheckImplicitNullabilityTypeSpecifier(QualType &Type,
|
||||
NullabilityKind Nullability,
|
||||
SourceLocation DiagLoc,
|
||||
bool AllowArrayTypes,
|
||||
bool OverrideExisting) {
|
||||
return CheckNullabilityTypeSpecifier(
|
||||
*this, nullptr, nullptr, Type, Nullability, DiagLoc,
|
||||
/*isContextSensitive*/ false, AllowArrayTypes, OverrideExisting);
|
||||
}
|
||||
|
||||
/// Check the application of the Objective-C '__kindof' qualifier to
|
||||
/// the given type.
|
||||
static bool checkObjCKindOfType(TypeProcessingState &state, QualType &type,
|
||||
@ -8950,10 +8989,7 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type,
|
||||
bool allowOnArrayType =
|
||||
state.getDeclarator().isPrototypeContext() &&
|
||||
!hasOuterPointerLikeChunk(state.getDeclarator(), endIndex);
|
||||
if (checkNullabilityTypeSpecifier(
|
||||
state,
|
||||
type,
|
||||
attr,
|
||||
if (CheckNullabilityTypeSpecifier(state, type, attr,
|
||||
allowOnArrayType)) {
|
||||
attr.setInvalid();
|
||||
}
|
||||
|
@ -3095,6 +3095,8 @@ public:
|
||||
|
||||
Expr *readExpr() { return Reader.readExpr(); }
|
||||
|
||||
Attr *readAttr() { return Reader.readAttr(); }
|
||||
|
||||
std::string readString() {
|
||||
return Reader.readString();
|
||||
}
|
||||
|
@ -53,7 +53,6 @@ typedef NS_ERROR_ENUM(unsigned char, MyCFTypedefErrorEnum, MyCFTypedefErrorDomai
|
||||
|
||||
extern char *const WrongErrorDomainType;
|
||||
enum __attribute__((ns_error_domain(WrongErrorDomainType))) MyWrongErrorDomainType { MyWrongErrorDomain };
|
||||
// expected-error@-1{{domain argument 'WrongErrorDomainType' does not point to an NSString or CFString constant}}
|
||||
|
||||
struct __attribute__((ns_error_domain(MyErrorDomain))) MyStructWithErrorDomain {};
|
||||
// expected-error@-1{{'ns_error_domain' attribute only applies to enums}}
|
||||
@ -68,7 +67,7 @@ enum __attribute__((ns_error_domain(MyErrorDomain, MyErrorDomain))) TwoArgs { Tw
|
||||
// expected-error@-1{{'ns_error_domain' attribute takes one argument}}
|
||||
|
||||
typedef NS_ERROR_ENUM(unsigned char, MyErrorEnumInvalid, InvalidDomain) {
|
||||
// expected-error@-1{{use of undeclared identifier 'InvalidDomain'}}
|
||||
// expected-error@-1{{domain argument 'InvalidDomain' does not refer to global constant}}
|
||||
MyErrFirstInvalid,
|
||||
MyErrSecondInvalid,
|
||||
};
|
||||
|
@ -1414,7 +1414,29 @@ namespace {
|
||||
}
|
||||
};
|
||||
|
||||
} // end anonymous namespace
|
||||
class WrappedAttr : public SimpleArgument {
|
||||
public:
|
||||
WrappedAttr(const Record &Arg, StringRef Attr)
|
||||
: SimpleArgument(Arg, Attr, "Attr *") {}
|
||||
|
||||
void writePCHReadDecls(raw_ostream &OS) const override {
|
||||
OS << " Attr *" << getLowerName() << " = Record.readAttr();";
|
||||
}
|
||||
|
||||
void writePCHWrite(raw_ostream &OS) const override {
|
||||
OS << " AddAttr(SA->get" << getUpperName() << "());";
|
||||
}
|
||||
|
||||
void writeDump(raw_ostream &OS) const override {}
|
||||
|
||||
void writeDumpChildren(raw_ostream &OS) const override {
|
||||
OS << " Visit(SA->get" << getUpperName() << "());\n";
|
||||
}
|
||||
|
||||
void writeHasChildren(raw_ostream &OS) const override { OS << "true"; }
|
||||
};
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
static std::unique_ptr<Argument>
|
||||
createArgument(const Record &Arg, StringRef Attr,
|
||||
@ -1470,6 +1492,8 @@ createArgument(const Record &Arg, StringRef Attr,
|
||||
Ptr = std::make_unique<VariadicIdentifierArgument>(Arg, Attr);
|
||||
else if (ArgName == "VersionArgument")
|
||||
Ptr = std::make_unique<VersionArgument>(Arg, Attr);
|
||||
else if (ArgName == "WrappedAttr")
|
||||
Ptr = std::make_unique<WrappedAttr>(Arg, Attr);
|
||||
else if (ArgName == "OMPTraitInfoArgument")
|
||||
Ptr = std::make_unique<SimpleArgument>(Arg, Attr, "OMPTraitInfo *");
|
||||
else if (ArgName == "VariadicOMPInteropInfoArgument")
|
||||
|
Loading…
x
Reference in New Issue
Block a user