diff --git a/src/Famix-Python-Importer-Tests/FamixPythonProject1Test.class.st b/src/Famix-Python-Importer-Tests/FamixPythonProject1Test.class.st index 9a50a86..1694390 100644 --- a/src/Famix-Python-Importer-Tests/FamixPythonProject1Test.class.st +++ b/src/Famix-Python-Importer-Tests/FamixPythonProject1Test.class.st @@ -655,6 +655,34 @@ FamixPythonProject1Test >> testInstanceVariableSourceAnchor [ self assert: variable sourceAnchor endPos equals: 361 ] +{ #category : 'tests - attributes' } +FamixPythonProject1Test >> testInstanceVariablesAssignedWithATuple [ + + | variable | + self denyEmpty: self model allAttributes. + + variable := self attributeNamed: 'ivarTuple1'. + + self assert: variable class equals: FamixPythonAttribute. + self assert: variable name equals: 'ivarTuple1'. + self assert: variable parentType equals: (self classNamed: 'ClassWithTuples'). + self deny: variable isClassSide. + + variable := self attributeNamed: 'ivarTuple2'. + + self assert: variable class equals: FamixPythonAttribute. + self assert: variable name equals: 'ivarTuple2'. + self assert: variable parentType equals: (self classNamed: 'ClassWithTuples'). + self deny: variable isClassSide. + + variable := self attributeNamed: 'ivarTuple3'. + + self assert: variable class equals: FamixPythonAttribute. + self assert: variable name equals: 'ivarTuple3'. + self assert: variable parentType equals: (self classNamed: 'ClassWithTuples'). + self deny: variable isClassSide +] + { #category : 'tests - attributes' } FamixPythonProject1Test >> testInstanceVariablesWithSameNameButDifferenClass [ diff --git a/src/Famix-Python-Importer/FamixPythonImporterVisitor.class.st b/src/Famix-Python-Importer/FamixPythonImporterVisitor.class.st index b2c244a..97bc5eb 100644 --- a/src/Famix-Python-Importer/FamixPythonImporterVisitor.class.st +++ b/src/Famix-Python-Importer/FamixPythonImporterVisitor.class.st @@ -5,7 +5,8 @@ Class { #classTraits : 'SRTSolverUserVisitor classTrait', #instVars : [ 'model', - 'rootFilePath' + 'rootFilePath', + 'isInLeftSideOfAssignation' ], #category : 'Famix-Python-Importer-Visitors', #package : 'Famix-Python-Importer', @@ -232,25 +233,6 @@ FamixPythonImporterVisitor >> ensureClassNamed: aName [ ^ (self classNamed: aName) ifNil: [ model newClass name: aName ] ] -{ #category : 'private-entity-creation' } -FamixPythonImporterVisitor >> ensureInstanceVariableFromAssignation: anAssignmentStatement [ - - | class | - class := self currentEntityOfType: FamixPythonClass. - - ^ (class childOfType: FamixTAttribute named: anAssignmentStatement lhs name) ifNil: [ - | variable | - variable := self model newAttribute - name: anAssignmentStatement lhs name; - parentType: class; - isClassSide: false; - yourself. - - "We select the lhs node because the source anchor should not be the full assignation." - self setSourceAnchor: variable from: anAssignmentStatement lhs. - variable ] -] - { #category : 'private-entity-creation' } FamixPythonImporterVisitor >> ensureMethod: aMethodNode [ @@ -333,28 +315,6 @@ FamixPythonImporterVisitor >> ensureVariable: aName localTo: aFamixEntity [ ifAbsent: [ aFamixEntity createLocalVariable: aName ] ] -{ #category : 'visiting' } -FamixPythonImporterVisitor >> ensureVariableFromAssignationStatement: anAssignmentStatement [ - "I am really not sure what is the best thing to do here. So for now I'll write some code that will keep all the tests I'm adding green and when the parser will cover most cases I might come back to refactore this code into something better with less #isKindOf: or ifs." - - "This is to manage global variables, class variables, local variables." - (anAssignmentStatement lhs isKindOf: PyVariableExpressionNode) ifTrue: [ - ^ (self currentEntity childOfType: FamixTStructuralEntity named: anAssignmentStatement lhs name) ifNil: [ - | variable | - variable := self currentEntity createLocalVariable: anAssignmentStatement lhs name. - "We select the lhs node because the source anchor should not be the full assignation." - self setSourceAnchor: variable from: anAssignmentStatement lhs. - ^ variable ] ]. - - anAssignmentStatement isInstanceVariableAssignation ifTrue: [ - self assert: self currentEntity isMethod. "Maybe this could be removed when the parser is stable?" - ^ self ensureInstanceVariableFromAssignation: anAssignmentStatement ]. - - self flag: #todo. "manage other cases." - "self error: 'We should not end up here.'" - ^ nil -] - { #category : 'accessing' } FamixPythonImporterVisitor >> extractArgumentsInformation: aSignature [ @@ -510,7 +470,8 @@ FamixPythonImporterVisitor >> initialize [ super initialize. model := FamixPythonModel new name: 'default Python Model'. - self initialiseSolver + self initialiseSolver. + isInLeftSideOfAssignation := false "In Python variables are created during their assignation. So it is good to know if some nodes are visited during this assignation." ] { #category : 'private - searching' } @@ -692,7 +653,11 @@ FamixPythonImporterVisitor >> unknownImportedNamed: aString [ { #category : 'visiting' } FamixPythonImporterVisitor >> visitAssignmentStatement: anAssignmentStatement [ - ^ self useCurrentEntity: (self ensureVariableFromAssignationStatement: anAssignmentStatement) during: [ super visitAssignmentStatement: anAssignmentStatement ] + | oldValue | + oldValue := isInLeftSideOfAssignation. + isInLeftSideOfAssignation := true. + [ self acceptNode: anAssignmentStatement lhs ] ensure: [ isInLeftSideOfAssignation := oldValue ]. + ^ super visitAssignmentStatement: anAssignmentStatement ] { #category : 'visiting' } @@ -703,6 +668,23 @@ FamixPythonImporterVisitor >> visitClassDefinition: aClassDef [ { #category : 'generated' } FamixPythonImporterVisitor >> visitFieldAccessExpression: aFieldAccessExpression [ + "If the receiver is self in an assignation then we have an assignation to an instance variable" + + (isInLeftSideOfAssignation and: [ aFieldAccessExpression receiver name = #self ]) ifTrue: [ + | class | + class := self currentEntityOfType: FamixPythonClass. + + ^ (class childOfType: FamixTAttribute named: aFieldAccessExpression name) ifNil: [ + | variable | + variable := self model newAttribute + name: aFieldAccessExpression name; + parentType: class; + isClassSide: false; + yourself. + + "We select the lhs node because the source anchor should not be the full assignation." + self setSourceAnchor: variable from: aFieldAccessExpression. + variable ] ]. ^ aFieldAccessExpression source ] @@ -821,6 +803,15 @@ FamixPythonImporterVisitor >> visitString: aStringNode [ { #category : 'visiting' } FamixPythonImporterVisitor >> visitVariableExpression: aVariableExpression [ + "This node is used in multiple situations. If it is in an assignation we need to check if the variable we assign is created. Other uses can be for example in an import, in this case we want to only return the name." + + isInLeftSideOfAssignation ifTrue: [ + (self currentEntity childOfType: FamixTStructuralEntity named: aVariableExpression name) ifNil: [ + | variable | + variable := self currentEntity createLocalVariable: aVariableExpression name. + "We select the lhs node because the source anchor should not be the full assignation." + self setSourceAnchor: variable from: aVariableExpression. + ^ variable ] ]. ^ aVariableExpression name ] diff --git a/src/Famix-Python-Importer/PyAssignmentStatementNode.extension.st b/src/Famix-Python-Importer/PyAssignmentStatementNode.extension.st deleted file mode 100644 index 2e45b86..0000000 --- a/src/Famix-Python-Importer/PyAssignmentStatementNode.extension.st +++ /dev/null @@ -1,10 +0,0 @@ -Extension { #name : 'PyAssignmentStatementNode' } - -{ #category : '*Famix-Python-Importer' } -PyAssignmentStatementNode >> isInstanceVariableAssignation [ - "I am an inst var assignation if I am in the form of `self.x = y`. For now I don't know if I need to do more than checking if the value of the receiver of the left side is #self. I might refine this method later." - - self lhs isSubscriptExpression ifTrue: [ ^ false ]. - - ^ self lhs receiver name = #self -]