[Attr] Fix parameter indexing for several attributes

The patch fixes a number of bugs related to parameter indexing in
attributes:

* Parameter indices in some attributes (argument_with_type_tag,
  pointer_with_type_tag, nonnull, ownership_takes, ownership_holds,
  and ownership_returns) are specified in source as one-origin
  including any C++ implicit this parameter, were stored as
  zero-origin excluding any this parameter, and were erroneously
  printing (-ast-print) and confusingly dumping (-ast-dump) as the
  stored values.

* For alloc_size, the C++ implicit this parameter was not subtracted
  correctly in Sema, leading to assert failures or to silent failures
  of __builtin_object_size to compute a value.

* For argument_with_type_tag, pointer_with_type_tag, and
  ownership_returns, the C++ implicit this parameter was not added
  back to parameter indices in some diagnostics.

This patch fixes the above bugs and aims to prevent similar bugs in
the future by introducing careful mechanisms for handling parameter
indices in attributes.  ParamIdx stores a parameter index and is
designed to hide the stored encoding while providing accessors that
require each use (such as printing) to make explicit the encoding that
is needed.  Attribute declarations declare parameter index arguments
as [Variadic]ParamIdxArgument, which are exposed as ParamIdx[*].  This
patch rewrites all attribute arguments that are processed by
checkFunctionOrMethodParameterIndex in SemaDeclAttr.cpp to be declared
as [Variadic]ParamIdxArgument.  The only exception is xray_log_args's
argument, which is encoded as a count not an index.

Differential Revision: https://reviews.llvm.org/D43248

llvm-svn: 326602
This commit is contained in:
Joel E. Denny 2018-03-02 19:03:22 +00:00
parent 1a8456da17
commit 4925445958
16 changed files with 503 additions and 150 deletions

View File

@ -195,6 +195,120 @@ public:
}
};
/// A single parameter index whose accessors require each use to make explicit
/// the parameter index encoding needed.
class ParamIdx {
// Idx is exposed only via accessors that specify specific encodings.
unsigned Idx : 30;
unsigned HasThis : 1;
unsigned IsValid : 1;
void assertComparable(const ParamIdx &I) const {
assert(isValid() && I.isValid() &&
"ParamIdx must be valid to be compared");
// It's possible to compare indices from separate functions, but so far
// it's not proven useful. Moreover, it might be confusing because a
// comparison on the results of getASTIndex might be inconsistent with a
// comparison on the ParamIdx objects themselves.
assert(HasThis == I.HasThis &&
"ParamIdx must be for the same function to be compared");
}
public:
/// Construct an invalid parameter index (\c isValid returns false and
/// accessors fail an assert).
ParamIdx() : Idx(0), HasThis(false), IsValid(false) {}
/// \param Idx is the parameter index as it is normally specified in
/// attributes in the source: one-origin including any C++ implicit this
/// parameter.
///
/// \param D is the declaration containing the parameters. It is used to
/// determine if there is a C++ implicit this parameter.
ParamIdx(unsigned Idx, const Decl *D)
: Idx(Idx), HasThis(false), IsValid(true) {
if (const auto *FD = dyn_cast<FunctionDecl>(D))
HasThis = FD->isCXXInstanceMember();
}
/// \param Idx is the parameter index as it is normally specified in
/// attributes in the source: one-origin including any C++ implicit this
/// parameter.
///
/// \param HasThis specifies whether the function has a C++ implicit this
/// parameter.
ParamIdx(unsigned Idx, bool HasThis)
: Idx(Idx), HasThis(HasThis), IsValid(true) {}
/// Is this parameter index valid?
bool isValid() const { return IsValid; }
/// Is there a C++ implicit this parameter?
bool hasThis() const {
assert(isValid() && "ParamIdx must be valid");
return HasThis;
}
/// Get the parameter index as it would normally be encoded for attributes at
/// the source level of representation: one-origin including any C++ implicit
/// this parameter.
///
/// This encoding thus makes sense for diagnostics, pretty printing, and
/// constructing new attributes from a source-like specification.
unsigned getSourceIndex() const {
assert(isValid() && "ParamIdx must be valid");
return Idx;
}
/// Get the parameter index as it would normally be encoded at the AST level
/// of representation: zero-origin not including any C++ implicit this
/// parameter.
///
/// This is the encoding primarily used in Sema. However, in diagnostics,
/// Sema uses \c getSourceIndex instead.
unsigned getASTIndex() const {
assert(isValid() && "ParamIdx must be valid");
assert(Idx >= 1 + HasThis &&
"stored index must be base-1 and not specify C++ implicit this");
return Idx - 1 - HasThis;
}
/// Get the parameter index as it would normally be encoded at the LLVM level
/// of representation: zero-origin including any C++ implicit this parameter.
///
/// This is the encoding primarily used in CodeGen.
unsigned getLLVMIndex() const {
assert(isValid() && "ParamIdx must be valid");
assert(Idx >= 1 && "stored index must be base-1");
return Idx - 1;
}
bool operator==(const ParamIdx &I) const {
assertComparable(I);
return Idx == I.Idx;
}
bool operator!=(const ParamIdx &I) const {
assertComparable(I);
return Idx != I.Idx;
}
bool operator<(const ParamIdx &I) const {
assertComparable(I);
return Idx < I.Idx;
}
bool operator>(const ParamIdx &I) const {
assertComparable(I);
return Idx > I.Idx;
}
bool operator<=(const ParamIdx &I) const {
assertComparable(I);
return Idx <= I.Idx;
}
bool operator>=(const ParamIdx &I) const {
assertComparable(I);
return Idx >= I.Idx;
}
};
#include "clang/AST/Attrs.inc"
inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,

View File

@ -166,6 +166,12 @@ class VariadicUnsignedArgument<string name> : Argument<name, 1>;
class VariadicExprArgument<string name> : Argument<name, 1>;
class VariadicStringArgument<string name> : Argument<name, 1>;
// Like VariadicUnsignedArgument except values are ParamIdx.
class VariadicParamIdxArgument<string name> : Argument<name, 1>;
// Like VariadicParamIdxArgument but for a single function parameter index.
class ParamIdxArgument<string name, bit opt = 0> : Argument<name, opt>;
// A version of the form major.minor[.subminor].
class VersionArgument<string name, bit opt = 0> : Argument<name, opt>;
@ -611,6 +617,12 @@ def XRayInstrument : InheritableAttr {
def XRayLogArgs : InheritableAttr {
let Spellings = [Clang<"xray_log_args">];
let Subjects = SubjectList<[Function, ObjCMethod]>;
// This argument is a count not an index, so it has the same encoding (base
// 1 including C++ implicit this parameter) at the source and LLVM levels of
// representation, so ParamIdxArgument is inappropriate. It is never used
// at the AST level of representation, so it never needs to be adjusted not
// to include any C++ implicit this parameter. Thus, we just store it and
// use it as an unsigned that never needs adjustment.
let Args = [UnsignedArgument<"ArgumentCount">];
let Documentation = [XRayDocs];
}
@ -1018,7 +1030,8 @@ def EmptyBases : InheritableAttr, TargetSpecificAttr<TargetMicrosoftCXXABI> {
def AllocSize : InheritableAttr {
let Spellings = [GCC<"alloc_size">];
let Subjects = SubjectList<[Function]>;
let Args = [IntArgument<"ElemSizeParam">, IntArgument<"NumElemsParam", 1>];
let Args = [ParamIdxArgument<"ElemSizeParam">,
ParamIdxArgument<"NumElemsParam", /*opt*/ 1>];
let TemplateDependent = 1;
let Documentation = [AllocSizeDocs];
}
@ -1105,7 +1118,7 @@ def Format : InheritableAttr {
def FormatArg : InheritableAttr {
let Spellings = [GCC<"format_arg">];
let Args = [IntArgument<"FormatIdx">];
let Args = [ParamIdxArgument<"FormatIdx">];
let Subjects = SubjectList<[ObjCMethod, HasFunctionProto]>;
let Documentation = [Undocumented];
}
@ -1385,16 +1398,16 @@ def NonNull : InheritableParamAttr {
let Spellings = [GCC<"nonnull">];
let Subjects = SubjectList<[ObjCMethod, HasFunctionProto, ParmVar], WarnDiag,
"functions, methods, and parameters">;
let Args = [VariadicUnsignedArgument<"Args">];
let AdditionalMembers =
[{bool isNonNull(unsigned idx) const {
if (!args_size())
return true;
for (const auto &V : args())
if (V == idx)
let Args = [VariadicParamIdxArgument<"Args">];
let AdditionalMembers = [{
bool isNonNull(unsigned IdxAST) const {
if (!args_size())
return true;
return false;
} }];
return args_end() != std::find_if(
args_begin(), args_end(),
[=](const ParamIdx &Idx) { return Idx.getASTIndex() == IdxAST; });
}
}];
// FIXME: We should merge duplicates into a single nonnull attribute.
let InheritEvenIfAlreadyPresent = 1;
let Documentation = [NonNullDocs];
@ -1452,7 +1465,7 @@ def AssumeAligned : InheritableAttr {
def AllocAlign : InheritableAttr {
let Spellings = [GCC<"alloc_align">];
let Subjects = SubjectList<[HasFunctionProto]>;
let Args = [IntArgument<"ParamIndex">];
let Args = [ParamIdxArgument<"ParamIndex">];
let Documentation = [AllocAlignDocs];
}
@ -1661,7 +1674,8 @@ def Ownership : InheritableAttr {
Returns;
}
}];
let Args = [IdentifierArgument<"Module">, VariadicUnsignedArgument<"Args">];
let Args = [IdentifierArgument<"Module">,
VariadicParamIdxArgument<"Args">];
let Subjects = SubjectList<[HasFunctionProto]>;
let Documentation = [Undocumented];
}
@ -2486,8 +2500,8 @@ def ArgumentWithTypeTag : InheritableAttr {
Clang<"pointer_with_type_tag">];
let Subjects = SubjectList<[HasFunctionProto], ErrorDiag>;
let Args = [IdentifierArgument<"ArgumentKind">,
UnsignedArgument<"ArgumentIdx">,
UnsignedArgument<"TypeTagIdx">,
ParamIdxArgument<"ArgumentIdx">,
ParamIdxArgument<"TypeTagIdx">,
BoolArgument<"IsPointer", /*opt*/0, /*fake*/1>];
let Documentation = [ArgumentWithTypeTagDocs, PointerWithTypeTagDocs];
}

View File

@ -5463,9 +5463,8 @@ static bool getBytesReturnedByAllocSizeCall(const ASTContext &Ctx,
llvm::APInt &Result) {
const AllocSizeAttr *AllocSize = getAllocSizeAttr(Call);
// alloc_size args are 1-indexed, 0 means not present.
assert(AllocSize && AllocSize->getElemSizeParam() != 0);
unsigned SizeArgNo = AllocSize->getElemSizeParam() - 1;
assert(AllocSize && AllocSize->elemSizeParam().isValid());
unsigned SizeArgNo = AllocSize->elemSizeParam().getASTIndex();
unsigned BitsInSizeT = Ctx.getTypeSize(Ctx.getSizeType());
if (Call->getNumArgs() <= SizeArgNo)
return false;
@ -5483,14 +5482,13 @@ static bool getBytesReturnedByAllocSizeCall(const ASTContext &Ctx,
if (!EvaluateAsSizeT(Call->getArg(SizeArgNo), SizeOfElem))
return false;
if (!AllocSize->getNumElemsParam()) {
if (!AllocSize->numElemsParam().isValid()) {
Result = std::move(SizeOfElem);
return true;
}
APSInt NumberOfElems;
// Argument numbers start at 1
unsigned NumArgNo = AllocSize->getNumElemsParam() - 1;
unsigned NumArgNo = AllocSize->numElemsParam().getASTIndex();
if (!EvaluateAsSizeT(Call->getArg(NumArgNo), NumberOfElems))
return false;

View File

@ -1847,10 +1847,9 @@ void CodeGenModule::ConstructAttributeList(
HasOptnone = TargetDecl->hasAttr<OptimizeNoneAttr>();
if (auto *AllocSize = TargetDecl->getAttr<AllocSizeAttr>()) {
Optional<unsigned> NumElemsParam;
// alloc_size args are base-1, 0 means not present.
if (unsigned N = AllocSize->getNumElemsParam())
NumElemsParam = N - 1;
FuncAttrs.addAllocSizeAttr(AllocSize->getElemSizeParam() - 1,
if (AllocSize->numElemsParam().isValid())
NumElemsParam = AllocSize->numElemsParam().getLLVMIndex();
FuncAttrs.addAllocSizeAttr(AllocSize->elemSizeParam().getLLVMIndex(),
NumElemsParam);
}
}
@ -4395,7 +4394,7 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
OffsetValue);
} else if (const auto *AA = TargetDecl->getAttr<AllocAlignAttr>()) {
llvm::Value *ParamVal =
CallArgs[AA->getParamIndex() - 1].RV.getScalarVal();
CallArgs[AA->paramIndex().getLLVMIndex()].RV.getScalarVal();
EmitAlignmentAssumption(Ret.getScalarVal(), ParamVal);
}
}

View File

@ -2619,12 +2619,13 @@ static void CheckNonNullArguments(Sema &S,
return;
}
for (unsigned Val : NonNull->args()) {
if (Val >= Args.size())
for (const ParamIdx &Idx : NonNull->args()) {
unsigned IdxAST = Idx.getASTIndex();
if (IdxAST >= Args.size())
continue;
if (NonNullArgs.empty())
NonNullArgs.resize(Args.size());
NonNullArgs.set(Val);
NonNullArgs.set(IdxAST);
}
}
}
@ -5002,12 +5003,7 @@ checkFormatStringExpr(Sema &S, const Expr *E, ArrayRef<const Expr *> Args,
const CallExpr *CE = cast<CallExpr>(E);
if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(CE->getCalleeDecl())) {
if (const FormatArgAttr *FA = ND->getAttr<FormatArgAttr>()) {
unsigned ArgIndex = FA->getFormatIdx();
if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(ND))
if (MD->isInstance())
--ArgIndex;
const Expr *Arg = CE->getArg(ArgIndex - 1);
const Expr *Arg = CE->getArg(FA->formatIdx().getASTIndex());
return checkFormatStringExpr(S, Arg, Args,
HasVAListArg, format_idx, firstDataArg,
Type, CallType, InFunctionCall,
@ -5032,8 +5028,7 @@ checkFormatStringExpr(Sema &S, const Expr *E, ArrayRef<const Expr *> Args,
const auto *ME = cast<ObjCMessageExpr>(E);
if (const auto *ND = ME->getMethodDecl()) {
if (const auto *FA = ND->getAttr<FormatArgAttr>()) {
unsigned ArgIndex = FA->getFormatIdx();
const Expr *Arg = ME->getArg(ArgIndex - 1);
const Expr *Arg = ME->getArg(FA->formatIdx().getASTIndex());
return checkFormatStringExpr(
S, Arg, Args, HasVAListArg, format_idx, firstDataArg, Type,
CallType, InFunctionCall, CheckedVarArgs, UncoveredArg, Offset);
@ -10086,8 +10081,8 @@ void Sema::DiagnoseAlwaysNonNullPointer(Expr *E,
return;
}
for (unsigned ArgNo : NonNull->args()) {
if (ArgNo == ParamNo) {
for (const ParamIdx &ArgNo : NonNull->args()) {
if (ArgNo.getASTIndex() == ParamNo) {
ComplainAboutNonnullParamOrCall(NonNull);
return;
}
@ -12242,13 +12237,13 @@ void Sema::CheckArgumentWithTypeTag(const ArgumentWithTypeTagAttr *Attr,
bool IsPointerAttr = Attr->getIsPointer();
// Retrieve the argument representing the 'type_tag'.
if (Attr->getTypeTagIdx() >= ExprArgs.size()) {
// Add 1 to display the user's specified value.
unsigned TypeTagIdxAST = Attr->typeTagIdx().getASTIndex();
if (TypeTagIdxAST >= ExprArgs.size()) {
Diag(CallSiteLoc, diag::err_tag_index_out_of_range)
<< 0 << Attr->getTypeTagIdx() + 1;
<< 0 << Attr->typeTagIdx().getSourceIndex();
return;
}
const Expr *TypeTagExpr = ExprArgs[Attr->getTypeTagIdx()];
const Expr *TypeTagExpr = ExprArgs[TypeTagIdxAST];
bool FoundWrongKind;
TypeTagData TypeInfo;
if (!GetMatchingCType(ArgumentKind, TypeTagExpr, Context,
@ -12262,13 +12257,13 @@ void Sema::CheckArgumentWithTypeTag(const ArgumentWithTypeTagAttr *Attr,
}
// Retrieve the argument representing the 'arg_idx'.
if (Attr->getArgumentIdx() >= ExprArgs.size()) {
// Add 1 to display the user's specified value.
unsigned ArgumentIdxAST = Attr->argumentIdx().getASTIndex();
if (ArgumentIdxAST >= ExprArgs.size()) {
Diag(CallSiteLoc, diag::err_tag_index_out_of_range)
<< 1 << Attr->getArgumentIdx() + 1;
<< 1 << Attr->argumentIdx().getSourceIndex();
return;
}
const Expr *ArgumentExpr = ExprArgs[Attr->getArgumentIdx()];
const Expr *ArgumentExpr = ExprArgs[ArgumentIdxAST];
if (IsPointerAttr) {
// Skip implicit cast of pointer to `void *' (as a function argument).
if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(ArgumentExpr))

View File

@ -13176,7 +13176,7 @@ void Sema::AddKnownFunctionAttributes(FunctionDecl *FD) {
// We already have a __builtin___CFStringMakeConstantString,
// but builds that use -fno-constant-cfstrings don't go through that.
if (!FD->hasAttr<FormatArgAttr>())
FD->addAttr(FormatArgAttr::CreateImplicit(Context, 1,
FD->addAttr(FormatArgAttr::CreateImplicit(Context, ParamIdx(1, FD),
FD->getLocation()));
}
}

View File

@ -311,7 +311,7 @@ static bool checkAttrMutualExclusion(Sema &S, Decl *D, SourceRange Range,
template <typename AttrInfo>
static bool checkFunctionOrMethodParameterIndex(
Sema &S, const Decl *D, const AttrInfo &AI, unsigned AttrArgNum,
const Expr *IdxExpr, uint64_t &Idx, bool AllowImplicitThis = false) {
const Expr *IdxExpr, ParamIdx &Idx, bool CanIndexImplicitThis = false) {
assert(isFunctionOrMethodOrBlock(D));
// In C++ the implicit 'this' function parameter also counts.
@ -331,21 +331,20 @@ static bool checkFunctionOrMethodParameterIndex(
return false;
}
Idx = IdxInt.getLimitedValue();
if (Idx < 1 || (!IV && Idx > NumParams)) {
Idx = ParamIdx(IdxInt.getLimitedValue(UINT_MAX), D);
unsigned IdxSource = Idx.getSourceIndex();
if (IdxSource < 1 || (!IV && IdxSource > NumParams)) {
S.Diag(getAttrLoc(AI), diag::err_attribute_argument_out_of_bounds)
<< getAttrName(AI) << AttrArgNum << IdxExpr->getSourceRange();
<< getAttrName(AI) << AttrArgNum << IdxExpr->getSourceRange();
return false;
}
Idx--; // Convert to zero-based.
if (HasImplicitThisParam && !AllowImplicitThis) {
if (Idx == 0) {
if (HasImplicitThisParam && !CanIndexImplicitThis) {
if (IdxSource == 1) {
S.Diag(getAttrLoc(AI),
diag::err_attribute_invalid_implicit_this_argument)
<< getAttrName(AI) << IdxExpr->getSourceRange();
<< getAttrName(AI) << IdxExpr->getSourceRange();
return false;
}
--Idx;
}
return true;
@ -772,18 +771,15 @@ static void handleAssertExclusiveLockAttr(Sema &S, Decl *D,
/// AttrArgNo is used to actually retrieve the argument, so it's base-0.
template <typename AttrInfo>
static bool checkParamIsIntegerType(Sema &S, const FunctionDecl *FD,
const AttrInfo &AI, unsigned AttrArgNo,
bool AllowDependentType = false) {
const AttrInfo &AI, unsigned AttrArgNo) {
assert(AI.isArgExpr(AttrArgNo) && "Expected expression argument");
Expr *AttrArg = AI.getArgAsExpr(AttrArgNo);
uint64_t Idx;
ParamIdx Idx;
if (!checkFunctionOrMethodParameterIndex(S, FD, AI, AttrArgNo + 1, AttrArg,
Idx))
return false;
const ParmVarDecl *Param = FD->getParamDecl(Idx);
if (AllowDependentType && Param->getType()->isDependentType())
return true;
const ParmVarDecl *Param = FD->getParamDecl(Idx.getASTIndex());
if (!Param->getType()->isIntegerType() && !Param->getType()->isCharType()) {
SourceLocation SrcLoc = AttrArg->getLocStart();
S.Diag(SrcLoc, diag::err_attribute_integers_only)
@ -806,22 +802,23 @@ static void handleAllocSizeAttr(Sema &S, Decl *D, const AttributeList &AL) {
}
const Expr *SizeExpr = AL.getArgAsExpr(0);
int SizeArgNo;
int SizeArgNoVal;
// Parameter indices are 1-indexed, hence Index=1
if (!checkPositiveIntArgument(S, AL, SizeExpr, SizeArgNo, /*Index=*/1))
if (!checkPositiveIntArgument(S, AL, SizeExpr, SizeArgNoVal, /*Index=*/1))
return;
ParamIdx SizeArgNo(SizeArgNoVal, D);
if (!checkParamIsIntegerType(S, FD, AL, /*AttrArgNo=*/0))
return;
// Args are 1-indexed, so 0 implies that the arg was not present
int NumberArgNo = 0;
ParamIdx NumberArgNo;
if (AL.getNumArgs() == 2) {
const Expr *NumberExpr = AL.getArgAsExpr(1);
int Val;
// Parameter indices are 1-based, hence Index=2
if (!checkPositiveIntArgument(S, AL, NumberExpr, NumberArgNo,
/*Index=*/2))
if (!checkPositiveIntArgument(S, AL, NumberExpr, Val, /*Index=*/2))
return;
NumberArgNo = ParamIdx(Val, D);
if (!checkParamIsIntegerType(S, FD, AL, /*AttrArgNo=*/1))
return;
@ -1424,18 +1421,19 @@ static bool attrNonNullArgCheck(Sema &S, QualType T, const AttributeList &AL,
}
static void handleNonNullAttr(Sema &S, Decl *D, const AttributeList &AL) {
SmallVector<unsigned, 8> NonNullArgs;
SmallVector<ParamIdx, 8> NonNullArgs;
for (unsigned I = 0; I < AL.getNumArgs(); ++I) {
Expr *Ex = AL.getArgAsExpr(I);
uint64_t Idx;
ParamIdx Idx;
if (!checkFunctionOrMethodParameterIndex(S, D, AL, I + 1, Ex, Idx))
return;
// Is the function argument a pointer type?
if (Idx < getFunctionOrMethodNumParams(D) &&
!attrNonNullArgCheck(S, getFunctionOrMethodParamType(D, Idx), AL,
Ex->getSourceRange(),
getFunctionOrMethodParamRange(D, Idx)))
if (Idx.getASTIndex() < getFunctionOrMethodNumParams(D) &&
!attrNonNullArgCheck(
S, getFunctionOrMethodParamType(D, Idx.getASTIndex()), AL,
Ex->getSourceRange(),
getFunctionOrMethodParamRange(D, Idx.getASTIndex())))
continue;
NonNullArgs.push_back(Idx);
@ -1459,12 +1457,12 @@ static void handleNonNullAttr(Sema &S, Decl *D, const AttributeList &AL) {
S.Diag(AL.getLoc(), diag::warn_attribute_nonnull_no_pointers);
}
unsigned *Start = NonNullArgs.data();
ParamIdx *Start = NonNullArgs.data();
unsigned Size = NonNullArgs.size();
llvm::array_pod_sort(Start, Start + Size);
D->addAttr(::new (S.Context)
NonNullAttr(AL.getRange(), S.Context, Start, Size,
AL.getAttributeSpellingListIndex()));
NonNullAttr(AL.getRange(), S.Context, Start, Size,
AL.getAttributeSpellingListIndex()));
}
static void handleNonNullAttrParameter(Sema &S, ParmVarDecl *D,
@ -1485,8 +1483,8 @@ static void handleNonNullAttrParameter(Sema &S, ParmVarDecl *D,
return;
D->addAttr(::new (S.Context)
NonNullAttr(AL.getRange(), S.Context, nullptr, 0,
AL.getAttributeSpellingListIndex()));
NonNullAttr(AL.getRange(), S.Context, nullptr, 0,
AL.getAttributeSpellingListIndex()));
}
static void handleReturnsNonNullAttr(Sema &S, Decl *D,
@ -1587,7 +1585,7 @@ void Sema::AddAllocAlignAttr(SourceRange AttrRange, Decl *D, Expr *ParamExpr,
unsigned SpellingListIndex) {
QualType ResultType = getFunctionOrMethodResultType(D);
AllocAlignAttr TmpAttr(AttrRange, Context, 0, SpellingListIndex);
AllocAlignAttr TmpAttr(AttrRange, Context, ParamIdx(), SpellingListIndex);
SourceLocation AttrLoc = AttrRange.getBegin();
if (!ResultType->isDependentType() &&
@ -1597,28 +1595,22 @@ void Sema::AddAllocAlignAttr(SourceRange AttrRange, Decl *D, Expr *ParamExpr,
return;
}
uint64_t IndexVal;
ParamIdx Idx;
const auto *FuncDecl = cast<FunctionDecl>(D);
if (!checkFunctionOrMethodParameterIndex(*this, FuncDecl, TmpAttr,
/*AttrArgNo=*/1, ParamExpr,
IndexVal))
/*AttrArgNo=*/1, ParamExpr, Idx))
return;
QualType Ty = getFunctionOrMethodParamType(D, IndexVal);
QualType Ty = getFunctionOrMethodParamType(D, Idx.getASTIndex());
if (!Ty->isDependentType() && !Ty->isIntegralType(Context)) {
Diag(ParamExpr->getLocStart(), diag::err_attribute_integers_only)
<< &TmpAttr << FuncDecl->getParamDecl(IndexVal)->getSourceRange();
<< &TmpAttr
<< FuncDecl->getParamDecl(Idx.getASTIndex())->getSourceRange();
return;
}
// We cannot use the Idx returned from checkFunctionOrMethodParameterIndex
// because that has corrected for the implicit this parameter, and is zero-
// based. The attribute expects what the user wrote explicitly.
llvm::APSInt Val;
ParamExpr->EvaluateAsInt(Val, Context);
D->addAttr(::new (Context) AllocAlignAttr(
AttrRange, Context, Val.getZExtValue(), SpellingListIndex));
D->addAttr(::new (Context)
AllocAlignAttr(AttrRange, Context, Idx, SpellingListIndex));
}
/// Normalize the attribute, __foo__ becomes foo.
@ -1678,15 +1670,15 @@ static void handleOwnershipAttr(Sema &S, Decl *D, const AttributeList &AL) {
Module = &S.PP.getIdentifierTable().get(ModuleName);
}
SmallVector<unsigned, 8> OwnershipArgs;
SmallVector<ParamIdx, 8> OwnershipArgs;
for (unsigned i = 1; i < AL.getNumArgs(); ++i) {
Expr *Ex = AL.getArgAsExpr(i);
uint64_t Idx;
ParamIdx Idx;
if (!checkFunctionOrMethodParameterIndex(S, D, AL, i, Ex, Idx))
return;
// Is the function argument a pointer type?
QualType T = getFunctionOrMethodParamType(D, Idx);
QualType T = getFunctionOrMethodParamType(D, Idx.getASTIndex());
int Err = -1; // No error
switch (K) {
case OwnershipAttr::Takes:
@ -1717,14 +1709,13 @@ static void handleOwnershipAttr(Sema &S, Decl *D, const AttributeList &AL) {
} else if (K == OwnershipAttr::Returns &&
I->getOwnKind() == OwnershipAttr::Returns) {
// A returns attribute conflicts with any other returns attribute using
// a different index. Note, diagnostic reporting is 1-based, but stored
// argument indexes are 0-based.
// a different index.
if (std::find(I->args_begin(), I->args_end(), Idx) == I->args_end()) {
S.Diag(I->getLocation(), diag::err_ownership_returns_index_mismatch)
<< *(I->args_begin()) + 1;
<< I->args_begin()->getSourceIndex();
if (I->args_size())
S.Diag(AL.getLoc(), diag::note_ownership_returns_index_mismatch)
<< (unsigned)Idx + 1 << Ex->getSourceRange();
<< Idx.getSourceIndex() << Ex->getSourceRange();
return;
}
}
@ -1732,13 +1723,12 @@ static void handleOwnershipAttr(Sema &S, Decl *D, const AttributeList &AL) {
OwnershipArgs.push_back(Idx);
}
unsigned* start = OwnershipArgs.data();
unsigned size = OwnershipArgs.size();
llvm::array_pod_sort(start, start + size);
ParamIdx *Start = OwnershipArgs.data();
unsigned Size = OwnershipArgs.size();
llvm::array_pod_sort(Start, Start + Size);
D->addAttr(::new (S.Context)
OwnershipAttr(AL.getLoc(), S.Context, Module, start, size,
AL.getAttributeSpellingListIndex()));
OwnershipAttr(AL.getLoc(), S.Context, Module, Start, Size,
AL.getAttributeSpellingListIndex()));
}
static void handleWeakRefAttr(Sema &S, Decl *D, const AttributeList &AL) {
@ -3109,12 +3099,12 @@ static void handleEnumExtensibilityAttr(Sema &S, Decl *D,
/// http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html
static void handleFormatArgAttr(Sema &S, Decl *D, const AttributeList &AL) {
Expr *IdxExpr = AL.getArgAsExpr(0);
uint64_t Idx;
ParamIdx Idx;
if (!checkFunctionOrMethodParameterIndex(S, D, AL, 1, IdxExpr, Idx))
return;
// Make sure the format string is really a string.
QualType Ty = getFunctionOrMethodParamType(D, Idx);
QualType Ty = getFunctionOrMethodParamType(D, Idx.getASTIndex());
bool NotNSStringTy = !isNSStringType(Ty, S.Context);
if (NotNSStringTy &&
@ -3137,15 +3127,8 @@ static void handleFormatArgAttr(Sema &S, Decl *D, const AttributeList &AL) {
return;
}
// We cannot use the Idx returned from checkFunctionOrMethodParameterIndex
// because that has corrected for the implicit this parameter, and is zero-
// based. The attribute expects what the user wrote explicitly.
llvm::APSInt Val;
IdxExpr->EvaluateAsInt(Val, S.Context);
D->addAttr(::new (S.Context)
FormatArgAttr(AL.getRange(), S.Context, Val.getZExtValue(),
AL.getAttributeSpellingListIndex()));
D->addAttr(::new (S.Context) FormatArgAttr(
AL.getRange(), S.Context, Idx, AL.getAttributeSpellingListIndex()));
}
enum FormatAttrKind {
@ -4539,13 +4522,13 @@ static void handleArgumentWithTypeTagAttr(Sema &S, Decl *D,
<< AL.getName() << /* arg num = */ 1 << AANT_ArgumentIdentifier;
return;
}
uint64_t ArgumentIdx;
ParamIdx ArgumentIdx;
if (!checkFunctionOrMethodParameterIndex(S, D, AL, 2, AL.getArgAsExpr(1),
ArgumentIdx))
return;
uint64_t TypeTagIdx;
ParamIdx TypeTagIdx;
if (!checkFunctionOrMethodParameterIndex(S, D, AL, 3, AL.getArgAsExpr(2),
TypeTagIdx))
return;
@ -4553,8 +4536,9 @@ static void handleArgumentWithTypeTagAttr(Sema &S, Decl *D,
bool IsPointer = AL.getName()->getName() == "pointer_with_type_tag";
if (IsPointer) {
// Ensure that buffer has a pointer type.
if (ArgumentIdx >= getFunctionOrMethodNumParams(D) ||
!getFunctionOrMethodParamType(D, ArgumentIdx)->isPointerType())
unsigned ArgumentIdxAST = ArgumentIdx.getASTIndex();
if (ArgumentIdxAST >= getFunctionOrMethodNumParams(D) ||
!getFunctionOrMethodParamType(D, ArgumentIdxAST)->isPointerType())
S.Diag(AL.getLoc(), diag::err_attribute_pointers_only)
<< AL.getName() << 0;
}
@ -4594,19 +4578,18 @@ static void handleTypeTagForDatatypeAttr(Sema &S, Decl *D,
AL.getAttributeSpellingListIndex()));
}
static void handleXRayLogArgsAttr(Sema &S, Decl *D,
const AttributeList &AL) {
uint64_t ArgCount;
static void handleXRayLogArgsAttr(Sema &S, Decl *D, const AttributeList &AL) {
ParamIdx ArgCount;
if (!checkFunctionOrMethodParameterIndex(S, D, AL, 1, AL.getArgAsExpr(0),
ArgCount,
true /* AllowImplicitThis*/))
true /* CanIndexImplicitThis */))
return;
// ArgCount isn't a parameter index [0;n), it's a count [1;n] - hence + 1.
D->addAttr(::new (S.Context)
XRayLogArgsAttr(AL.getRange(), S.Context, ++ArgCount,
AL.getAttributeSpellingListIndex()));
// ArgCount isn't a parameter index [0;n), it's a count [1;n]
D->addAttr(::new (S.Context) XRayLogArgsAttr(
AL.getRange(), S.Context, ArgCount.getSourceIndex(),
AL.getAttributeSpellingListIndex()));
}
//===----------------------------------------------------------------------===//

View File

@ -176,7 +176,7 @@ static void instantiateDependentAllocAlignAttr(
Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs,
const AllocAlignAttr *Align, Decl *New) {
Expr *Param = IntegerLiteral::Create(
S.getASTContext(), llvm::APInt(64, Align->getParamIndex()),
S.getASTContext(), llvm::APInt(64, Align->paramIndex().getSourceIndex()),
S.getASTContext().UnsignedLongLongTy, Align->getLocation());
S.AddAllocAlignAttr(Align->getLocation(), New, Param,
Align->getSpellingListIndex());

View File

@ -1231,9 +1231,10 @@ MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE,
if (Att->getModule() != II_malloc)
return nullptr;
OwnershipAttr::args_iterator I = Att->args_begin(), E = Att->args_end();
ParamIdx *I = Att->args_begin(), *E = Att->args_end();
if (I != E) {
return MallocMemAux(C, CE, CE->getArg(*I), UndefinedVal(), State);
return MallocMemAux(C, CE, CE->getArg(I->getASTIndex()), UndefinedVal(),
State);
}
return MallocMemAux(C, CE, UnknownVal(), UndefinedVal(), State);
}
@ -1331,9 +1332,9 @@ ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C,
bool ReleasedAllocated = false;
for (const auto &Arg : Att->args()) {
ProgramStateRef StateI = FreeMemAux(C, CE, State, Arg,
Att->getOwnKind() == OwnershipAttr::Holds,
ReleasedAllocated);
ProgramStateRef StateI = FreeMemAux(
C, CE, State, Arg.getASTIndex(),
Att->getOwnKind() == OwnershipAttr::Holds, ReleasedAllocated);
if (StateI)
State = StateI;
}

View File

@ -58,10 +58,11 @@ void NonNullParamChecker::checkPreCall(const CallEvent &Call,
AttrNonNull.set(0, NumArgs);
break;
}
for (unsigned Val : NonNull->args()) {
if (Val >= NumArgs)
for (const ParamIdx &Idx : NonNull->args()) {
unsigned IdxAST = Idx.getASTIndex();
if (IdxAST >= NumArgs)
continue;
AttrNonNull.set(Val);
AttrNonNull.set(IdxAST);
}
}

View File

@ -69,4 +69,22 @@ int testIt() {
__builtin_object_size(dependent_calloc<7, 8>(), 0) +
__builtin_object_size(dependent_calloc2<int, 9>(), 0);
}
} // namespace templated_alloc_size
class C {
public:
void *my_malloc(int N) __attribute__((alloc_size(2)));
void *my_calloc(int N, int M) __attribute__((alloc_size(2, 3)));
};
// CHECK-LABEL: define i32 @_Z16callMemberMallocv
int callMemberMalloc() {
// CHECK: ret i32 16
return __builtin_object_size(C().my_malloc(16), 0);
}
// CHECK-LABEL: define i32 @_Z16callMemberCallocv
int callMemberCalloc() {
// CHECK: ret i32 32
return __builtin_object_size(C().my_calloc(16, 2), 0);
}

View File

@ -68,12 +68,12 @@ __attribute__((pointer_with_type_tag(ident1,1,2)));
void TestBool(void *, int)
__attribute__((pointer_with_type_tag(bool1,1,2)));
// CHECK: FunctionDecl{{.*}}TestBool
// CHECK: ArgumentWithTypeTagAttr{{.*}}pointer_with_type_tag bool1 0 1 IsPointer
// CHECK: ArgumentWithTypeTagAttr{{.*}}pointer_with_type_tag bool1 1 2 IsPointer
void TestUnsigned(void *, int)
__attribute__((pointer_with_type_tag(unsigned1,1,2)));
// CHECK: FunctionDecl{{.*}}TestUnsigned
// CHECK: ArgumentWithTypeTagAttr{{.*}} pointer_with_type_tag unsigned1 0 1
// CHECK: ArgumentWithTypeTagAttr{{.*}} pointer_with_type_tag unsigned1 1 2
void TestInt(void) __attribute__((constructor(123)));
// CHECK: FunctionDecl{{.*}}TestInt

View File

@ -0,0 +1,7 @@
// RUN: %clang_cc1 %s -verify -fsyntax-only
class C {
void f(int, int)
__attribute__((ownership_returns(foo, 2))) // expected-note {{declared with index 2 here}}
__attribute__((ownership_returns(foo, 3))); // expected-error {{'ownership_returns' attribute index does not match; here it is 3}}
};

View File

@ -1,6 +1,67 @@
// RUN: %clang_cc1 %s -ast-print | FileCheck %s
// CHECK: void xla(int a) __attribute__((xray_log_args(1)));
void xla(int a) __attribute__((xray_log_args(1)));
// CHECK: void *as2(int, int) __attribute__((alloc_size(1, 2)));
void *as2(int, int) __attribute__((alloc_size(1, 2)));
// CHECK: void *as1(void *, int) __attribute__((alloc_size(2)));
void *as1(void *, int) __attribute__((alloc_size(2)));
// CHECK: void fmt(int, const char *, ...) __attribute__((format(printf, 2, 3)));
void fmt(int, const char *, ...) __attribute__((format(printf, 2, 3)));
// CHECK: char *fmta(int, const char *) __attribute__((format_arg(2)));
char *fmta(int, const char *) __attribute__((format_arg(2)));
// CHECK: void nn(int *, int *) __attribute__((nonnull(1, 2)));
void nn(int *, int *) __attribute__((nonnull(1, 2)));
// CHECK: int *aa(int i) __attribute__((alloc_align(1)));
int *aa(int i) __attribute__((alloc_align(1)));
// CHECK: void ownt(int *, int *) __attribute__((ownership_takes(foo, 1, 2)));
void ownt(int *, int *) __attribute__((ownership_takes(foo, 1, 2)));
// CHECK: void ownh(int *, int *) __attribute__((ownership_holds(foo, 1, 2)));
void ownh(int *, int *) __attribute__((ownership_holds(foo, 1, 2)));
// CHECK: void ownr(int) __attribute__((ownership_returns(foo, 1)));
void ownr(int) __attribute__((ownership_returns(foo, 1)));
// CHECK: void awtt(int, int, ...) __attribute__((argument_with_type_tag(foo, 3, 2)));
void awtt(int, int, ...) __attribute__((argument_with_type_tag(foo, 3, 2)));
// CHECK: void pwtt(void *, int) __attribute__((pointer_with_type_tag(foo, 1, 2)));
void pwtt(void *, int) __attribute__((pointer_with_type_tag(foo, 1, 2)));
class C {
// CHECK: void xla(int a) __attribute__((xray_log_args(2)));
void xla(int a) __attribute__((xray_log_args(2)));
// CHECK: void *as2(int, int) __attribute__((alloc_size(2, 3)));
void *as2(int, int) __attribute__((alloc_size(2, 3)));
// CHECK: void *as1(void *, int) __attribute__((alloc_size(3)));
void *as1(void *, int) __attribute__((alloc_size(3)));
// CHECK: void fmt(int, const char *, ...) __attribute__((format(printf, 3, 4)));
void fmt(int, const char *, ...) __attribute__((format(printf, 3, 4)));
// CHECK: char *fmta(int, const char *) __attribute__((format_arg(3)));
char *fmta(int, const char *) __attribute__((format_arg(3)));
// CHECK: void nn(int *, int *) __attribute__((nonnull(2, 3)));
void nn(int *, int *) __attribute__((nonnull(2, 3)));
// CHECK: int *aa(int i) __attribute__((alloc_align(2)));
int *aa(int i) __attribute__((alloc_align(2)));
// CHECK: void ownt(int *, int *) __attribute__((ownership_takes(foo, 2, 3)));
void ownt(int *, int *) __attribute__((ownership_takes(foo, 2, 3)));
// CHECK: void ownh(int *, int *) __attribute__((ownership_holds(foo, 2, 3)));
void ownh(int *, int *) __attribute__((ownership_holds(foo, 2, 3)));
// CHECK: void ownr(int) __attribute__((ownership_returns(foo, 2)));
void ownr(int) __attribute__((ownership_returns(foo, 2)));
// CHECK: void awtt(int, int, ...) __attribute__((argument_with_type_tag(foo, 4, 3)));
void awtt(int, int, ...) __attribute__((argument_with_type_tag(foo, 4, 3)));
// CHECK: void pwtt(void *, int) __attribute__((pointer_with_type_tag(foo, 2, 3)));
void pwtt(void *, int) __attribute__((pointer_with_type_tag(foo, 2, 3)));
};

View File

@ -3,21 +3,50 @@
#define INT_TAG 42
static const int test_in
__attribute__((type_tag_for_datatype(test, int))) = INT_TAG;
__attribute__((type_tag_for_datatype(test, int))) = INT_TAG;
// Argument index: 1, Type tag index: 2
void test_bounds_index(...)
__attribute__((argument_with_type_tag(test, 1, 2)));
__attribute__((argument_with_type_tag(test, 1, 2)));
// Argument index: 1, Type tag index: 2
void test_bounds_index_ptr(void *, ...)
__attribute__((pointer_with_type_tag(test, 1, 2)));
// Argument index: 3, Type tag index: 1
void test_bounds_arg_index(...)
__attribute__((argument_with_type_tag(test, 3, 1)));
__attribute__((argument_with_type_tag(test, 3, 1)));
class C {
public:
// Argument index: 2, Type tag index: 3
void test_bounds_index(...)
__attribute__((argument_with_type_tag(test, 2, 3)));
// Argument index: 2, Type tag index: 3
void test_bounds_index_ptr(void *, ...)
__attribute__((pointer_with_type_tag(test, 2, 3)));
// Argument index: 4, Type tag index: 2
void test_bounds_arg_index(...)
__attribute__((argument_with_type_tag(test, 4, 2)));
};
void test_bounds()
{
C c;
// Test the boundary edges (ensure no off-by-one) with argument indexing.
test_bounds_index(1, INT_TAG);
c.test_bounds_index(1, INT_TAG);
test_bounds_index_ptr(0, INT_TAG);
c.test_bounds_index_ptr(0, INT_TAG);
test_bounds_index(1); // expected-error {{type tag index 2 is greater than the number of arguments specified}}
test_bounds_arg_index(INT_TAG, 1); // expected-error {{argument index 3 is greater than the number of arguments specified}}
test_bounds_index(1); // expected-error {{type tag index 2 is greater than the number of arguments specified}}
c.test_bounds_index(1); // expected-error {{type tag index 3 is greater than the number of arguments specified}}
test_bounds_index_ptr(0); // expected-error {{type tag index 2 is greater than the number of arguments specified}}
c.test_bounds_index_ptr(0); // expected-error {{type tag index 3 is greater than the number of arguments specified}}
test_bounds_arg_index(INT_TAG, 1); // expected-error {{argument index 3 is greater than the number of arguments specified}}
c.test_bounds_arg_index(INT_TAG, 1); // expected-error {{argument index 4 is greater than the number of arguments specified}}
}

View File

@ -302,9 +302,6 @@ namespace {
std::string getIsOmitted() const override {
if (type == "IdentifierInfo *")
return "!get" + getUpperName().str() + "()";
// FIXME: Do this declaratively in Attr.td.
if (getAttrName() == "AllocSize")
return "0 == get" + getUpperName().str() + "()";
return "false";
}
@ -748,6 +745,138 @@ namespace {
}
};
class VariadicParamIdxArgument : public VariadicArgument {
public:
VariadicParamIdxArgument(const Record &Arg, StringRef Attr)
: VariadicArgument(Arg, Attr, "ParamIdx") {}
public:
void writeCtorBody(raw_ostream &OS) const override {
VariadicArgument::writeCtorBody(OS);
OS << " #ifndef NDEBUG\n"
<< " if (" << getLowerName() << "_size()) {\n"
<< " bool HasThis = " << getLowerName()
<< "_begin()->hasThis();\n"
<< " for (const auto Idx : " << getLowerName() << "()) {\n"
<< " assert(Idx.isValid() && \"ParamIdx must be valid\");\n"
<< " assert(HasThis == Idx.hasThis() && "
<< "\"HasThis must be consistent\");\n"
<< " }\n"
<< " }\n"
<< " #endif\n";
}
void writePCHReadDecls(raw_ostream &OS) const override {
OS << " unsigned " << getUpperName() << "Size = Record.readInt();\n";
OS << " bool " << getUpperName() << "HasThis = " << getUpperName()
<< "Size ? Record.readInt() : false;\n";
OS << " SmallVector<ParamIdx, 4> " << getUpperName() << ";\n"
<< " " << getUpperName() << ".reserve(" << getUpperName()
<< "Size);\n"
<< " for (unsigned i = 0; i != " << getUpperName()
<< "Size; ++i) {\n"
<< " " << getUpperName()
<< ".push_back(ParamIdx(Record.readInt(), " << getUpperName()
<< "HasThis));\n"
<< " }\n";
}
void writePCHReadArgs(raw_ostream &OS) const override {
OS << getUpperName() << ".data(), " << getUpperName() << "Size";
}
void writePCHWrite(raw_ostream &OS) const override {
OS << " Record.push_back(SA->" << getLowerName() << "_size());\n";
OS << " if (SA->" << getLowerName() << "_size())\n"
<< " Record.push_back(SA->" << getLowerName()
<< "_begin()->hasThis());\n";
OS << " for (auto Idx : SA->" << getLowerName() << "())\n"
<< " Record.push_back(Idx.getSourceIndex());\n";
}
void writeValueImpl(raw_ostream &OS) const override {
OS << " OS << Val.getSourceIndex();\n";
}
void writeDump(raw_ostream &OS) const override {
OS << " for (auto Idx : SA->" << getLowerName() << "())\n";
OS << " OS << \" \" << Idx.getSourceIndex();\n";
}
};
class ParamIdxArgument : public Argument {
std::string IdxName;
public:
ParamIdxArgument(const Record &Arg, StringRef Attr)
: Argument(Arg, Attr), IdxName(getUpperName()) {}
void writeDeclarations(raw_ostream &OS) const override {
OS << "ParamIdx " << IdxName << ";\n";
}
void writeAccessors(raw_ostream &OS) const override {
OS << "\n"
<< " ParamIdx " << getLowerName() << "() const {"
<< " return " << IdxName << "; }\n";
}
void writeCtorParameters(raw_ostream &OS) const override {
OS << "ParamIdx " << IdxName;
}
void writeCloneArgs(raw_ostream &OS) const override { OS << IdxName; }
void writeTemplateInstantiationArgs(raw_ostream &OS) const override {
OS << "A->" << getLowerName() << "()";
}
void writeImplicitCtorArgs(raw_ostream &OS) const override {
OS << IdxName;
}
void writeCtorInitializers(raw_ostream &OS) const override {
OS << IdxName << "(" << IdxName << ")";
}
void writeCtorDefaultInitializers(raw_ostream &OS) const override {
OS << IdxName << "()";
}
void writePCHReadDecls(raw_ostream &OS) const override {
OS << " unsigned " << IdxName << "Src = Record.readInt();\n";
OS << " bool " << IdxName << "HasThis = Record.readInt();\n";
}
void writePCHReadArgs(raw_ostream &OS) const override {
OS << "ParamIdx(" << IdxName << "Src, " << IdxName << "HasThis)";
}
void writePCHWrite(raw_ostream &OS) const override {
OS << " Record.push_back(SA->" << getLowerName()
<< "().isValid() ? SA->" << getLowerName()
<< "().getSourceIndex() : 0);\n";
OS << " Record.push_back(SA->" << getLowerName()
<< "().isValid() ? SA->" << getLowerName()
<< "().hasThis() : false);\n";
}
std::string getIsOmitted() const override {
return "!" + IdxName + ".isValid()";
}
void writeValue(raw_ostream &OS) const override {
OS << "\" << " << IdxName << ".getSourceIndex() << \"";
}
void writeDump(raw_ostream &OS) const override {
if (isOptional())
OS << " if (SA->" << getLowerName() << "().isValid())\n ";
OS << " OS << \" \" << SA->" << getLowerName()
<< "().getSourceIndex();\n";
}
};
// Unique the enums, but maintain the original declaration ordering.
std::vector<StringRef>
uniqueEnumsInOrder(const std::vector<StringRef> &enums) {
@ -1247,6 +1376,10 @@ createArgument(const Record &Arg, StringRef Attr,
Ptr = llvm::make_unique<VariadicEnumArgument>(Arg, Attr);
else if (ArgName == "VariadicExprArgument")
Ptr = llvm::make_unique<VariadicExprArgument>(Arg, Attr);
else if (ArgName == "VariadicParamIdxArgument")
Ptr = llvm::make_unique<VariadicParamIdxArgument>(Arg, Attr);
else if (ArgName == "ParamIdxArgument")
Ptr = llvm::make_unique<ParamIdxArgument>(Arg, Attr);
else if (ArgName == "VersionArgument")
Ptr = llvm::make_unique<VersionArgument>(Arg, Attr);