diff --git a/src/Microsoft.Windows.CsWin32/Generator.cs b/src/Microsoft.Windows.CsWin32/Generator.cs
index 1b54adbc..20ff6b6f 100644
--- a/src/Microsoft.Windows.CsWin32/Generator.cs
+++ b/src/Microsoft.Windows.CsWin32/Generator.cs
@@ -759,6 +759,38 @@ public bool TryGenerateConstant(string name)
return false;
}
+ ///
+ /// Produces a sequence of suggested APIs with a similar name to the specified one.
+ ///
+ /// The user-supplied name.
+ /// A sequence of API names.
+ public IEnumerable GetSuggestions(string name)
+ {
+ if (name is null)
+ {
+ throw new ArgumentNullException(nameof(name));
+ }
+
+ // Trim suffixes off the name.
+ var suffixes = new List { "A", "W", "32", "64", "Ex" };
+ foreach (string suffix in suffixes)
+ {
+ if (name.EndsWith(suffix, StringComparison.Ordinal))
+ {
+ name = name.Substring(0, name.Length - suffix.Length);
+ }
+ }
+
+ // We should match on any API for which the given string is a substring.
+ foreach (string candidate in this.fieldsByName.Keys.Concat(this.typesByName.Keys).Concat(this.methodsByName.Keys))
+ {
+ if (candidate.Contains(name))
+ {
+ yield return candidate;
+ }
+ }
+ }
+
///
/// Collects the result of code generation.
///
diff --git a/src/Microsoft.Windows.CsWin32/SourceGenerator.cs b/src/Microsoft.Windows.CsWin32/SourceGenerator.cs
index b2b69c96..d2f30364 100644
--- a/src/Microsoft.Windows.CsWin32/SourceGenerator.cs
+++ b/src/Microsoft.Windows.CsWin32/SourceGenerator.cs
@@ -31,6 +31,14 @@ public class SourceGenerator : ISourceGenerator
DiagnosticSeverity.Warning,
isEnabledByDefault: true);
+ private static readonly DiagnosticDescriptor NoMatchingMethodOrTypeWithSuggestions = new DiagnosticDescriptor(
+ "PInvoke001",
+ "No matching method or type found",
+ "Method or type \"{0}\" not found. Did you mean {1}?",
+ "Functionality",
+ DiagnosticSeverity.Warning,
+ isEnabledByDefault: true);
+
private static readonly DiagnosticDescriptor NoMethodsForModule = new DiagnosticDescriptor(
"PInvoke001",
"No module found",
@@ -152,12 +160,12 @@ public void Execute(GeneratorExecutionContext context)
context.ReportDiagnostic(Diagnostic.Create(UseEnumValueDeclaringType, location, declaringEnum));
if (!generator.TryGenerate(declaringEnum, context.CancellationToken))
{
- context.ReportDiagnostic(Diagnostic.Create(NoMatchingMethodOrType, location, declaringEnum));
+ ReportNoMatch(location, declaringEnum);
}
}
else
{
- context.ReportDiagnostic(Diagnostic.Create(NoMatchingMethodOrType, location, name));
+ ReportNoMatch(location, name);
}
}
}
@@ -167,6 +175,32 @@ public void Execute(GeneratorExecutionContext context)
{
context.AddSource(unit.Key, unit.Value.ToFullString());
}
+
+ void ReportNoMatch(Location? location, string failedAttempt)
+ {
+ var suggestions = generator.GetSuggestions(failedAttempt).Take(4).ToList();
+ if (suggestions.Count > 0)
+ {
+ var suggestionBuilder = new StringBuilder();
+ for (int i = 0; i < suggestions.Count; i++)
+ {
+ if (i > 0)
+ {
+ suggestionBuilder.Append(i < suggestions.Count - 1 ? ", " : " or ");
+ }
+
+ suggestionBuilder.Append('"');
+ suggestionBuilder.Append(suggestions[i]);
+ suggestionBuilder.Append('"');
+ }
+
+ context.ReportDiagnostic(Diagnostic.Create(NoMatchingMethodOrTypeWithSuggestions, location, failedAttempt, suggestionBuilder));
+ }
+ else
+ {
+ context.ReportDiagnostic(Diagnostic.Create(NoMatchingMethodOrType, location, failedAttempt));
+ }
+ }
}
}
}