blob: 3df160a987c558be7fbc06fbc45303ceb30a55ec [file] [log] [blame]
(* M2Students.mod checks for new programmer errors.
Copyright (C) 2001-2025 Free Software Foundation, Inc.
Contributed by Gaius Mulley <gaius.mulley@southwales.ac.uk>.
This file is part of GNU Modula-2.
GNU Modula-2 is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GNU Modula-2 is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU Modula-2; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. *)
IMPLEMENTATION MODULE M2Students ;
FROM SymbolTable IMPORT FinalSymbol, IsVar, IsProcedure, IsModule,
GetMainModule, IsType, NulSym, IsRecord, GetSymName, GetNth, GetNthProcedure, GetDeclaredMod, NoOfParam ;
FROM NameKey IMPORT GetKey, WriteKey, MakeKey, IsSameExcludingCase, NulName, makekey, KeyToCharStar ;
FROM M2MetaError IMPORT MetaErrorStringT0, MetaError2 ;
FROM Lists IMPORT List, InitList, IsItemInList, IncludeItemIntoList ;
FROM M2Reserved IMPORT IsReserved, toktype ;
FROM DynamicStrings IMPORT String, InitString, KillString, ToUpper, InitStringCharStar, string, Mark, ToUpper, Dup ;
FROM FormatStrings IMPORT Sprintf0, Sprintf1, Sprintf2 ;
FROM M2LexBuf IMPORT GetTokenNo ;
FROM ASCII IMPORT nul ;
FROM M2Options IMPORT StyleChecking ;
VAR
ErrantNames,
ErrantSymbols: List ;
(*
IsNotADuplicate - returns TRUE if either s1 or s2 have not been reported before.
*)
PROCEDURE IsNotADuplicate (s1, s2: CARDINAL) : BOOLEAN ;
BEGIN
IF (NOT IsItemInList(ErrantSymbols, s1)) AND (NOT IsItemInList(ErrantSymbols, s2))
THEN
IncludeItemIntoList(ErrantSymbols, s1) ;
IncludeItemIntoList(ErrantSymbols, s2) ;
RETURN( TRUE )
ELSE
RETURN( FALSE )
END
END IsNotADuplicate ;
(*
IsNotADuplicateName - returns TRUE if name has not been reported before.
*)
PROCEDURE IsNotADuplicateName (name: Name) : BOOLEAN ;
BEGIN
IF NOT IsItemInList(ErrantNames, name)
THEN
IncludeItemIntoList(ErrantNames, name) ;
RETURN( TRUE )
ELSE
RETURN( FALSE )
END
END IsNotADuplicateName ;
(*
CheckVariableAgainstKeyword - checks for a identifier that looks the same
as a keyword except for its case.
*)
PROCEDURE CheckVariableAgainstKeyword (tok: CARDINAL; name: Name) ;
BEGIN
IF StyleChecking
THEN
PerformVariableKeywordCheck (tok, name)
END
END CheckVariableAgainstKeyword ;
(*
PerformVariableKeywordCheck - performs the check and constructs the metaerror notes if appropriate.
*)
PROCEDURE PerformVariableKeywordCheck (tok: CARDINAL; name: Name) ;
VAR
upper : Name ;
token : toktype ;
orig,
upperS: String ;
BEGIN
orig := InitStringCharStar (KeyToCharStar (name)) ;
upperS := ToUpper (Dup (orig)) ;
upper := makekey (string (upperS)) ;
IF IsReserved (upper, token)
THEN
IF IsNotADuplicateName (name)
THEN
MetaErrorStringT0 (tok,
Sprintf2 (Mark (InitString ('either the identifier has the same name as a keyword or alternatively a keyword has the wrong case ({%%K%s} and {!%%O:{%%K%s}})')),
upperS, orig)) ;
MetaErrorStringT0 (tok,
Sprintf1 (Mark (InitString ('the symbol name {!%%O:{%%K%s}} is legal as an identifier, however as such it might cause confusion and is considered bad programming practice')), orig))
END
END ;
upperS := KillString (upperS) ;
orig := KillString (orig)
END PerformVariableKeywordCheck ;
(*
CheckAsciiName - checks to see whether ascii names, s1, and, s2, are similar.
*)
PROCEDURE CheckAsciiName (previous, s1, newblock, s2: CARDINAL) ;
VAR
a1, a2: Name ;
BEGIN
a1 := GetSymName (s1) ;
a2 := GetSymName (s2) ;
IF (a1 = a2) AND (a1 # NulName)
THEN
IF IsNotADuplicate (s1, s2)
THEN
MetaError2 ('identical symbol name in two different scopes, scope {%1Oad} has symbol {%2Mad}', previous, s1) ;
MetaError2 ('identical symbol name in two different scopes, scope {%1Oad} has symbol {%2Mad}', newblock, s2)
END
ELSIF IsSameExcludingCase (a1, a2)
THEN
IF IsNotADuplicate (s1, s2)
THEN
MetaError2 ('very similar symbol names (different case) in two different scopes, scope {%1ORad} has symbol {%2Mad}', previous, s1) ;
MetaError2 ('very similar symbol names (different case) in two different scopes, scope {%1OCad} has symbol {%2Mad}', newblock, s2)
END
END
END CheckAsciiName ;
(*
CheckProcedure - checks the procedure, p, for symbols which look like, s.
*)
PROCEDURE CheckProcedure (m, p: CARDINAL) ;
VAR
i, n1,
j, n2: CARDINAL ;
BEGIN
IF p#NulSym
THEN
i := 1 ; (* I would have used NoOfParam(p)+1 but Stuart wants parameters checked as well - maybe he is right. *)
REPEAT
n1 := GetNth(p, i) ;
IF n1#NulSym
THEN
IF IsVar(n1) OR IsType(n1) OR IsProcedure(n1) OR IsRecord(n1)
THEN
j := 1 ;
REPEAT
n2 := GetNth(m, j) ;
IF n2#NulSym
THEN
IF IsVar(n2) OR IsType(n2) OR IsProcedure(n2) OR IsRecord(n2)
THEN
CheckAsciiName(m, n2, p, n1)
END
END ;
INC(j)
UNTIL n2=NulSym
END
END ;
INC(i)
UNTIL n1=NulSym
END
END CheckProcedure ;
(*
CheckModule - checks the module, m, for symbols which look like, s.
*)
PROCEDURE CheckModule (m, s: CARDINAL) ;
VAR
i, n: CARDINAL ;
BEGIN
IF m#NulSym
THEN
i := 1 ;
REPEAT
n := GetNth(m, i) ;
IF n#NulSym
THEN
IF (n#NulSym) AND (n#s)
THEN
IF IsVar(n) OR IsType(n) OR IsProcedure(n) OR IsRecord(n)
THEN
CheckAsciiName(m, s, m, n)
END
END
END ;
INC(i)
UNTIL n=NulSym
END
END CheckModule ;
(*
StudentVariableCheck - checks to see that variables are quite different from keywords and
issues an message if they are not. It ignores case so to catch
1st and 2nd semester programming errors.
*)
PROCEDURE StudentVariableCheck ;
VAR
i, n, m: CARDINAL ;
BEGIN
m := GetMainModule() ;
(* first check global scope *)
i := 1 ;
REPEAT
n := GetNth(m, i) ;
IF n#NulSym
THEN
IF IsVar(n) OR IsType(n) OR IsProcedure(n) OR IsRecord(n)
THEN
CheckModule(m, n)
END
END ;
INC(i)
UNTIL n=NulSym ;
(* now check local scope *)
i := 1 ;
REPEAT
n := GetNthProcedure(m, i) ;
IF n#NulSym
THEN
IF IsProcedure(n)
THEN
CheckProcedure(m, n)
END
END ;
INC(i)
UNTIL n=NulSym
END StudentVariableCheck ;
BEGIN
InitList(ErrantSymbols) ;
InitList(ErrantNames)
END M2Students.