let ASTNode = 0;
}
+def NoDeref : TypeAttr {
+ let Spellings = [Clang<"noderef">];
+ let Documentation = [NoDerefDocs];
+}
+
def ReqdWorkGroupSize : InheritableAttr {
// Does not have a [[]] spelling because it is an OpenCL-related attribute.
let Spellings = [GNU<"reqd_work_group_size">];
}];
}
+def NoDerefDocs : Documentation {
+ let Category = DocCatType;
+ let Content = [{
+The ``noderef`` attribute causes clang to diagnose dereferences of annotated pointer types.
+This is ideally used with pointers that point to special memory which cannot be read
+from or written to, but allowing for the pointer to be used in pointer arithmetic.
+The following are examples of valid expressions where dereferences are diagnosed:
+
+.. code-block:: c
+
+ int __attribute__((noderef)) *p;
+ int x = *p; // warning
+
+ int __attribute__((noderef)) **p2;
+ x = **p2; // warning
+
+ int * __attribute__((noderef)) *p3;
+ p = *p3; // warning
+
+ struct S {
+ int a;
+ };
+ struct S __attribute__((noderef)) *s;
+ x = s->a; // warning
+ x = (*s).a; // warning
+
+Not all dereferences may diagnose a warning if the value directed by the pointer may not be
+accessed. The following are examples of valid expressions where may not be diagnosed:
+
+.. code-block:: c
+
+ int *q;
+ int __attribute__((noderef)) *p;
+ q = &*p;
+ q = *&p;
+
+ struct S {
+ int a;
+ };
+ struct S __attribute__((noderef)) *s;
+ p = &s->a;
+ p = &(*s).a;
+
+``noderef`` is currently only supported for pointers and arrays and not usable for
+references or Objective-C object pointers.
+
+.. code-block: c++
+
+ int x = 2;
+ int __attribute__((noderef)) &y = x; // warning: 'noderef' can only be used on an array or pointer type
+
+.. code-block: objc
+
+ id __attribute__((noderef)) obj = [NSObject new]; // warning: 'noderef' can only be used on an array or pointer type
+}];
+}
+
def ReinitializesDocs : Documentation {
let Category = DocCatFunction;
let Content = [{
// A warning group specifically for warnings related to function
// multiversioning.
def FunctionMultiVersioning : DiagGroup<"function-multiversion">;
+
+def NoDeref : DiagGroup<"noderef">;
"member '%2' is missing|"
"the type is not trivially copyable|"
"the type does not have the expected form}1">;
+
+def warn_dereference_of_noderef_type : Warning<
+ "dereferencing %0; was declared with a 'noderef' type">, InGroup<NoDeref>;
+def warn_dereference_of_noderef_type_no_decl : Warning<
+ "dereferencing expression marked as 'noderef'">, InGroup<NoDeref>;
+def warn_noderef_on_non_pointer_or_array : Warning<
+ "'noderef' can only be used on an array or pointer type">, InGroup<IgnoredAttributes>;
+def warn_noderef_to_dereferenceable_pointer : Warning<
+ "casting to dereferenceable pointer removes 'noderef' attribute">, InGroup<NoDeref>;
} // end of sema component.
/// expressions for which we have deferred checking the destructor.
SmallVector<CXXBindTemporaryExpr *, 8> DelayedDecltypeBinds;
+ llvm::SmallPtrSet<const Expr *, 8> PossibleDerefs;
+
/// \brief Describes whether we are in an expression constext which we have
/// to handle differently.
enum ExpressionKind {
/// A stack of expression evaluation contexts.
SmallVector<ExpressionEvaluationContextRecord, 8> ExprEvalContexts;
+ /// Emit a warning for all pending noderef expressions that we recorded.
+ void WarnOnPendingNoDerefs(ExpressionEvaluationContextRecord &Rec);
+
/// Compute the mangling number context for a lambda expression or
/// block literal.
///
};
private:
+ /// Methods for marking which expressions involve dereferencing a pointer
+ /// marked with the 'noderef' attribute. Expressions are checked bottom up as
+ /// they are parsed, meaning that a noderef pointer may not be accessed. For
+ /// example, in `&*p` where `p` is a noderef pointer, we will first parse the
+ /// `*p`, but need to check that `address of` is called on it. This requires
+ /// keeping a container of all pending expressions and checking if the address
+ /// of them are eventually taken.
+ void CheckSubscriptAccessOfNoDeref(const ArraySubscriptExpr *E);
+ void CheckAddressOfNoDeref(const Expr *E);
+ void CheckMemberAccessOfNoDeref(const MemberExpr *E);
+
bool RequireCompleteTypeImpl(SourceLocation Loc, QualType T,
TypeDiagnoser *Diagnoser);
case attr::PreserveAll:
OS << "preserve_all";
break;
+ case attr::NoDeref:
+ OS << "noderef";
+ break;
}
OS << "))";
}
return CreateOverloadedArraySubscriptExpr(lbLoc, rbLoc, base, idx);
}
- return CreateBuiltinArraySubscriptExpr(base, lbLoc, idx, rbLoc);
+ ExprResult Res = CreateBuiltinArraySubscriptExpr(base, lbLoc, idx, rbLoc);
+
+ if (!Res.isInvalid() && isa<ArraySubscriptExpr>(Res.get()))
+ CheckSubscriptAccessOfNoDeref(cast<ArraySubscriptExpr>(Res.get()));
+
+ return Res;
+}
+
+void Sema::CheckAddressOfNoDeref(const Expr *E) {
+ ExpressionEvaluationContextRecord &LastRecord = ExprEvalContexts.back();
+ const Expr *StrippedExpr = E->IgnoreParenImpCasts();
+
+ // For expressions like `&(*s).b`, the base is recorded and what should be
+ // checked.
+ const MemberExpr *Member = nullptr;
+ while ((Member = dyn_cast<MemberExpr>(StrippedExpr)) && !Member->isArrow())
+ StrippedExpr = Member->getBase()->IgnoreParenImpCasts();
+
+ LastRecord.PossibleDerefs.erase(StrippedExpr);
+}
+
+void Sema::CheckSubscriptAccessOfNoDeref(const ArraySubscriptExpr *E) {
+ QualType ResultTy = E->getType();
+ ExpressionEvaluationContextRecord &LastRecord = ExprEvalContexts.back();
+
+ // Bail if the element is an array since it is not memory access.
+ if (isa<ArrayType>(ResultTy))
+ return;
+
+ if (ResultTy->hasAttr(attr::NoDeref)) {
+ LastRecord.PossibleDerefs.insert(E);
+ return;
+ }
+
+ // Check if the base type is a pointer to a member access of a struct
+ // marked with noderef.
+ const Expr *Base = E->getBase();
+ QualType BaseTy = Base->getType();
+ if (!(isa<ArrayType>(BaseTy) || isa<PointerType>(BaseTy)))
+ // Not a pointer access
+ return;
+
+ const MemberExpr *Member = nullptr;
+ while ((Member = dyn_cast<MemberExpr>(Base->IgnoreParenCasts())) &&
+ Member->isArrow())
+ Base = Member->getBase();
+
+ if (const auto *Ptr = dyn_cast<PointerType>(Base->getType())) {
+ if (Ptr->getPointeeType()->hasAttr(attr::NoDeref))
+ LastRecord.PossibleDerefs.insert(E);
+ }
}
ExprResult Sema::ActOnOMPArraySectionExpr(Expr *Base, SourceLocation LBLoc,
ExprResult LocalRHS = CallerRHS;
ExprResult &RHS = ConvertRHS ? CallerRHS : LocalRHS;
+ if (const auto *LHSPtrType = LHSType->getAs<PointerType>()) {
+ if (const auto *RHSPtrType = RHS.get()->getType()->getAs<PointerType>()) {
+ if (RHSPtrType->getPointeeType()->hasAttr(attr::NoDeref) &&
+ !LHSPtrType->getPointeeType()->hasAttr(attr::NoDeref)) {
+ Diag(RHS.get()->getExprLoc(),
+ diag::warn_noderef_to_dereferenceable_pointer)
+ << RHS.get()->getSourceRange();
+ }
+ }
+ }
+
if (getLangOpts().CPlusPlus) {
if (!LHSType->isRecordType() && !LHSType->isAtomicType()) {
// C++ 5.17p3: If the left operand is not of class type, the
if (ConvertRHS)
RHS = ImpCastExprToType(E, Ty, Kind);
}
+
return result;
}
break;
case UO_AddrOf:
resultType = CheckAddressOfOperand(Input, OpLoc);
+ CheckAddressOfNoDeref(InputExpr);
RecordModifiableNonNullParam(*this, InputExpr);
break;
case UO_Deref: {
auto *UO = new (Context)
UnaryOperator(Input.get(), Opc, resultType, VK, OK, OpLoc, CanOverflow);
+
+ if (Opc == UO_Deref && UO->getType()->hasAttr(attr::NoDeref) &&
+ !isa<ArrayType>(UO->getType().getDesugaredType(Context)))
+ ExprEvalContexts.back().PossibleDerefs.insert(UO);
+
// Convert the result back to a half vector.
if (ConvertHalfVec)
return convertVector(UO, Context.HalfTy, *this);
PushExpressionEvaluationContext(NewContext, ClosureContextDecl, ExprContext);
}
+namespace {
+
+const DeclRefExpr *CheckPossibleDeref(Sema &S, const Expr *PossibleDeref) {
+ PossibleDeref = PossibleDeref->IgnoreParenImpCasts();
+ if (const auto *E = dyn_cast<UnaryOperator>(PossibleDeref)) {
+ if (E->getOpcode() == UO_Deref)
+ return CheckPossibleDeref(S, E->getSubExpr());
+ } else if (const auto *E = dyn_cast<ArraySubscriptExpr>(PossibleDeref)) {
+ return CheckPossibleDeref(S, E->getBase());
+ } else if (const auto *E = dyn_cast<MemberExpr>(PossibleDeref)) {
+ return CheckPossibleDeref(S, E->getBase());
+ } else if (const auto E = dyn_cast<DeclRefExpr>(PossibleDeref)) {
+ QualType Inner;
+ QualType Ty = E->getType();
+ if (const auto *Ptr = Ty->getAs<PointerType>())
+ Inner = Ptr->getPointeeType();
+ else if (const auto *Arr = S.Context.getAsArrayType(Ty))
+ Inner = Arr->getElementType();
+ else
+ return nullptr;
+
+ if (Inner->hasAttr(attr::NoDeref))
+ return E;
+ }
+ return nullptr;
+}
+
+} // namespace
+
+void Sema::WarnOnPendingNoDerefs(ExpressionEvaluationContextRecord &Rec) {
+ for (const Expr *E : Rec.PossibleDerefs) {
+ const DeclRefExpr *DeclRef = CheckPossibleDeref(*this, E);
+ if (DeclRef) {
+ const ValueDecl *Decl = DeclRef->getDecl();
+ Diag(E->getExprLoc(), diag::warn_dereference_of_noderef_type)
+ << Decl->getName() << E->getSourceRange();
+ Diag(Decl->getLocation(), diag::note_previous_decl) << Decl->getName();
+ } else {
+ Diag(E->getExprLoc(), diag::warn_dereference_of_noderef_type_no_decl)
+ << E->getSourceRange();
+ }
+ }
+ Rec.PossibleDerefs.clear();
+}
+
void Sema::PopExpressionEvaluationContext() {
ExpressionEvaluationContextRecord& Rec = ExprEvalContexts.back();
unsigned NumTypos = Rec.NumTypos;
}
}
+ WarnOnPendingNoDerefs(Rec);
+
// When are coming out of an unevaluated context, clear out any
// temporaries that we may have created as part of the evaluation of
// the expression in that context: they aren't relevant because they
}
ActOnMemberAccessExtraArgs ExtraArgs = {S, Id, ObjCImpDecl};
- return BuildMemberReferenceExpr(Base, Base->getType(), OpLoc, IsArrow, SS,
- TemplateKWLoc, FirstQualifierInScope,
- NameInfo, TemplateArgs, S, &ExtraArgs);
+ ExprResult Res = BuildMemberReferenceExpr(
+ Base, Base->getType(), OpLoc, IsArrow, SS, TemplateKWLoc,
+ FirstQualifierInScope, NameInfo, TemplateArgs, S, &ExtraArgs);
+
+ if (!Res.isInvalid() && isa<MemberExpr>(Res.get()))
+ CheckMemberAccessOfNoDeref(cast<MemberExpr>(Res.get()));
+
+ return Res;
+}
+
+void Sema::CheckMemberAccessOfNoDeref(const MemberExpr *E) {
+ QualType ResultTy = E->getType();
+
+ // Do not warn on member accesses to arrays since this returns an array
+ // lvalue and does not actually dereference memory.
+ if (isa<ArrayType>(ResultTy))
+ return;
+
+ if (E->isArrow()) {
+ if (const auto *Ptr = dyn_cast<PointerType>(
+ E->getBase()->getType().getDesugaredType(Context))) {
+ if (Ptr->getPointeeType()->hasAttr(attr::NoDeref))
+ ExprEvalContexts.back().PossibleDerefs.insert(E);
+ }
+ }
}
ExprResult
case SK_ConversionSequence:
case SK_ConversionSequenceNoNarrowing: {
+ if (const auto *FromPtrType =
+ CurInit.get()->getType()->getAs<PointerType>()) {
+ if (const auto *ToPtrType = Step->Type->getAs<PointerType>()) {
+ if (FromPtrType->getPointeeType()->hasAttr(attr::NoDeref) &&
+ !ToPtrType->getPointeeType()->hasAttr(attr::NoDeref)) {
+ S.Diag(CurInit.get()->getExprLoc(),
+ diag::warn_noderef_to_dereferenceable_pointer)
+ << CurInit.get()->getSourceRange();
+ }
+ }
+ }
+
Sema::CheckedConversionKind CCK
= Kind.isCStyleCast()? Sema::CCK_CStyleCast
: Kind.isFunctionalCast()? Sema::CCK_FunctionalCast
case SK_CAssignment: {
QualType SourceType = CurInit.get()->getType();
+
// Save off the initial CurInit in case we need to emit a diagnostic
ExprResult InitialCurInit = CurInit;
ExprResult Result = CurInit;
SmallVector<TypeAttrPair, 8> AttrsForTypes;
bool AttrsForTypesSorted = true;
+ /// Flag to indicate we parsed a noderef attribute. This is used for
+ /// validating that noderef was used on a pointer or array.
+ bool parsedNoDeref;
+
public:
TypeProcessingState(Sema &sema, Declarator &declarator)
- : sema(sema), declarator(declarator),
- chunkIndex(declarator.getNumTypeObjects()),
- trivial(true), hasSavedAttrs(false) {}
+ : sema(sema), declarator(declarator),
+ chunkIndex(declarator.getNumTypeObjects()), trivial(true),
+ hasSavedAttrs(false), parsedNoDeref(false) {}
Sema &getSema() const {
return sema;
llvm_unreachable("no Attr* for AttributedType*");
}
+ void setParsedNoDeref(bool parsed) { parsedNoDeref = parsed; }
+
+ bool didParseNoDeref() const { return parsedNoDeref; }
+
~TypeProcessingState() {
if (trivial) return;
return false;
}
+static bool IsNoDerefableChunk(DeclaratorChunk Chunk) {
+ return (Chunk.Kind == DeclaratorChunk::Pointer ||
+ Chunk.Kind == DeclaratorChunk::Array);
+}
+
template<typename AttrT>
static AttrT *createSimpleAttr(ASTContext &Ctx, ParsedAttr &Attr) {
Attr.setUsedAsTypeAttr();
}
}
+ bool ExpectNoDerefChunk =
+ state.getCurrentAttributes().hasAttribute(ParsedAttr::AT_NoDeref);
+
// Walk the DeclTypeInfo, building the recursive type as we go.
// DeclTypeInfos are ordered from the identifier out, which is
// opposite of what we want :).
// See if there are any attributes on this declarator chunk.
processTypeAttrs(state, T, TAL_DeclChunk, DeclType.getAttrs());
+
+ if (DeclType.Kind != DeclaratorChunk::Paren) {
+ if (ExpectNoDerefChunk) {
+ if (!IsNoDerefableChunk(DeclType))
+ S.Diag(DeclType.Loc, diag::warn_noderef_on_non_pointer_or_array);
+ ExpectNoDerefChunk = false;
+ }
+
+ ExpectNoDerefChunk = state.didParseNoDeref();
+ }
}
+ if (ExpectNoDerefChunk)
+ S.Diag(state.getDeclarator().getBeginLoc(),
+ diag::warn_noderef_on_non_pointer_or_array);
+
// GNU warning -Wstrict-prototypes
// Warn if a function declaration is without a prototype.
// This warning is issued for all kinds of unprototyped function
// sure we visit every element once. Copy the attributes list, and iterate
// over that.
ParsedAttributesView AttrsCopy{attrs};
+
+ state.setParsedNoDeref(false);
+
for (ParsedAttr &attr : AttrsCopy) {
// Skip attributes that were marked to be invalid.
HandleLifetimeBoundAttr(state, type, attr);
break;
+ case ParsedAttr::AT_NoDeref: {
+ ASTContext &Ctx = state.getSema().Context;
+ type = state.getAttributedType(createSimpleAttr<NoDerefAttr>(Ctx, attr),
+ type, type);
+ attr.setUsedAsTypeAttr();
+ state.setParsedNoDeref(true);
+ break;
+ }
+
MS_TYPE_ATTRS_CASELIST:
if (!handleMSPointerTypeQualifierAttr(state, attr, type))
attr.setUsedAsTypeAttr();
--- /dev/null
+// RUN: %clang_cc1 -Wno-unused-value -verify %s
+
+#define NODEREF __attribute__((noderef))
+
+struct S {
+ int a;
+ int b;
+};
+
+struct S2 {
+ int a[2];
+ int NODEREF a2[2];
+ int *b;
+ int NODEREF *b2;
+ struct S *s;
+ struct S NODEREF *s2;
+};
+
+int NODEREF *func(int NODEREF *arg) { // expected-note{{arg declared here}}
+ int y = *arg; // expected-warning{{dereferencing arg; was declared with a 'noderef' type}}
+ return arg;
+}
+
+void func2(int x) {}
+
+int test() {
+ int NODEREF *p; // expected-note 34 {{p declared here}}
+ int *p2;
+
+ int x = *p; // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+ x = *((int NODEREF *)p2); // expected-warning{{dereferencing expression marked as 'noderef'}}
+
+ int NODEREF **q;
+ int *NODEREF *q2; // expected-note 4 {{q2 declared here}}
+
+ // Indirection
+ x = **q; // expected-warning{{dereferencing expression marked as 'noderef'}}
+ p2 = *q2; // expected-warning{{dereferencing q2; was declared with a 'noderef' type}}
+
+ **q; // expected-warning{{dereferencing expression marked as 'noderef'}}
+
+ p = *&*q;
+ p = **&q;
+ q = &**&q;
+ p = &*p;
+ p = *&p;
+ p = &(*p);
+ p = *(&p);
+ x = **&p; // expected-warning{{dereferencing expression marked as 'noderef'}}
+
+ *p = 2; // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+ *q = p; // ok
+ **q = 2; // expected-warning{{dereferencing expression marked as 'noderef'}}
+ *q2 = p2; // expected-warning{{dereferencing q2; was declared with a 'noderef' type}}
+
+ p = p + 1;
+ p = &*(p + 1);
+
+ // Struct member access
+ struct S NODEREF *s; // expected-note 2 {{s declared here}}
+ x = s->a; // expected-warning{{dereferencing s; was declared with a 'noderef' type}}
+ x = (*s).b; // expected-warning{{dereferencing s; was declared with a 'noderef' type}}
+ p = &s->a;
+ p = &(*s).b;
+
+ // Nested struct access
+ struct S2 NODEREF *s2_noderef; // expected-note 5 {{s2_noderef declared here}}
+ p = s2_noderef->a; // ok since result is an array in a struct
+ p = s2_noderef->a2; // ok
+ p = s2_noderef->b; // expected-warning{{dereferencing s2_noderef; was declared with a 'noderef' type}}
+ p = s2_noderef->b2; // expected-warning{{dereferencing s2_noderef; was declared with a 'noderef' type}}
+ s = s2_noderef->s; // expected-warning{{dereferencing s2_noderef; was declared with a 'noderef' type}}
+ s = s2_noderef->s2; // expected-warning{{dereferencing s2_noderef; was declared with a 'noderef' type}}
+ p = s2_noderef->a + 1;
+
+ struct S2 *s2;
+ p = s2->a;
+ p = s2->a2;
+ p = s2->b;
+ p = s2->b2;
+ s = s2->s;
+ s = s2->s2;
+ &(*(*s2).s2).b;
+
+ // Subscript access
+ x = p[1]; // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+ x = q[0][0]; // expected-warning{{dereferencing expression marked as 'noderef'}}
+ p2 = q2[0]; // expected-warning{{dereferencing q2; was declared with a 'noderef' type}}
+ p = q[*p]; // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+ x = p[*p]; // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+ // expected-warning@-1{{dereferencing p; was declared with a 'noderef' type}}
+
+ int NODEREF arr[10]; // expected-note 1 {{arr declared here}}
+ x = arr[1]; // expected-warning{{dereferencing arr; was declared with a 'noderef' type}}
+
+ int NODEREF *(arr2[10]);
+ int NODEREF *elem = *arr2;
+
+ int NODEREF(*arr3)[10];
+ elem = *arr3;
+
+ // Combinations between indirection, subscript, and member access
+ struct S2 NODEREF *s2_arr[10];
+ struct S2 NODEREF *s2_arr2[10][10];
+
+ p = s2_arr[1]->a;
+ p = s2_arr[1]->b; // expected-warning{{dereferencing expression marked as 'noderef'}}
+ int **bptr = &s2_arr[1]->b;
+
+ x = s2->s2->a; // expected-warning{{dereferencing expression marked as 'noderef'}}
+ x = s2_noderef->a[1]; // expected-warning{{dereferencing s2_noderef; was declared with a 'noderef' type}}
+ p = &s2_noderef->a[1];
+
+ // Casting to dereferenceable pointer
+ p2 = p; // expected-warning{{casting to dereferenceable pointer removes 'noderef' attribute}}
+ p2 = *q; // expected-warning{{casting to dereferenceable pointer removes 'noderef' attribute}}
+ p2 = q[0]; // expected-warning{{casting to dereferenceable pointer removes 'noderef' attribute}}
+ s2 = s2_arr[1]; // expected-warning{{casting to dereferenceable pointer removes 'noderef' attribute}}
+ s2 = s2_arr2[1][1]; // expected-warning{{casting to dereferenceable pointer removes 'noderef' attribute}}
+ p2 = p, p2 = *q; // expected-warning 2 {{casting to dereferenceable pointer removes 'noderef' attribute}}
+
+ // typedefs
+ typedef int NODEREF *ptr_t;
+ ptr_t ptr; // expected-note 2 {{ptr declared here}}
+ ptr_t *ptr2;
+ *ptr; // expected-warning{{dereferencing ptr; was declared with a 'noderef' type}}
+ *ptr2;
+ **ptr2; // expected-warning{{dereferencing expression marked as 'noderef'}}
+
+ typedef struct S2 NODEREF *s2_ptr_t;
+ s2_ptr_t s2_ptr; // expected-note 4 {{s2_ptr declared here}}
+ s2_ptr->a; // ok since result is an array in a struct
+ s2_ptr->a2; // ok
+ s2_ptr->b; // expected-warning{{dereferencing s2_ptr; was declared with a 'noderef' type}}
+ s2_ptr->b2; // expected-warning{{dereferencing s2_ptr; was declared with a 'noderef' type}}
+ s2_ptr->s; // expected-warning{{dereferencing s2_ptr; was declared with a 'noderef' type}}
+ s2_ptr->s2; // expected-warning{{dereferencing s2_ptr; was declared with a 'noderef' type}}
+ s2_ptr->a + 1;
+
+ typedef int(int_t);
+ typedef int_t NODEREF *(noderef_int_t);
+ typedef noderef_int_t *noderef_int_nested_t;
+ noderef_int_nested_t noderef_int_nested_ptr;
+ *noderef_int_nested_ptr;
+ **noderef_int_nested_ptr; // expected-warning{{dereferencing expression marked as 'noderef'}}
+
+ typedef int_t *(NODEREF noderef_int2_t);
+ typedef noderef_int2_t *noderef_int2_nested_t;
+ noderef_int2_nested_t noderef_int2_nested_ptr; // expected-note{{noderef_int2_nested_ptr declared here}}
+ *noderef_int2_nested_ptr; // expected-warning{{dereferencing noderef_int2_nested_ptr; was declared with a 'noderef' type}}
+
+ typedef int_t *(noderef_int3_t);
+ typedef noderef_int3_t(NODEREF(*(noderef_int3_nested_t)));
+ noderef_int3_nested_t noderef_int3_nested_ptr; // expected-note{{noderef_int3_nested_ptr declared here}}
+ *noderef_int3_nested_ptr; // expected-warning{{dereferencing noderef_int3_nested_ptr; was declared with a 'noderef' type}}
+
+ // Parentheses
+ (((*((p))))); // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+ (*(*(&(p)))); // expected-warning{{dereferencing expression marked as 'noderef'}}
+
+ (p[1]); // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+ (q[0]); // ok
+ (q[0][0]); // expected-warning{{dereferencing expression marked as 'noderef'}}
+ (q2[0]); // expected-warning{{dereferencing q2; was declared with a 'noderef' type}}
+ (q[(*(p))]); // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+ (p[(*(p))]); // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+ // expected-warning@-1{{dereferencing p; was declared with a 'noderef' type}}
+
+ (*(ptr)); // expected-warning{{dereferencing ptr; was declared with a 'noderef' type}}
+ (*(ptr2));
+ (*(*(ptr2))); // expected-warning{{dereferencing expression marked as 'noderef'}}
+
+ // Functions
+ x = *(func(p)); // expected-warning{{dereferencing expression marked as 'noderef'}}
+
+ // Casting is ok
+ q = (int NODEREF **)&p;
+ q = (int NODEREF **)&p2;
+ q = &p;
+ q = &p2;
+ x = s2->s2->a; // expected-warning{{dereferencing expression marked as 'noderef'}}
+
+ // Other expressions
+ func2(*p); // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+ func2(*p + 1); // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+ func2(!*p); // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+ func2((x = *p)); // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+ func2((char)(*p)); // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+
+ // Other statements
+ if (*p) {} // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+ else if (*p) {} // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+ switch (*p){} // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+ for (*p; *p; *p){} // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+ // expected-warning@-1{{dereferencing p; was declared with a 'noderef' type}}
+ // expected-warning@-2{{dereferencing p; was declared with a 'noderef' type}}
+ for (*p; *p;){} // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+ // expected-warning@-1{{dereferencing p; was declared with a 'noderef' type}}
+ for (*p;; *p){} // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+ // expected-warning@-1{{dereferencing p; was declared with a 'noderef' type}}
+ for (; *p; *p){} // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+ // expected-warning@-1{{dereferencing p; was declared with a 'noderef' type}}
+ for (*p;;){} // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+ for (;*p;){} // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+ for (;;*p){} // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+ while (*p){} // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+ do {} while (*p); // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+ return *p; // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+}
--- /dev/null
+// RUN: %clang_cc1 -fblocks -verify %s
+
+/**
+ * Test 'noderef' attribute with c++ constructs.
+ */
+
+#define NODEREF __attribute__((noderef))
+
+void Normal() {
+ int NODEREF i; // expected-warning{{'noderef' can only be used on an array or pointer type}}
+ int NODEREF *i_ptr; // expected-note 2 {{i_ptr declared here}}
+ int NODEREF **i_ptr2; // ok
+ int *NODEREF i_ptr3; // expected-warning{{'noderef' can only be used on an array or pointer type}}
+ int *NODEREF *i_ptr4; // ok
+
+ auto NODEREF *auto_i_ptr = i_ptr;
+ auto NODEREF auto_i = i; // expected-warning{{'noderef' can only be used on an array or pointer type}}
+
+ struct {
+ int x;
+ int y;
+ } NODEREF *s;
+
+ int __attribute__((noderef(10))) * no_args; // expected-error{{'noderef' attribute takes no arguments}}
+
+ int i2 = *i_ptr; // expected-warning{{dereferencing i_ptr; was declared with a 'noderef' type}}
+ int &i3 = *i_ptr; // expected-warning{{dereferencing i_ptr; was declared with a 'noderef' type}}
+ int *i_ptr5 = i_ptr; // expected-warning{{casting to dereferenceable pointer removes 'noderef' attribute}}
+ int *i_ptr6(i_ptr); // expected-warning{{casting to dereferenceable pointer removes 'noderef' attribute}}
+}
+
+const int NODEREF *const_i_ptr;
+static int NODEREF *static_i_ptr;
+
+void ParenTypes() {
+ int NODEREF(*i_ptr); // ok (same as `int NODEREF *`)
+ int NODEREF *(*i_ptr2); // ok (same as `int NODEREF **`)
+}
+
+// Function declarations
+int NODEREF func(); // expected-warning{{'noderef' can only be used on an array or pointer type}}
+int NODEREF *func2(); // ok (returning pointer)
+
+typedef int NODEREF (*func3)(int); // expected-warning{{'noderef' can only be used on an array or pointer type}}
+typedef int NODEREF *(*func4)(int);
+
+void Arrays() {
+ int NODEREF i_arr[10]; // ok
+ int NODEREF i_arr2[10][10]; // ok
+ int NODEREF *i_arr3[10]; // ok
+ int NODEREF i_arr4[] = {1, 2};
+}
+
+void ParenArrays() {
+ int NODEREF(i_ptr[10]);
+ int NODEREF(i_ptr2[10])[10];
+}
+
+typedef int NODEREF *(*func5[10])(int);
+
+// Arguments
+void func6(int NODEREF x); // expected-warning{{'noderef' can only be used on an array or pointer type}}
+void func7(int NODEREF *x);
+void func8() NODEREF;
+
+void References() {
+ int x = 2;
+ int NODEREF &y = x; // expected-warning{{'noderef' can only be used on an array or pointer type}}
+ int *xp = &x;
+ int NODEREF *&a = xp; // ok (reference to a NODEREF *)
+ int *NODEREF &b = xp; // expected-warning{{'noderef' can only be used on an array or pointer type}}
+}
+
+void BlockPointers() {
+ typedef int NODEREF (^IntBlock)(); // expected-warning{{'noderef' can only be used on an array or pointer type}}
+}
+
+class A {
+public:
+ int member;
+ int NODEREF *member2;
+ int NODEREF member3; // expected-warning{{'noderef' can only be used on an array or pointer type}}
+};
+
+void MemberPointer() {
+ int NODEREF A::*var = &A::member; // expected-warning{{'noderef' can only be used on an array or pointer type}}
+}
+
+template <class Ty>
+class B {
+ Ty NODEREF *member;
+ Ty NODEREF member2; // expected-warning{{'noderef' can only be used on an array or pointer type}}
+};
+
+void test_lambdas() {
+ auto l = [](int NODEREF *x){ // expected-note{{x declared here}}
+ return *x; // expected-warning{{dereferencing x; was declared with a 'noderef' type}}
+ };
+}
+
+int NODEREF *glob_ptr; // expected-note{{glob_ptr declared here}}
+int glob_int = *glob_ptr; // expected-warning{{dereferencing glob_ptr; was declared with a 'noderef' type}}
--- /dev/null
+// RUN: %clang_cc1 -verify %s
+
+#define NODEREF __attribute__((noderef))
+
+@interface NSObject
++ (id)new;
+@end
+
+void func() {
+ id NODEREF obj = [NSObject new]; // expected-warning{{'noderef' can only be used on an array or pointer type}}
+}
--- /dev/null
+// RUN: %clang_cc1 -verify %s
+
+#define NODEREF __attribute__((noderef))
+
+template <typename T>
+int func(T NODEREF *a) { // expected-note 2 {{a declared here}}
+ return *a + 1; // expected-warning 2 {{dereferencing a; was declared with a 'noderef' type}}
+}
+
+void func() {
+ float NODEREF *f;
+ int NODEREF *i;
+ func(f); // expected-note{{in instantiation of function template specialization 'func<float>' requested here}}
+ func(i); // expected-note{{in instantiation of function template specialization 'func<int>' requested here}}
+}