Hi,

It's possible by mistake to create a class with duplicate instance
variable, the following patch checks and warms the user. I plan to
do the same but for duplicate methods defined on a class.

Gwen
>From 892f5a7b77462e1005f555833ff2a2b75c07486d Mon Sep 17 00:00:00 2001
From: Gwenael Casaccio <[email protected]>
Date: Fri, 29 Jul 2011 17:24:30 +0200
Subject: [PATCH] duplicate instance variables

---
 kernel/Behavior.st             |   21 +++++++++--
 kernel/SysExcept.st            |   72 ++++++++++++++++++++++++++++++++++++++++
 snprintfv/snprintfv/filament.h |    4 +-
 snprintfv/snprintfv/printf.h   |    8 ++--
 snprintfv/snprintfv/stream.h   |    4 +-
 tests/untrusted.ok             |    1 +
 6 files changed, 98 insertions(+), 12 deletions(-)

diff --git a/kernel/Behavior.st b/kernel/Behavior.st
index 65603b3..1b93e63 100644
--- a/kernel/Behavior.st
+++ b/kernel/Behavior.st
@@ -79,6 +79,17 @@ method dictionary, and iterating over the class hierarchy.'>
 	    compileAllSubclasses
     ]
 
+    checkExistanceOfIVar: anArray [
+
+	| oldInstVarNames set |
+	KernelInitialized ifFalse: [ ^ self ].
+	oldInstVarNames := self superclass ifNil: [ #() ] ifNotNil: [ self superclass allInstVarNames ].
+	(oldInstVarNames includesAnyOf: anArray) ifTrue: [ DuplicateInstanceVariable behavior: self vars: anArray ].
+	oldInstVarNames := Set new.
+	self allSubclassesDo: [ :each | oldInstVarNames addAll: each asClass instVarNames ].
+	(oldInstVarNames includesAnyOf: anArray) ifTrue: [ DuplicateInstanceVariable behavior: self vars: anArray ].
+    ]
+
     instanceVariableNames: instVarNames [
         "Set the instance variables for the receiver to be those
          in instVarNames"
@@ -86,7 +97,9 @@ method dictionary, and iterating over the class hierarchy.'>
         <category: 'instance variables'>
         | variableArray oldInstVarNames oldSize removed changed added |
         variableArray := self parseInstanceVariableString: instVarNames.
+	[ self checkExistanceOfIVar: variableArray ] on: DuplicateInstanceVariable do: [ :each | each messageText printNl ].
         variableArray := self subclassInstVarNames, variableArray.
+
         oldInstVarNames := self allInstVarNames.
 
         "If instance variables change, update  instance variables and
@@ -1433,7 +1446,7 @@ method dictionary, and iterating over the class hierarchy.'>
 	oldSuper := self superclass.
 	newSuper == oldSuper ifFalse: [
 	    [ newSuper includesBehavior: oldSuper ] whileFalse: [
-	        oldSuper := oldSuper superclass ] ].
+		oldSuper := oldSuper superclass ] ].
 
 	"Make map for inherited instance variables."
 	oldInstVars := self allInstVarNames.
@@ -1446,9 +1459,9 @@ method dictionary, and iterating over the class hierarchy.'>
 	oldInstVars
 	    from: oldSuper instSize + 1 to: oldInstVars size
 	    keysAndValuesDo: [ :index :var |
-	        | map |
-	        map := newInstVars findLast: [:each | each = var].
-	        map > 0 ifTrue: [instVarMap at: index put: map + newSuper instSize]].
+		| map |
+		map := newInstVars findLast: [:each | each = var].
+		map > 0 ifTrue: [instVarMap at: index put: map + newSuper instSize]].
 
 	"Fix up all subclasses."
 	self allSubclassesDo: 
diff --git a/kernel/SysExcept.st b/kernel/SysExcept.st
index 9660f7c..19baf8c 100644
--- a/kernel/SysExcept.st
+++ b/kernel/SysExcept.st
@@ -103,6 +103,78 @@ even though it is not to be considered an error.'
 ]
 
 
+Warning subclass: DuplicateInstanceVariable [
+
+    DuplicateInstanceVariable class >> behavior: aBehavior vars: anArray [
+       <category: 'instance creation'>
+
+        ^ (self new)
+	    behavior: aBehavior vars: anArray;
+            signal
+    ]
+
+    | behavior array |
+
+    behavior: aBehavior vars: anArray [
+	<category: 'initialization'>
+
+	behavior := aBehavior.
+	array := anArray
+    ]
+
+    description [
+       <category: 'description'>
+
+       ^ 'Trying to add an instance variable which was defined on a superclass or a subclass'
+    ]
+
+    checkSuperclasses [
+	<category: 'private'>
+
+        | oldInstVarNames result |
+        result := Set new.
+        oldInstVarNames := behavior superclass ifNil: [ #() ] ifNotNil: [ behavior superclass allInstVarNames ].
+        oldInstVarNames do: [ :each | (array includes: each) ifTrue: [ result add: each ] ].
+        ^ result
+    ]
+
+    checkSubclasses [
+	<category: 'private'>
+
+        | oldInstVarNames result |
+        result := Set new.
+        oldInstVarNames := Set new.
+        behavior allSubclassesDo: [ :each | oldInstVarNames addAll: each asClass instVarNames ].
+        oldInstVarNames do: [ :each | (array includes: each) ifTrue: [ result add: each ] ].
+        ^ result
+    ]
+
+    print: aSet1 with: aSet2 [
+	<category: 'private'>
+
+	| string |
+	string := behavior displayString.
+        aSet1 isEmpty ifFalse: [ string := string, ' redefines instance variables : #', aSet1 asArray displayString, ' already defined on superclasses' ].
+        aSet2 isEmpty ifFalse: [
+            aSet1 isEmpty ifFalse: [ string := string, ' and' ].
+            string := string, ' redefines instance variables : #', aSet2 asArray displayString, ' already defined on subclasses' ].
+	^ string
+    ]
+
+    messageText [
+	<category: 'accessing'>
+
+	^ self print: self checkSuperclasses with: self checkSubclasses
+    ]
+
+    defaultAction [
+        <category: 'exception description'>
+
+        self resignalAsUnhandled: self messageText
+    ]
+]
+
+
 Exception subclass: Halt [
     
     <category: 'Language-Exceptions'>
diff --git a/snprintfv/snprintfv/filament.h b/snprintfv/snprintfv/filament.h
index 4a91eb6..8a7ce6c 100644
--- a/snprintfv/snprintfv/filament.h
+++ b/snprintfv/snprintfv/filament.h
@@ -1,4 +1,4 @@
-#line 1 "../../../snprintfv/snprintfv/filament.in"
+#line 1 "./filament.in"
 /*  -*- Mode: C -*-  */
 
 /* filament.h --- a bit like a string but different =)O|
@@ -118,7 +118,7 @@ extern char * fildelete (Filament *fil);
 extern void _fil_extend (Filament *fil, size_t len, boolean copy);
 
 
-#line 61 "../../../snprintfv/snprintfv/filament.in"
+#line 61 "./filament.in"
 
 /* Save the overhead of a function call in the great majority of cases. */
 #define fil_maybe_extend(fil, len, copy)  \
diff --git a/snprintfv/snprintfv/printf.h b/snprintfv/snprintfv/printf.h
index 49a2e9f..1437dd5 100644
--- a/snprintfv/snprintfv/printf.h
+++ b/snprintfv/snprintfv/printf.h
@@ -1,4 +1,4 @@
-#line 1 "../../../snprintfv/snprintfv/printf.in"
+#line 1 "./printf.in"
 /*  -*- Mode: C -*-  */
 
 /* printf.in --- printf clone for argv arrays
@@ -266,7 +266,7 @@ enum
       } \
   } SNV_STMT_END
 
-#line 269 "../../../snprintfv/snprintfv/printf.in"
+#line 269 "./printf.in"
 /**
  * printf_generic_info:   
  * @pinfo: the current state information for the format
@@ -302,7 +302,7 @@ extern int printf_generic_info (struct printf_info *const pinfo, size_t n, int *
 extern int printf_generic (STREAM *stream, struct printf_info *const pinfo, union printf_arg const *args);
 
 
-#line 270 "../../../snprintfv/snprintfv/printf.in"
+#line 270 "./printf.in"
 /**
  * register_printf_function:  
  * @spec: the character which will trigger @func, cast to an unsigned int.
@@ -789,7 +789,7 @@ extern int snv_vasprintf (char **result, const char *format, va_list ap);
 extern int snv_asprintfv (char **result, const char *format, snv_constpointer const args[]);
 
 
-#line 271 "../../../snprintfv/snprintfv/printf.in"
+#line 271 "./printf.in"
 
 /* If you don't want to use snprintfv functions for *all* of your string
    formatting API, then define COMPILING_SNPRINTFV_C and use the snv_
diff --git a/snprintfv/snprintfv/stream.h b/snprintfv/snprintfv/stream.h
index 496bd33..0bebce1 100644
--- a/snprintfv/snprintfv/stream.h
+++ b/snprintfv/snprintfv/stream.h
@@ -1,4 +1,4 @@
-#line 1 "../../../snprintfv/snprintfv/stream.in"
+#line 1 "./stream.in"
 /*  -*- Mode: C -*-  */
 
 /* stream.h --- customizable stream routines
@@ -180,7 +180,7 @@ extern int stream_puts (char *s, STREAM *stream);
 extern int stream_get (STREAM *stream);
 
 
-#line 88 "../../../snprintfv/snprintfv/stream.in"
+#line 88 "./stream.in"
 #ifdef __cplusplus
 #if 0
 /* This brace is so that emacs can still indent properly: */
diff --git a/tests/untrusted.ok b/tests/untrusted.ok
index 8465e63..6fd3f09 100644
--- a/tests/untrusted.ok
+++ b/tests/untrusted.ok
@@ -1,3 +1,4 @@
+'A redefines instance variables : #(#a ) already defined on superclasses'
 
 Execution begins...
 returned value is true
-- 
1.7.4.1

_______________________________________________
help-smalltalk mailing list
[email protected]
https://lists.gnu.org/mailman/listinfo/help-smalltalk

Reply via email to