Skip to content

Commit b90e652

Browse files
committed
[BoundsSafety] Parse external bounds attributes in ObjC methods
This parses counted_by et. al. for parameters and return types in Objective-C method signatures. No semantic checking is done to ensure that protocol, interface and implementation are aligned. rdar://145190177
1 parent 30c69c2 commit b90e652

File tree

4 files changed

+298
-72
lines changed

4 files changed

+298
-72
lines changed

clang/include/clang/Parse/Parser.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1382,7 +1382,10 @@ class Parser : public CodeCompletionHandler {
13821382

13831383
void ParseLexedAttributes() override;
13841384

1385-
void addDecl(Decl *D) { Decls.push_back(D); }
1385+
void addDecl(Decl *D) {
1386+
assert(D && "cannot add null decl!");
1387+
Decls.push_back(D);
1388+
}
13861389
};
13871390

13881391
/// Contains the lexed tokens of a pragma with arguments that
@@ -1824,8 +1827,10 @@ class Parser : public CodeCompletionHandler {
18241827

18251828
bool isTokIdentifier_in() const;
18261829

1830+
// TO_UPSTREAM(BoundsSafety) Added LateParsedAttrs
18271831
ParsedType ParseObjCTypeName(ObjCDeclSpec &DS, DeclaratorContext Ctx,
1828-
ParsedAttributes *ParamAttrs);
1832+
ParsedAttributes *ParamAttrs,
1833+
LateParsedAttrList *LateParsedAttrs = nullptr);
18291834
Decl *ParseObjCMethodPrototype(
18301835
tok::ObjCKeywordKind MethodImplKind = tok::objc_not_keyword,
18311836
bool MethodDefinition = true);

clang/lib/Parse/ParseObjc.cpp

Lines changed: 70 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1298,9 +1298,11 @@ static void takeDeclAttributes(ParsedAttributes &attrs,
12981298
/// '(' objc-type-qualifiers[opt] type-name ')'
12991299
/// '(' objc-type-qualifiers[opt] ')'
13001300
///
1301+
/// TO_UPSTREAM(BoundsSafety) Added LateParsedAttrs
13011302
ParsedType Parser::ParseObjCTypeName(ObjCDeclSpec &DS,
13021303
DeclaratorContext context,
1303-
ParsedAttributes *paramAttrs) {
1304+
ParsedAttributes *paramAttrs,
1305+
LateParsedAttrList *LateParsedAttrs) {
13041306
assert(context == DeclaratorContext::ObjCParameter ||
13051307
context == DeclaratorContext::ObjCResult);
13061308
assert((paramAttrs != nullptr) ==
@@ -1325,9 +1327,10 @@ ParsedType Parser::ParseObjCTypeName(ObjCDeclSpec &DS,
13251327
DeclSpecContext dsContext = DeclSpecContext::DSC_normal;
13261328
if (context == DeclaratorContext::ObjCResult)
13271329
dsContext = DeclSpecContext::DSC_objc_method_result;
1328-
ParseSpecifierQualifierList(declSpec, AS_none, dsContext);
1330+
ParseSpecifierQualifierList(declSpec, AS_none, dsContext, LateParsedAttrs);
13291331
Declarator declarator(declSpec, ParsedAttributesView::none(), context);
13301332
ParseDeclarator(declarator);
1333+
DistributeCLateParsedAttrs(declarator, nullptr, LateParsedAttrs);
13311334

13321335
// If that's not invalid, extract a type.
13331336
if (!declarator.isInvalidType()) {
@@ -1406,17 +1409,25 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc,
14061409
return nullptr;
14071410
}
14081411

1412+
/* TO_UPSTREAM(BoundsSafety) ON */
1413+
LateParsedAttrList LateParsedAttrs(/*PSoon=*/true,
1414+
/*LateAttrParseExperimentalExtOnly=*/true);
1415+
LateParsedAttrList LateParsedReturnAttrs(
1416+
/*PSoon=*/false,
1417+
/*LateAttrParseExperimentalExtOnly=*/true);
1418+
/* TO_UPSTREAM(BoundsSafety) OFF */
1419+
14091420
// Parse the return type if present.
14101421
ParsedType ReturnType;
14111422
ObjCDeclSpec DSRet;
14121423
if (Tok.is(tok::l_paren))
1413-
ReturnType =
1414-
ParseObjCTypeName(DSRet, DeclaratorContext::ObjCResult, nullptr);
1424+
ReturnType = ParseObjCTypeName(DSRet, DeclaratorContext::ObjCResult,
1425+
nullptr, &LateParsedReturnAttrs);
14151426

14161427
// If attributes exist before the method, parse them.
14171428
ParsedAttributes methodAttrs(AttrFactory);
14181429
MaybeParseAttributes(PAKM_CXX11 | (getLangOpts().ObjC ? PAKM_GNU : 0),
1419-
methodAttrs);
1430+
methodAttrs, &LateParsedReturnAttrs);
14201431

14211432
if (Tok.is(tok::code_completion)) {
14221433
cutOffParsing();
@@ -1450,33 +1461,51 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc,
14501461
selLoc, Sel, nullptr, CParamInfo.data(), CParamInfo.size(), methodAttrs,
14511462
MethodImplKind, false, MethodDefinition);
14521463
PD.complete(Result);
1464+
/* TO_UPSTREAM(BoundsSafety) ON */
1465+
if (Result) {
1466+
for (auto *LateAttr : LateParsedReturnAttrs) {
1467+
// there are no parameters with late attrs to parse
1468+
assert(LateAttr->Decls.empty());
1469+
LateAttr->addDecl(Result);
1470+
ParseLexedCAttribute(*LateAttr, true);
1471+
}
1472+
}
1473+
/* TO_UPSTREAM(BoundsSafety) OFF */
14531474
return Result;
14541475
}
14551476

14561477
SmallVector<const IdentifierInfo *, 12> KeyIdents;
14571478
SmallVector<SourceLocation, 12> KeyLocs;
14581479
SmallVector<SemaObjC::ObjCArgInfo, 12> ArgInfos;
1480+
/* TO_UPSTREAM(BoundsSafety) ON */
1481+
SmallVector<LateParsedAttrList, 12> LateParamAttrs;
1482+
/* TO_UPSTREAM(BoundsSafety) OFF */
14591483
ParseScope PrototypeScope(this, Scope::FunctionPrototypeScope |
14601484
Scope::FunctionDeclarationScope | Scope::DeclScope);
14611485

14621486
AttributePool allParamAttrs(AttrFactory);
14631487
while (true) {
14641488
ParsedAttributes paramAttrs(AttrFactory);
14651489
SemaObjC::ObjCArgInfo ArgInfo;
1490+
/* TO_UPSTREAM(BoundsSafety) ON */
1491+
LateParsedAttrList LateAttrs(/*PSoon*/ false,
1492+
/*LateAttrParseExperimentalExtOnly*/ true);
1493+
/* TO_UPSTREAM(BoundsSafety) OFF */
14661494

14671495
// Each iteration parses a single keyword argument.
14681496
if (ExpectAndConsume(tok::colon))
14691497
break;
14701498

14711499
ArgInfo.Type = nullptr;
14721500
if (Tok.is(tok::l_paren)) // Parse the argument type if present.
1473-
ArgInfo.Type = ParseObjCTypeName(
1474-
ArgInfo.DeclSpec, DeclaratorContext::ObjCParameter, &paramAttrs);
1501+
ArgInfo.Type =
1502+
ParseObjCTypeName(ArgInfo.DeclSpec, DeclaratorContext::ObjCParameter,
1503+
&paramAttrs, &LateAttrs);
14751504

14761505
// If attributes exist before the argument name, parse them.
14771506
// Regardless, collect all the attributes we've parsed so far.
14781507
MaybeParseAttributes(PAKM_CXX11 | (getLangOpts().ObjC ? PAKM_GNU : 0),
1479-
paramAttrs);
1508+
paramAttrs, &LateAttrs);
14801509
ArgInfo.ArgAttrs = paramAttrs;
14811510

14821511
// Code completion for the next piece of the selector.
@@ -1497,6 +1526,7 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc,
14971526
ConsumeToken(); // Eat the identifier.
14981527

14991528
ArgInfos.push_back(ArgInfo);
1529+
LateParamAttrs.push_back(LateAttrs); // TO_UPSTREAM(BoundsSafety)
15001530
KeyIdents.push_back(SelIdent);
15011531
KeyLocs.push_back(selLoc);
15021532

@@ -1543,13 +1573,23 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc,
15431573
}
15441574
DeclSpec DS(AttrFactory);
15451575
ParsedTemplateInfo TemplateInfo;
1546-
ParseDeclarationSpecifiers(DS, TemplateInfo);
1576+
ParseDeclarationSpecifiers(
1577+
DS, TemplateInfo,
1578+
/* AccessSpecifier AS */ AS_none,
1579+
/* DeclSpecContext DSC */ DeclSpecContext::DSC_normal,
1580+
&LateParsedAttrs);
15471581
// Parse the declarator.
15481582
Declarator ParmDecl(DS, ParsedAttributesView::none(),
15491583
DeclaratorContext::Prototype);
15501584
ParseDeclarator(ParmDecl);
15511585
const IdentifierInfo *ParmII = ParmDecl.getIdentifier();
15521586
Decl *Param = Actions.ActOnParamDeclarator(getCurScope(), ParmDecl);
1587+
/* TO_UPSTREAM(BoundsSafety) ON */
1588+
// This will add Param to any late attrs that do not already have an
1589+
// assigned decl, so it's important that other late attrs are not mixed
1590+
// in to LateParsedAttrs without a decl at this point
1591+
DistributeCLateParsedAttrs(ParmDecl, Param, &LateParsedAttrs);
1592+
/* TO_UPSTREAM(BoundsSafety) OFF */
15531593
CParamInfo.push_back(DeclaratorChunk::ParamInfo(ParmII,
15541594
ParmDecl.getIdentifierLoc(),
15551595
Param,
@@ -1561,9 +1601,18 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc,
15611601
// instance, if a method declares a parameter called "id", that parameter must
15621602
// not shadow the "id" type.)
15631603
SmallVector<ParmVarDecl *, 12> ObjCParamInfo;
1564-
for (auto &ArgInfo : ArgInfos) {
1604+
for (const auto &[ArgInfo, LateAttrs] : llvm::zip(ArgInfos, LateParamAttrs)) {
15651605
ParmVarDecl *Param = Actions.ObjC().ActOnMethodParmDeclaration(
15661606
getCurScope(), ArgInfo, ObjCParamInfo.size(), MethodDefinition);
1607+
/* TO_UPSTREAM(BoundsSafety) ON */
1608+
if (Param) {
1609+
for (auto *LateAttr : LateAttrs) {
1610+
assert(LateAttr->Decls.empty());
1611+
LateAttr->addDecl(Param);
1612+
}
1613+
LateParsedAttrs.append(LateAttrs);
1614+
}
1615+
/* TO_UPSTREAM(BoundsSafety) OFF */
15671616
ObjCParamInfo.push_back(Param);
15681617
}
15691618

@@ -1581,6 +1630,17 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc,
15811630
getCurScope(), mLoc, Tok.getLocation(), mType, DSRet, ReturnType, KeyLocs,
15821631
Sel, ObjCParamInfo.data(), CParamInfo.data(), CParamInfo.size(),
15831632
methodAttrs, MethodImplKind, isVariadic, MethodDefinition);
1633+
/* TO_UPSTREAM(BoundsSafety) ON */
1634+
if (Result) {
1635+
for (auto *LateAttr : LateParsedReturnAttrs) {
1636+
assert(LateAttr->Decls.empty());
1637+
LateAttr->addDecl(Result);
1638+
}
1639+
LateParsedAttrs.append(LateParsedReturnAttrs);
1640+
ParseLexedCAttributeList(LateParsedAttrs,
1641+
/*we already have parameters in scope*/ false);
1642+
}
1643+
/* TO_UPSTREAM(BoundsSafety) OFF */
15841644

15851645
PD.complete(Result);
15861646
return Result;

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 67 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -6657,6 +6657,7 @@ class DynamicBoundsAttrInfo {
66576657
TypedefNameDecl *TND;
66586658
ValueDecl *VD;
66596659
VarDecl *Var;
6660+
ObjCMethodDecl *ObjCMethod;
66606661
QualType DeclTy;
66616662
QualType Ty;
66626663
unsigned EffectiveLevel;
@@ -6668,7 +6669,13 @@ class DynamicBoundsAttrInfo {
66686669
TND = dyn_cast<TypedefNameDecl>(D);
66696670
VD = dyn_cast<ValueDecl>(D);
66706671
Var = dyn_cast<VarDecl>(D);
6671-
DeclTy = TND ? TND->getUnderlyingType() : VD->getType();
6672+
ObjCMethod = dyn_cast<ObjCMethodDecl>(D);
6673+
if (TND)
6674+
DeclTy = TND->getUnderlyingType();
6675+
else if (ObjCMethod)
6676+
DeclTy = ObjCMethod->getReturnType();
6677+
else
6678+
DeclTy = VD->getType();
66726679
IsFPtr = false;
66736680
EffectiveLevel = Level;
66746681
Ty = DeclTy;
@@ -6773,72 +6780,69 @@ void Sema::applyPtrCountedByEndedByAttr(Decl *D, unsigned Level,
67736780
}
67746781
}
67756782

6776-
if (Info.VD) {
6777-
const auto *FD = dyn_cast<FieldDecl>(Info.VD);
6778-
if (FD && FD->getParent()->isUnion()) {
6779-
Diag(Loc, diag::err_invalid_decl_kind_bounds_safety_union_count)
6780-
<< DiagName;
6781-
return;
6782-
}
6783+
const auto *FD = dyn_cast<FieldDecl>(D);
6784+
if (FD && FD->getParent()->isUnion()) {
6785+
Diag(Loc, diag::err_invalid_decl_kind_bounds_safety_union_count)
6786+
<< DiagName;
6787+
return;
6788+
}
67836789

6784-
if (Info.EffectiveLevel != 0 &&
6785-
(!isa<ParmVarDecl>(Info.VD) || Info.DeclTy->isBoundsAttributedType())) {
6786-
Diag(Loc, diag::err_bounds_safety_nested_dynamic_bound) << DiagName;
6787-
return;
6788-
}
6790+
if (Info.EffectiveLevel != 0 &&
6791+
(!isa<ParmVarDecl>(D) || Info.DeclTy->isBoundsAttributedType())) {
6792+
Diag(Loc, diag::err_bounds_safety_nested_dynamic_bound) << DiagName;
6793+
return;
6794+
}
67896795

6790-
// Clang causes array parameters to decay to pointers so quickly that
6791-
// attributes aren't even parsed yet. This causes arrays with both an
6792-
// explicit size and a count attribute to go to the CountAttributedType
6793-
// case of ConstructCountAttributedType, which complains that the type
6794-
// has two count attributes. See if we can produce a better diagnostic here
6795-
// instead.
6796-
if (const auto *PVD = dyn_cast_or_null<ParmVarDecl>(Info.Var)) {
6797-
QualType TSITy = PVD->getTypeSourceInfo()->getType();
6798-
if (IsEndedBy) {
6799-
if (Level == 0 && TSITy->isArrayType()) {
6800-
Diag(Loc, diag::err_attribute_pointers_only) << DiagName << 0;
6801-
return;
6802-
}
6803-
} else {
6804-
const auto *ATy = Context.getAsArrayType(TSITy);
6805-
if (Level == 0 && ATy && !ATy->isIncompleteArrayType() &&
6806-
!TSITy->hasAttr(attr::ArrayDecayDiscardsCountInParameters)) {
6807-
Diag(Loc, diag::err_bounds_safety_complete_array_with_count);
6808-
return;
6809-
}
6796+
// Clang causes array parameters to decay to pointers so quickly that
6797+
// attributes aren't even parsed yet. This causes arrays with both an
6798+
// explicit size and a count attribute to go to the CountAttributedType
6799+
// case of ConstructCountAttributedType, which complains that the type
6800+
// has two count attributes. See if we can produce a better diagnostic here
6801+
// instead.
6802+
if (const auto *PVD = dyn_cast<ParmVarDecl>(D)) {
6803+
QualType TSITy = PVD->getTypeSourceInfo()->getType();
6804+
if (IsEndedBy) {
6805+
if (Level == 0 && TSITy->isArrayType()) {
6806+
Diag(Loc, diag::err_attribute_pointers_only) << DiagName << 0;
6807+
return;
6808+
}
6809+
} else {
6810+
const auto *ATy = Context.getAsArrayType(TSITy);
6811+
if (Level == 0 && ATy && !ATy->isIncompleteArrayType() &&
6812+
!TSITy->hasAttr(attr::ArrayDecayDiscardsCountInParameters)) {
6813+
Diag(Loc, diag::err_bounds_safety_complete_array_with_count);
6814+
return;
68106815
}
68116816
}
6817+
}
68126818

6813-
if (Info.Ty->isArrayType() && OrNull &&
6814-
(FD || Info.EffectiveLevel > 0 ||
6815-
(Info.Var && Info.Var->hasExternalStorage()))) {
6816-
auto ErrDiag = Diag(Loc, diag::err_bounds_safety_nullable_fam);
6817-
// Pointers to dynamic count types are only allowed for parameters, so any
6818-
// FieldDecl containing a dynamic count type is a FAM. I.e. a struct field
6819-
// with type 'int(*)[__counted_by(...)]' is not valid.
6820-
ErrDiag << CountInBytes << /*is FAM?*/ !!FD << DiagName;
6821-
assert(!FD || Info.EffectiveLevel == 0);
6819+
if (Info.Ty->isArrayType() && OrNull &&
6820+
(FD || Info.EffectiveLevel > 0 ||
6821+
(Info.Var && Info.Var->hasExternalStorage()))) {
6822+
auto ErrDiag = Diag(Loc, diag::err_bounds_safety_nullable_fam);
6823+
// Pointers to dynamic count types are only allowed for parameters, so any
6824+
// FieldDecl containing a dynamic count type is a FAM. I.e. a struct field
6825+
// with type 'int(*)[__counted_by(...)]' is not valid.
6826+
ErrDiag << CountInBytes << /*is FAM?*/ !!FD << DiagName;
6827+
assert(!FD || Info.EffectiveLevel == 0);
68226828

6823-
SourceLocation FixItLoc = getSourceManager().getExpansionLoc(Loc);
6824-
SourceLocation EndLoc =
6825-
Lexer::getLocForEndOfToken(FixItLoc, /* Don't include '(' */ -1,
6826-
getSourceManager(), getLangOpts());
6827-
std::string Attribute = CountInBytes ? "__sized_by" : "__counted_by";
6828-
ErrDiag << FixItHint::CreateReplacement({FixItLoc, EndLoc}, Attribute);
6829+
SourceLocation FixItLoc = getSourceManager().getExpansionLoc(Loc);
6830+
SourceLocation EndLoc =
6831+
Lexer::getLocForEndOfToken(FixItLoc, /* Don't include '(' */ -1,
6832+
getSourceManager(), getLangOpts());
6833+
std::string Attribute = CountInBytes ? "__sized_by" : "__counted_by";
6834+
ErrDiag << FixItHint::CreateReplacement({FixItLoc, EndLoc}, Attribute);
68296835

6830-
return;
6831-
}
6836+
return;
6837+
}
68326838

6833-
if (Info.Ty->isArrayType() && Info.EffectiveLevel > 0) {
6834-
auto ErrDiag =
6835-
Diag(
6836-
Loc,
6837-
diag::
6838-
err_bounds_safety_unsupported_address_of_incomplete_array_type)
6839-
<< Info.Ty;
6840-
// apply attribute anyways to avoid too misleading follow-up diagnostics
6841-
}
6839+
if (Info.Ty->isArrayType() && Info.EffectiveLevel > 0) {
6840+
auto ErrDiag =
6841+
Diag(Loc,
6842+
diag::
6843+
err_bounds_safety_unsupported_address_of_incomplete_array_type)
6844+
<< Info.Ty;
6845+
// apply attribute anyways to avoid too misleading follow-up diagnostics
68426846
}
68436847

68446848
QualType NewDeclTy{};
@@ -6860,8 +6864,9 @@ void Sema::applyPtrCountedByEndedByAttr(Decl *D, unsigned Level,
68606864
// Make started_by() pointers if VD is a field or variable. We don't want to
68616865
// create started_by(X) pointers where X is a function etc.
68626866
std::optional<TypeCoupledDeclRefInfo> StartPtrInfo;
6863-
if (Info.VD && (isa<FieldDecl>(Info.VD) || isa<VarDecl>(Info.VD))) {
6867+
if (isa<FieldDecl, VarDecl>(D)) {
68646868
assert(Level <= 1);
6869+
assert(Info.VD);
68656870
StartPtrInfo = TypeCoupledDeclRefInfo(Info.VD, /*Deref=*/Level != 0);
68666871
}
68676872

@@ -6917,6 +6922,8 @@ void Sema::applyPtrCountedByEndedByAttr(Decl *D, unsigned Level,
69176922
Info.TND->getTypeSourceInfo()->getTypeLoc().getBeginLoc();
69186923
TypeSourceInfo *TSI = Context.getTrivialTypeSourceInfo(NewDeclTy, Loc);
69196924
Info.TND->setTypeSourceInfo(TSI);
6925+
} else if (Info.ObjCMethod) {
6926+
Info.ObjCMethod->setReturnType(NewDeclTy);
69206927
} else {
69216928
Info.VD->setType(NewDeclTy);
69226929
// Reconstruct implicit cast for initializer after variable type change.

0 commit comments

Comments
 (0)