code that rewrites code, out of sight

Macro & Mixin Misuse

Textual macros and order-dependent mixins inject behavior at a distance, so what runs is not what the call site appears to say.

Metaprogramming expands or mixes behavior the reader never sees.

01in the wild

In the wild

Unhygienic Macros

A textual macro substitutes without parentheses or types, so it expands into something you did not write.

example.c
/* SMELL: function-like macro with unguarded args */
#define SQUARE(x) x * x
SQUARE(1 + 2)        /* expands to 1 + 2 * 1 + 2 = 5, not 9 */

/* SMELL: argument evaluated twice */
#define MAX(a, b) ((a) > (b) ? (a) : (b))
MAX(i++, j++)        /* increments the winner twice */

/* RIGHT: a real (inline) function -- typed, one evaluation */
static inline int square(int x) { return x * x; }
Macros are textual substitution with no type checking and no scope. An inline function gives the same speed with none of the traps.
// observed
macro: SQUARE(1+2) = 5  (surprise)
func:  square(1+2) = 9
example.js
// SMELL: a mixin that mutates the target's state
const Timestamped = (obj) => Object.assign(obj, {
  touched: Date.now(),         // impure + shared mutation
});

// RIGHT: compose explicitly, keep data and behavior apart
const withTimestamp = (obj, now) => ({ ...obj, touched: now });
Object.assign mutates its target and the impure Date.now() hides inside the mixin. Composition keeps state explicit.
// observed
mixin: original object is mutated; time hidden inside
pure:  caller supplies `now`; original untouched

Clobbered by Mixin Order

Two mixins define the same member, so which one wins depends only on the order they were applied.

example.js
const Serializable = { save() { return "json"; } };
const Auditable    = { save() { return "audit-log"; } };

// SMELL: last mixin silently wins; the other save() vanishes
const model = Object.assign({}, Serializable, Auditable);
model.save();          // "audit-log" -- did you mean to lose json?

// RIGHT: name the behaviors; never let them collide
const model = { saveJson: Serializable.save, audit: Auditable.save };
Object.assign copies left to right, so a later mixin overwrites an earlier method with no warning.
// observed
mixed:  save() -> 'audit-log' (json lost)
explicit: saveJson() and audit() both reachable
example.py
# SMELL: cooperative methods clobber unless each calls super()
class A:        # mixin
    def setup(self): self.log = ["A"]
class B:        # mixin
    def setup(self): self.log = ["B"]
class W(A, B):  # MRO is A, B -- B.setup never runs
    pass
W().setup()              # log == ['A']

# RIGHT: cooperate via super() so every mixin contributes
class A:
    def setup(self): super().setup(); self.log.append("A")
Python's MRO picks the first matching method; without super() chaining, later mixins are silently skipped.
// observed
naive: log == ['A']  (B skipped)
super: log == ['B', 'A']  (both run)
02cross-pollination

Where this compounds

03weakness catalog

Mapped weaknesses (CWE)

On its own, this defect is catalogued by MITRE as one or more of these weaknesses. The exploitable vulnerability usually appears only when it chains or combines with another.