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]>
---
 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 ++++++++++++++++---
 .../gm2.dg/spell/iso/fail/badimport.mod       | 14 +++
 6 files changed, 163 insertions(+), 53 deletions(-)
 create mode 100644 gcc/testsuite/gm2.dg/spell/iso/fail/badimport.mod

diff --git a/gcc/m2/gm2-compiler/M2Comp.mod b/gcc/m2/gm2-compiler/M2Comp.mod
index 741daeb036d..0190e016366 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 aae0f02eb10..22a155731ec 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 5ceeb4f139a..a263ce36f72 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 7c1d00b7b59..c45074ae4c1 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 7a072ae95ec..ac58c1c98d0 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 00000000000..337cf346d50
--- /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.
--
2.39.5

Reply via email to