https://gcc.gnu.org/g:54a172402a844474dbf0d9dc0075e244174425d5

commit r16-4731-g54a172402a844474dbf0d9dc0075e244174425d5
Author: Gaius Mulley <[email protected]>
Date:   Thu Oct 30 11:19:08 2025 +0000

    PR modula2/122485: add spell checking to module names
    
    This patch introduces spell checking during module imports.
    If the correct module name has been seen prior to the incorrect import
    then it will attempt to provide a hint during the error message.
    
    gcc/m2/ChangeLog:
    
            PR modula2/122485
            * gm2-compiler/M2Comp.mod (Pass0CheckDef): Add spell check
            format specifier filtering on module names.
            * gm2-compiler/M2MetaError.mod (errorBlock): New field
            filterDef.
            (initErrorBlock): Initialize filterDef.
            (continuation): Add 'D' filter on definition module specifier.
            (SpellHint): Rewrite to check for filterDef and defimp symbols.
            (FilterOnDefinitionModule): New procedure.
            * gm2-compiler/M2Quads.mod (BuildSizeFunction): Rewrite to
            ensure variables are initialized.
            * gm2-compiler/M2StackSpell.def (GetDefModuleSpellHint): New
            procedure function.
            * gm2-compiler/M2StackSpell.mod (GetDefModuleSpellHint): New
            procedure function.
            (CandidatePushName): New procedure.
            (BuildHintStr): New procedure.
            (CheckForHintStr): Rewrite.
    
    gcc/testsuite/ChangeLog:
    
            PR modula2/122485
            * gm2.dg/spell/iso/fail/badimport.mod: New test.
    
    Signed-off-by: Gaius Mulley <[email protected]>

Diff:
---
 gcc/m2/gm2-compiler/M2Comp.mod                    | 11 ++-
 gcc/m2/gm2-compiler/M2MetaError.mod               | 29 +++++--
 gcc/m2/gm2-compiler/M2Quads.mod                   | 59 +++++++-------
 gcc/m2/gm2-compiler/M2StackSpell.def              | 10 +++
 gcc/m2/gm2-compiler/M2StackSpell.mod              | 93 ++++++++++++++++++++---
 gcc/testsuite/gm2.dg/spell/iso/fail/badimport.mod | 14 ++++
 6 files changed, 163 insertions(+), 53 deletions(-)

diff --git a/gcc/m2/gm2-compiler/M2Comp.mod b/gcc/m2/gm2-compiler/M2Comp.mod
index 741daeb036d0..0190e0163664 100644
--- a/gcc/m2/gm2-compiler/M2Comp.mod
+++ b/gcc/m2/gm2-compiler/M2Comp.mod
@@ -851,12 +851,11 @@ BEGIN
             MergeDeps (DepContent, ChildDep, LibName)
          ELSE
             (* Unrecoverable error.  *)
-            MetaErrorString1 (Sprintf1 (InitString ('file {%%1EUAF%s} 
containing module {%%1a} cannot be found'),
+            MetaErrorString1 (Sprintf1 (InitString ('file {%%1EUAF%s} 
containing module {%%1a} cannot be found {%%1&Ds}'),
                                         FileName), sym)
          END
       ELSE
-         (* Unrecoverable error.  *)
-         MetaError1 ('the file containing the definition module {%1EMAa} 
cannot be found', sym)
+         MetaError1 ('the file containing the definition module {%1EMAa} 
cannot be found {%1&Ds}', sym)
       END ;
       ModuleType := Implementation
    ELSE
@@ -928,15 +927,15 @@ BEGIN
             qprintf0 ('\n') ;
             CloseSource
          ELSE
-            (* It is quite legitimate to implement a module in C (and pretend 
it was a M2
+            (* It is legitimate to implement a module in C (and pretend it was 
a M2
                implementation) providing that it is not the main program 
module and the
                definition module does not declare a hidden type when 
-fextended-opaque
                is used.  *)
             IF (NOT WholeProgram) OR (sym = Main) OR IsHiddenTypeDeclared (sym)
             THEN
                (* Unrecoverable error.  *)
-               MetaErrorString1 (Sprintf1 (InitString ('file {%%1EUAF%s} 
containing module {%%1a} cannot be found'),
-                                           FileName), sym) ;
+               MetaErrorString1 (Sprintf1 (InitString ('file {%%1EUAF%s} 
containing module {%%1a} cannot be found {%%1&Ds}'),
+                                           FileName), sym)
             END
          END
       END
diff --git a/gcc/m2/gm2-compiler/M2MetaError.mod 
b/gcc/m2/gm2-compiler/M2MetaError.mod
index aae0f02eb100..22a155731ecd 100644
--- a/gcc/m2/gm2-compiler/M2MetaError.mod
+++ b/gcc/m2/gm2-compiler/M2MetaError.mod
@@ -42,7 +42,6 @@ FROM SYSTEM IMPORT ADDRESS ;
 FROM M2Error IMPORT MoveError ;
 FROM M2Debug IMPORT Assert ;
 FROM Storage IMPORT ALLOCATE ;
-FROM M2StackSpell IMPORT GetSpellHint ;
 
 FROM Indexing IMPORT Index, InitIndex, KillIndex, GetIndice, PutIndice,
                      DeleteIndice, HighIndice ;
@@ -74,6 +73,7 @@ IMPORT M2Error ;
 IMPORT FilterError ;
 
 FROM FilterError IMPORT Filter, AddSymError, IsSymError ;
+FROM M2StackSpell IMPORT GetDefModuleSpellHint, GetSpellHint ;
 
 
 CONST
@@ -100,6 +100,7 @@ TYPE
                    len,
                    ini       : INTEGER ;
                    vowel,
+                   filterDef,
                    importHint,
                    exportHint,
                    withStackHint,
@@ -533,6 +534,7 @@ BEGIN
       ini        := 0 ;
       glyph      := FALSE ;  (* Nothing to output yet.  *)
       vowel      := FALSE ;  (* Check for a vowel when outputing string?  *)
+      filterDef  := FALSE ;  (* Filter on definition module list?  *)
       importHint := FALSE;
       exportHint := FALSE ;
       withStackHint := FALSE ;
@@ -1840,7 +1842,7 @@ END op ;
 
 
 (*
-   continuation := {':'|'1'|'2'|'3'|'4'|'i'|'s'|'x'|'w'} =:
+   continuation := {':'|'1'|'2'|'3'|'4'|'i'|'s'|'x'|'w'|'D'} =:
 *)
 
 PROCEDURE continuation (VAR eb: errorBlock;
@@ -1860,7 +1862,8 @@ BEGIN
       'i':  AddImportsHint (eb) |
       's':  SpellHint (eb, sym, bol) |
       'x':  AddExportsHint (eb) |
-      'w':  AddWithStackHint (eb)
+      'w':  AddWithStackHint (eb) |
+      'D':  FilterOnDefinitionModule (eb)
 
       ELSE
          InternalFormat (eb, 'expecting one of [:1234isxw]',
@@ -1956,9 +1959,15 @@ END JoinSentances ;
 
 PROCEDURE SpellHint (VAR eb: errorBlock; sym: ARRAY OF CARDINAL; bol: 
CARDINAL) ;
 BEGIN
-   IF (bol <= HIGH (sym)) AND IsUnknown (sym[bol])
+   IF bol <= HIGH (sym)
    THEN
-      JoinSentances (eb, GetSpellHint (sym[bol]))
+      IF eb.filterDef AND IsDefImp (sym[bol])
+      THEN
+         JoinSentances (eb, GetDefModuleSpellHint (sym[bol]))
+      ELSIF IsUnknown (sym[bol])
+      THEN
+         JoinSentances (eb, GetSpellHint (sym[bol]))
+      END
    END
 END SpellHint ;
 
@@ -1993,6 +2002,16 @@ BEGIN
 END AddWithStackHint ;
 
 
+(*
+   FilterOnDefinitionModule - turn on filtering and include all the definition 
modules.
+*)
+
+PROCEDURE FilterOnDefinitionModule (VAR eb: errorBlock) ;
+BEGIN
+   eb.filterDef := TRUE
+END FilterOnDefinitionModule ;
+
+
 (*
    changeColor - changes to color, c.
 *)
diff --git a/gcc/m2/gm2-compiler/M2Quads.mod b/gcc/m2/gm2-compiler/M2Quads.mod
index 5ceeb4f139ad..a263ce36f729 100644
--- a/gcc/m2/gm2-compiler/M2Quads.mod
+++ b/gcc/m2/gm2-compiler/M2Quads.mod
@@ -10697,44 +10697,43 @@ BEGIN
                    NoOfParam) ;
       resulttok := functok ;
       ReturnVar := MakeConstLit (resulttok, MakeKey('0'), Cardinal)
-   ELSIF IsAModula2Type (OperandT (1))
-   THEN
-      paramtok := OperandTok (1) ;
-      resulttok := MakeVirtualTok (functok, functok, paramtok) ;
-      BuildSizeCheckEnd (ProcSym) ;   (* Quadruple generation now on.  *)
-      ReturnVar := MakeTemporary (resulttok, ImmediateValue) ;
-      GenQuadO (resulttok, SizeOp, ReturnVar, NulSym, OperandT(1), TRUE)
-   ELSIF IsVar (OperandT (1))
-   THEN
-      BuildSizeCheckEnd (ProcSym) ;   (* Quadruple generation now on.  *)
-      Type := GetSType (OperandT (1)) ;
+   ELSE
       paramtok := OperandTok (1) ;
       resulttok := MakeVirtualTok (functok, functok, paramtok) ;
-      IF IsUnbounded (Type)
+      IF IsAModula2Type (OperandT (1))
       THEN
-         (* Eg. SIZE(a) ; where a is unbounded dereference HIGH and multiply 
by the TYPE.  *)
-         dim := OperandD (1) ;
-         IF dim = 0
+         BuildSizeCheckEnd (ProcSym) ;   (* Quadruple generation now on.  *)
+         ReturnVar := MakeTemporary (resulttok, ImmediateValue) ;
+         GenQuadO (resulttok, SizeOp, ReturnVar, NulSym, OperandT(1), TRUE)
+      ELSIF IsVar (OperandT (1))
+      THEN
+         BuildSizeCheckEnd (ProcSym) ;   (* Quadruple generation now on.  *)
+         Type := GetSType (OperandT (1)) ;
+         IF IsUnbounded (Type)
          THEN
-            ReturnVar := calculateMultipicand (resulttok, OperandT (1), Type, 
dim)
+            (* Eg. SIZE(a) ; where a is unbounded dereference HIGH and 
multiply by the TYPE.  *)
+            dim := OperandD (1) ;
+            IF dim = 0
+            THEN
+               ReturnVar := calculateMultipicand (resulttok, OperandT (1), 
Type, dim)
+            ELSE
+               ReturnVar := calculateMultipicand (resulttok, OperandA (1), 
Type, dim)
+            END
          ELSE
-            ReturnVar := calculateMultipicand (resulttok, OperandA (1), Type, 
dim)
+            ReturnVar := MakeTemporary (resulttok, ImmediateValue) ;
+            IF Type = NulSym
+            THEN
+               MetaErrorT1 (resulttok,
+                            'cannot get the type and size of {%1Ead}', 
OperandT (1))
+            END ;
+            GenQuadO (resulttok, SizeOp, ReturnVar, NulSym, Type, TRUE)
          END
       ELSE
-         ReturnVar := MakeTemporary (resulttok, ImmediateValue) ;
-         IF Type = NulSym
-         THEN
-            MetaErrorT1 (resulttok,
-                         'cannot get the type and size of {%1Ead}', OperandT 
(1))
-         END ;
-         GenQuadO (resulttok, SizeOp, ReturnVar, NulSym, Type, TRUE)
+         MetaErrorT1 (paramtok,
+                      '{%E}SYSTEM procedure {%kSIZE} expects a variable or 
type as its parameter, seen {%1Ed} {%1&s}',
+                      OperandT (1)) ;
+         ReturnVar := MakeConstLit (paramtok, MakeKey('0'), Cardinal)
       END
-   ELSE
-      paramtok := OperandTok (1) ;
-      MetaErrorT1 (paramtok,
-                   '{%E}SYSTEM procedure {%kSIZE} expects a variable or type 
as its parameter, seen {%1Ed} {%1&s}',
-                   OperandT (1)) ;
-      ReturnVar := MakeConstLit (resulttok, MakeKey('0'), Cardinal)
    END ;
    PopN (NoOfParam+1) ;       (* Destroy the arguments and function.  *)
    PushTFtok (ReturnVar, GetSType(ProcSym), resulttok)
diff --git a/gcc/m2/gm2-compiler/M2StackSpell.def 
b/gcc/m2/gm2-compiler/M2StackSpell.def
index 7c1d00b7b592..c45074ae4c1f 100644
--- a/gcc/m2/gm2-compiler/M2StackSpell.def
+++ b/gcc/m2/gm2-compiler/M2StackSpell.def
@@ -59,4 +59,14 @@ PROCEDURE GetRecordField (tokno: CARDINAL;
                           fieldName: Name) : CARDINAL ;
 
 
+(*
+   GetDefModuleSpellHint - return a string describing a spelling
+                           hint for the definition module name
+                           similiar to unknown.  NIL is returned
+                           if no hint can be given.
+*)
+
+PROCEDURE GetDefModuleSpellHint (defimp: CARDINAL) : String ;
+
+
 END M2StackSpell.
diff --git a/gcc/m2/gm2-compiler/M2StackSpell.mod 
b/gcc/m2/gm2-compiler/M2StackSpell.mod
index 7a072ae95ece..ac58c1c98d09 100644
--- a/gcc/m2/gm2-compiler/M2StackSpell.mod
+++ b/gcc/m2/gm2-compiler/M2StackSpell.mod
@@ -31,7 +31,7 @@ FROM SymbolTable IMPORT NulSym, IsModule, IsDefImp, IsRecord,
 FROM SymbolKey IMPORT PerformOperation ;
 FROM DynamicStrings IMPORT InitStringCharStar, InitString, Mark, string, 
ConCat ;
 FROM FormatStrings IMPORT Sprintf1, Sprintf2, Sprintf3 ;
-FROM NameKey IMPORT KeyToCharStar ;
+FROM NameKey IMPORT KeyToCharStar, NulName ;
 FROM M2MetaError IMPORT MetaErrorStringT0 ;
 
 FROM M2StackWord IMPORT StackOfWord, PushWord, PopWord,
@@ -39,6 +39,7 @@ FROM M2StackWord IMPORT StackOfWord, PushWord, PopWord,
                         NoOfItemsInStackWord, PeepWord ;
 
 FROM CDataTypes IMPORT ConstCharStar ;
+FROM M2Batch IMPORT GetModuleNo ;
 
 IMPORT m2spellcheck ;
 FROM m2spellcheck IMPORT Candidates ;
@@ -96,6 +97,60 @@ BEGIN
 END GetRecordField ;
 
 
+(*
+   CandidatePushName - push a symbol name to the candidate list.
+*)
+
+PROCEDURE CandidatePushName (cand: Candidates; sym: CARDINAL) ;
+VAR
+   str: String ;
+BEGIN
+   str := InitStringCharStar (KeyToCharStar (GetSymName (sym))) ;
+   m2spellcheck.Push (cand, string (str)) ;
+   INC (PushCount)
+END CandidatePushName ;
+
+
+(*
+   GetDefModuleSpellHint - return a string describing a spelling
+                           hint for the definition module name
+                           similiar to defimp.  The premise is that
+                           defimp has been misspelt.  NIL is returned
+                           if no hint can be given.
+*)
+
+PROCEDURE GetDefModuleSpellHint (defimp: CARDINAL) : String ;
+VAR
+   i        : CARDINAL ;
+   sym      : CARDINAL ;
+   cand     : Candidates ;
+   misspell,
+   content  : ConstCharStar ;
+   HintStr  : String ;
+BEGIN
+   HintStr := NIL ;
+   IF GetSymName (defimp) # NulName
+   THEN
+      misspell := KeyToCharStar (GetSymName (defimp)) ;
+      i := 1 ;
+      sym := GetModuleNo (i) ;
+      cand := m2spellcheck.InitCandidates () ;
+      WHILE sym # NulSym DO
+         IF sym # defimp
+         THEN
+            CandidatePushName (cand, sym)
+         END ;
+         INC (i) ;
+         sym := GetModuleNo (i)
+      END ;
+      content := m2spellcheck.FindClosestCharStar (cand, misspell) ;
+      HintStr := BuildHintStr (HintStr, content) ;
+      m2spellcheck.KillCandidates (cand)
+   END ;
+   RETURN AddPunctuation (HintStr, '?')
+END GetDefModuleSpellHint ;
+
+
 (*
    Push - push a scope onto the spelling stack.
           sym might be a ModSym, DefImpSym or a varsym
@@ -183,6 +238,30 @@ BEGIN
 END PushCandidates ;
 
 
+(*
+   BuildHintStr - create the did you mean hint and return it
+                  if HintStr is NIL.  Otherwise append a hint
+                  to HintStr.  If content is NIL then return NIL.
+*)
+
+PROCEDURE BuildHintStr (HintStr: String; content: ConstCharStar) : String ;
+VAR
+   str: String ;
+BEGIN
+   IF content # NIL
+   THEN
+      str := InitStringCharStar (content) ;
+      IF HintStr = NIL
+      THEN
+         RETURN Sprintf1 (Mark (InitString (", did you mean %s")), str)
+      ELSE
+         RETURN Sprintf2 (Mark (InitString ("%s or %s")), HintStr, str)
+      END
+   END ;
+   RETURN NIL
+END BuildHintStr ;
+
+
 (*
    CheckForHintStr - lookup a spell hint matching misspelt.  If one exists
                      then append it to HintStr.  Return HintStr.
@@ -193,7 +272,6 @@ PROCEDURE CheckForHintStr (sym: CARDINAL;
 VAR
    cand   : Candidates ;
    content: ConstCharStar ;
-   str    : String ;
 BEGIN
    IF IsModule (sym) OR IsDefImp (sym) OR IsProcedure (sym) OR
       IsRecord (sym) OR IsEnumeration (sym)
@@ -206,16 +284,7 @@ BEGIN
          content := NIL
       END ;
       m2spellcheck.KillCandidates (cand) ;
-      IF content # NIL
-      THEN
-         str := InitStringCharStar (content) ;
-         IF HintStr = NIL
-         THEN
-            RETURN Sprintf1 (Mark (InitString (", did you mean %s")), str)
-         ELSE
-            RETURN Sprintf2 (Mark (InitString ("%s or %s")), HintStr, str)
-         END
-      END
+      HintStr := BuildHintStr (HintStr, content)
    END ;
    RETURN HintStr
 END CheckForHintStr ;
diff --git a/gcc/testsuite/gm2.dg/spell/iso/fail/badimport.mod 
b/gcc/testsuite/gm2.dg/spell/iso/fail/badimport.mod
new file mode 100644
index 000000000000..337cf346d50f
--- /dev/null
+++ b/gcc/testsuite/gm2.dg/spell/iso/fail/badimport.mod
@@ -0,0 +1,14 @@
+
+(* { dg-do compile } *)
+(* { dg-options "-g -c" } *)
+
+MODULE badimport ;
+
+IMPORT ASCII ;
+FROM StrIO IMPORT WriteString ;
+FROM ASCIi IMPORT nul ;
+ (* { dg-error "error: the file containing the definition module 'ASCIi' 
cannot be found, did you mean ASCII" "ASCIi" { target *-*-* } 9 } *)
+
+BEGIN
+
+END badimport.

Reply via email to