From bb5b11c24a403181d13cb4ee93b71bcd0fb52881 Mon Sep 17 00:00:00 2001 From: "Henrik G. Olsson" Date: Wed, 19 Feb 2025 20:41:48 -0800 Subject: [PATCH 1/2] [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 --- clang/include/clang/Parse/Parser.h | 9 +- clang/lib/Parse/ParseObjc.cpp | 80 +++++++++++++-- clang/lib/Sema/SemaDeclAttr.cpp | 127 +++++++++++++----------- clang/test/BoundsSafety/AST/objc.m | 154 +++++++++++++++++++++++++++++ 4 files changed, 298 insertions(+), 72 deletions(-) create mode 100644 clang/test/BoundsSafety/AST/objc.m diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index fe4501bd31b1b..d6f9e3712c239 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -1382,7 +1382,10 @@ class Parser : public CodeCompletionHandler { void ParseLexedAttributes() override; - void addDecl(Decl *D) { Decls.push_back(D); } + void addDecl(Decl *D) { + assert(D && "cannot add null decl!"); + Decls.push_back(D); + } }; /// Contains the lexed tokens of a pragma with arguments that @@ -1821,8 +1824,10 @@ class Parser : public CodeCompletionHandler { bool isTokIdentifier_in() const; + // TO_UPSTREAM(BoundsSafety) Added LateParsedAttrs ParsedType ParseObjCTypeName(ObjCDeclSpec &DS, DeclaratorContext Ctx, - ParsedAttributes *ParamAttrs); + ParsedAttributes *ParamAttrs, + LateParsedAttrList *LateParsedAttrs = nullptr); Decl *ParseObjCMethodPrototype( tok::ObjCKeywordKind MethodImplKind = tok::objc_not_keyword, bool MethodDefinition = true); diff --git a/clang/lib/Parse/ParseObjc.cpp b/clang/lib/Parse/ParseObjc.cpp index 86210ab7bac2b..7d469abe42bce 100644 --- a/clang/lib/Parse/ParseObjc.cpp +++ b/clang/lib/Parse/ParseObjc.cpp @@ -1298,9 +1298,11 @@ static void takeDeclAttributes(ParsedAttributes &attrs, /// '(' objc-type-qualifiers[opt] type-name ')' /// '(' objc-type-qualifiers[opt] ')' /// +/// TO_UPSTREAM(BoundsSafety) Added LateParsedAttrs ParsedType Parser::ParseObjCTypeName(ObjCDeclSpec &DS, DeclaratorContext context, - ParsedAttributes *paramAttrs) { + ParsedAttributes *paramAttrs, + LateParsedAttrList *LateParsedAttrs) { assert(context == DeclaratorContext::ObjCParameter || context == DeclaratorContext::ObjCResult); assert((paramAttrs != nullptr) == @@ -1325,9 +1327,10 @@ ParsedType Parser::ParseObjCTypeName(ObjCDeclSpec &DS, DeclSpecContext dsContext = DeclSpecContext::DSC_normal; if (context == DeclaratorContext::ObjCResult) dsContext = DeclSpecContext::DSC_objc_method_result; - ParseSpecifierQualifierList(declSpec, AS_none, dsContext); + ParseSpecifierQualifierList(declSpec, AS_none, dsContext, LateParsedAttrs); Declarator declarator(declSpec, ParsedAttributesView::none(), context); ParseDeclarator(declarator); + DistributeCLateParsedAttrs(declarator, nullptr, LateParsedAttrs); // If that's not invalid, extract a type. if (!declarator.isInvalidType()) { @@ -1406,17 +1409,25 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc, return nullptr; } + /* TO_UPSTREAM(BoundsSafety) ON */ + LateParsedAttrList LateParsedAttrs(/*PSoon=*/true, + /*LateAttrParseExperimentalExtOnly=*/true); + LateParsedAttrList LateParsedReturnAttrs( + /*PSoon=*/false, + /*LateAttrParseExperimentalExtOnly=*/true); + /* TO_UPSTREAM(BoundsSafety) OFF */ + // Parse the return type if present. ParsedType ReturnType; ObjCDeclSpec DSRet; if (Tok.is(tok::l_paren)) - ReturnType = - ParseObjCTypeName(DSRet, DeclaratorContext::ObjCResult, nullptr); + ReturnType = ParseObjCTypeName(DSRet, DeclaratorContext::ObjCResult, + nullptr, &LateParsedReturnAttrs); // If attributes exist before the method, parse them. ParsedAttributes methodAttrs(AttrFactory); MaybeParseAttributes(PAKM_CXX11 | (getLangOpts().ObjC ? PAKM_GNU : 0), - methodAttrs); + methodAttrs, &LateParsedReturnAttrs); if (Tok.is(tok::code_completion)) { cutOffParsing(); @@ -1450,12 +1461,25 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc, selLoc, Sel, nullptr, CParamInfo.data(), CParamInfo.size(), methodAttrs, MethodImplKind, false, MethodDefinition); PD.complete(Result); + /* TO_UPSTREAM(BoundsSafety) ON */ + if (Result) { + for (auto *LateAttr : LateParsedReturnAttrs) { + // there are no parameters with late attrs to parse + assert(LateAttr->Decls.empty()); + LateAttr->addDecl(Result); + ParseLexedCAttribute(*LateAttr, true); + } + } + /* TO_UPSTREAM(BoundsSafety) OFF */ return Result; } SmallVector KeyIdents; SmallVector KeyLocs; SmallVector ArgInfos; + /* TO_UPSTREAM(BoundsSafety) ON */ + SmallVector LateParamAttrs; + /* TO_UPSTREAM(BoundsSafety) OFF */ ParseScope PrototypeScope(this, Scope::FunctionPrototypeScope | Scope::FunctionDeclarationScope | Scope::DeclScope); @@ -1463,6 +1487,10 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc, while (true) { ParsedAttributes paramAttrs(AttrFactory); SemaObjC::ObjCArgInfo ArgInfo; + /* TO_UPSTREAM(BoundsSafety) ON */ + LateParsedAttrList LateAttrs(/*PSoon*/ false, + /*LateAttrParseExperimentalExtOnly*/ true); + /* TO_UPSTREAM(BoundsSafety) OFF */ // Each iteration parses a single keyword argument. if (ExpectAndConsume(tok::colon)) @@ -1470,13 +1498,14 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc, ArgInfo.Type = nullptr; if (Tok.is(tok::l_paren)) // Parse the argument type if present. - ArgInfo.Type = ParseObjCTypeName( - ArgInfo.DeclSpec, DeclaratorContext::ObjCParameter, ¶mAttrs); + ArgInfo.Type = + ParseObjCTypeName(ArgInfo.DeclSpec, DeclaratorContext::ObjCParameter, + ¶mAttrs, &LateAttrs); // If attributes exist before the argument name, parse them. // Regardless, collect all the attributes we've parsed so far. MaybeParseAttributes(PAKM_CXX11 | (getLangOpts().ObjC ? PAKM_GNU : 0), - paramAttrs); + paramAttrs, &LateAttrs); ArgInfo.ArgAttrs = paramAttrs; // Code completion for the next piece of the selector. @@ -1497,6 +1526,7 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc, ConsumeToken(); // Eat the identifier. ArgInfos.push_back(ArgInfo); + LateParamAttrs.push_back(LateAttrs); // TO_UPSTREAM(BoundsSafety) KeyIdents.push_back(SelIdent); KeyLocs.push_back(selLoc); @@ -1543,13 +1573,23 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc, } DeclSpec DS(AttrFactory); ParsedTemplateInfo TemplateInfo; - ParseDeclarationSpecifiers(DS, TemplateInfo); + ParseDeclarationSpecifiers( + DS, TemplateInfo, + /* AccessSpecifier AS */ AS_none, + /* DeclSpecContext DSC */ DeclSpecContext::DSC_normal, + &LateParsedAttrs); // Parse the declarator. Declarator ParmDecl(DS, ParsedAttributesView::none(), DeclaratorContext::Prototype); ParseDeclarator(ParmDecl); const IdentifierInfo *ParmII = ParmDecl.getIdentifier(); Decl *Param = Actions.ActOnParamDeclarator(getCurScope(), ParmDecl); + /* TO_UPSTREAM(BoundsSafety) ON */ + // This will add Param to any late attrs that do not already have an + // assigned decl, so it's important that other late attrs are not mixed + // in to LateParsedAttrs without a decl at this point + DistributeCLateParsedAttrs(ParmDecl, Param, &LateParsedAttrs); + /* TO_UPSTREAM(BoundsSafety) OFF */ CParamInfo.push_back(DeclaratorChunk::ParamInfo(ParmII, ParmDecl.getIdentifierLoc(), Param, @@ -1561,9 +1601,18 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc, // instance, if a method declares a parameter called "id", that parameter must // not shadow the "id" type.) SmallVector ObjCParamInfo; - for (auto &ArgInfo : ArgInfos) { + for (const auto &[ArgInfo, LateAttrs] : llvm::zip(ArgInfos, LateParamAttrs)) { ParmVarDecl *Param = Actions.ObjC().ActOnMethodParmDeclaration( getCurScope(), ArgInfo, ObjCParamInfo.size(), MethodDefinition); + /* TO_UPSTREAM(BoundsSafety) ON */ + if (Param) { + for (auto *LateAttr : LateAttrs) { + assert(LateAttr->Decls.empty()); + LateAttr->addDecl(Param); + } + LateParsedAttrs.append(LateAttrs); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ ObjCParamInfo.push_back(Param); } @@ -1581,6 +1630,17 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc, getCurScope(), mLoc, Tok.getLocation(), mType, DSRet, ReturnType, KeyLocs, Sel, ObjCParamInfo.data(), CParamInfo.data(), CParamInfo.size(), methodAttrs, MethodImplKind, isVariadic, MethodDefinition); + /* TO_UPSTREAM(BoundsSafety) ON */ + if (Result) { + for (auto *LateAttr : LateParsedReturnAttrs) { + assert(LateAttr->Decls.empty()); + LateAttr->addDecl(Result); + } + LateParsedAttrs.append(LateParsedReturnAttrs); + ParseLexedCAttributeList(LateParsedAttrs, + /*we already have parameters in scope*/ false); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ PD.complete(Result); return Result; diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 0e6a9bb1779c3..3c9edad09b8f1 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -6451,6 +6451,7 @@ class DynamicBoundsAttrInfo { TypedefNameDecl *TND; ValueDecl *VD; VarDecl *Var; + ObjCMethodDecl *ObjCMethod; QualType DeclTy; QualType Ty; unsigned EffectiveLevel; @@ -6462,7 +6463,13 @@ class DynamicBoundsAttrInfo { TND = dyn_cast(D); VD = dyn_cast(D); Var = dyn_cast(D); - DeclTy = TND ? TND->getUnderlyingType() : VD->getType(); + ObjCMethod = dyn_cast(D); + if (TND) + DeclTy = TND->getUnderlyingType(); + else if (ObjCMethod) + DeclTy = ObjCMethod->getReturnType(); + else + DeclTy = VD->getType(); IsFPtr = false; EffectiveLevel = Level; Ty = DeclTy; @@ -6567,72 +6574,69 @@ void Sema::applyPtrCountedByEndedByAttr(Decl *D, unsigned Level, } } - if (Info.VD) { - const auto *FD = dyn_cast(Info.VD); - if (FD && FD->getParent()->isUnion()) { - Diag(Loc, diag::err_invalid_decl_kind_bounds_safety_union_count) - << DiagName; - return; - } + const auto *FD = dyn_cast(D); + if (FD && FD->getParent()->isUnion()) { + Diag(Loc, diag::err_invalid_decl_kind_bounds_safety_union_count) + << DiagName; + return; + } - if (Info.EffectiveLevel != 0 && - (!isa(Info.VD) || Info.DeclTy->isBoundsAttributedType())) { - Diag(Loc, diag::err_bounds_safety_nested_dynamic_bound) << DiagName; - return; - } + if (Info.EffectiveLevel != 0 && + (!isa(D) || Info.DeclTy->isBoundsAttributedType())) { + Diag(Loc, diag::err_bounds_safety_nested_dynamic_bound) << DiagName; + return; + } - // Clang causes array parameters to decay to pointers so quickly that - // attributes aren't even parsed yet. This causes arrays with both an - // explicit size and a count attribute to go to the CountAttributedType - // case of ConstructCountAttributedType, which complains that the type - // has two count attributes. See if we can produce a better diagnostic here - // instead. - if (const auto *PVD = dyn_cast_or_null(Info.Var)) { - QualType TSITy = PVD->getTypeSourceInfo()->getType(); - if (IsEndedBy) { - if (Level == 0 && TSITy->isArrayType()) { - Diag(Loc, diag::err_attribute_pointers_only) << DiagName << 0; - return; - } - } else { - const auto *ATy = Context.getAsArrayType(TSITy); - if (Level == 0 && ATy && !ATy->isIncompleteArrayType() && - !TSITy->hasAttr(attr::ArrayDecayDiscardsCountInParameters)) { - Diag(Loc, diag::err_bounds_safety_complete_array_with_count); - return; - } + // Clang causes array parameters to decay to pointers so quickly that + // attributes aren't even parsed yet. This causes arrays with both an + // explicit size and a count attribute to go to the CountAttributedType + // case of ConstructCountAttributedType, which complains that the type + // has two count attributes. See if we can produce a better diagnostic here + // instead. + if (const auto *PVD = dyn_cast(D)) { + QualType TSITy = PVD->getTypeSourceInfo()->getType(); + if (IsEndedBy) { + if (Level == 0 && TSITy->isArrayType()) { + Diag(Loc, diag::err_attribute_pointers_only) << DiagName << 0; + return; + } + } else { + const auto *ATy = Context.getAsArrayType(TSITy); + if (Level == 0 && ATy && !ATy->isIncompleteArrayType() && + !TSITy->hasAttr(attr::ArrayDecayDiscardsCountInParameters)) { + Diag(Loc, diag::err_bounds_safety_complete_array_with_count); + return; } } + } - if (Info.Ty->isArrayType() && OrNull && - (FD || Info.EffectiveLevel > 0 || - (Info.Var && Info.Var->hasExternalStorage()))) { - auto ErrDiag = Diag(Loc, diag::err_bounds_safety_nullable_fam); - // Pointers to dynamic count types are only allowed for parameters, so any - // FieldDecl containing a dynamic count type is a FAM. I.e. a struct field - // with type 'int(*)[__counted_by(...)]' is not valid. - ErrDiag << CountInBytes << /*is FAM?*/ !!FD << DiagName; - assert(!FD || Info.EffectiveLevel == 0); + if (Info.Ty->isArrayType() && OrNull && + (FD || Info.EffectiveLevel > 0 || + (Info.Var && Info.Var->hasExternalStorage()))) { + auto ErrDiag = Diag(Loc, diag::err_bounds_safety_nullable_fam); + // Pointers to dynamic count types are only allowed for parameters, so any + // FieldDecl containing a dynamic count type is a FAM. I.e. a struct field + // with type 'int(*)[__counted_by(...)]' is not valid. + ErrDiag << CountInBytes << /*is FAM?*/ !!FD << DiagName; + assert(!FD || Info.EffectiveLevel == 0); - SourceLocation FixItLoc = getSourceManager().getExpansionLoc(Loc); - SourceLocation EndLoc = - Lexer::getLocForEndOfToken(FixItLoc, /* Don't include '(' */ -1, - getSourceManager(), getLangOpts()); - std::string Attribute = CountInBytes ? "__sized_by" : "__counted_by"; - ErrDiag << FixItHint::CreateReplacement({FixItLoc, EndLoc}, Attribute); + SourceLocation FixItLoc = getSourceManager().getExpansionLoc(Loc); + SourceLocation EndLoc = + Lexer::getLocForEndOfToken(FixItLoc, /* Don't include '(' */ -1, + getSourceManager(), getLangOpts()); + std::string Attribute = CountInBytes ? "__sized_by" : "__counted_by"; + ErrDiag << FixItHint::CreateReplacement({FixItLoc, EndLoc}, Attribute); - return; - } + return; + } - if (Info.Ty->isArrayType() && Info.EffectiveLevel > 0) { - auto ErrDiag = - Diag( - Loc, - diag:: - err_bounds_safety_unsupported_address_of_incomplete_array_type) - << Info.Ty; - // apply attribute anyways to avoid too misleading follow-up diagnostics - } + if (Info.Ty->isArrayType() && Info.EffectiveLevel > 0) { + auto ErrDiag = + Diag(Loc, + diag:: + err_bounds_safety_unsupported_address_of_incomplete_array_type) + << Info.Ty; + // apply attribute anyways to avoid too misleading follow-up diagnostics } QualType NewDeclTy{}; @@ -6654,8 +6658,9 @@ void Sema::applyPtrCountedByEndedByAttr(Decl *D, unsigned Level, // Make started_by() pointers if VD is a field or variable. We don't want to // create started_by(X) pointers where X is a function etc. std::optional StartPtrInfo; - if (Info.VD && (isa(Info.VD) || isa(Info.VD))) { + if (isa(D)) { assert(Level <= 1); + assert(Info.VD); StartPtrInfo = TypeCoupledDeclRefInfo(Info.VD, /*Deref=*/Level != 0); } @@ -6711,6 +6716,8 @@ void Sema::applyPtrCountedByEndedByAttr(Decl *D, unsigned Level, Info.TND->getTypeSourceInfo()->getTypeLoc().getBeginLoc(); TypeSourceInfo *TSI = Context.getTrivialTypeSourceInfo(NewDeclTy, Loc); Info.TND->setTypeSourceInfo(TSI); + } else if (Info.ObjCMethod) { + Info.ObjCMethod->setReturnType(NewDeclTy); } else { Info.VD->setType(NewDeclTy); // Reconstruct implicit cast for initializer after variable type change. diff --git a/clang/test/BoundsSafety/AST/objc.m b/clang/test/BoundsSafety/AST/objc.m new file mode 100644 index 0000000000000..03e91c2e825ab --- /dev/null +++ b/clang/test/BoundsSafety/AST/objc.m @@ -0,0 +1,154 @@ +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -Wno-deprecated-declarations -Wno-return-type -Wno-objc-root-class -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x objective-c -Wno-deprecated-declarations -Wno-return-type -Wno-objc-root-class -ast-dump %s 2>&1 | FileCheck %s + +#include + +@protocol CountedByProtocol + - (void)simpleParam:(int)len :(int * __counted_by(len))p; + - (void)reverseParam:(int * __counted_by(len))p :(int)len; + - (void)nestedParam:(int*)len :(int * __counted_by(*len))p; + + - (int * __counted_by(len))simpleRet:(int)len; + - (int * __counted_by(*len))nestedRet:(int*)len; + + - (void)cParam:(int)len, int * __counted_by(len) p; + - (void)reverseCParam:(int * __counted_by(len))p, int len; +@end + +// CHECK-LABEL: -ObjCProtocolDecl {{.*}} CountedByProtocol +// CHECK-NEXT: |-ObjCMethodDecl {{.*}} - simpleParam:: 'void' +// CHECK-NEXT: | |-ParmVarDecl {{.*}} len 'int' +// CHECK-NOT: IsDeref +// CHECK-NEXT: | | `-DependerDeclsAttr +// CHECK-NEXT: | `-ParmVarDecl {{.*}} p 'int *{{(__single)?}} __counted_by(len)':'int *{{(__single)?}}' +// CHECK-NEXT: |-ObjCMethodDecl {{.*}} - reverseParam:: 'void' +// CHECK-NEXT: | |-ParmVarDecl {{.*}} p 'int *{{(__single)?}} __counted_by(len)':'int *{{(__single)?}}' +// CHECK-NEXT: | `-ParmVarDecl {{.*}} len 'int' +// CHECK-NOT: IsDeref +// CHECK-NEXT: | `-DependerDeclsAttr +// CHECK-NEXT: |-ObjCMethodDecl {{.*}} - nestedParam:: 'void' +// CHECK-NEXT: | |-ParmVarDecl {{.*}} len 'int *' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.*}} IsDeref +// CHECK-NEXT: | `-ParmVarDecl {{.*}} p 'int *{{(__single)?}} __counted_by(*len)':'int *{{(__single)?}}' +// CHECK-NEXT: |-ObjCMethodDecl {{.*}} - simpleRet: 'int *{{(__single)?}} __counted_by(len)':'int *{{(__single)?}}' +// CHECK-NEXT: | `-ParmVarDecl {{.*}} len 'int' +// CHECK-NEXT: |-ObjCMethodDecl {{.*}} - nestedRet: 'int *{{(__single)?}} __counted_by(*len)':'int *{{(__single)?}}' +// CHECK-NEXT: | `-ParmVarDecl {{.*}} len 'int *' +// CHECK-NEXT: |-ObjCMethodDecl {{.*}} - cParam: 'void' +// CHECK-NEXT: | |-ParmVarDecl {{.*}} len 'int' +// CHECK-NOT: IsDeref +// CHECK-NEXT: | | `-DependerDeclsAttr +// CHECK-NEXT: | `-ParmVarDecl {{.*}} p 'int *{{(__single)?}} __counted_by(len)':'int *{{(__single)?}}' +// CHECK-NEXT: `-ObjCMethodDecl {{.*}} - reverseCParam: 'void' +// CHECK-NEXT: |-ParmVarDecl {{.*}} p 'int *{{(__single)?}} __counted_by(len)':'int *{{(__single)?}}' +// CHECK-NEXT: `-ParmVarDecl {{.*}} len 'int' +// CHECK-NOT: IsDeref +// CHECK-NEXT: `-DependerDeclsAttr + +@interface CountedByClass + - (void)simpleParam:(int)len :(int * __counted_by(len))p; + - (void)reverseParam:(int * __counted_by(len))p :(int)len; + - (void)nestedParam:(int*)len :(int * __counted_by(*len))p; + + - (int * __counted_by(len))simpleRet:(int)len; + - (int * __counted_by(*len))nestedRet:(int*)len; + + - (void)cParam:(int)len, int * __counted_by(len) p; + - (void)reverseCParam:(int * __counted_by(len))p, int len; +@end + +// CHECK-LABEL: -ObjCInterfaceDecl {{.*}} CountedByClass +// CHECK-NEXT: |-ObjCImplementation {{.*}} 'CountedByClass' +// CHECK-NEXT: |-ObjCProtocol {{.*}} 'CountedByProtocol' +// CHECK-NEXT: |-ObjCMethodDecl {{.*}} - simpleParam:: 'void' +// CHECK-NEXT: | |-ParmVarDecl {{.*}} len 'int' +// CHECK-NOT: IsDeref +// CHECK-NEXT: | | `-DependerDeclsAttr +// CHECK-NEXT: | `-ParmVarDecl {{.*}} p 'int *{{(__single)?}} __counted_by(len)':'int *{{(__single)?}}' +// CHECK-NEXT: |-ObjCMethodDecl {{.*}} - reverseParam:: 'void' +// CHECK-NEXT: | |-ParmVarDecl {{.*}} p 'int *{{(__single)?}} __counted_by(len)':'int *{{(__single)?}}' +// CHECK-NEXT: | `-ParmVarDecl {{.*}} len 'int' +// CHECK-NOT: IsDeref +// CHECK-NEXT: | `-DependerDeclsAttr +// CHECK-NEXT: |-ObjCMethodDecl {{.*}} - nestedParam:: 'void' +// CHECK-NEXT: | |-ParmVarDecl {{.*}} len 'int *' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.*}} IsDeref +// CHECK-NEXT: | `-ParmVarDecl {{.*}} p 'int *{{(__single)?}} __counted_by(*len)':'int *{{(__single)?}}' +// CHECK-NEXT: |-ObjCMethodDecl {{.*}} - simpleRet: 'int *{{(__single)?}} __counted_by(len)':'int *{{(__single)?}}' +// CHECK-NEXT: | `-ParmVarDecl {{.*}} len 'int' +// CHECK-NEXT: |-ObjCMethodDecl {{.*}} - nestedRet: 'int *{{(__single)?}} __counted_by(*len)':'int *{{(__single)?}}' +// CHECK-NEXT: | `-ParmVarDecl {{.*}} len 'int *' +// CHECK-NEXT: |-ObjCMethodDecl {{.*}} - cParam: 'void' +// CHECK-NEXT: | |-ParmVarDecl {{.*}} len 'int' +// CHECK-NOT: IsDeref +// CHECK-NEXT: | | `-DependerDeclsAttr +// CHECK-NEXT: | `-ParmVarDecl {{.*}} p 'int *{{(__single)?}} __counted_by(len)':'int *{{(__single)?}}' +// CHECK-NEXT: `-ObjCMethodDecl {{.*}} - reverseCParam: 'void' +// CHECK-NEXT: |-ParmVarDecl {{.*}} p 'int *{{(__single)?}} __counted_by(len)':'int *{{(__single)?}}' +// CHECK-NEXT: `-ParmVarDecl {{.*}} len 'int' +// CHECK-NOT: IsDeref +// CHECK-NEXT: `-DependerDeclsAttr + +@implementation CountedByClass + - (void)simpleParam:(int)len :(int * __counted_by(len))p {} + - (void)reverseParam:(int * __counted_by(len))p :(int)len {} + - (void)nestedParam:(int*)len :(int * __counted_by(*len))p {} + + - (int * __counted_by(len))simpleRet:(int)len {} + - (int * __counted_by(*len))nestedRet:(int*)len {} + + - (void)cParam:(int)len, int * __counted_by(len) p {} + - (void)reverseCParam:(int * __counted_by(len))p, int len {} +@end + +// CHECK-LABEL: -ObjCImplementationDecl {{.*}} CountedByClass +// CHECK-NEXT: |-ObjCInterface {{.*}} 'CountedByClass' +// CHECK-NEXT: |-ObjCMethodDecl {{.*}} - simpleParam:: 'void' +// CHECK-NEXT: | |-ImplicitParamDecl {{.*}} self 'CountedByClass *' +// CHECK-NEXT: | |-ImplicitParamDecl {{.*}} _cmd 'SEL':'SEL *' +// CHECK-NEXT: | |-ParmVarDecl {{.*}} len 'int' +// CHECK-NOT: IsDeref +// CHECK-NEXT: | | `-DependerDeclsAttr +// CHECK-NEXT: | |-ParmVarDecl {{.*}} p 'int *{{(__single)?}} __counted_by(len)':'int *{{(__single)?}}' +// CHECK-NEXT: | `-CompoundStmt +// CHECK-NEXT: |-ObjCMethodDecl {{.*}} - reverseParam:: 'void' +// CHECK-NEXT: | |-ImplicitParamDecl {{.*}} self 'CountedByClass *' +// CHECK-NEXT: | |-ImplicitParamDecl {{.*}} _cmd 'SEL':'SEL *' +// CHECK-NEXT: | |-ParmVarDecl {{.*}} p 'int *{{(__single)?}} __counted_by(len)':'int *{{(__single)?}}' +// CHECK-NEXT: | |-ParmVarDecl {{.*}} len 'int' +// CHECK-NOT: IsDeref +// CHECK-NEXT: | `-DependerDeclsAttr +// CHECK-NEXT: | `-CompoundStmt +// CHECK-NEXT: |-ObjCMethodDecl {{.*}} - nestedParam:: 'void' +// CHECK-NEXT: | |-ImplicitParamDecl {{.*}} self 'CountedByClass *' +// CHECK-NEXT: | |-ImplicitParamDecl {{.*}} _cmd 'SEL':'SEL *' +// CHECK-NEXT: | |-ParmVarDecl {{.*}} len 'int *' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.*}} IsDeref +// CHECK-NEXT: | |-ParmVarDecl {{.*}} p 'int *{{(__single)?}} __counted_by(*len)':'int *{{(__single)?}}' +// CHECK-NEXT: | `-CompoundStmt +// CHECK-NEXT: |-ObjCMethodDecl {{.*}} - simpleRet: 'int *{{(__single)?}} __counted_by(len)':'int *{{(__single)?}}' +// CHECK-NEXT: | |-ImplicitParamDecl {{.*}} self 'CountedByClass *' +// CHECK-NEXT: | |-ImplicitParamDecl {{.*}} _cmd 'SEL':'SEL *' +// CHECK-NEXT: | |-ParmVarDecl {{.*}} len 'int' +// CHECK-NEXT: | `-CompoundStmt +// CHECK-NEXT: |-ObjCMethodDecl {{.*}} - nestedRet: 'int *{{(__single)?}} __counted_by(*len)':'int *{{(__single)?}}' +// CHECK-NEXT: | |-ImplicitParamDecl {{.*}} self 'CountedByClass *' +// CHECK-NEXT: | |-ImplicitParamDecl {{.*}} _cmd 'SEL':'SEL *' +// CHECK-NEXT: | |-ParmVarDecl {{.*}} len 'int *' +// CHECK-NEXT: | `-CompoundStmt +// CHECK-NEXT: |-ObjCMethodDecl {{.*}} - cParam: 'void' +// CHECK-NEXT: | |-ImplicitParamDecl {{.*}} self 'CountedByClass *' +// CHECK-NEXT: | |-ImplicitParamDecl {{.*}} _cmd 'SEL':'SEL *' +// CHECK-NEXT: | |-ParmVarDecl {{.*}} len 'int' +// CHECK-NOT: IsDeref +// CHECK-NEXT: | | `-DependerDeclsAttr +// CHECK-NEXT: | |-ParmVarDecl {{.*}} p 'int *{{(__single)?}} __counted_by(len)':'int *{{(__single)?}}' +// CHECK-NEXT: | `-CompoundStmt +// CHECK-NEXT: `-ObjCMethodDecl {{.*}} - reverseCParam: 'void' +// CHECK-NEXT: |-ImplicitParamDecl {{.*}} self 'CountedByClass *' +// CHECK-NEXT: |-ImplicitParamDecl {{.*}} _cmd 'SEL':'SEL *' +// CHECK-NEXT: |-ParmVarDecl {{.*}} p 'int *{{(__single)?}} __counted_by(len)':'int *{{(__single)?}}' +// CHECK-NEXT: |-ParmVarDecl {{.*}} len 'int' +// CHECK-NOT: IsDeref +// CHECK-NEXT: `-DependerDeclsAttr +// CHECK-NEXT: `-CompoundStmt From 7bde0fa94e3ad2b5fdc074b28832d54d0db16b4f Mon Sep 17 00:00:00 2001 From: "Henrik G. Olsson" Date: Thu, 27 Mar 2025 15:13:11 -0700 Subject: [PATCH 2/2] [BoundsSafety] Update objc test case (NFC) By separating the check lines containing implicit __single, we explicitly check that attribute-only mode does not contain any implicit __single attributes, while -fbounds-safety does. --- clang/test/BoundsSafety/AST/objc.m | 247 ++++++++++++++++------------- 1 file changed, 134 insertions(+), 113 deletions(-) diff --git a/clang/test/BoundsSafety/AST/objc.m b/clang/test/BoundsSafety/AST/objc.m index 03e91c2e825ab..dd842f8697745 100644 --- a/clang/test/BoundsSafety/AST/objc.m +++ b/clang/test/BoundsSafety/AST/objc.m @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -Wno-deprecated-declarations -Wno-return-type -Wno-objc-root-class -ast-dump %s 2>&1 | FileCheck %s -// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x objective-c -Wno-deprecated-declarations -Wno-return-type -Wno-objc-root-class -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -Wno-deprecated-declarations -Wno-return-type -Wno-objc-root-class -ast-dump %s 2>&1 | FileCheck --check-prefixes=COMMON-CHECK,BOUNDS-CHECK %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x objective-c -Wno-deprecated-declarations -Wno-return-type -Wno-objc-root-class -ast-dump %s 2>&1 | FileCheck --check-prefixes=COMMON-CHECK,ATTR-CHECK %s #include @@ -15,35 +15,42 @@ - (void)cParam:(int)len, int * __counted_by(len) p; - (void)reverseCParam:(int * __counted_by(len))p, int len; @end -// CHECK-LABEL: -ObjCProtocolDecl {{.*}} CountedByProtocol -// CHECK-NEXT: |-ObjCMethodDecl {{.*}} - simpleParam:: 'void' -// CHECK-NEXT: | |-ParmVarDecl {{.*}} len 'int' -// CHECK-NOT: IsDeref -// CHECK-NEXT: | | `-DependerDeclsAttr -// CHECK-NEXT: | `-ParmVarDecl {{.*}} p 'int *{{(__single)?}} __counted_by(len)':'int *{{(__single)?}}' -// CHECK-NEXT: |-ObjCMethodDecl {{.*}} - reverseParam:: 'void' -// CHECK-NEXT: | |-ParmVarDecl {{.*}} p 'int *{{(__single)?}} __counted_by(len)':'int *{{(__single)?}}' -// CHECK-NEXT: | `-ParmVarDecl {{.*}} len 'int' -// CHECK-NOT: IsDeref -// CHECK-NEXT: | `-DependerDeclsAttr -// CHECK-NEXT: |-ObjCMethodDecl {{.*}} - nestedParam:: 'void' -// CHECK-NEXT: | |-ParmVarDecl {{.*}} len 'int *' -// CHECK-NEXT: | | `-DependerDeclsAttr {{.*}} IsDeref -// CHECK-NEXT: | `-ParmVarDecl {{.*}} p 'int *{{(__single)?}} __counted_by(*len)':'int *{{(__single)?}}' -// CHECK-NEXT: |-ObjCMethodDecl {{.*}} - simpleRet: 'int *{{(__single)?}} __counted_by(len)':'int *{{(__single)?}}' -// CHECK-NEXT: | `-ParmVarDecl {{.*}} len 'int' -// CHECK-NEXT: |-ObjCMethodDecl {{.*}} - nestedRet: 'int *{{(__single)?}} __counted_by(*len)':'int *{{(__single)?}}' -// CHECK-NEXT: | `-ParmVarDecl {{.*}} len 'int *' -// CHECK-NEXT: |-ObjCMethodDecl {{.*}} - cParam: 'void' -// CHECK-NEXT: | |-ParmVarDecl {{.*}} len 'int' -// CHECK-NOT: IsDeref -// CHECK-NEXT: | | `-DependerDeclsAttr -// CHECK-NEXT: | `-ParmVarDecl {{.*}} p 'int *{{(__single)?}} __counted_by(len)':'int *{{(__single)?}}' -// CHECK-NEXT: `-ObjCMethodDecl {{.*}} - reverseCParam: 'void' -// CHECK-NEXT: |-ParmVarDecl {{.*}} p 'int *{{(__single)?}} __counted_by(len)':'int *{{(__single)?}}' -// CHECK-NEXT: `-ParmVarDecl {{.*}} len 'int' -// CHECK-NOT: IsDeref -// CHECK-NEXT: `-DependerDeclsAttr +// COMMON-CHECK-LABEL: -ObjCProtocolDecl {{.*}} CountedByProtocol +// COMMON-CHECK-NEXT: |-ObjCMethodDecl {{.*}} - simpleParam:: 'void' +// COMMON-CHECK-NEXT: | |-ParmVarDecl {{.*}} len 'int' +// COMMON-CHECK-NOT: IsDeref +// COMMON-CHECK-NEXT: | | `-DependerDeclsAttr +// BOUNDS-CHECK-NEXT: | `-ParmVarDecl {{.*}} p 'int *__single __counted_by(len)':'int *__single' +// ATTR-CHECK-NEXT: | `-ParmVarDecl {{.*}} p 'int * __counted_by(len)':'int *' +// COMMON-CHECK-NEXT: |-ObjCMethodDecl {{.*}} - reverseParam:: 'void' +// BOUNDS-CHECK-NEXT: | |-ParmVarDecl {{.*}} p 'int *__single __counted_by(len)':'int *__single' +// ATTR-CHECK-NEXT: | |-ParmVarDecl {{.*}} p 'int * __counted_by(len)':'int *' +// COMMON-CHECK-NEXT: | `-ParmVarDecl {{.*}} len 'int' +// COMMON-CHECK-NOT: IsDeref +// COMMON-CHECK-NEXT: | `-DependerDeclsAttr +// COMMON-CHECK-NEXT: |-ObjCMethodDecl {{.*}} - nestedParam:: 'void' +// COMMON-CHECK-NEXT: | |-ParmVarDecl {{.*}} len 'int *' +// COMMON-CHECK-NEXT: | | `-DependerDeclsAttr {{.*}} IsDeref +// BOUNDS-CHECK-NEXT: | `-ParmVarDecl {{.*}} p 'int *__single __counted_by(*len)':'int *__single' +// ATTR-CHECK-NEXT: | `-ParmVarDecl {{.*}} p 'int * __counted_by(*len)':'int *' +// BOUNDS-CHECK-NEXT: |-ObjCMethodDecl {{.*}} - simpleRet: 'int *__single __counted_by(len)':'int *__single' +// ATTR-CHECK-NEXT: |-ObjCMethodDecl {{.*}} - simpleRet: 'int * __counted_by(len)':'int *' +// COMMON-CHECK-NEXT: | `-ParmVarDecl {{.*}} len 'int' +// BOUNDS-CHECK-NEXT: |-ObjCMethodDecl {{.*}} - nestedRet: 'int *__single __counted_by(*len)':'int *__single' +// ATTR-CHECK-NEXT: |-ObjCMethodDecl {{.*}} - nestedRet: 'int * __counted_by(*len)':'int *' +// COMMON-CHECK-NEXT: | `-ParmVarDecl {{.*}} len 'int *' +// COMMON-CHECK-NEXT: |-ObjCMethodDecl {{.*}} - cParam: 'void' +// COMMON-CHECK-NEXT: | |-ParmVarDecl {{.*}} len 'int' +// COMMON-CHECK-NOT: IsDeref +// COMMON-CHECK-NEXT: | | `-DependerDeclsAttr +// BOUNDS-CHECK-NEXT: | `-ParmVarDecl {{.*}} p 'int *__single __counted_by(len)':'int *__single' +// ATTR-CHECK-NEXT: | `-ParmVarDecl {{.*}} p 'int * __counted_by(len)':'int *' +// COMMON-CHECK-NEXT: `-ObjCMethodDecl {{.*}} - reverseCParam: 'void' +// BOUNDS-CHECK-NEXT: |-ParmVarDecl {{.*}} p 'int *__single __counted_by(len)':'int *__single' +// ATTR-CHECK-NEXT: |-ParmVarDecl {{.*}} p 'int * __counted_by(len)':'int *' +// COMMON-CHECK-NEXT: `-ParmVarDecl {{.*}} len 'int' +// COMMON-CHECK-NOT: IsDeref +// COMMON-CHECK-NEXT: `-DependerDeclsAttr @interface CountedByClass - (void)simpleParam:(int)len :(int * __counted_by(len))p; @@ -57,37 +64,44 @@ - (void)cParam:(int)len, int * __counted_by(len) p; - (void)reverseCParam:(int * __counted_by(len))p, int len; @end -// CHECK-LABEL: -ObjCInterfaceDecl {{.*}} CountedByClass -// CHECK-NEXT: |-ObjCImplementation {{.*}} 'CountedByClass' -// CHECK-NEXT: |-ObjCProtocol {{.*}} 'CountedByProtocol' -// CHECK-NEXT: |-ObjCMethodDecl {{.*}} - simpleParam:: 'void' -// CHECK-NEXT: | |-ParmVarDecl {{.*}} len 'int' -// CHECK-NOT: IsDeref -// CHECK-NEXT: | | `-DependerDeclsAttr -// CHECK-NEXT: | `-ParmVarDecl {{.*}} p 'int *{{(__single)?}} __counted_by(len)':'int *{{(__single)?}}' -// CHECK-NEXT: |-ObjCMethodDecl {{.*}} - reverseParam:: 'void' -// CHECK-NEXT: | |-ParmVarDecl {{.*}} p 'int *{{(__single)?}} __counted_by(len)':'int *{{(__single)?}}' -// CHECK-NEXT: | `-ParmVarDecl {{.*}} len 'int' -// CHECK-NOT: IsDeref -// CHECK-NEXT: | `-DependerDeclsAttr -// CHECK-NEXT: |-ObjCMethodDecl {{.*}} - nestedParam:: 'void' -// CHECK-NEXT: | |-ParmVarDecl {{.*}} len 'int *' -// CHECK-NEXT: | | `-DependerDeclsAttr {{.*}} IsDeref -// CHECK-NEXT: | `-ParmVarDecl {{.*}} p 'int *{{(__single)?}} __counted_by(*len)':'int *{{(__single)?}}' -// CHECK-NEXT: |-ObjCMethodDecl {{.*}} - simpleRet: 'int *{{(__single)?}} __counted_by(len)':'int *{{(__single)?}}' -// CHECK-NEXT: | `-ParmVarDecl {{.*}} len 'int' -// CHECK-NEXT: |-ObjCMethodDecl {{.*}} - nestedRet: 'int *{{(__single)?}} __counted_by(*len)':'int *{{(__single)?}}' -// CHECK-NEXT: | `-ParmVarDecl {{.*}} len 'int *' -// CHECK-NEXT: |-ObjCMethodDecl {{.*}} - cParam: 'void' -// CHECK-NEXT: | |-ParmVarDecl {{.*}} len 'int' -// CHECK-NOT: IsDeref -// CHECK-NEXT: | | `-DependerDeclsAttr -// CHECK-NEXT: | `-ParmVarDecl {{.*}} p 'int *{{(__single)?}} __counted_by(len)':'int *{{(__single)?}}' -// CHECK-NEXT: `-ObjCMethodDecl {{.*}} - reverseCParam: 'void' -// CHECK-NEXT: |-ParmVarDecl {{.*}} p 'int *{{(__single)?}} __counted_by(len)':'int *{{(__single)?}}' -// CHECK-NEXT: `-ParmVarDecl {{.*}} len 'int' -// CHECK-NOT: IsDeref -// CHECK-NEXT: `-DependerDeclsAttr +// COMMON-CHECK-LABEL: -ObjCInterfaceDecl {{.*}} CountedByClass +// COMMON-CHECK-NEXT: |-ObjCImplementation {{.*}} 'CountedByClass' +// COMMON-CHECK-NEXT: |-ObjCProtocol {{.*}} 'CountedByProtocol' +// COMMON-CHECK-NEXT: |-ObjCMethodDecl {{.*}} - simpleParam:: 'void' +// COMMON-CHECK-NEXT: | |-ParmVarDecl {{.*}} len 'int' +// COMMON-CHECK-NOT: IsDeref +// COMMON-CHECK-NEXT: | | `-DependerDeclsAttr +// BOUNDS-CHECK-NEXT: | `-ParmVarDecl {{.*}} p 'int *__single __counted_by(len)':'int *__single' +// ATTR-CHECK-NEXT: | `-ParmVarDecl {{.*}} p 'int * __counted_by(len)':'int *' +// COMMON-CHECK-NEXT: |-ObjCMethodDecl {{.*}} - reverseParam:: 'void' +// BOUNDS-CHECK-NEXT: | |-ParmVarDecl {{.*}} p 'int *__single __counted_by(len)':'int *__single' +// ATTR-CHECK-NEXT: | |-ParmVarDecl {{.*}} p 'int * __counted_by(len)':'int *' +// COMMON-CHECK-NEXT: | `-ParmVarDecl {{.*}} len 'int' +// COMMON-CHECK-NOT: IsDeref +// COMMON-CHECK-NEXT: | `-DependerDeclsAttr +// COMMON-CHECK-NEXT: |-ObjCMethodDecl {{.*}} - nestedParam:: 'void' +// COMMON-CHECK-NEXT: | |-ParmVarDecl {{.*}} len 'int *' +// COMMON-CHECK-NEXT: | | `-DependerDeclsAttr {{.*}} IsDeref +// BOUNDS-CHECK-NEXT: | `-ParmVarDecl {{.*}} p 'int *__single __counted_by(*len)':'int *__single' +// ATTR-CHECK-NEXT: | `-ParmVarDecl {{.*}} p 'int * __counted_by(*len)':'int *' +// BOUNDS-CHECK-NEXT: |-ObjCMethodDecl {{.*}} - simpleRet: 'int *__single __counted_by(len)':'int *__single' +// ATTR-CHECK-NEXT: |-ObjCMethodDecl {{.*}} - simpleRet: 'int * __counted_by(len)':'int *' +// COMMON-CHECK-NEXT: | `-ParmVarDecl {{.*}} len 'int' +// BOUNDS-CHECK-NEXT: |-ObjCMethodDecl {{.*}} - nestedRet: 'int *__single __counted_by(*len)':'int *__single' +// ATTR-CHECK-NEXT: |-ObjCMethodDecl {{.*}} - nestedRet: 'int * __counted_by(*len)':'int *' +// COMMON-CHECK-NEXT: | `-ParmVarDecl {{.*}} len 'int *' +// COMMON-CHECK-NEXT: |-ObjCMethodDecl {{.*}} - cParam: 'void' +// COMMON-CHECK-NEXT: | |-ParmVarDecl {{.*}} len 'int' +// COMMON-CHECK-NOT: IsDeref +// COMMON-CHECK-NEXT: | | `-DependerDeclsAttr +// BOUNDS-CHECK-NEXT: | `-ParmVarDecl {{.*}} p 'int *__single __counted_by(len)':'int *__single' +// ATTR-CHECK-NEXT: | `-ParmVarDecl {{.*}} p 'int * __counted_by(len)':'int *' +// COMMON-CHECK-NEXT: `-ObjCMethodDecl {{.*}} - reverseCParam: 'void' +// BOUNDS-CHECK-NEXT: |-ParmVarDecl {{.*}} p 'int *__single __counted_by(len)':'int *__single' +// ATTR-CHECK-NEXT: |-ParmVarDecl {{.*}} p 'int * __counted_by(len)':'int *' +// COMMON-CHECK-NEXT: `-ParmVarDecl {{.*}} len 'int' +// COMMON-CHECK-NOT: IsDeref +// COMMON-CHECK-NEXT: `-DependerDeclsAttr @implementation CountedByClass - (void)simpleParam:(int)len :(int * __counted_by(len))p {} @@ -101,54 +115,61 @@ - (void)cParam:(int)len, int * __counted_by(len) p {} - (void)reverseCParam:(int * __counted_by(len))p, int len {} @end -// CHECK-LABEL: -ObjCImplementationDecl {{.*}} CountedByClass -// CHECK-NEXT: |-ObjCInterface {{.*}} 'CountedByClass' -// CHECK-NEXT: |-ObjCMethodDecl {{.*}} - simpleParam:: 'void' -// CHECK-NEXT: | |-ImplicitParamDecl {{.*}} self 'CountedByClass *' -// CHECK-NEXT: | |-ImplicitParamDecl {{.*}} _cmd 'SEL':'SEL *' -// CHECK-NEXT: | |-ParmVarDecl {{.*}} len 'int' -// CHECK-NOT: IsDeref -// CHECK-NEXT: | | `-DependerDeclsAttr -// CHECK-NEXT: | |-ParmVarDecl {{.*}} p 'int *{{(__single)?}} __counted_by(len)':'int *{{(__single)?}}' -// CHECK-NEXT: | `-CompoundStmt -// CHECK-NEXT: |-ObjCMethodDecl {{.*}} - reverseParam:: 'void' -// CHECK-NEXT: | |-ImplicitParamDecl {{.*}} self 'CountedByClass *' -// CHECK-NEXT: | |-ImplicitParamDecl {{.*}} _cmd 'SEL':'SEL *' -// CHECK-NEXT: | |-ParmVarDecl {{.*}} p 'int *{{(__single)?}} __counted_by(len)':'int *{{(__single)?}}' -// CHECK-NEXT: | |-ParmVarDecl {{.*}} len 'int' -// CHECK-NOT: IsDeref -// CHECK-NEXT: | `-DependerDeclsAttr -// CHECK-NEXT: | `-CompoundStmt -// CHECK-NEXT: |-ObjCMethodDecl {{.*}} - nestedParam:: 'void' -// CHECK-NEXT: | |-ImplicitParamDecl {{.*}} self 'CountedByClass *' -// CHECK-NEXT: | |-ImplicitParamDecl {{.*}} _cmd 'SEL':'SEL *' -// CHECK-NEXT: | |-ParmVarDecl {{.*}} len 'int *' -// CHECK-NEXT: | | `-DependerDeclsAttr {{.*}} IsDeref -// CHECK-NEXT: | |-ParmVarDecl {{.*}} p 'int *{{(__single)?}} __counted_by(*len)':'int *{{(__single)?}}' -// CHECK-NEXT: | `-CompoundStmt -// CHECK-NEXT: |-ObjCMethodDecl {{.*}} - simpleRet: 'int *{{(__single)?}} __counted_by(len)':'int *{{(__single)?}}' -// CHECK-NEXT: | |-ImplicitParamDecl {{.*}} self 'CountedByClass *' -// CHECK-NEXT: | |-ImplicitParamDecl {{.*}} _cmd 'SEL':'SEL *' -// CHECK-NEXT: | |-ParmVarDecl {{.*}} len 'int' -// CHECK-NEXT: | `-CompoundStmt -// CHECK-NEXT: |-ObjCMethodDecl {{.*}} - nestedRet: 'int *{{(__single)?}} __counted_by(*len)':'int *{{(__single)?}}' -// CHECK-NEXT: | |-ImplicitParamDecl {{.*}} self 'CountedByClass *' -// CHECK-NEXT: | |-ImplicitParamDecl {{.*}} _cmd 'SEL':'SEL *' -// CHECK-NEXT: | |-ParmVarDecl {{.*}} len 'int *' -// CHECK-NEXT: | `-CompoundStmt -// CHECK-NEXT: |-ObjCMethodDecl {{.*}} - cParam: 'void' -// CHECK-NEXT: | |-ImplicitParamDecl {{.*}} self 'CountedByClass *' -// CHECK-NEXT: | |-ImplicitParamDecl {{.*}} _cmd 'SEL':'SEL *' -// CHECK-NEXT: | |-ParmVarDecl {{.*}} len 'int' -// CHECK-NOT: IsDeref -// CHECK-NEXT: | | `-DependerDeclsAttr -// CHECK-NEXT: | |-ParmVarDecl {{.*}} p 'int *{{(__single)?}} __counted_by(len)':'int *{{(__single)?}}' -// CHECK-NEXT: | `-CompoundStmt -// CHECK-NEXT: `-ObjCMethodDecl {{.*}} - reverseCParam: 'void' -// CHECK-NEXT: |-ImplicitParamDecl {{.*}} self 'CountedByClass *' -// CHECK-NEXT: |-ImplicitParamDecl {{.*}} _cmd 'SEL':'SEL *' -// CHECK-NEXT: |-ParmVarDecl {{.*}} p 'int *{{(__single)?}} __counted_by(len)':'int *{{(__single)?}}' -// CHECK-NEXT: |-ParmVarDecl {{.*}} len 'int' -// CHECK-NOT: IsDeref -// CHECK-NEXT: `-DependerDeclsAttr -// CHECK-NEXT: `-CompoundStmt +// COMMON-CHECK-LABEL: -ObjCImplementationDecl {{.*}} CountedByClass +// COMMON-CHECK-NEXT: |-ObjCInterface {{.*}} 'CountedByClass' +// COMMON-CHECK-NEXT: |-ObjCMethodDecl {{.*}} - simpleParam:: 'void' +// COMMON-CHECK-NEXT: | |-ImplicitParamDecl {{.*}} self 'CountedByClass *' +// COMMON-CHECK-NEXT: | |-ImplicitParamDecl {{.*}} _cmd 'SEL':'SEL *' +// COMMON-CHECK-NEXT: | |-ParmVarDecl {{.*}} len 'int' +// COMMON-CHECK-NOT: IsDeref +// COMMON-CHECK-NEXT: | | `-DependerDeclsAttr +// BOUNDS-CHECK-NEXT: | |-ParmVarDecl {{.*}} p 'int *__single __counted_by(len)':'int *__single' +// ATTR-CHECK-NEXT: | |-ParmVarDecl {{.*}} p 'int * __counted_by(len)':'int *' +// COMMON-CHECK-NEXT: | `-CompoundStmt +// COMMON-CHECK-NEXT: |-ObjCMethodDecl {{.*}} - reverseParam:: 'void' +// COMMON-CHECK-NEXT: | |-ImplicitParamDecl {{.*}} self 'CountedByClass *' +// COMMON-CHECK-NEXT: | |-ImplicitParamDecl {{.*}} _cmd 'SEL':'SEL *' +// BOUNDS-CHECK-NEXT: | |-ParmVarDecl {{.*}} p 'int *__single __counted_by(len)':'int *__single' +// ATTR-CHECK-NEXT: | |-ParmVarDecl {{.*}} p 'int * __counted_by(len)':'int *' +// COMMON-CHECK-NEXT: | |-ParmVarDecl {{.*}} len 'int' +// COMMON-CHECK-NOT: IsDeref +// COMMON-CHECK-NEXT: | `-DependerDeclsAttr +// COMMON-CHECK-NEXT: | `-CompoundStmt +// COMMON-CHECK-NEXT: |-ObjCMethodDecl {{.*}} - nestedParam:: 'void' +// COMMON-CHECK-NEXT: | |-ImplicitParamDecl {{.*}} self 'CountedByClass *' +// COMMON-CHECK-NEXT: | |-ImplicitParamDecl {{.*}} _cmd 'SEL':'SEL *' +// COMMON-CHECK-NEXT: | |-ParmVarDecl {{.*}} len 'int *' +// COMMON-CHECK-NEXT: | | `-DependerDeclsAttr {{.*}} IsDeref +// BOUNDS-CHECK-NEXT: | |-ParmVarDecl {{.*}} p 'int *__single __counted_by(*len)':'int *__single' +// ATTR-CHECK-NEXT: | |-ParmVarDecl {{.*}} p 'int * __counted_by(*len)':'int *' +// COMMON-CHECK-NEXT: | `-CompoundStmt +// BOUNDS-CHECK-NEXT: |-ObjCMethodDecl {{.*}} - simpleRet: 'int *__single __counted_by(len)':'int *__single' +// ATTR-CHECK-NEXT: |-ObjCMethodDecl {{.*}} - simpleRet: 'int * __counted_by(len)':'int *' +// COMMON-CHECK-NEXT: | |-ImplicitParamDecl {{.*}} self 'CountedByClass *' +// COMMON-CHECK-NEXT: | |-ImplicitParamDecl {{.*}} _cmd 'SEL':'SEL *' +// COMMON-CHECK-NEXT: | |-ParmVarDecl {{.*}} len 'int' +// COMMON-CHECK-NEXT: | `-CompoundStmt +// BOUNDS-CHECK-NEXT: |-ObjCMethodDecl {{.*}} - nestedRet: 'int *__single __counted_by(*len)':'int *__single' +// ATTR-CHECK-NEXT: |-ObjCMethodDecl {{.*}} - nestedRet: 'int * __counted_by(*len)':'int *' +// COMMON-CHECK-NEXT: | |-ImplicitParamDecl {{.*}} self 'CountedByClass *' +// COMMON-CHECK-NEXT: | |-ImplicitParamDecl {{.*}} _cmd 'SEL':'SEL *' +// COMMON-CHECK-NEXT: | |-ParmVarDecl {{.*}} len 'int *' +// COMMON-CHECK-NEXT: | `-CompoundStmt +// COMMON-CHECK-NEXT: |-ObjCMethodDecl {{.*}} - cParam: 'void' +// COMMON-CHECK-NEXT: | |-ImplicitParamDecl {{.*}} self 'CountedByClass *' +// COMMON-CHECK-NEXT: | |-ImplicitParamDecl {{.*}} _cmd 'SEL':'SEL *' +// COMMON-CHECK-NEXT: | |-ParmVarDecl {{.*}} len 'int' +// COMMON-CHECK-NOT: IsDeref +// COMMON-CHECK-NEXT: | | `-DependerDeclsAttr +// BOUNDS-CHECK-NEXT: | |-ParmVarDecl {{.*}} p 'int *__single __counted_by(len)':'int *__single' +// ATTR-CHECK-NEXT: | |-ParmVarDecl {{.*}} p 'int * __counted_by(len)':'int *' +// COMMON-CHECK-NEXT: | `-CompoundStmt +// COMMON-CHECK-NEXT: `-ObjCMethodDecl {{.*}} - reverseCParam: 'void' +// COMMON-CHECK-NEXT: |-ImplicitParamDecl {{.*}} self 'CountedByClass *' +// COMMON-CHECK-NEXT: |-ImplicitParamDecl {{.*}} _cmd 'SEL':'SEL *' +// BOUNDS-CHECK-NEXT: |-ParmVarDecl {{.*}} p 'int *__single __counted_by(len)':'int *__single' +// ATTR-CHECK-NEXT: |-ParmVarDecl {{.*}} p 'int * __counted_by(len)':'int *' +// COMMON-CHECK-NEXT: |-ParmVarDecl {{.*}} len 'int' +// COMMON-CHECK-NOT: IsDeref +// COMMON-CHECK-NEXT: `-DependerDeclsAttr +// COMMON-CHECK-NEXT: `-CompoundStmt