19 changes: 17 additions & 2 deletions clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10410,10 +10410,25 @@ bool Expr::isCXX11ConstantExpr(const ASTContext &Ctx, APValue *Result,

bool Expr::EvaluateWithSubstitution(APValue &Value, ASTContext &Ctx,
const FunctionDecl *Callee,
ArrayRef<const Expr*> Args) const {
ArrayRef<const Expr*> Args,
const Expr *This) const {
Expr::EvalStatus Status;
EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantExpressionUnevaluated);

LValue ThisVal;
const LValue *ThisPtr = nullptr;
if (This) {
#ifndef NDEBUG
auto *MD = dyn_cast<CXXMethodDecl>(Callee);
assert(MD && "Don't provide `this` for non-methods.");
assert(!MD->isStatic() && "Don't provide `this` for static methods.");
#endif
if (EvaluateObjectArgument(Info, This, ThisVal))
ThisPtr = &ThisVal;
if (Info.EvalStatus.HasSideEffects)
return false;
}

ArgVector ArgValues(Args.size());
for (ArrayRef<const Expr*>::iterator I = Args.begin(), E = Args.end();
I != E; ++I) {
Expand All @@ -10426,7 +10441,7 @@ bool Expr::EvaluateWithSubstitution(APValue &Value, ASTContext &Ctx,
}

// Build fake call to Callee.
CallStackFrame Frame(Info, Callee->getLocation(), Callee, /*This*/nullptr,
CallStackFrame Frame(Info, Callee->getLocation(), Callee, ThisPtr,
ArgValues.data());
return Evaluate(Value, Info, this) && !Info.EvalStatus.HasSideEffects;
}
Expand Down
109 changes: 98 additions & 11 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Scope.h"
#include "clang/Sema/SemaInternal.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/MathExtras.h"

Expand Down Expand Up @@ -890,34 +891,117 @@ static void handleLocksExcludedAttr(Sema &S, Decl *D,
Attr.getAttributeSpellingListIndex()));
}

static void handleEnableIfAttr(Sema &S, Decl *D, const AttributeList &Attr) {
S.Diag(Attr.getLoc(), diag::ext_clang_enable_if);

Expr *Cond = Attr.getArgAsExpr(0);
static bool checkFunctionConditionAttr(Sema &S, Decl *D,
const AttributeList &Attr,
Expr *&Cond, StringRef &Msg) {
Cond = Attr.getArgAsExpr(0);
if (!Cond->isTypeDependent()) {
ExprResult Converted = S.PerformContextuallyConvertToBool(Cond);
if (Converted.isInvalid())
return;
return false;
Cond = Converted.get();
}

StringRef Msg;
if (!S.checkStringLiteralArgumentAttr(Attr, 1, Msg))
return;
return false;

if (Msg.empty())
Msg = "<no message provided>";

SmallVector<PartialDiagnosticAt, 8> Diags;
if (!Cond->isValueDependent() &&
!Expr::isPotentialConstantExprUnevaluated(Cond, cast<FunctionDecl>(D),
Diags)) {
S.Diag(Attr.getLoc(), diag::err_enable_if_never_constant_expr);
S.Diag(Attr.getLoc(), diag::err_attr_cond_never_constant_expr)
<< Attr.getName();
for (const PartialDiagnosticAt &PDiag : Diags)
S.Diag(PDiag.first, PDiag.second);
return false;
}
return true;
}

static void handleEnableIfAttr(Sema &S, Decl *D, const AttributeList &Attr) {
S.Diag(Attr.getLoc(), diag::ext_clang_enable_if);

Expr *Cond;
StringRef Msg;
if (checkFunctionConditionAttr(S, D, Attr, Cond, Msg))
D->addAttr(::new (S.Context)
EnableIfAttr(Attr.getRange(), S.Context, Cond, Msg,
Attr.getAttributeSpellingListIndex()));
}

namespace {
/// Determines if a given Expr references any of the given function's
/// ParmVarDecls, or the function's implicit `this` parameter (if applicable).
class ArgumentDependenceChecker
: public RecursiveASTVisitor<ArgumentDependenceChecker> {
#ifndef NDEBUG
const CXXRecordDecl *ClassType;
#endif
llvm::SmallPtrSet<const ParmVarDecl *, 16> Parms;
bool Result;

public:
ArgumentDependenceChecker(const FunctionDecl *FD) {
#ifndef NDEBUG
if (const auto *MD = dyn_cast<CXXMethodDecl>(FD))
ClassType = MD->getParent();
else
ClassType = nullptr;
#endif
Parms.insert(FD->param_begin(), FD->param_end());
}

bool referencesArgs(Expr *E) {
Result = false;
TraverseStmt(E);
return Result;
}

bool VisitCXXThisExpr(CXXThisExpr *E) {
assert(E->getType()->getPointeeCXXRecordDecl() == ClassType &&
"`this` doesn't refer to the enclosing class?");
Result = true;
return false;
}

bool VisitDeclRefExpr(DeclRefExpr *DRE) {
if (const auto *PVD = dyn_cast<ParmVarDecl>(DRE->getDecl()))
if (Parms.count(PVD)) {
Result = true;
return false;
}
return true;
}
};
}

static void handleDiagnoseIfAttr(Sema &S, Decl *D, const AttributeList &Attr) {
S.Diag(Attr.getLoc(), diag::ext_clang_diagnose_if);

Expr *Cond;
StringRef Msg;
if (!checkFunctionConditionAttr(S, D, Attr, Cond, Msg))
return;

StringRef DiagTypeStr;
if (!S.checkStringLiteralArgumentAttr(Attr, 2, DiagTypeStr))
return;

DiagnoseIfAttr::DiagnosticType DiagType;
if (!DiagnoseIfAttr::ConvertStrToDiagnosticType(DiagTypeStr, DiagType)) {
S.Diag(Attr.getArgAsExpr(2)->getLocStart(),
diag::err_diagnose_if_invalid_diagnostic_type);
return;
}

D->addAttr(::new (S.Context)
EnableIfAttr(Attr.getRange(), S.Context, Cond, Msg,
Attr.getAttributeSpellingListIndex()));
auto *FD = cast<FunctionDecl>(D);
bool ArgDependent = ArgumentDependenceChecker(FD).referencesArgs(Cond);
D->addAttr(::new (S.Context) DiagnoseIfAttr(
Attr.getRange(), S.Context, Cond, Msg, DiagType, ArgDependent, FD,
Attr.getAttributeSpellingListIndex()));
}

static void handlePassObjectSizeAttr(Sema &S, Decl *D,
Expand Down Expand Up @@ -5682,6 +5766,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
case AttributeList::AT_EnableIf:
handleEnableIfAttr(S, D, Attr);
break;
case AttributeList::AT_DiagnoseIf:
handleDiagnoseIfAttr(S, D, Attr);
break;
case AttributeList::AT_ExtVectorType:
handleExtVectorTypeAttr(S, scope, D, Attr);
break;
Expand Down
73 changes: 47 additions & 26 deletions clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc,
}

// See if this is a deleted function.
SmallVector<DiagnoseIfAttr *, 4> DiagnoseIfWarnings;
if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
if (FD->isDeleted()) {
auto *Ctor = dyn_cast<CXXConstructorDecl>(FD);
Expand All @@ -363,6 +364,12 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc,

if (getLangOpts().CUDA && !CheckCUDACall(Loc, FD))
return true;

if (const DiagnoseIfAttr *A =
checkArgIndependentDiagnoseIf(FD, DiagnoseIfWarnings)) {
emitDiagnoseIfDiagnostic(Loc, A);
return true;
}
}

// [OpenMP 4.0], 2.15 declare reduction Directive, Restrictions
Expand All @@ -377,6 +384,10 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc,
Diag(D->getLocation(), diag::note_entity_declared_at) << D;
return true;
}

for (const auto *W : DiagnoseIfWarnings)
emitDiagnoseIfDiagnostic(Loc, W);

DiagnoseAvailabilityOfDecl(*this, D, Loc, UnknownObjCClass,
ObjCPropertyAccess);

Expand Down Expand Up @@ -5154,12 +5165,40 @@ static FunctionDecl *rewriteBuiltinFunctionDecl(Sema *Sema, ASTContext &Context,
return OverloadDecl;
}

static bool isNumberOfArgsValidForCall(Sema &S, const FunctionDecl *Callee,
std::size_t NumArgs) {
if (S.TooManyArguments(Callee->getNumParams(), NumArgs,
/*PartialOverloading=*/false))
return Callee->isVariadic();
return Callee->getMinRequiredArguments() <= NumArgs;
static void checkDirectCallValidity(Sema &S, const Expr *Fn,
FunctionDecl *Callee,
MultiExprArg ArgExprs) {
// `Callee` (when called with ArgExprs) may be ill-formed. enable_if (and
// similar attributes) really don't like it when functions are called with an
// invalid number of args.
if (S.TooManyArguments(Callee->getNumParams(), ArgExprs.size(),
/*PartialOverloading=*/false) &&
!Callee->isVariadic())
return;
if (Callee->getMinRequiredArguments() > ArgExprs.size())
return;

if (const EnableIfAttr *Attr = S.CheckEnableIf(Callee, ArgExprs, true)) {
S.Diag(Fn->getLocStart(),
isa<CXXMethodDecl>(Callee)
? diag::err_ovl_no_viable_member_function_in_call
: diag::err_ovl_no_viable_function_in_call)
<< Callee << Callee->getSourceRange();
S.Diag(Callee->getLocation(),
diag::note_ovl_candidate_disabled_by_function_cond_attr)
<< Attr->getCond()->getSourceRange() << Attr->getMessage();
return;
}

SmallVector<DiagnoseIfAttr *, 4> Nonfatal;
if (const DiagnoseIfAttr *Attr = S.checkArgDependentDiagnoseIf(
Callee, ArgExprs, Nonfatal, /*MissingImplicitThis=*/true)) {
S.emitDiagnoseIfDiagnostic(Fn->getLocStart(), Attr);
return;
}

for (const auto *W : Nonfatal)
S.emitDiagnoseIfDiagnostic(Fn->getLocStart(), W);
}

/// ActOnCallExpr - Handle a call to Fn with the specified array of arguments.
Expand Down Expand Up @@ -5294,26 +5333,8 @@ ExprResult Sema::ActOnCallExpr(Scope *Scope, Expr *Fn, SourceLocation LParenLoc,

if (getLangOpts().OpenCL && checkOpenCLDisabledDecl(*FD, *Fn))
return ExprError();

// CheckEnableIf assumes that the we're passing in a sane number of args for
// FD, but that doesn't always hold true here. This is because, in some
// cases, we'll emit a diag about an ill-formed function call, but then
// we'll continue on as if the function call wasn't ill-formed. So, if the
// number of args looks incorrect, don't do enable_if checks; we should've
// already emitted an error about the bad call.
if (FD->hasAttr<EnableIfAttr>() &&
isNumberOfArgsValidForCall(*this, FD, ArgExprs.size())) {
if (const EnableIfAttr *Attr = CheckEnableIf(FD, ArgExprs, true)) {
Diag(Fn->getLocStart(),
isa<CXXMethodDecl>(FD)
? diag::err_ovl_no_viable_member_function_in_call
: diag::err_ovl_no_viable_function_in_call)
<< FD << FD->getSourceRange();
Diag(FD->getLocation(),
diag::note_ovl_candidate_disabled_by_enable_if_attr)
<< Attr->getCond()->getSourceRange() << Attr->getMessage();
}
}

checkDirectCallValidity(*this, Fn, FD, ArgExprs);
}

return BuildResolvedCallExpr(Fn, NDecl, LParenLoc, ArgExprs, RParenLoc,
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Sema/SemaLookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2960,6 +2960,7 @@ Sema::SpecialMemberOverloadResult *Sema::LookupSpecialMember(CXXRecordDecl *RD,
if (CXXMethodDecl *M = dyn_cast<CXXMethodDecl>(Cand->getUnderlyingDecl())) {
if (SM == CXXCopyAssignment || SM == CXXMoveAssignment)
AddMethodCandidate(M, Cand, RD, ThisTy, Classification,
/*ThisArg=*/nullptr,
llvm::makeArrayRef(&Arg, NumArgs), OCS, true);
else if (CtorInfo)
AddOverloadCandidate(CtorInfo.Constructor, CtorInfo.FoundDecl,
Expand All @@ -2972,7 +2973,7 @@ Sema::SpecialMemberOverloadResult *Sema::LookupSpecialMember(CXXRecordDecl *RD,
if (SM == CXXCopyAssignment || SM == CXXMoveAssignment)
AddMethodTemplateCandidate(
Tmpl, Cand, RD, nullptr, ThisTy, Classification,
llvm::makeArrayRef(&Arg, NumArgs), OCS, true);
/*ThisArg=*/nullptr, llvm::makeArrayRef(&Arg, NumArgs), OCS, true);
else if (CtorInfo)
AddTemplateOverloadCandidate(
CtorInfo.ConstructorTmpl, CtorInfo.FoundDecl, nullptr,
Expand Down
344 changes: 266 additions & 78 deletions clang/lib/Sema/SemaOverload.cpp

Large diffs are not rendered by default.

63 changes: 44 additions & 19 deletions clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -168,40 +168,59 @@ static void instantiateDependentAlignValueAttr(
Aligned->getSpellingListIndex());
}

static void instantiateDependentEnableIfAttr(
static Expr *instantiateDependentFunctionAttrCondition(
Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs,
const EnableIfAttr *A, const Decl *Tmpl, Decl *New) {
const Attr *A, Expr *OldCond, const Decl *Tmpl, FunctionDecl *New) {
Expr *Cond = nullptr;
{
Sema::ContextRAII SwitchContext(S, cast<FunctionDecl>(New));
Sema::ContextRAII SwitchContext(S, New);
EnterExpressionEvaluationContext Unevaluated(S, Sema::ConstantEvaluated);
ExprResult Result = S.SubstExpr(A->getCond(), TemplateArgs);
ExprResult Result = S.SubstExpr(OldCond, TemplateArgs);
if (Result.isInvalid())
return;
return nullptr;
Cond = Result.getAs<Expr>();
}
if (!Cond->isTypeDependent()) {
ExprResult Converted = S.PerformContextuallyConvertToBool(Cond);
if (Converted.isInvalid())
return;
return nullptr;
Cond = Converted.get();
}

SmallVector<PartialDiagnosticAt, 8> Diags;
if (A->getCond()->isValueDependent() && !Cond->isValueDependent() &&
!Expr::isPotentialConstantExprUnevaluated(Cond, cast<FunctionDecl>(New),
Diags)) {
S.Diag(A->getLocation(), diag::err_enable_if_never_constant_expr);
for (int I = 0, N = Diags.size(); I != N; ++I)
S.Diag(Diags[I].first, Diags[I].second);
return;
if (OldCond->isValueDependent() && !Cond->isValueDependent() &&
!Expr::isPotentialConstantExprUnevaluated(Cond, New, Diags)) {
S.Diag(A->getLocation(), diag::err_attr_cond_never_constant_expr) << A;
for (const auto &P : Diags)
S.Diag(P.first, P.second);
return nullptr;
}
return Cond;
}

EnableIfAttr *EIA = new (S.getASTContext())
EnableIfAttr(A->getLocation(), S.getASTContext(), Cond,
A->getMessage(),
A->getSpellingListIndex());
New->addAttr(EIA);
static void instantiateDependentEnableIfAttr(
Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs,
const EnableIfAttr *EIA, const Decl *Tmpl, FunctionDecl *New) {
Expr *Cond = instantiateDependentFunctionAttrCondition(
S, TemplateArgs, EIA, EIA->getCond(), Tmpl, New);

if (Cond)
New->addAttr(new (S.getASTContext()) EnableIfAttr(
EIA->getLocation(), S.getASTContext(), Cond, EIA->getMessage(),
EIA->getSpellingListIndex()));
}

static void instantiateDependentDiagnoseIfAttr(
Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs,
const DiagnoseIfAttr *DIA, const Decl *Tmpl, FunctionDecl *New) {
Expr *Cond = instantiateDependentFunctionAttrCondition(
S, TemplateArgs, DIA, DIA->getCond(), Tmpl, New);

if (Cond)
New->addAttr(new (S.getASTContext()) DiagnoseIfAttr(
DIA->getLocation(), S.getASTContext(), Cond, DIA->getMessage(),
DIA->getDiagnosticType(), DIA->getArgDependent(), New,
DIA->getSpellingListIndex()));
}

// Constructs and adds to New a new instance of CUDALaunchBoundsAttr using
Expand Down Expand Up @@ -335,7 +354,13 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs,

if (const auto *EnableIf = dyn_cast<EnableIfAttr>(TmplAttr)) {
instantiateDependentEnableIfAttr(*this, TemplateArgs, EnableIf, Tmpl,
New);
cast<FunctionDecl>(New));
continue;
}

if (const auto *DiagnoseIf = dyn_cast<DiagnoseIfAttr>(TmplAttr)) {
instantiateDependentDiagnoseIfAttr(*this, TemplateArgs, DiagnoseIf, Tmpl,
cast<FunctionDecl>(New));
continue;
}

Expand Down
152 changes: 152 additions & 0 deletions clang/test/Sema/diagnose_if.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
// RUN: %clang_cc1 %s -verify -fno-builtin

#define _diagnose_if(...) __attribute__((diagnose_if(__VA_ARGS__)))

void failure() _diagnose_if(); // expected-error{{exactly 3 arguments}}
void failure() _diagnose_if(0); // expected-error{{exactly 3 arguments}}
void failure() _diagnose_if(0, ""); // expected-error{{exactly 3 arguments}}
void failure() _diagnose_if(0, "", "error", 1); // expected-error{{exactly 3 arguments}}
void failure() _diagnose_if(0, 0, "error"); // expected-error{{requires a string}}
void failure() _diagnose_if(0, "", "invalid"); // expected-error{{invalid diagnostic type for 'diagnose_if'; use "error" or "warning" instead}}
void failure() _diagnose_if(0, "", "ERROR"); // expected-error{{invalid diagnostic type}}
void failure(int a) _diagnose_if(a, "", ""); // expected-error{{invalid diagnostic type}}
void failure() _diagnose_if(a, "", ""); // expected-error{{undeclared identifier 'a'}}

int globalVar;
void never_constant() _diagnose_if(globalVar, "", "error"); // expected-error{{'diagnose_if' attribute expression never produces a constant expression}} expected-note{{subexpression not valid}}
void never_constant() _diagnose_if(globalVar, "", "warning"); // expected-error{{'diagnose_if' attribute expression never produces a constant expression}} expected-note{{subexpression not valid}}

int alwaysok(int q) _diagnose_if(0, "", "error");
int neverok(int q) _diagnose_if(1, "oh no", "error"); // expected-note 5{{from 'diagnose_if' attribute on 'neverok'}}
int alwayswarn(int q) _diagnose_if(1, "oh no", "warning"); // expected-note 5{{from 'diagnose_if' attribute}}
int neverwarn(int q) _diagnose_if(0, "", "warning");

void runConstant() {
int m;
alwaysok(0);
alwaysok(1);
alwaysok(m);

{
int (*pok)(int) = alwaysok;
pok = &alwaysok;
}

neverok(0); // expected-error{{oh no}}
neverok(1); // expected-error{{oh no}}
neverok(m); // expected-error{{oh no}}
{
int (*pok)(int) = neverok; // expected-error{{oh no}}
pok = &neverok; // expected-error{{oh no}}
}

alwayswarn(0); // expected-warning{{oh no}}
alwayswarn(1); // expected-warning{{oh no}}
alwayswarn(m); // expected-warning{{oh no}}
{
int (*pok)(int) = alwayswarn; // expected-warning{{oh no}}
pok = &alwayswarn; // expected-warning{{oh no}}
}

neverwarn(0);
neverwarn(1);
neverwarn(m);
{
int (*pok)(int) = neverwarn;
pok = &neverwarn;
}
}

int abs(int q) _diagnose_if(q >= 0, "redundant abs call", "error"); //expected-note{{from 'diagnose_if'}}
void runVariable() {
int m;
abs(-1);
abs(1); // expected-error{{redundant abs call}}
abs(m);

int (*pabs)(int) = abs;
pabs = &abs;
}

#define _overloadable __attribute__((overloadable))

int ovl1(const char *n) _overloadable _diagnose_if(n, "oh no", "error"); // expected-note{{oh no}}
int ovl1(void *m) _overloadable; // expected-note{{candidate function}}

int ovl2(const char *n) _overloadable _diagnose_if(n, "oh no", "error"); // expected-note{{candidate function}}
int ovl2(char *m) _overloadable; // expected-note{{candidate function}}
void overloadsYay() {
ovl1((void *)0);
ovl1(""); // expected-error{{call to unavailable function}}

ovl2((void *)0); // expected-error{{ambiguous}}
}

void errorWarnDiagnose1() _diagnose_if(1, "oh no", "error") // expected-note{{from 'diagnose_if'}}
_diagnose_if(1, "nop", "warning");
void errorWarnDiagnose2() _diagnose_if(1, "oh no", "error") // expected-note{{from 'diagnose_if'}}
_diagnose_if(1, "nop", "error");
void errorWarnDiagnose3() _diagnose_if(1, "nop", "warning")
_diagnose_if(1, "oh no", "error"); // expected-note{{from 'diagnose_if'}}

void errorWarnDiagnoseArg1(int a) _diagnose_if(a == 1, "oh no", "error") // expected-note{{from 'diagnose_if'}}
_diagnose_if(a == 1, "nop", "warning");
void errorWarnDiagnoseArg2(int a) _diagnose_if(a == 1, "oh no", "error") // expected-note{{from 'diagnose_if'}}
_diagnose_if(a == 1, "nop", "error");
void errorWarnDiagnoseArg3(int a) _diagnose_if(a == 1, "nop", "warning")
_diagnose_if(a == 1, "oh no", "error"); // expected-note{{from 'diagnose_if'}}

void runErrorWarnDiagnose() {
errorWarnDiagnose1(); // expected-error{{oh no}}
errorWarnDiagnose2(); // expected-error{{oh no}}
errorWarnDiagnose3(); // expected-error{{oh no}}

errorWarnDiagnoseArg1(1); // expected-error{{oh no}}
errorWarnDiagnoseArg2(1); // expected-error{{oh no}}
errorWarnDiagnoseArg3(1); // expected-error{{oh no}}
}

void warnWarnDiagnose() _diagnose_if(1, "oh no!", "warning") _diagnose_if(1, "foo", "warning"); // expected-note 2{{from 'diagnose_if'}}
void runWarnWarnDiagnose() {
warnWarnDiagnose(); // expected-warning{{oh no!}} expected-warning{{foo}}
}

void declsStackErr1(int a) _diagnose_if(a & 1, "decl1", "error"); // expected-note 2{{from 'diagnose_if'}}
void declsStackErr1(int a) _diagnose_if(a & 2, "decl2", "error"); // expected-note{{from 'diagnose_if'}}
void declsStackErr2();
void declsStackErr2() _diagnose_if(1, "complaint", "error"); // expected-note{{from 'diagnose_if'}}
void declsStackErr3() _diagnose_if(1, "complaint", "error"); // expected-note{{from 'diagnose_if'}}
void declsStackErr3();
void runDeclsStackErr() {
declsStackErr1(0);
declsStackErr1(1); // expected-error{{decl1}}
declsStackErr1(2); // expected-error{{decl2}}
declsStackErr1(3); // expected-error{{decl1}}
declsStackErr2(); // expected-error{{complaint}}
declsStackErr3(); // expected-error{{complaint}}
}

void declsStackWarn1(int a) _diagnose_if(a & 1, "decl1", "warning"); // expected-note 2{{from 'diagnose_if'}}
void declsStackWarn1(int a) _diagnose_if(a & 2, "decl2", "warning"); // expected-note 2{{from 'diagnose_if'}}
void declsStackWarn2();
void declsStackWarn2() _diagnose_if(1, "complaint", "warning"); // expected-note{{from 'diagnose_if'}}
void declsStackWarn3() _diagnose_if(1, "complaint", "warning"); // expected-note{{from 'diagnose_if'}}
void declsStackWarn3();
void runDeclsStackWarn() {
declsStackWarn1(0);
declsStackWarn1(1); // expected-warning{{decl1}}
declsStackWarn1(2); // expected-warning{{decl2}}
declsStackWarn1(3); // expected-warning{{decl1}} expected-warning{{decl2}}
declsStackWarn2(); // expected-warning{{complaint}}
declsStackWarn3(); // expected-warning{{complaint}}
}

void noMsg(int n) _diagnose_if(n, "", "warning"); // expected-note{{from 'diagnose_if'}}
void runNoMsg() {
noMsg(1); // expected-warning{{<no message provided>}}
}

void alwaysWarnWithArg(int a) _diagnose_if(1 || a, "alwaysWarn", "warning"); // expected-note{{from 'diagnose_if'}}
void runAlwaysWarnWithArg(int a) {
alwaysWarnWithArg(a); // expected-warning{{alwaysWarn}}
}
460 changes: 460 additions & 0 deletions clang/test/SemaCXX/diagnose_if.cpp

Large diffs are not rendered by default.