Line data Source code
1 : /* do not edit automatically generated by mc from M2Students. */
2 : /* M2Students.mod checks for new programmer errors.
3 :
4 : Copyright (C) 2001-2026 Free Software Foundation, Inc.
5 : Contributed by Gaius Mulley <gaius.mulley@southwales.ac.uk>.
6 :
7 : This file is part of GNU Modula-2.
8 :
9 : GNU Modula-2 is free software; you can redistribute it and/or modify
10 : it under the terms of the GNU General Public License as published by
11 : the Free Software Foundation; either version 3, or (at your option)
12 : any later version.
13 :
14 : GNU Modula-2 is distributed in the hope that it will be useful, but
15 : WITHOUT ANY WARRANTY; without even the implied warranty of
16 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 : General Public License for more details.
18 :
19 : You should have received a copy of the GNU General Public License
20 : along with GNU Modula-2; see the file COPYING3. If not see
21 : <http://www.gnu.org/licenses/>. */
22 :
23 : #include "config.h"
24 : #include "system.h"
25 : #include "gcc-consolidation.h"
26 :
27 : #include <stdbool.h>
28 : # if !defined (PROC_D)
29 : # define PROC_D
30 : typedef void (*PROC_t) (void);
31 : typedef struct { PROC_t proc; } PROC;
32 : # endif
33 :
34 : # if !defined (TRUE)
35 : # define TRUE (1==1)
36 : # endif
37 :
38 : # if !defined (FALSE)
39 : # define FALSE (1==0)
40 : # endif
41 :
42 : #define _M2Students_C
43 :
44 : #include "GM2Students.h"
45 : # include "GSymbolTable.h"
46 : # include "GNameKey.h"
47 : # include "GM2MetaError.h"
48 : # include "GLists.h"
49 : # include "GM2Reserved.h"
50 : # include "GDynamicStrings.h"
51 : # include "GFormatStrings.h"
52 : # include "GM2LexBuf.h"
53 : # include "GASCII.h"
54 : # include "GM2Options.h"
55 :
56 : static Lists_List ErrantNames;
57 : static Lists_List ErrantSymbols;
58 :
59 : /*
60 : CheckVariableAgainstKeyword - checks for a identifier that looks the same
61 : as a keyword except for its case.
62 : */
63 :
64 : extern "C" void M2Students_CheckVariableAgainstKeyword (unsigned int tok, NameKey_Name name);
65 :
66 : /*
67 : StudentVariableCheck - checks to see that variables are quite different from keywords and
68 : issues an message if they are not. It ignores case so to catch
69 : 1st and 2nd semester programming errors.
70 : */
71 :
72 : extern "C" void M2Students_StudentVariableCheck (void);
73 :
74 : /*
75 : IsNotADuplicate - returns TRUE if either s1 or s2 have not been reported before.
76 : */
77 :
78 : static bool IsNotADuplicate (unsigned int s1, unsigned int s2);
79 :
80 : /*
81 : IsNotADuplicateName - returns TRUE if name has not been reported before.
82 : */
83 :
84 : static bool IsNotADuplicateName (NameKey_Name name);
85 :
86 : /*
87 : PerformVariableKeywordCheck - performs the check and constructs the metaerror notes if appropriate.
88 : */
89 :
90 : static void PerformVariableKeywordCheck (unsigned int tok, NameKey_Name name);
91 :
92 : /*
93 : CheckAsciiName - checks to see whether ascii names, s1, and, s2, are similar.
94 : */
95 :
96 : static void CheckAsciiName (unsigned int previous, unsigned int s1, unsigned int newblock, unsigned int s2);
97 :
98 : /*
99 : CheckProcedure - checks the procedure, p, for symbols which look like, s.
100 : */
101 :
102 : static void CheckProcedure (unsigned int m, unsigned int p);
103 :
104 : /*
105 : CheckModule - checks the module, m, for symbols which look like, s.
106 : */
107 :
108 : static void CheckModule (unsigned int m, unsigned int s);
109 :
110 :
111 : /*
112 : IsNotADuplicate - returns TRUE if either s1 or s2 have not been reported before.
113 : */
114 :
115 0 : static bool IsNotADuplicate (unsigned int s1, unsigned int s2)
116 : {
117 0 : if ((! (Lists_IsItemInList (ErrantSymbols, s1))) && (! (Lists_IsItemInList (ErrantSymbols, s2))))
118 : {
119 0 : Lists_IncludeItemIntoList (ErrantSymbols, s1);
120 0 : Lists_IncludeItemIntoList (ErrantSymbols, s2);
121 0 : return true;
122 : }
123 : else
124 : {
125 0 : return false;
126 : }
127 : /* static analysis guarentees a RETURN statement will be used before here. */
128 : __builtin_unreachable ();
129 : }
130 :
131 :
132 : /*
133 : IsNotADuplicateName - returns TRUE if name has not been reported before.
134 : */
135 :
136 1 : static bool IsNotADuplicateName (NameKey_Name name)
137 : {
138 1 : if (! (Lists_IsItemInList (ErrantNames, name)))
139 : {
140 1 : Lists_IncludeItemIntoList (ErrantNames, name);
141 1 : return true;
142 : }
143 : else
144 : {
145 : return false;
146 : }
147 : /* static analysis guarentees a RETURN statement will be used before here. */
148 : __builtin_unreachable ();
149 : }
150 :
151 :
152 : /*
153 : PerformVariableKeywordCheck - performs the check and constructs the metaerror notes if appropriate.
154 : */
155 :
156 29745 : static void PerformVariableKeywordCheck (unsigned int tok, NameKey_Name name)
157 : {
158 29745 : NameKey_Name upper;
159 29745 : M2Reserved_toktype token;
160 29745 : DynamicStrings_String orig;
161 29745 : DynamicStrings_String upperS;
162 :
163 29745 : orig = DynamicStrings_InitStringCharStar (NameKey_KeyToCharStar (name));
164 29745 : upperS = DynamicStrings_ToUpper (DynamicStrings_Dup (orig));
165 29745 : upper = NameKey_makekey (DynamicStrings_string (upperS));
166 29745 : if (M2Reserved_IsReserved (upper, &token))
167 : {
168 1 : if (IsNotADuplicateName (name))
169 : {
170 1 : M2MetaError_MetaErrorStringT0 (tok, FormatStrings_Sprintf2 (DynamicStrings_Mark (DynamicStrings_InitString ((const char *) "either the identifier has the same name as a keyword or alternatively a keyword has the wrong case ({%%K%s} and {!%%O:{%%K%s}})", 127)), (const unsigned char *) &upperS, (sizeof (upperS)-1), (const unsigned char *) &orig, (sizeof (orig)-1)));
171 1 : M2MetaError_MetaErrorStringT0 (tok, FormatStrings_Sprintf1 (DynamicStrings_Mark (DynamicStrings_InitString ((const char *) "the symbol name {!%%O:{%%K%s}} is legal as an identifier, however as such it might cause confusion and is considered bad programming practice", 141)), (const unsigned char *) &orig, (sizeof (orig)-1)));
172 : }
173 : }
174 29745 : upperS = DynamicStrings_KillString (upperS);
175 29745 : orig = DynamicStrings_KillString (orig);
176 29745 : }
177 :
178 :
179 : /*
180 : CheckAsciiName - checks to see whether ascii names, s1, and, s2, are similar.
181 : */
182 :
183 288960 : static void CheckAsciiName (unsigned int previous, unsigned int s1, unsigned int newblock, unsigned int s2)
184 : {
185 288960 : NameKey_Name a1;
186 288960 : NameKey_Name a2;
187 :
188 288960 : a1 = SymbolTable_GetSymName (s1);
189 288960 : a2 = SymbolTable_GetSymName (s2);
190 288960 : if ((a1 == a2) && (a1 != NameKey_NulName))
191 : {
192 : /* avoid dangling else. */
193 0 : if (IsNotADuplicate (s1, s2))
194 : {
195 0 : M2MetaError_MetaError2 ((const char *) "identical symbol name in two different scopes, scope {%1Oad} has symbol {%2Mad}", 79, previous, s1);
196 0 : M2MetaError_MetaError2 ((const char *) "identical symbol name in two different scopes, scope {%1Oad} has symbol {%2Mad}", 79, newblock, s2);
197 : }
198 : }
199 288960 : else if (NameKey_IsSameExcludingCase (a1, a2))
200 : {
201 : /* avoid dangling else. */
202 0 : if (IsNotADuplicate (s1, s2))
203 : {
204 0 : M2MetaError_MetaError2 ((const char *) "very similar symbol names (different case) in two different scopes, scope {%1ORad} has symbol {%2Mad}", 101, previous, s1);
205 0 : M2MetaError_MetaError2 ((const char *) "very similar symbol names (different case) in two different scopes, scope {%1OCad} has symbol {%2Mad}", 101, newblock, s2);
206 : }
207 : }
208 288960 : }
209 :
210 :
211 : /*
212 : CheckProcedure - checks the procedure, p, for symbols which look like, s.
213 : */
214 :
215 11960 : static void CheckProcedure (unsigned int m, unsigned int p)
216 : {
217 11960 : unsigned int i;
218 11960 : unsigned int n1;
219 11960 : unsigned int j;
220 11960 : unsigned int n2;
221 :
222 11960 : if (p != SymbolTable_NulSym)
223 : {
224 : i = 1; /* I would have used NoOfParam(p)+1 but Stuart wants parameters checked as well - maybe he is right. */
225 120496 : do { /* I would have used NoOfParam(p)+1 but Stuart wants parameters checked as well - maybe he is right. */
226 120496 : n1 = SymbolTable_GetNth (p, i);
227 120496 : if (n1 != SymbolTable_NulSym)
228 : {
229 108536 : if ((((SymbolTable_IsVar (n1)) || (SymbolTable_IsType (n1))) || (SymbolTable_IsProcedure (n1))) || (SymbolTable_IsRecord (n1)))
230 : {
231 : j = 1;
232 391848 : do {
233 391848 : n2 = SymbolTable_GetNth (m, j);
234 391848 : if (n2 != SymbolTable_NulSym)
235 : {
236 283312 : if ((((SymbolTable_IsVar (n2)) || (SymbolTable_IsType (n2))) || (SymbolTable_IsProcedure (n2))) || (SymbolTable_IsRecord (n2)))
237 : {
238 283312 : CheckAsciiName (m, n2, p, n1);
239 : }
240 : }
241 391848 : j += 1;
242 391848 : } while (! (n2 == SymbolTable_NulSym));
243 : }
244 : }
245 120496 : i += 1;
246 120496 : } while (! (n1 == SymbolTable_NulSym));
247 : }
248 11960 : }
249 :
250 :
251 : /*
252 : CheckModule - checks the module, m, for symbols which look like, s.
253 : */
254 :
255 852 : static void CheckModule (unsigned int m, unsigned int s)
256 : {
257 852 : unsigned int i;
258 852 : unsigned int n;
259 :
260 852 : if (m != SymbolTable_NulSym)
261 : {
262 : i = 1;
263 7352 : do {
264 7352 : n = SymbolTable_GetNth (m, i);
265 7352 : if (n != SymbolTable_NulSym)
266 : {
267 6500 : if ((n != SymbolTable_NulSym) && (n != s))
268 : {
269 5648 : if ((((SymbolTable_IsVar (n)) || (SymbolTable_IsType (n))) || (SymbolTable_IsProcedure (n))) || (SymbolTable_IsRecord (n)))
270 : {
271 5648 : CheckAsciiName (m, s, m, n);
272 : }
273 : }
274 : }
275 7352 : i += 1;
276 7352 : } while (! (n == SymbolTable_NulSym));
277 : }
278 852 : }
279 :
280 :
281 : /*
282 : CheckVariableAgainstKeyword - checks for a identifier that looks the same
283 : as a keyword except for its case.
284 : */
285 :
286 1694938 : extern "C" void M2Students_CheckVariableAgainstKeyword (unsigned int tok, NameKey_Name name)
287 : {
288 1694938 : if (M2Options_StyleChecking)
289 : {
290 29745 : PerformVariableKeywordCheck (tok, name);
291 : }
292 1694938 : }
293 :
294 :
295 : /*
296 : StudentVariableCheck - checks to see that variables are quite different from keywords and
297 : issues an message if they are not. It ignores case so to catch
298 : 1st and 2nd semester programming errors.
299 : */
300 :
301 613 : extern "C" void M2Students_StudentVariableCheck (void)
302 : {
303 613 : unsigned int i;
304 613 : unsigned int n;
305 613 : unsigned int m;
306 :
307 613 : m = SymbolTable_GetMainModule ();
308 : /* first check global scope */
309 613 : i = 1;
310 1465 : do {
311 1465 : n = SymbolTable_GetNth (m, i);
312 1465 : if (n != SymbolTable_NulSym)
313 : {
314 852 : if ((((SymbolTable_IsVar (n)) || (SymbolTable_IsType (n))) || (SymbolTable_IsProcedure (n))) || (SymbolTable_IsRecord (n)))
315 : {
316 852 : CheckModule (m, n);
317 : }
318 : }
319 1465 : i += 1;
320 1465 : } while (! (n == SymbolTable_NulSym));
321 : /* now check local scope */
322 : i = 1;
323 12573 : do {
324 12573 : n = SymbolTable_GetNthProcedure (m, i);
325 12573 : if (n != SymbolTable_NulSym)
326 : {
327 11960 : if (SymbolTable_IsProcedure (n))
328 : {
329 11960 : CheckProcedure (m, n);
330 : }
331 : }
332 12573 : i += 1;
333 12573 : } while (! (n == SymbolTable_NulSym));
334 613 : }
335 :
336 14952 : extern "C" void _M2_M2Students_init (__attribute__((unused)) int argc, __attribute__((unused)) char *argv[], __attribute__((unused)) char *envp[])
337 : {
338 14952 : Lists_InitList (&ErrantSymbols);
339 14952 : Lists_InitList (&ErrantNames);
340 14952 : }
341 :
342 0 : extern "C" void _M2_M2Students_fini (__attribute__((unused)) int argc, __attribute__((unused)) char *argv[], __attribute__((unused)) char *envp[])
343 : {
344 0 : }
|