Option Explicit
Dim NomModule As String
Dim NomProc As String
Dim TypeProcedure As vbext_ProcKind
Dim LigneStartProc As Long
Dim LigneBodyProc As Long
Dim NbLignesProc As Long
Dim CodeContenu As String
Dim CodeDeclaration As String
Dim TypeDeclaration As String
Dim ListeVarInuProc As String, ListeVarInuModule As String
Dim NbInutile As Integer
Dim TabDimEc() As String, ITabDimEc As Integer
Dim TabDimEcc() As String, ITabDimEcc As Integer, ITabDimEcc2 As Integer
Dim TabDimEccVide As Boolean
Sub ListerComposantsClasseurs()
' Liste des composanst des classeurs ouverts
Dim DerLigne As Long, DerColonne As Long
Dim VBComp As VBComponent
Dim i As Integer
Dim WorkBookCourant As Workbook
With Sheets("Variables Inutilisées")
DerLigne = .Cells(.Rows.Count, .Range("A2").Column).End(xlUp).Row
DerColonne = .Cells(.Range("A1").Row, .Columns.Count).End(xlToLeft).Column
Application.EnableEvents = False
.Range(.Range("A2"), .Cells(DerLigne, DerColonne)).ClearContents
Application.EnableEvents = True
i = 2
For Each WorkBookCourant In Workbooks
For Each VBComp In WorkBookCourant.VBProject.VBComponents
.Cells(i, 1) = WorkBookCourant.Name
.Cells(i, 2) = VBComp.Name
i = i + 1
Next VBComp
Next WorkBookCourant
End With
End Sub
Sub LancerAnalyseCodeVarInu()
' Demande d'analyse pour un module d'un Classeur OUVERT
' de la ligne sélectionnée dans la feuille
With ActiveSheet
AnalyseCodeVarInu .Cells(ActiveCell.Row, 1), .Cells(ActiveCell.Row, 2)
End With
End Sub
Sub AnalyseCodeVarInu(pNomClasseur As String, pNomModule As String)
' Fonctionne pour les Variables déclarées Dim dans le Module
' Il y aura besoin de code supplémentaire pour des variables :
' Public, Private, Static, Array, Type ... End Type
Dim Titre As String
Dim Message As String
Dim Debut As Long
Dim i As Integer, j As Integer
Titre = "Analyse des variables (Dim) inutilisées dans un Module"
'On analyse uniquement les variables déclarées Dim
TypeDeclaration = "Dim "
NomModule = pNomModule
NbInutile = 0
With Workbooks(pNomClasseur).VBProject.VBComponents(NomModule).CodeModule
TabDimEccVide = True
'Déclarations du Module
'----------------------
NomProc = ""
NbLignesProc = .CountOfDeclarationLines
If NbLignesProc > 0 Then
CodeDeclaration = .Lines(1, .CountOfDeclarationLines) & vbLf
MajTableauDimProcedure CodeDeclaration
End If
' Déclarations de Chaque Procédure
'---------------------------------
'positionnement après la partie Déclarations du Module
Debut = .CountOfDeclarationLines + 1
Do Until Debut >= .CountOfLines
NomProc = .ProcOfLine(Debut, TypeProcedure)
LigneBodyProc = .ProcBodyLine(NomProc, TypeProcedure)
NbLignesProc = .ProcCountLines(NomProc, TypeProcedure)
CodeContenu = .Lines(LigneBodyProc, NbLignesProc)
MajTableauDimProcedure CodeContenu
Debut = Debut + NbLignesProc + 1
Loop
' liste des Variables Dim du Module (nom procédure - nom variable)
If ITabDimEcc > 0 Then
For ITabDimEcc = LBound(TabDimEcc, 2) To UBound(TabDimEcc, 2)
Message = Message & TabDimEcc(0, ITabDimEcc) & " - " & TabDimEcc(1, ITabDimEcc) & vbLf
Next ITabDimEcc
Else
Message = "Aucune variable déclarée !"
MsgBox Message, vbInformation, "Liste des variables du Module" & NomModule
GoTo FinTraitement
End If
' Pour afficher la Liste des variables déclarées
MsgBox Message, vbInformation, "Liste des variables du Module" & NomModule
'Analyse des Déclarations du Module
'----------------------------------
ListeVarInuProc = ""
NomProc = ""
LigneStartProc = 1
LigneBodyProc = 1
NbLignesProc = .CountOfDeclarationLines
If NbLignesProc > 0 Then
CodeContenu = .Lines(1, .CountOfLines)
AnalyseCodeVarInuProcedure True, CodeContenu
End If
ListeVarInuModule = "--- Déclarations du Module" & vbLf
If ListeVarInuProc = "" Then ListeVarInuModule = ListeVarInuModule & "Aucune" Else ListeVarInuModule = ListeVarInuModule & ListeVarInuProc
'Analyse des Déclarations de Chaque Procédure
'--------------------------------------------
'positionnement après la partie Déclarations du Module
Debut = .CountOfDeclarationLines + 1
Do Until Debut >= .CountOfLines
ListeVarInuProc = ""
NomProc = .ProcOfLine(Debut, TypeProcedure)
LigneStartProc = .ProcStartLine(NomProc, TypeProcedure)
LigneBodyProc = .ProcBodyLine(NomProc, TypeProcedure)
NbLignesProc = .ProcCountLines(NomProc, TypeProcedure)
CodeContenu = .Lines(LigneBodyProc, NbLignesProc)
AnalyseCodeVarInuProcedure False, CodeContenu
Debut = Debut + NbLignesProc + 1
ListeVarInuModule = ListeVarInuModule & vbLf & "--- Déclarations de la Procédure " & NomProc & vbLf
If ListeVarInuProc = "" Then ListeVarInuModule = ListeVarInuModule & "Aucune" Else ListeVarInuModule = ListeVarInuModule & ListeVarInuProc
Loop
' Pour afficher la Liste des variables inutilisées dans tout le module
'MsgBox ListeVarInuModule, vbInformation, "Liste de toutes les variables inutilisées du Module" & NomModule
Debug.Print "-----------------------------------------------------------------------------------------------------------"
Debug.Print " Liste de toutes les variables inutilisées du Module" & NomModule & " du classeur " & pNomClasseur
Debug.Print "-----------------------------------------------------------------------------------------------------------"
Debug.Print ListeVarInuModule
FinTraitement:
If ITabDimEcc > 0 Then
Message = "Analyse du Code du Module " & pNomModule & " du classeur " & pNomClasseur & " terminée." & vbLf _
& NbInutile & " variables inutilisées." & vbLf _
& "Voir la fenêtre Exécution de l'éditeur VBE"
Else
Message = "Analyse du Code du Module " & pNomModule & " du classeur " & pNomClasseur & " terminée." & vbLf _
& "Aucune variable déclarée !"
End If
MsgBox Message, vbInformation, Titre
End With
End Sub
Sub MajTableauDimProcedure(pCodeDeclaration As String)
Dim TabCodeDeclaration() As String
Dim ChaineDim As String
Dim TabDim() As String, ITabDim As Integer
Dim i As Integer, j As Integer
Dim Pos As Integer
' Préparation des tableaux de déclarations Dim
'---------------------------------------------
TabCodeDeclaration = Split(pCodeDeclaration, vbLf)
'la dernière ligne est vide
ReDim Preserve TabCodeDeclaration(0 To UBound(TabCodeDeclaration) - 1)
For i = LBound(TabCodeDeclaration) To UBound(TabCodeDeclaration)
' on enlève les espaces de gauche
TabCodeDeclaration(i) = LTrim(TabCodeDeclaration(i))
' on enlève le saut de ligne
If Len(TabCodeDeclaration(i)) <> 0 And i < UBound(TabCodeDeclaration) - LBound(TabCodeDeclaration) Then
TabCodeDeclaration(i) = Left(TabCodeDeclaration(i), Len(TabCodeDeclaration(i)) - 1)
End If
If Left(LTrim(TabCodeDeclaration(i)), Len(TypeDeclaration)) = TypeDeclaration Then
' Ligne Dim
If ChaineDim = "" Then ChaineDim = TabCodeDeclaration(i) Else ChaineDim = ChaineDim & vbLf & TabCodeDeclaration(i)
End If
Next i
' TabDim contient toutes les déclarations Dim : "Dim xxx"
TabDim = Split(ChaineDim, vbLf)
' Contrôle du contenu à partir des tableaux de déclarations Dim
'--------------------------------------------------------------
For ITabDim = LBound(TabDim) To UBound(TabDim)
' ici on éclate les déclarations multiples sur la même e lignes en x lignes
' on élimine ce qui est en commentaire car il peut y avoir par exemple "Dim Var 1 ' Dim Var2"
Pos = InStr(TabDim(ITabDim), "'")
If Pos <> 0 Then
TabDim(ITabDim) = Left(TabDim(ITabDim), Pos - 1)
End If
TabDimEc = Filter(Split(Replace(Replace(TabDim(ITabDim), "Dim ", "Dim µ"), ", ", " µ")), "µ")
ITabDimEc = 0
For i = LBound(TabDimEc) To UBound(TabDimEc)
ITabDimEc = ITabDimEc + 1
TabDimEc(i) = Replace(TabDimEc(i), "µ", "")
If InStr(TabDimEc(i), "(") <> 0 Then
' Variable Tableau
' -> pour détecter l'utilisation dans le code on testera "Var(" et "Var"
TabDimEc(i) = Left(TabDimEc(i), InStr(TabDimEc(i), "(") - 1)
End If
Next i
If ITabDimEc > 0 Then
'tableau TabDimEc non vide
If TabDimEccVide Then
ITabDimEcc = 0
ReDim TabDimEcc(1, UBound(TabDimEc) - LBound(TabDimEc))
TabDimEccVide = False
Else
ITabDimEcc = UBound(TabDimEcc, 2) + 1
ReDim Preserve TabDimEcc(1, UBound(TabDimEcc, 2) + UBound(TabDimEc) + 1)
End If
For i = LBound(TabDimEc) To UBound(TabDimEc)
TabDimEcc(0, ITabDimEcc) = NomProc
TabDimEcc(1, ITabDimEcc) = TabDimEc(i)
ITabDimEcc = ITabDimEcc + 1
Next i
End If
Next ITabDim
End Sub
Sub AnalyseCodeVarInuProcedure(pDeclarationModule As Boolean, pCodeContenu As String)
Dim TabCodeContenu() As String
Dim TabDim() As String, ITabDim As Integer
Dim ChaineSansCom As String
Dim TabSansCom() As String, ITabSansCom As Integer
Dim i As Integer
Dim ChaineW As String
Dim Pos As Integer, Pos2 As Integer
Dim Absent As Boolean
Dim TrouveMot As Boolean
Dim LigneProcedureCourante As String
Dim NbOcc As Integer
Dim Fin As Boolean
' Analyse variables (pour le moment seulement Dim)
'-------------------
' Tableau du contenu sans Dim et sans les lignes commentaires
'---------------------------------------------
ChaineSansCom = ""
TabCodeContenu = Split(pCodeContenu, vbLf)
For i = LBound(TabCodeContenu) To UBound(TabCodeContenu)
If Len(TabCodeContenu(i)) <> 0 And i <> UBound(TabCodeContenu) Then TabCodeContenu(i) = Left(TabCodeContenu(i), Len(TabCodeContenu(i)) - 1)
If Left(LTrim(TabCodeContenu(i)), Len(TypeDeclaration)) <> TypeDeclaration Then
If Left(LTrim(TabCodeContenu(i)), 1) <> "'" Then
If ChaineSansCom = "" Then ChaineSansCom = TabCodeContenu(i) Else ChaineSansCom = ChaineSansCom & vbLf & TabCodeContenu(i)
End If
End If
Next i
' élimination des lignes commentaires
TabSansCom = Split(ChaineSansCom, vbLf)
' Contrôle du contenu à partir du tableau de déclarations Dim
'--------------------------------------------------------------
LigneProcedureCourante = ""
' liste des Variables Dim du Module (nom procédure - nom variable
For ITabDimEcc = LBound(TabDimEcc, 2) To UBound(TabDimEcc, 2)
'on ne considère que les variables déclarées dans la procédure courante (NomProc ="" pour les déclarations de niveau module
If TabDimEcc(0, ITabDimEcc) = NomProc Then
Absent = True
For ITabSansCom = LBound(TabSansCom) To UBound(TabSansCom)
If pDeclarationModule Then
'on mémorise le nom de la procédure courante
ChaineW = MemoProcCourante(LTrim(TabSansCom(ITabSansCom)))
If ChaineW <> "" Then LigneProcedureCourante = ChaineW
End If
' Recherche de toutes les occurences de présence de la variable courante dans la ligne de code courante
' On examine toutes les coccurrences jusqu'à ce que une soit correcte
' - Pas en commentaire
' - Pas encadrée par ""
Pos = InStr(TabSansCom(ITabSansCom), TabDimEcc(1, ITabDimEcc))
If Pos = 0 Then
Fin = True
Else
ChaineW = TabSansCom(ITabSansCom)
Fin = False
End If
While Not Fin
If EstMot(ChaineW, TabDimEcc(1, ITabDimEcc), Pos) Then
' Pos contient la position de la variable dans la ligne courante
' on regarde si trouvé en dehors d'une zone commentaire
ChaineW = Mid(ChaineW, Pos, Len(ChaineW))
Pos2 = InStr(ChaineW, "'")
If Pos2 <> 0 Then
' on a trouvé le caractère ' sur la ligne
If ChaineEntreApostrophes(ChaineW, "'") Then
'le caractère ' est encadré par "" -> ce n'est donc pas commentaire
'on regarde si variable encadré par ""
Absent = ChaineEntreApostrophes(ChaineW, TabDimEcc(1, ITabDimEcc))
'''If Not Absent Then Exit For
End If
Else
'pas dans zone commentaire
'on regarde si encadré par ""
Absent = ChaineEntreApostrophes(ChaineW, TabDimEcc(1, ITabDimEcc))
End If
If Absent Then
'on cherche l'occurence suivante dans la ligne
ChaineW = Mid(ChaineW, Pos, Len(ChaineW))
If ChaineW = "" Then Fin = True
Else
Fin = True
End If
Else
Fin = True
End If
Wend
If Not Absent Then
If Not pDeclarationModule Then
Exit For
Else
' Variable de Niveau Module
' La variable est utilisée dans une procédure mais il faut vérifier qu'elle n'est pas aussi déclarée dans la procédure
For ITabDimEcc2 = LBound(TabDimEcc, 2) To UBound(TabDimEcc, 2)
If LigneProcedureCourante <> "" _
And (TabDimEcc(0, ITabDimEcc2) = LigneProcedureCourante) _
And (TabDimEcc(1, ITabDimEcc2) = TabDimEcc(1, ITabDimEcc)) Then
' La variable est utilisée dans la procédure mais elle est aussi déclarée dans la procédure
Absent = True
End If
Next ITabDimEcc2
If Not Absent Then Exit For
End If
End If
Next ITabSansCom
If Absent Then
NbInutile = NbInutile + 1
' pour le Debug.Print
If ListeVarInuProc = "" Then ListeVarInuProc = ListeVarInuProc & TabDimEcc(1, ITabDimEcc) Else ListeVarInuProc = ListeVarInuProc & vbLf & TabDimEcc(1, ITabDimEcc)
End If
End If
Next ITabDimEcc
End Sub
Function MemoProcCourante(Pchaine As String) As String
MemoProcCourante = ""
Select Case True
Case Pchaine Like "Private Sub*"
MemoProcCourante = Mid(Pchaine, Len("Private Sub*") + 1)
MemoProcCourante = Left(MemoProcCourante, InStr(MemoProcCourante, "(") - 1)
Case Pchaine Like "Private Function*"
MemoProcCourante = Mid(Pchaine, Len("Private Function*") + 1)
MemoProcCourante = Left(MemoProcCourante, InStr(MemoProcCourante, "(") - 1)
Case Pchaine Like "Sub*"
MemoProcCourante = Mid(Pchaine, Len("Sub*") + 1)
MemoProcCourante = Left(MemoProcCourante, InStr(MemoProcCourante, "(") - 1)
Case Pchaine Like "Function*"
MemoProcCourante = Mid(Pchaine, Len("Function*") + 1)
MemoProcCourante = Left(MemoProcCourante, InStr(MemoProcCourante, "(") - 1)
End Select
End Function
Function ChaineEntreApostrophes(Pchaine As String, pRech As String) As Boolean
'on regarde si encadré par ""
Dim ChaineW As String
Dim Pos As Long, Pos2 As Long
Dim Absent As Boolean
Dim Fin As Boolean
ChaineEntreApostrophes = True
Pos = InStr(Pchaine, pRech)
Pos2 = InStr(Pchaine, """")
If Pos2 < Pos Then
' le premier " est situé avant, on va regarder où est le suivant
ChaineW = Mid(Pchaine, Pos2 + 1, Len(Pchaine))
Fin = False
While Not Fin
Pos = InStr(ChaineW, pRech)
Pos2 = InStr(ChaineW, """")
Select Case True
Case Pos = 0
' Var non trouvée hors zone commentaire
Fin = True
Case Pos2 = 0 And Pos <> 0
' pas de 2ème "
ChaineEntreApostrophes = False
Fin = True
Case Else
' 2ème " avant ou après la variable -> on avance
ChaineW = Mid(ChaineW, Pos2 + 1, Len(ChaineW))
End Select
Wend
Else
ChaineEntreApostrophes = False
End If
End Function
Function EstMot(Pchaine As String, pRech As String, ByRef pPosMot As Integer)
' pPosMot contient la position du MOT pRech trouvé dans pChaine
'''Const SEPARATEUR = " [.!#$%&'*/=?^_`{|} ~+-]"
Const SEPARATEUR = " ,()"
Dim i As Integer
Dim Pos As Integer, PosSuiv As Integer
Dim Fin As Boolean
Dim ChaineW As String
Dim AvantOk As Boolean
EstMot = False
ChaineW = Pchaine
pPosMot = 0
Fin = False
While Not Fin
Pos = InStr(ChaineW, pRech)
PosSuiv = Pos + Len(pRech)
AvantOk = False
Select Case Pos
Case 0
Fin = True
Case 1
'pas de caractère précédent
AvantOk = True
Case Else
For i = 1 To Len(SEPARATEUR)
If Mid(ChaineW, Pos - 1, 1) = Mid(SEPARATEUR, i, 1) Then
'Caractère précédent est un signe de mot
AvantOk = True
Exit For
End If
Next i
End Select
If AvantOk Then
If PosSuiv > Len(ChaineW) Then
EstMot = True
Else
For i = 1 To Len(SEPARATEUR)
If Mid(ChaineW, PosSuiv, 1) = Mid(SEPARATEUR, i, 1) Then
'Caractère suivant est un signe de mot
EstMot = True
Exit For
End If
Next i
End If
End If
If Not EstMot Then
ChaineW = Mid(ChaineW, PosSuiv, Len(ChaineW))
pPosMot = pPosMot + PosSuiv - 1
If ChaineW = "" Then Fin = True
Else
pPosMot = pPosMot + PosSuiv - Len(pRech)
Fin = True
End If
Wend
If Not EstMot Then pPosMot = 0
End Function