Skip to content

Commit 97a3858

Browse files
forkiKevinRansom
authored andcommitted
Suggestions for misspelled values, namespaces, attributes, functions and more - fixes dotnet#1909 (dotnet#1911)
* Suggestions for mispelled values, namespaces and co - fixes dotnet#1909 * Do not discard specialized errors for undefined message - fixes dotnet#1933 * fix tests * Test for suggestion in generic type * Flatten predictions in ErrorLogger when in IDE * Fix ranges * Fix tests * Regularize all undefined name messages * Suggest type parameters * Suggest attributes * Suggest Methods * fix tests * Optimize perf * Calculate all predictions lazily * Be more careful with error allocations * Remove one a string comparision per Resolver call * fix merge error
1 parent baebacb commit 97a3858

File tree

65 files changed

+560
-772
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+560
-772
lines changed

src/fsharp/CompileOps.fs

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ let GetRangeOfError(err:PhasedError) =
156156
| RecursiveUseCheckedAtRuntime (_,_,m)
157157
| LetRecEvaluatedOutOfOrder (_,_,_,m)
158158
| Error (_,m)
159+
| ErrorWithPredictions (_,m,_,_)
159160
| NumberedError (_,m)
160161
| SyntaxError (_,m)
161162
| InternalError (_,m)
@@ -369,6 +370,7 @@ let GetErrorNumber(err:PhasedError) =
369370
| WrappedError(e,_) -> GetFromException e
370371

371372
| Error ((n,_),_) -> n
373+
| ErrorWithPredictions ((n,_),_,_,_) -> n
372374
| Failure _ -> 192
373375
| NumberedError((n,_),_) -> n
374376
| IllegalFileNameChar(fileName,invalidChar) -> fst (FSComp.SR.buildUnexpectedFileNameCharacter(fileName,string invalidChar))
@@ -387,6 +389,7 @@ let GetWarningLevel err =
387389
| DefensiveCopyWarning _
388390
| FullAbstraction _ -> 5
389391
| NumberedError((n,_),_)
392+
| ErrorWithPredictions((n,_),_,_,_)
390393
| Error((n,_),_) ->
391394
// 1178,tcNoComparisonNeeded1,"The struct, record or union type '%s' is not structurally comparable because the type parameter %s does not satisfy the 'comparison' constraint. Consider adding the 'NoComparison' attribute to this type to clarify that the type is not comparable"
392395
// 1178,tcNoComparisonNeeded2,"The struct, record or union type '%s' is not structurally comparable because the type '%s' does not satisfy the 'comparison' constraint. Consider adding the 'NoComparison' attribute to this type to clarify that the type is not comparable"
@@ -606,7 +609,7 @@ let getErrorString key = SR.GetString key
606609

607610
let (|InvalidArgument|_|) (exn:exn) = match exn with :? ArgumentException as e -> Some e.Message | _ -> None
608611

609-
let OutputPhasedErrorR (os:System.Text.StringBuilder) (err:PhasedError) =
612+
let OutputPhasedErrorR errorStyle (os:System.Text.StringBuilder) (err:PhasedError) =
610613
let rec OutputExceptionR (os:System.Text.StringBuilder) = function
611614
| ConstraintSolverTupleDiffLengths(_,tl1,tl2,m,m2) ->
612615
os.Append(ConstraintSolverTupleDiffLengthsE().Format tl1.Length tl2.Length) |> ignore
@@ -774,11 +777,11 @@ let OutputPhasedErrorR (os:System.Text.StringBuilder) (err:PhasedError) =
774777
os.Append(Duplicate1E().Format (DecompileOpName s)) |> ignore
775778
else
776779
os.Append(Duplicate2E().Format k (DecompileOpName s)) |> ignore
777-
| UndefinedName(_,k,id,predictions) ->
780+
| UndefinedName(_,k,id,predictionsF) ->
778781
os.Append(k (DecompileOpName id.idText)) |> ignore
779-
if Set.isEmpty predictions |> not then
780-
let filtered = ErrorResolutionHints.FilterPredictions id.idText predictions
781-
os.Append(ErrorResolutionHints.FormatPredictions filtered) |> ignore
782+
let filtered = ErrorResolutionHints.FilterPredictions id.idText predictionsF
783+
if List.isEmpty filtered |> not then
784+
os.Append(ErrorResolutionHints.FormatPredictions errorStyle DecompileOpName filtered) |> ignore
782785

783786
| InternalUndefinedItemRef(f,smr,ccuName,s) ->
784787
let _, errs = f(smr, ccuName, s)
@@ -1258,13 +1261,18 @@ let OutputPhasedErrorR (os:System.Text.StringBuilder) (err:PhasedError) =
12581261
os.Append(NonUniqueInferredAbstractSlot1E().Format bindnm) |> ignore
12591262
let ty1 = bvirt1.EnclosingType
12601263
let ty2 = bvirt2.EnclosingType
1261-
// REVIEW: consider if we need to show _cxs (the type parameter constrants)
1264+
// REVIEW: consider if we need to show _cxs (the type parameter constraints)
12621265
let t1, t2, _cxs = NicePrint.minimalStringsOfTwoTypes denv ty1 ty2
12631266
os.Append(NonUniqueInferredAbstractSlot2E().Format) |> ignore
12641267
if t1 <> t2 then
12651268
os.Append(NonUniqueInferredAbstractSlot3E().Format t1 t2) |> ignore
12661269
os.Append(NonUniqueInferredAbstractSlot4E().Format) |> ignore
12671270
| Error ((_,s),_) -> os.Append(s) |> ignore
1271+
| ErrorWithPredictions ((_,s),_,idText,predictionsF) ->
1272+
os.Append(DecompileOpName s) |> ignore
1273+
let filtered = ErrorResolutionHints.FilterPredictions idText predictionsF
1274+
if List.isEmpty filtered |> not then
1275+
os.Append(ErrorResolutionHints.FormatPredictions errorStyle DecompileOpName filtered) |> ignore
12681276
| NumberedError ((_,s),_) -> os.Append(s) |> ignore
12691277
| InternalError (s,_)
12701278
| InvalidArgument s
@@ -1416,22 +1424,14 @@ let OutputPhasedErrorR (os:System.Text.StringBuilder) (err:PhasedError) =
14161424

14171425

14181426
// remove any newlines and tabs
1419-
let OutputPhasedError (os:System.Text.StringBuilder) (err:PhasedError) (flattenErrors:bool) =
1427+
let OutputPhasedError errorStyle (os:System.Text.StringBuilder) (err:PhasedError) (flattenErrors:bool) =
14201428
let buf = new System.Text.StringBuilder()
14211429

1422-
OutputPhasedErrorR buf err
1430+
OutputPhasedErrorR errorStyle buf err
14231431
let s = if flattenErrors then ErrorLogger.NormalizeErrorString (buf.ToString()) else buf.ToString()
14241432

14251433
os.Append(s) |> ignore
14261434

1427-
1428-
type ErrorStyle =
1429-
| DefaultErrors
1430-
| EmacsErrors
1431-
| TestErrors
1432-
| VSErrors
1433-
| GccErrors
1434-
14351435
let SanitizeFileName fileName implicitIncludeDir =
14361436
// The assert below is almost ok, but it fires in two cases:
14371437
// - fsi.exe sometimes passes "stdin" as a dummy filename
@@ -1550,7 +1550,7 @@ let CollectErrorOrWarning (implicitIncludeDir,showFullPaths,flattenErrors,errorS
15501550
let canonical = OutputCanonicalInformation(err.Subcategory(),GetErrorNumber mainError)
15511551
let message =
15521552
let os = System.Text.StringBuilder()
1553-
OutputPhasedError os mainError flattenErrors
1553+
OutputPhasedError errorStyle os mainError flattenErrors
15541554
os.ToString()
15551555

15561556
let entry : DetailedIssueInfo = { Location = where; Canonical = canonical; Message = message }
@@ -1565,15 +1565,15 @@ let CollectErrorOrWarning (implicitIncludeDir,showFullPaths,flattenErrors,errorS
15651565
let relCanonical = OutputCanonicalInformation(err.Subcategory(),GetErrorNumber mainError) // Use main error for code
15661566
let relMessage =
15671567
let os = System.Text.StringBuilder()
1568-
OutputPhasedError os err flattenErrors
1568+
OutputPhasedError errorStyle os err flattenErrors
15691569
os.ToString()
15701570

15711571
let entry : DetailedIssueInfo = { Location = relWhere; Canonical = relCanonical; Message = relMessage}
15721572
errors.Add( ErrorOrWarning.Long (not warn, entry) )
15731573

15741574
| _ ->
15751575
let os = System.Text.StringBuilder()
1576-
OutputPhasedError os err flattenErrors
1576+
OutputPhasedError errorStyle os err flattenErrors
15771577
errors.Add( ErrorOrWarning.Short((not warn), os.ToString()) )
15781578

15791579
relatedErrors |> List.iter OutputRelatedError

src/fsharp/CompileOps.fsi

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -73,14 +73,6 @@ val ParseInput : (UnicodeLexing.Lexbuf -> Parser.token) * ErrorLogger * UnicodeL
7373
// Error and warnings
7474
//--------------------------------------------------------------------------
7575

76-
/// Represents the style being used to format errros
77-
type ErrorStyle =
78-
| DefaultErrors
79-
| EmacsErrors
80-
| TestErrors
81-
| VSErrors
82-
| GccErrors
83-
8476
/// Get the location associated with an error
8577
val GetRangeOfError : PhasedError -> range option
8678

@@ -91,7 +83,7 @@ val GetErrorNumber : PhasedError -> int
9183
val SplitRelatedErrors : PhasedError -> PhasedError * PhasedError list
9284

9385
/// Output an error to a buffer
94-
val OutputPhasedError : StringBuilder -> PhasedError -> bool -> unit
86+
val OutputPhasedError : ErrorLogger.ErrorStyle -> StringBuilder -> PhasedError -> bool -> unit
9587

9688
/// Output an error or warning to a buffer
9789
val OutputErrorOrWarning : implicitIncludeDir:string * showFullPaths: bool * flattenErrors: bool * errorStyle: ErrorStyle * warning:bool -> StringBuilder -> PhasedError -> unit

src/fsharp/ConstraintSolver.fs

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2022,22 +2022,18 @@ and ReportNoCandidatesError (csenv:ConstraintSolverEnv) (nUnnamedCallerArgs,nNam
20222022
| _,_,_,_,([],[cmeth]) ->
20232023
let minfo = cmeth.Method
20242024
let msgNum,msgText = FSComp.SR.csRequiredSignatureIs(NicePrint.stringOfMethInfo amap m denv minfo)
2025-
let msgNum,msgText,msgRange =
2026-
match cmeth.UnassignedNamedArgs with
2027-
| CallerNamedArg(id,_) :: _ ->
2028-
if minfo.IsConstructor then
2029-
let typ = minfo.DeclaringEntityRef
2030-
2031-
let predictions =
2032-
typ.AllInstanceFieldsAsList
2033-
|> List.map (fun p -> p.Name.Replace("@",""))
2034-
|> ErrorResolutionHints.FilterPredictions id.idText
2035-
2036-
msgNum,FSComp.SR.csCtorHasNoArgumentOrReturnProperty(methodName, id.idText, msgText, ErrorResolutionHints.FormatPredictions predictions),id.idRange
2037-
else
2038-
msgNum,FSComp.SR.csMemberHasNoArgumentOrReturnProperty(methodName, id.idText, msgText),id.idRange
2039-
| [] -> (msgNum,msgText,m)
2040-
Error ((msgNum,msgText),msgRange)
2025+
match cmeth.UnassignedNamedArgs with
2026+
| CallerNamedArg(id,_) :: _ ->
2027+
if minfo.IsConstructor then
2028+
let predictFields() =
2029+
minfo.DeclaringEntityRef.AllInstanceFieldsAsList
2030+
|> List.map (fun p -> p.Name.Replace("@",""))
2031+
|> Set.ofList
2032+
2033+
ErrorWithPredictions((msgNum,FSComp.SR.csCtorHasNoArgumentOrReturnProperty(methodName, id.idText, msgText)),id.idRange,id.idText,predictFields)
2034+
else
2035+
Error((msgNum,FSComp.SR.csMemberHasNoArgumentOrReturnProperty(methodName, id.idText, msgText)),id.idRange)
2036+
| [] -> Error((msgNum,msgText),m)
20412037

20422038
// One method, incorrect number of arguments provided by the user
20432039
| _,_,([],[cmeth]),_,_ when not cmeth.HasCorrectArity ->

src/fsharp/ErrorLogger.fs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,15 @@ open System
1414
// General error recovery mechanism
1515
//-----------------------------------------------------------------------
1616

17+
/// Represents the style being used to format errros
18+
[<RequireQualifiedAccess>]
19+
type ErrorStyle =
20+
| DefaultErrors
21+
| EmacsErrors
22+
| TestErrors
23+
| VSErrors
24+
| GccErrors
25+
1726
/// Thrown when we want to add some range information to a .NET exception
1827
exception WrappedError of exn * range
1928

@@ -37,6 +46,10 @@ let rec findOriginalException err =
3746
| _ -> err
3847

3948

49+
type Predictions = unit -> Set<string>
50+
51+
let NoPredictions : Predictions = fun () -> Set.empty
52+
4053
/// Thrown when we stop processing the F# Interactive entry or #load.
4154
exception StopProcessingExn of exn option
4255
let (|StopProcessing|_|) exn = match exn with StopProcessingExn _ -> Some () | _ -> None
@@ -48,11 +61,20 @@ exception NumberedError of (int * string) * range with // int is e.g. 191 in F
4861
match this :> exn with
4962
| NumberedError((_,msg),_) -> msg
5063
| _ -> "impossible"
64+
5165
exception Error of (int * string) * range with // int is e.g. 191 in FS0191 // eventually remove this type, it is a transitional artifact of the old unnumbered error style
5266
override this.Message =
5367
match this :> exn with
5468
| Error((_,msg),_) -> msg
5569
| _ -> "impossible"
70+
71+
72+
exception ErrorWithPredictions of (int * string) * range * string * Predictions with // int is e.g. 191 in FS0191
73+
override this.Message =
74+
match this :> exn with
75+
| ErrorWithPredictions((_,msg),_,_,_) -> msg
76+
| _ -> "impossible"
77+
5678
exception InternalError of string * range
5779
exception UserCompilerMessage of string * int * range
5880
exception LibraryUseOnly of range
@@ -517,7 +539,7 @@ let NewlineifyErrorString (message:string) = message.Replace(stringThatIsAProxyF
517539
/// fixes given string by replacing all control chars with spaces.
518540
/// NOTE: newlines are recognized and replaced with stringThatIsAProxyForANewlineInFlatErrors (ASCII 29, the 'group separator'),
519541
/// which is decoded by the IDE with 'NewlineifyErrorString' back into newlines, so that multi-line errors can be displayed in QuickInfo
520-
let NormalizeErrorString (text : string) =
542+
let NormalizeErrorString (text : string) =
521543
if isNull text then nullArg "text"
522544
let text = text.Trim()
523545

@@ -530,7 +552,7 @@ let NormalizeErrorString (text : string) =
530552
// handle \r\n sequence - replace it with one single space
531553
buf.Append(stringThatIsAProxyForANewlineInFlatErrors) |> ignore
532554
2
533-
| '\n' ->
555+
| '\n' | '\r' ->
534556
buf.Append(stringThatIsAProxyForANewlineInFlatErrors) |> ignore
535557
1
536558
| c ->

src/fsharp/ErrorResolutionHints.fs

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,36 @@
44
module internal Microsoft.FSharp.Compiler.ErrorResolutionHints
55

66
/// Filters predictions based on edit distance to an unknown identifier.
7-
let FilterPredictions unknownIdent allPredictions =
7+
let FilterPredictions (unknownIdent:string) (predictionsF:ErrorLogger.Predictions) =
88
let rec take n predictions =
99
predictions
1010
|> Seq.mapi (fun i x -> i,x)
1111
|> Seq.takeWhile (fun (i,_) -> i < n)
1212
|> Seq.map snd
1313
|> Seq.toList
1414

15-
allPredictions
16-
|> Seq.toList
17-
|> List.distinct
18-
|> List.sortByDescending (Internal.Utilities.EditDistance.JaroWinklerDistance unknownIdent)
15+
let unknownIdent = unknownIdent.ToUpperInvariant()
16+
predictionsF()
17+
|> Seq.sortByDescending (fun p -> Internal.Utilities.EditDistance.JaroWinklerDistance unknownIdent (p.ToUpperInvariant()))
1918
|> take 5
2019

21-
let FormatPredictions predictions =
20+
let FormatPredictions errorStyle normalizeF predictions =
2221
match predictions with
2322
| [] -> System.String.Empty
2423
| _ ->
25-
let predictionText =
26-
predictions
27-
|> Seq.map (sprintf "%s %s" System.Environment.NewLine)
28-
|> String.concat ""
29-
System.Environment.NewLine + FSComp.SR.undefinedNameRecordLabelDetails() + predictionText
24+
match errorStyle with
25+
| ErrorLogger.ErrorStyle.VSErrors ->
26+
let predictionText =
27+
predictions
28+
|> List.map normalizeF
29+
|> String.concat ", "
30+
31+
" " + FSComp.SR.undefinedNameRecordLabelDetails() + " " + predictionText
32+
| _ ->
33+
let predictionText =
34+
predictions
35+
|> List.map normalizeF
36+
|> Seq.map (sprintf "%s %s" System.Environment.NewLine)
37+
|> String.concat ""
38+
39+
System.Environment.NewLine + FSComp.SR.undefinedNameRecordLabelDetails() + predictionText

0 commit comments

Comments
 (0)