[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:
Egor Zhdan 2024-01-17 13:13:10 +00:00 committed by GitHub
parent 9791e54149
commit 77d21e758e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 349 additions and 92 deletions

View File

@ -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];

View File

@ -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++">,

View File

@ -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);

View File

@ -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);

View File

@ -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,

View File

@ -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

View File

@ -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) {

View File

@ -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,

View File

@ -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) {

View File

@ -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;
}
// Conflicting nullability.
S.Diag(nullabilityLoc, diag::err_nullability_conflicting)
<< DiagNullabilityKind(nullability, isContextSensitive)
<< DiagNullabilityKind(*existingNullability, false);
return true;
if (!OverrideExisting) {
// Conflicting nullability.
S.Diag(NullabilityLoc, diag::err_nullability_conflicting)
<< DiagNullabilityKind(Nullability, IsContextSensitive)
<< DiagNullabilityKind(*ExistingNullability, false);
return true;
}
// Rebuild the attributed type, dropping the existing nullability.
QT = rebuildAttributedTypeWithoutNullability(S.Context, QT);
}
desugared = attributed->getModifiedType();
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,11 +8989,8 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type,
bool allowOnArrayType =
state.getDeclarator().isPrototypeContext() &&
!hasOuterPointerLikeChunk(state.getDeclarator(), endIndex);
if (checkNullabilityTypeSpecifier(
state,
type,
attr,
allowOnArrayType)) {
if (CheckNullabilityTypeSpecifier(state, type, attr,
allowOnArrayType)) {
attr.setInvalid();
}

View File

@ -3095,6 +3095,8 @@ public:
Expr *readExpr() { return Reader.readExpr(); }
Attr *readAttr() { return Reader.readAttr(); }
std::string readString() {
return Reader.readString();
}

View File

@ -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,
};

View File

@ -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")