Skip to content

Commit faca2d8

Browse files
committedFeb 18, 2016
Add -Wcomma warning to Clang.
-Wcomma will detect and warn on most uses of the builtin comma operator. It currently whitelists the first and third statements of the for-loop. For other cases, the warning can be silenced by casting the first operand of the comma operator to void. Differential Revision: http://reviews.llvm.org/D3976 llvm-svn: 261278
1 parent 0adbea4 commit faca2d8

File tree

5 files changed

+376
-0
lines changed

5 files changed

+376
-0
lines changed
 

‎clang/include/clang/Basic/DiagnosticSemaKinds.td

+4
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ def warn_infinite_recursive_function : Warning<
7474
"all paths through this function will call itself">,
7575
InGroup<InfiniteRecursion>, DefaultIgnore;
7676

77+
def warn_comma_operator : Warning<"possible misuse of comma operator here">,
78+
InGroup<DiagGroup<"comma">>, DefaultIgnore;
79+
def note_cast_to_void : Note<"cast expression to void to silence warning">;
80+
7781
// Constant expressions
7882
def err_expr_not_ice : Error<
7983
"expression is not an %select{integer|integral}0 constant expression">;

‎clang/include/clang/Sema/Sema.h

+2
Original file line numberDiff line numberDiff line change
@@ -3971,6 +3971,8 @@ class Sema {
39713971
ExprResult CreateBuiltinBinOp(SourceLocation OpLoc, BinaryOperatorKind Opc,
39723972
Expr *LHSExpr, Expr *RHSExpr);
39733973

3974+
void DiagnoseCommaOperator(const Expr *LHS, SourceLocation Loc);
3975+
39743976
/// ActOnConditionalOp - Parse a ?: operation. Note that 'LHS' may be null
39753977
/// in the case of a the GNU conditional expr extension.
39763978
ExprResult ActOnConditionalOp(SourceLocation QuestionLoc,

‎clang/lib/Sema/SemaExpr.cpp

+64
Original file line numberDiff line numberDiff line change
@@ -9867,6 +9867,67 @@ QualType Sema::CheckAssignmentOperands(Expr *LHSExpr, ExprResult &RHS,
98679867
? LHSType : LHSType.getUnqualifiedType());
98689868
}
98699869

9870+
// Only ignore explicit casts to void.
9871+
static bool IgnoreCommaOperand(const Expr *E) {
9872+
E = E->IgnoreParens();
9873+
9874+
if (const CastExpr *CE = dyn_cast<CastExpr>(E)) {
9875+
if (CE->getCastKind() == CK_ToVoid) {
9876+
return true;
9877+
}
9878+
}
9879+
9880+
return false;
9881+
}
9882+
9883+
// Look for instances where it is likely the comma operator is confused with
9884+
// another operator. There is a whitelist of acceptable expressions for the
9885+
// left hand side of the comma operator, otherwise emit a warning.
9886+
void Sema::DiagnoseCommaOperator(const Expr *LHS, SourceLocation Loc) {
9887+
// No warnings in macros
9888+
if (Loc.isMacroID())
9889+
return;
9890+
9891+
// Don't warn in template instantiations.
9892+
if (!ActiveTemplateInstantiations.empty())
9893+
return;
9894+
9895+
// Scope isn't fine-grained enough to whitelist the specific cases, so
9896+
// instead, skip more than needed, then call back into here with the
9897+
// CommaVisitor in SemaStmt.cpp.
9898+
// The whitelisted locations are the initialization and increment portions
9899+
// of a for loop. The additional checks are on the condition of
9900+
// if statements, do/while loops, and for loops.
9901+
const unsigned ForIncreamentFlags =
9902+
Scope::ControlScope | Scope::ContinueScope | Scope::BreakScope;
9903+
const unsigned ForInitFlags = Scope::ControlScope | Scope::DeclScope;
9904+
const unsigned ScopeFlags = getCurScope()->getFlags();
9905+
if ((ScopeFlags & ForIncrementFlags) == ForIncrementFlags ||
9906+
(ScopeFlags & ForInitFlags) == ForInitFlags)
9907+
return;
9908+
9909+
// If there are multiple comma operators used together, get the RHS of the
9910+
// of the comma operator as the LHS.
9911+
while (const BinaryOperator *BO = dyn_cast<BinaryOperator>(LHS)) {
9912+
if (BO->getOpcode() != BO_Comma)
9913+
break;
9914+
LHS = BO->getRHS();
9915+
}
9916+
9917+
// Only allow some expressions on LHS to not warn.
9918+
if (IgnoreCommaOperand(LHS))
9919+
return;
9920+
9921+
Diag(Loc, diag::warn_comma_operator);
9922+
Diag(LHS->getLocStart(), diag::note_cast_to_void)
9923+
<< LHS->getSourceRange()
9924+
<< FixItHint::CreateInsertion(LHS->getLocStart(),
9925+
LangOpts.CPlusPlus ? "static_cast<void>("
9926+
: "(void)(")
9927+
<< FixItHint::CreateInsertion(PP.getLocForEndOfToken(LHS->getLocEnd()),
9928+
")");
9929+
}
9930+
98709931
// C99 6.5.17
98719932
static QualType CheckCommaOperands(Sema &S, ExprResult &LHS, ExprResult &RHS,
98729933
SourceLocation Loc) {
@@ -9896,6 +9957,9 @@ static QualType CheckCommaOperands(Sema &S, ExprResult &LHS, ExprResult &RHS,
98969957
diag::err_incomplete_type);
98979958
}
98989959

9960+
if (!S.getDiagnostics().isIgnored(diag::warn_comma_operator, Loc))
9961+
S.DiagnoseCommaOperator(LHS.get(), Loc);
9962+
98999963
return RHS.get()->getType();
99009964
}
99019965

‎clang/lib/Sema/SemaStmt.cpp

+28
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,20 @@ StmtResult Sema::ActOnAttributedStmt(SourceLocation AttrLoc,
488488
return LS;
489489
}
490490

491+
namespace {
492+
class CommaVisitor : public EvaluatedExprVisitor<CommaVisitor> {
493+
typedef EvaluatedExprVisitor<CommaVisitor> Inherited;
494+
Sema &SemaRef;
495+
public:
496+
CommaVisitor(Sema &SemaRef) : Inherited(SemaRef.Context), SemaRef(SemaRef) {}
497+
void VisitBinaryOperator(BinaryOperator *E) {
498+
if (E->getOpcode() == BO_Comma)
499+
SemaRef.DiagnoseCommaOperator(E->getLHS(), E->getExprLoc());
500+
EvaluatedExprVisitor<CommaVisitor>::VisitBinaryOperator(E);
501+
}
502+
};
503+
}
504+
491505
StmtResult
492506
Sema::ActOnIfStmt(SourceLocation IfLoc, FullExprArg CondVal, Decl *CondVar,
493507
Stmt *thenStmt, SourceLocation ElseLoc,
@@ -502,6 +516,11 @@ Sema::ActOnIfStmt(SourceLocation IfLoc, FullExprArg CondVal, Decl *CondVar,
502516
}
503517
Expr *ConditionExpr = CondResult.getAs<Expr>();
504518
if (ConditionExpr) {
519+
520+
if (!Diags.isIgnored(diag::warn_comma_operator,
521+
ConditionExpr->getExprLoc()))
522+
CommaVisitor(*this).Visit(ConditionExpr);
523+
505524
DiagnoseUnusedExprResult(thenStmt);
506525

507526
if (!elseStmt) {
@@ -1240,6 +1259,10 @@ Sema::ActOnWhileStmt(SourceLocation WhileLoc, FullExprArg Cond,
12401259
return StmtError();
12411260
CheckBreakContinueBinding(ConditionExpr);
12421261

1262+
if (ConditionExpr &&
1263+
!Diags.isIgnored(diag::warn_comma_operator, ConditionExpr->getExprLoc()))
1264+
CommaVisitor(*this).Visit(ConditionExpr);
1265+
12431266
DiagnoseUnusedExprResult(Body);
12441267

12451268
if (isa<NullStmt>(Body))
@@ -1642,6 +1665,11 @@ Sema::ActOnForStmt(SourceLocation ForLoc, SourceLocation LParenLoc,
16421665
return StmtError();
16431666
}
16441667

1668+
if (SecondResult.get() &&
1669+
!Diags.isIgnored(diag::warn_comma_operator,
1670+
SecondResult.get()->getExprLoc()))
1671+
CommaVisitor(*this).Visit(SecondResult.get());
1672+
16451673
Expr *Third = third.release().getAs<Expr>();
16461674

16471675
DiagnoseUnusedExprResult(First);

0 commit comments

Comments
 (0)
Please sign in to comment.