Index: src/InitTweak/FixInit.cc
===================================================================
--- src/InitTweak/FixInit.cc	(revision a16764a6fbfe44300fc8834400a31c89befda091)
+++ src/InitTweak/FixInit.cc	(revision babeedade3db87533af6179c3a700ed59a31106a)
@@ -68,4 +68,8 @@
 		typedef std::unordered_map< int, int > UnqCount;
 
+		struct SelfAssignChecker {
+			void previsit( ApplicationExpr * appExpr );
+		};
+
 		struct InsertImplicitCalls : public WithTypeSubstitution {
 			/// wrap function application expressions as ImplicitCopyCtorExpr nodes so that it is easy to identify which
@@ -235,4 +239,7 @@
 
 	void fix( std::list< Declaration * > & translationUnit, const std::string & filename, bool inLibrary ) {
+		PassVisitor<SelfAssignChecker> checker;
+		acceptAll( translationUnit, checker );
+
 		// fixes ConstructorInit for global variables. should happen before fixInitializers.
 		InitTweak::fixGlobalInit( translationUnit, filename, inLibrary );
@@ -309,4 +316,81 @@
 			PassVisitor<FixCtorExprs> fixer;
 			mutateAll( translationUnit, fixer );
+		}
+
+		namespace {
+			// Relatively simple structural comparison for expressions, needed to determine
+			// if two expressions are "the same" (used to determine if self assignment occurs)
+			struct StructuralChecker {
+				Expression * stripCasts( Expression * expr ) {
+					// this might be too permissive. It's possible that only particular casts are relevant.
+					while ( CastExpr * cast = dynamic_cast< CastExpr * >( expr ) ) {
+						expr = cast->arg;
+					}
+					return expr;
+				}
+
+				void previsit( Expression * ) {
+					// anything else does not qualify
+					isSimilar = false;
+				}
+
+				template<typename T>
+				T * cast( Expression * node ) {
+					// all expressions need to ignore casts, so this bit has been factored out
+					return dynamic_cast< T * >( stripCasts( node ) );
+				}
+
+				// ignore casts
+				void previsit( CastExpr * ) {}
+
+				void previsit( MemberExpr * memExpr ) {
+					if ( MemberExpr * otherMember = cast< MemberExpr >( other ) ) {
+						if ( otherMember->member == memExpr->member ) {
+							other = otherMember->aggregate;
+							return;
+						}
+					}
+					isSimilar = false;
+				}
+
+				void previsit( VariableExpr * varExpr ) {
+					if ( VariableExpr * otherVar = cast< VariableExpr >( other ) ) {
+						if ( otherVar->var == varExpr->var ) {
+							return;
+						}
+					}
+					isSimilar = false;
+				}
+
+				void previsit( AddressExpr * ) {
+					if ( AddressExpr * addrExpr = cast< AddressExpr >( other ) ) {
+						other = addrExpr->arg;
+						return;
+					}
+					isSimilar = false;
+				}
+
+				Expression * other = nullptr;
+				bool isSimilar = true;
+			};
+
+			bool structurallySimilar( Expression * e1, Expression * e2 ) {
+				PassVisitor<StructuralChecker> checker;
+				checker.pass.other = e2;
+				e1->accept( checker );
+				return checker.pass.isSimilar;
+			}
+		}
+
+		void SelfAssignChecker::previsit( ApplicationExpr * appExpr ) {
+			DeclarationWithType * function = getFunction( appExpr );
+			if ( isAssignment( function ) ) {
+				if ( appExpr->args.size() == 2 ) {
+					// check for structural similarity (same variable use, ignore casts, etc. - but does not look too deeply, anything looking like a function is off limits)
+					if ( structurallySimilar( appExpr->args.front(), appExpr->args.back() ) ) {
+						SemanticWarning( appExpr->location, Warning::SelfAssignment, toCString( appExpr->args.front() ) );
+					}
+				}
+			}
 		}
 
