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.
