Skip to content
This repository has been archived by the owner on Jan 20, 2024. It is now read-only.

Commit

Permalink
[APINotes] Upstream dependencies of Sema logic to apply API Notes to …
Browse files Browse the repository at this point in the history
…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.
  • Loading branch information
egorzhdan authored Jan 17, 2024
1 parent 9791e54 commit 77d21e7
Show file tree
Hide file tree
Showing 13 changed files with 348 additions and 91 deletions.
46 changes: 45 additions & 1 deletion clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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];
}

Expand Down Expand Up @@ -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">];
Expand All @@ -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];
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/DiagnosticParseKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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++">,
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/Lex/Lexer.h
Original file line number Diff line number Diff line change
Expand Up @@ -198,11 +198,11 @@ class Lexer : public PreprocessorLexer {
/// 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);

Expand Down
12 changes: 12 additions & 0 deletions clang/include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -3656,6 +3656,18 @@ class Parser : public CodeCompletionHandler {
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);
Expand Down
30 changes: 30 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -955,6 +955,10 @@ class Sema final {
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 {
Expand Down Expand Up @@ -3017,6 +3021,9 @@ class Sema final {
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,
Expand Down Expand Up @@ -4816,6 +4823,29 @@ class Sema final {
/// 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,
Expand Down
65 changes: 65 additions & 0 deletions clang/lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/Parse/Parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
31 changes: 31 additions & 0 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
34 changes: 20 additions & 14 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();

auto *DRE = dyn_cast<DeclRefExpr>(AL.getArgAsExpr(0));
if (!DRE) {
S.Diag(Loc, diag::err_nserrordomain_invalid_decl) << 0;
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;
}

auto *VD = dyn_cast<VarDecl>(DRE->getDecl());
if (!VD) {
S.Diag(Loc, diag::err_nserrordomain_invalid_decl) << 1 << DRE->getDecl();
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();

S.Diag(Loc, diag::err_nserrordomain_invalid_decl) << 0;
return;
}

if (!isNSStringType(VD->getType(), S.Context) &&
!isCFStringType(VD->getType(), S.Context)) {
S.Diag(Loc, diag::err_nserrordomain_wrong_type) << VD;
// 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;
}

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) {
Expand Down
Loading

0 comments on commit 77d21e7

Please sign in to comment.