https://github.com/Qayyum-Ahmed updated https://github.com/llvm/llvm-project/pull/184924
>From 03af8b8eee46c965b51c0c8e3038402fb319bfdf Mon Sep 17 00:00:00 2001 From: Qayyum <[email protected]> Date: Fri, 6 Mar 2026 05:12:20 +0500 Subject: [PATCH] [clang][Sema] Warn when macro operator mixes with outer operand due to precedence --- .../clang/Basic/DiagnosticSemaKinds.td | 5 + clang/lib/Sema/SemaExpr.cpp | 51 +++++ clang/test/Sema/macro-mixed-operator.c | 200 ++++++++++++++++++ 3 files changed, 256 insertions(+) create mode 100644 clang/test/Sema/macro-mixed-operator.c diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index b5410237e05e7..c86faae1f1f07 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -7576,6 +7576,11 @@ def warn_addition_in_bitshift : Warning< "operator '%0' has lower precedence than '%1'; " "'%1' will be evaluated first">, InGroup<ShiftOpParentheses>; +def warn_operator_in_macro_with_mixed_operand : Warning< + "operator '%0' in macro expansion has operand outside the macro; " + "operator precedence may be different than expected">, + InGroup<DiagGroup<"macro-mixed-operator">>, DefaultIgnore; + def warn_self_assignment_builtin : Warning< "explicitly assigning value of variable of type %0 to itself%select{|; did " "you mean to assign to member %2?}1">, diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 53d215f5c5e3e..f2e9834908ed4 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -15841,6 +15841,55 @@ static void DiagnoseShiftCompare(Sema &S, SourceLocation OpLoc, SourceRange(OCE->getArg(1)->getBeginLoc(), RHSExpr->getEndLoc())); } +static bool operandEscapesImmediateMacroExpansion(const SourceManager &SM, + SourceLocation OpLoc, + SourceRange OperandRange) { + + if (!OpLoc.isMacroID()) + return false; + + SourceLocation Begin = OperandRange.getBegin(); + SourceLocation End = OperandRange.getEnd(); + + if (!Begin.isValid() || !End.isValid()) + return false; + + // If operand comes directly from file → definitely escapes + if (!Begin.isMacroID() || !End.isMacroID()) + return true; + + // Compare the immediate expansion location + SourceLocation OpExp = SM.getImmediateMacroCallerLoc(OpLoc); + SourceLocation BeginExp = SM.getImmediateMacroCallerLoc(Begin); + SourceLocation EndExp = SM.getImmediateMacroCallerLoc(End); + + return BeginExp != OpExp || EndExp != OpExp; +} +static void DiagnoseMacroMixedOperator(Sema &Self, BinaryOperatorKind Opc, + SourceLocation OpLoc, Expr *LHSExpr, + Expr *RHSExpr) { + const SourceManager &SM = Self.getSourceManager(); + + if (!OpLoc.isMacroID()) + return; + + // Only when the operator token comes from the macro *body*. + if (!SM.isMacroBodyExpansion(OpLoc)) + return; + + bool LHSBad = operandEscapesImmediateMacroExpansion( + SM, OpLoc, LHSExpr->getSourceRange()); + bool RHSBad = operandEscapesImmediateMacroExpansion( + SM, OpLoc, RHSExpr->getSourceRange()); + + if (!LHSBad && !RHSBad) + return; + + Self.Diag(OpLoc, diag::warn_operator_in_macro_with_mixed_operand) + << BinaryOperator::getOpcodeStr(Opc) << LHSExpr->getSourceRange() + << RHSExpr->getSourceRange(); +} + /// DiagnoseBinOpPrecedence - Emit warnings for expressions with tricky /// precedence. static void DiagnoseBinOpPrecedence(Sema &Self, BinaryOperatorKind Opc, @@ -15887,6 +15936,8 @@ ExprResult Sema::ActOnBinOp(Scope *S, SourceLocation TokLoc, // Emit warnings for tricky precedence issues, e.g. "bitfield & 0x4 == 0" DiagnoseBinOpPrecedence(*this, Opc, TokLoc, LHSExpr, RHSExpr); + DiagnoseMacroMixedOperator(*this, Opc, TokLoc, LHSExpr, RHSExpr); + BuiltinCountedByRefKind K = BinaryOperator::isAssignmentOp(Opc) ? BuiltinCountedByRefKind::Assignment : BuiltinCountedByRefKind::BinaryExpr; diff --git a/clang/test/Sema/macro-mixed-operator.c b/clang/test/Sema/macro-mixed-operator.c new file mode 100644 index 0000000000000..f9f2212409cd5 --- /dev/null +++ b/clang/test/Sema/macro-mixed-operator.c @@ -0,0 +1,200 @@ +// RUN: %clang_cc1 -fsyntax-only -Wmacro-mixed-operator -verify %s + +// ------------------------------------------------------------ +// Basic macro body precedence problem +// ------------------------------------------------------------ +#define FOO 2+3 +// 2+(3*4) == 14, not (2+3)*4 == 20 +int n = FOO*4; // expected-warning {{operator '+' in macro expansion has operand outside the macro; operator precedence may be different than expected}} + +// ------------------------------------------------------------ +// Macro argument precedence problem +// ------------------------------------------------------------ +#define FOO_ARG(x) 2+x +int m = FOO_ARG(3*4); // expected-warning {{operator '+' in macro expansion has operand outside the macro; operator precedence may be different than expected}} + +// ------------------------------------------------------------ +// Operator entirely outside macros (should NOT warn) +// ------------------------------------------------------------ +int k = 2 + 3 * 4; // no-warning + +// ------------------------------------------------------------ +// Operator comes from macro argument only (should NOT warn) +// ------------------------------------------------------------ +#define ID(x) x +int p = ID(2+3)*4; // no-warning + +// ------------------------------------------------------------ +// Macro with proper parentheses (should NOT warn) +// ------------------------------------------------------------ +#define SAFE_ADD (2+3) +int q = SAFE_ADD*4; // no-warning + +#define SAFE_ARG(x) (2+(x)) +int r = SAFE_ARG(3*4); // no-warning + +// ------------------------------------------------------------ +// Nested macro expansion +// ------------------------------------------------------------ +#define INNER 2+3 +#define OUTER INNER +int s = OUTER*4; // expected-warning {{operator '+' in macro expansion has operand outside the macro; operator precedence may be different than expected}} + +// ------------------------------------------------------------ +// Macro producing multiplication interacting with external + +// ------------------------------------------------------------ +#define MUL 2*3 +int t = MUL+4; // no-warning + +// ------------------------------------------------------------ +// Macro with multiple operators +// ------------------------------------------------------------ +#define MIXED 1+2*3 +int u = MIXED+4; // no-warning + +// ------------------------------------------------------------ +// Macro argument containing another macro +// ------------------------------------------------------------ +#define ADD(x) 2+x +#define VALUE 3*4 +int v = ADD(VALUE); // expected-warning {{operator '+' in macro expansion has operand outside the macro; operator precedence may be different than expected}} + +// ------------------------------------------------------------ +// Macro where entire expression stays inside expansion +// ------------------------------------------------------------ +#define FULL (2+3) +int w = FULL; // no-warning + +// ------------------------------------------------------------ +// Operator both operands inside macro body +// ------------------------------------------------------------ +#define ADD_TWO (2+3) +int x = ADD_TWO; // no-warning + +// ------------------------------------------------------------ +// Chained macro expansions +// ------------------------------------------------------------ +#define A 2+3 +#define B A +#define C B +int y = C*4; // expected-warning {{operator '+' in macro expansion has operand outside the macro; operator precedence may be different than expected}} + +// ------------------------------------------------------------ +// Unary operator on macro (should warn) +// ------------------------------------------------------------ +#define VAL 2+3 +int un1 = -VAL; // expected-warning {{operator '+' in macro expansion has operand outside the macro; operator precedence may be different than expected}} + +// ------------------------------------------------------------ +// Macro used as function argument (should NOT warn) +// ------------------------------------------------------------ +extern int func(int, int); +#define EXPR 2+3 +void test_func_args(void) { +int fa1 = func(EXPR, 1); // no-warning +int fa2 = func(1, EXPR); // no-warning +} + +// ------------------------------------------------------------ +// Macro on both sides of operator (should NOT warn) +// ------------------------------------------------------------ +#define TWO 2 +#define THREE 3 +int bb1 = TWO + THREE; // no-warning + +// ------------------------------------------------------------ +// Macro result used in comparison (lower precedence, should NOT warn) +// ------------------------------------------------------------ +#define SUM 2+3 +int cmp1 = SUM == 5; // no-warning + +// ------------------------------------------------------------ +// Macro result used in logical op (lower precedence, should NOT warn) +// ------------------------------------------------------------ +int cmp2 = SUM && 1; // no-warning +int cmp3 = SUM || 0; // no-warning + +// ------------------------------------------------------------ +// Higher-precedence op outside, macro has lower-precedence op inside +// ------------------------------------------------------------ +#define ADDM 1+2 +int ho1 = ADDM * 3; // expected-warning {{operator '+' in macro expansion has operand outside the macro; operator precedence may be different than expected}} + +// ------------------------------------------------------------ +// Ternary condition uses macro (should NOT warn) +// ------------------------------------------------------------ +#define COND 1+1 +int tern1 = COND ? 1 : 0; // no-warning + +// ------------------------------------------------------------ +// Macro in assignment RHS (should NOT warn) +// ------------------------------------------------------------ +#define RVAL 3+4 +int asgn2 = RVAL; // no-warning + +// ------------------------------------------------------------ +// Macro argument with same-precedence op (should warn) +// ------------------------------------------------------------ +#define WRAP(x) 1+x +int sa1 = WRAP(2+3); // expected-warning {{operator '+' in macro expansion has operand outside the macro; operator precedence may be different than expected}} + +// ------------------------------------------------------------ +// Double-nested argument macro +// ------------------------------------------------------------ +#define OUTER2(x) 10+x +#define INNER2(x) x*2 +int dn1 = OUTER2(INNER2(3)); // expected-warning {{operator '+' in macro expansion has operand outside the macro; operator precedence may be different than expected}} + +// ------------------------------------------------------------ +// Macro used twice in same expression +// ------------------------------------------------------------ +#define BASE 1+2 +int tw1 = BASE * BASE; // expected-warning 2 {{operator '+' in macro expansion has operand outside the macro; operator precedence may be different than expected}} + +// ------------------------------------------------------------ +// Parenthesized macro argument (should NOT warn) +// ------------------------------------------------------------ +#define ADDP(x) 5+(x) +int pa1 = ADDP(3*2); // no-warning + +// ------------------------------------------------------------ +// Shift operators interacting with macro addition: existing precedence warning +// ------------------------------------------------------------ +#define SHIFT_BASE 1+2 +int sh1 = SHIFT_BASE << 1; // expected-warning {{operator '<<' has lower precedence than '+'; '+' will be evaluated first}} expected-note {{place parentheses around the '+' expression to silence this warning}} +int sh2 = SHIFT_BASE >> 1; // expected-warning {{operator '>>' has lower precedence than '+'; '+' will be evaluated first}} expected-note {{place parentheses around the '+' expression to silence this warning}} + +// ------------------------------------------------------------ +// Bitwise AND with macro addition (should NOT warn) +// ------------------------------------------------------------ +#define BVAL 2+3 +int bw1 = BVAL & 0xFF; // no-warning + +// ------------------------------------------------------------ +// Subtraction in macro body with multiplication outside (should warn) +// ------------------------------------------------------------ +#define SUBM 5-2 +int sub1 = SUBM * 3; // expected-warning {{operator '-' in macro expansion has operand outside the macro; operator precedence may be different than expected}} + +// ------------------------------------------------------------ +// Division in macro body with addition outside (should NOT warn) +// ------------------------------------------------------------ +#define DIVM 6/2 +int div1 = DIVM + 1; // no-warning + +// ------------------------------------------------------------ +// Deeply chained: DD->DC->DB->DA, multiply outside (should warn) +// ------------------------------------------------------------ +#define DA 1+2 +#define DB DA +#define DC DB +#define DD DC +int dc1 = DD * 5; // expected-warning {{operator '+' in macro expansion has operand outside the macro; operator precedence may be different than expected}} + +// ------------------------------------------------------------ +// Nested macro expansion: FileID approach would incorrectly think +// the operand escapes, getImmediateMacroCallerLoc handles it correctly +// ------------------------------------------------------------ +#define INNER_MACRO 2+3 +#define OUTER_MACRO INNER_MACRO +int nested_test = OUTER_MACRO*4; // expected-warning {{operator '+' in macro expansion has operand outside the macro; operator precedence may be different than expected}} \ No newline at end of file _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
