em 03: extract-first scan + full suggestion report
- ExtractFirstScanner: reads-only scan of non-local/param read expressions (member/field/property access, element access, invocation, cast), deduped by symbol+text with occurrence counts; invocations flagged (hoisting changes eval count); own-class field/property reads marked optional. Assignment LHS skipped whole (decision 5) — surfaced as a report note. - SignatureBuilder: promotes bucket overlaps into one coherent signature (declared-inside vars drop from params; declared-inside return candidates become the return; outside candidates already covered by their ref write-back stay ref params). Trailing composite returns stay void with a note suggesting an extract-variable first. - ExtractionReporter + ReportFormatter: single composition shared by CLI and tests; report = header, params (ref), returns, locals, extract-first (count + flags), notes, suggested-signature line last. - DataFlowClassifier: Returns now carry types (ReturnSuggestion) — the signature line needs them. - Displays: compact symbol/type formatting for the report. - Fixture: RepeatReads (dedupe ×2 + skipped field write), Casts (cast candidate) appended at the end so all pinned line numbers stay put.
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
using System.Collections.Immutable;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace ExtractMethod.Tooling;
|
||||
|
||||
/// <summary>
|
||||
/// Compact symbol display for the suggestion report. The defaults are wrong
|
||||
/// for a report a human reads next to their own source:
|
||||
/// - <c>ToDisplayString()</c> fully qualifies every namespace
|
||||
/// (<c>System.Collections.Generic.List<Widget></c>),
|
||||
/// - <c>ToMinimalDisplayString()</c> keeps containing types AND prefixes
|
||||
/// members with their own type (<c>int Demo._seed</c>).
|
||||
/// The report wants the shortest form that stays readable in context:
|
||||
/// types as bare names (<c>Widget</c>, <c>List<Widget></c>), members as
|
||||
/// <c>ContainingType.Member</c> with parameter types.
|
||||
/// </summary>
|
||||
internal static class Displays
|
||||
{
|
||||
/// <summary>
|
||||
/// Type display for suggestion lines: shortest unambiguous-enough form
|
||||
/// (no namespaces, no containing types, keyword spellings for special
|
||||
/// types, nullable annotations kept). Built explicitly because
|
||||
/// MinimallyQualifiedFormat keeps containing types for nested types
|
||||
/// (<c>Demo.Widget</c>) and has no With-style override here.
|
||||
/// </summary>
|
||||
internal static readonly SymbolDisplayFormat TypeFormat = new(
|
||||
globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted,
|
||||
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameOnly,
|
||||
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters
|
||||
| SymbolDisplayGenericsOptions.IncludeVariance,
|
||||
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes
|
||||
| SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers
|
||||
| SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier);
|
||||
|
||||
/// <summary>Compact member display for extract-first entries:
|
||||
/// <c>Demo._seed</c>, <c>Demo.Scale</c>, <c>Widget.Bigger(Widget)</c>,
|
||||
/// <c>List<Widget>.this[int]</c>.</summary>
|
||||
internal static string Member(ISymbol symbol) => symbol switch
|
||||
{
|
||||
IMethodSymbol method =>
|
||||
$"{Type(method.ContainingType)}.{method.Name}({Parameters(method.Parameters)})",
|
||||
IPropertySymbol { IsIndexer: true } indexer =>
|
||||
$"{Type(indexer.ContainingType)}.this[{Parameters(indexer.Parameters)}]",
|
||||
IFieldSymbol or IPropertySymbol or IEventSymbol =>
|
||||
$"{Type(symbol.ContainingType)}.{symbol.Name}",
|
||||
_ => symbol.ToDisplayString(TypeFormat),
|
||||
};
|
||||
|
||||
private static string Parameters(ImmutableArray<IParameterSymbol> parameters)
|
||||
=> string.Join(", ", parameters.Select(p => Type(p.Type)));
|
||||
|
||||
private static string Type(ITypeSymbol? type) => type?.ToDisplayString(TypeFormat) ?? "?";
|
||||
}
|
||||
Reference in New Issue
Block a user