Skip to content

Commit e56bb88

Browse files
authored
Merge pull request #5419 from BZngr/InterfaceMemberCodeResuse
Consolidate Signature and Member content generation code
2 parents ea6edd9 + 8786184 commit e56bb88

21 files changed

+566
-538
lines changed
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
using Rubberduck.Parsing;
2+
using Rubberduck.Parsing.Grammar;
3+
using Rubberduck.Parsing.Symbols;
4+
using Rubberduck.Refactorings.Common;
5+
using System;
6+
using System.Collections.Generic;
7+
using System.Linq;
8+
9+
namespace Rubberduck.Refactorings.Common
10+
{
11+
public static class DeclarationExtensions
12+
{
13+
public static bool IsVariable(this Declaration declaration)
14+
=> declaration.DeclarationType.HasFlag(DeclarationType.Variable);
15+
16+
public static bool IsMemberVariable(this Declaration declaration)
17+
=> declaration.IsVariable() && !declaration.ParentDeclaration.IsMember();
18+
19+
public static bool IsLocalVariable(this Declaration declaration)
20+
=> declaration.IsVariable() && declaration.ParentDeclaration.IsMember();
21+
22+
public static bool IsLocalConstant(this Declaration declaration)
23+
=> declaration.IsConstant() && declaration.ParentDeclaration.IsMember();
24+
25+
public static bool HasPrivateAccessibility(this Declaration declaration)
26+
=> declaration.Accessibility.Equals(Accessibility.Private);
27+
28+
public static bool IsMember(this Declaration declaration)
29+
=> declaration.DeclarationType.HasFlag(DeclarationType.Member);
30+
31+
public static bool IsConstant(this Declaration declaration)
32+
=> declaration.DeclarationType.HasFlag(DeclarationType.Constant);
33+
34+
public static bool IsUserDefinedTypeField(this Declaration declaration)
35+
=> declaration.IsMemberVariable() && IsUserDefinedType(declaration);
36+
37+
public static bool IsUserDefinedType(this Declaration declaration)
38+
=> (declaration.AsTypeDeclaration?.DeclarationType.Equals(DeclarationType.UserDefinedType) ?? false);
39+
40+
public static bool IsEnumField(this Declaration declaration)
41+
=> declaration.IsMemberVariable() && (declaration.AsTypeDeclaration?.DeclarationType.Equals(DeclarationType.Enumeration) ?? false);
42+
43+
public static bool IsDeclaredInList(this Declaration declaration)
44+
{
45+
return declaration.Context.TryGetAncestor<VBAParser.VariableListStmtContext>(out var varList)
46+
&& varList.ChildCount > 1;
47+
}
48+
49+
/// <summary>
50+
/// Generates a Property Member code block specified by the letSetGet DeclarationType argument.
51+
/// </summary>
52+
/// <param name="variable"></param>
53+
/// <param name="letSetGetType"></param>
54+
/// <param name="propertyIdentifier"></param>
55+
/// <param name="accessibility"></param>
56+
/// <param name="content"></param>
57+
/// <param name="parameterIdentifier"></param>
58+
/// <returns></returns>
59+
public static string FieldToPropertyBlock(this Declaration variable, DeclarationType letSetGetType, string propertyIdentifier, string accessibility = null, string content = null, string parameterIdentifier = null)
60+
{
61+
//"value" is the default
62+
var propertyValueParam = parameterIdentifier ?? Resources.RubberduckUI.EncapsulateField_DefaultPropertyParameter;
63+
64+
var propertyEndStmt = $"{Tokens.End} {Tokens.Property}";
65+
66+
var asType = variable.IsArray
67+
? $"{Tokens.Variant}"
68+
: variable.IsEnumField() && variable.AsTypeDeclaration.HasPrivateAccessibility()
69+
? $"{Tokens.Long}"
70+
: $"{variable.AsTypeName}";
71+
72+
var asTypeClause = $"{Tokens.As} {asType}";
73+
74+
var paramAccessibility = variable.IsUserDefinedType() ? Tokens.ByRef : Tokens.ByVal;
75+
76+
var letSetParameter = $"{paramAccessibility} {propertyValueParam} {Tokens.As} {asType}";
77+
78+
switch (letSetGetType)
79+
{
80+
case DeclarationType.PropertyGet:
81+
return string.Join(Environment.NewLine, $"{accessibility ?? Tokens.Public} {PropertyTypeStatement(letSetGetType)} {propertyIdentifier}() {asTypeClause}", content, propertyEndStmt);
82+
case DeclarationType.PropertyLet:
83+
case DeclarationType.PropertySet:
84+
return string.Join(Environment.NewLine, $"{accessibility ?? Tokens.Public} {PropertyTypeStatement(letSetGetType)} {propertyIdentifier}({letSetParameter})", content, propertyEndStmt);
85+
default:
86+
throw new ArgumentException();
87+
}
88+
}
89+
90+
private static string PropertyTypeStatement(DeclarationType declarationType)
91+
{
92+
switch (declarationType)
93+
{
94+
case DeclarationType.PropertyGet:
95+
return $"{Tokens.Property} {Tokens.Get}";
96+
case DeclarationType.PropertyLet:
97+
return $"{Tokens.Property} {Tokens.Let}";
98+
case DeclarationType.PropertySet:
99+
return $"{Tokens.Property} {Tokens.Set}";
100+
default:
101+
throw new ArgumentException();
102+
}
103+
104+
}
105+
}
106+
}
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
using Rubberduck.Parsing.Grammar;
2+
using Rubberduck.Parsing.Symbols;
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using System.Text;
7+
using System.Threading.Tasks;
8+
9+
namespace Rubberduck.Refactorings.Common
10+
{
11+
public static class ModuleBodyElementDeclarationExtensions
12+
{
13+
/// <summary>
14+
/// Returns ModuleBodyElementDeclaration signature with an ImprovedArgument list
15+
/// 1. Explicitly declares Property Let\Set value parameter as ByVal
16+
/// 2. Ensures UserDefined Type parameters are declared either explicitly or implicitly as ByRef
17+
/// </summary>
18+
/// <param name="declaration"></param>
19+
/// <returns></returns>
20+
public static string FullMemberSignature(this ModuleBodyElementDeclaration declaration,
21+
string accessibility = null,
22+
string newIdentifier = null)
23+
{
24+
var accessibilityToken = declaration.Accessibility.Equals(Accessibility.Implicit)
25+
? Tokens.Public
26+
: $"{declaration.Accessibility.ToString()}";
27+
28+
var asTypeClause = declaration.AsTypeName == null
29+
? string.Empty
30+
: $" {Tokens.As} {declaration.AsTypeName}";
31+
32+
var fullSignatureFormat = RetrieveFullSignatureFormat(declaration);
33+
34+
var fullSignature = string.Format(fullSignatureFormat,
35+
accessibility ?? accessibilityToken,
36+
newIdentifier ?? declaration.IdentifierName,
37+
ImprovedArgumentList(declaration),
38+
asTypeClause);
39+
40+
return fullSignature.Trim();
41+
}
42+
43+
public static string AsCodeBlock(this ModuleBodyElementDeclaration declaration,
44+
string content = null,
45+
string accessibility = null,
46+
string newIdentifier = null)
47+
{
48+
var endStatement = string.Empty;
49+
switch (declaration.Context)
50+
{
51+
case VBAParser.SubStmtContext _:
52+
endStatement = $"{Tokens.End} {Tokens.Sub}";
53+
break;
54+
case VBAParser.FunctionStmtContext _:
55+
endStatement = $"{Tokens.End} {Tokens.Function}";
56+
break;
57+
case VBAParser.PropertyGetStmtContext _:
58+
case VBAParser.PropertyLetStmtContext _:
59+
case VBAParser.PropertySetStmtContext _:
60+
endStatement = $"{Tokens.End} {Tokens.Property}";
61+
break;
62+
default:
63+
throw new ArgumentException();
64+
}
65+
66+
if (content != null)
67+
{
68+
return string.Format("{0}{1}{2}{1}{3}{1}",
69+
FullMemberSignature(declaration, accessibility, newIdentifier),
70+
Environment.NewLine,
71+
content,
72+
endStatement);
73+
}
74+
75+
return string.Format("{0}{1}{2}{1}",
76+
FullMemberSignature(declaration, accessibility, newIdentifier),
77+
Environment.NewLine,
78+
endStatement);
79+
}
80+
81+
/// <summary>
82+
/// 1. Explicitly declares Property Let\Set value parameter as ByVal
83+
/// 2. Ensures UserDefined Type parameters are declared either explicitly or implicitly as ByRef
84+
/// </summary>
85+
/// <param name="declaration"></param>
86+
/// <returns></returns>
87+
public static string ImprovedArgumentList(this ModuleBodyElementDeclaration declaration)
88+
{
89+
var arguments = Enumerable.Empty<string>();
90+
if (declaration is IParameterizedDeclaration parameterizedDeclaration)
91+
{
92+
arguments = parameterizedDeclaration.Parameters
93+
.OrderBy(parameter => parameter.Selection)
94+
.Select(parameter => BuildParameterDeclaration(
95+
parameter,
96+
parameter.Equals(parameterizedDeclaration.Parameters.LastOrDefault())
97+
&& declaration.DeclarationType.HasFlag(DeclarationType.Property)
98+
&& !declaration.DeclarationType.Equals(DeclarationType.PropertyGet)));
99+
}
100+
return $"{string.Join(", ", arguments)}";
101+
}
102+
103+
private static string BuildParameterDeclaration(ParameterDeclaration parameter, bool forceExplicitByValAccess)
104+
{
105+
var accessibility = parameter.IsImplicitByRef
106+
? string.Empty
107+
: parameter.IsByRef
108+
? Tokens.ByRef
109+
: Tokens.ByVal;
110+
111+
if (forceExplicitByValAccess)
112+
{
113+
accessibility = Tokens.ByVal;
114+
}
115+
116+
if (accessibility.Equals(Tokens.ByVal)
117+
&& (parameter.AsTypeDeclaration?.DeclarationType.HasFlag(DeclarationType.UserDefinedType) ?? false))
118+
{
119+
accessibility = Tokens.ByRef;
120+
}
121+
122+
var name = parameter.IsArray
123+
? $"{parameter.IdentifierName}()"
124+
: parameter.IdentifierName;
125+
126+
var optional = parameter.IsParamArray
127+
? Tokens.ParamArray
128+
: parameter.IsOptional
129+
? Tokens.Optional
130+
: string.Empty;
131+
132+
var defaultValue = parameter.DefaultValue;
133+
134+
return $"{FormatStandardElement(optional)}{FormatStandardElement(accessibility)}{FormatStandardElement(name)}{FormattedAsTypeName(parameter.AsTypeName)}{FormattedDefaultValue(defaultValue)}".Trim();
135+
}
136+
137+
private static string RetrieveFullSignatureFormat(Declaration declaration)
138+
{
139+
var fullSignatureFormat = $"{{0}} THE_MEMBER_TYPE {{1}}({{2}}){{3}}";
140+
141+
switch (declaration.Context)
142+
{
143+
case VBAParser.SubStmtContext _:
144+
fullSignatureFormat = fullSignatureFormat.Replace("THE_MEMBER_TYPE", Tokens.Sub);
145+
break;
146+
case VBAParser.FunctionStmtContext _:
147+
fullSignatureFormat = fullSignatureFormat.Replace("THE_MEMBER_TYPE", Tokens.Function);
148+
break;
149+
case VBAParser.PropertyGetStmtContext _:
150+
fullSignatureFormat = fullSignatureFormat.Replace("THE_MEMBER_TYPE", $"{Tokens.Property} {Tokens.Get}");
151+
break;
152+
case VBAParser.PropertyLetStmtContext _:
153+
fullSignatureFormat = fullSignatureFormat.Replace("THE_MEMBER_TYPE", $"{Tokens.Property} {Tokens.Let}");
154+
break;
155+
case VBAParser.PropertySetStmtContext _:
156+
fullSignatureFormat = fullSignatureFormat.Replace("THE_MEMBER_TYPE", $"{Tokens.Property} {Tokens.Set}");
157+
break;
158+
default:
159+
throw new ArgumentException();
160+
}
161+
return fullSignatureFormat;
162+
}
163+
164+
private static string FormatStandardElement(string element) => string.IsNullOrEmpty(element)
165+
? string.Empty
166+
: $"{element} ";
167+
168+
private static string FormattedAsTypeName(string AsTypeName) => string.IsNullOrEmpty(AsTypeName)
169+
? string.Empty
170+
: $"As {AsTypeName} ";
171+
172+
private static string FormattedDefaultValue(string DefaultValue) => string.IsNullOrEmpty(DefaultValue)
173+
? string.Empty
174+
: $"= {DefaultValue}";
175+
}
176+
}

Rubberduck.Refactorings/EncapsulateField/EncapsulationStrategies/ConvertFieldsToUDTMembers.cs

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
using Antlr4.Runtime;
2+
using Rubberduck.Parsing.Grammar;
23
using Rubberduck.Parsing.Rewriter;
4+
using Rubberduck.Parsing.Symbols;
35
using Rubberduck.Parsing.VBA;
6+
using Rubberduck.Refactorings.Common;
7+
using Rubberduck.Refactorings.EncapsulateField.Extensions;
48
using Rubberduck.SmartIndenter;
59
using Rubberduck.VBEditor;
610
using System;
@@ -66,10 +70,33 @@ protected override void LoadNewPropertyBlocks()
6670
{
6771
var propertyGenerationSpecs = SelectedFields.SelectMany(f => f.PropertyAttributeSets);
6872

69-
var generator = new PropertyGenerator();
70-
foreach (var spec in propertyGenerationSpecs)
73+
foreach (var selectedField in SelectedFields)
7174
{
72-
AddContentBlock(NewContentTypes.MethodBlock, generator.AsPropertyBlock(spec, _indenter));
75+
var converted = selectedField as IConvertToUDTMember;
76+
foreach (var set in selectedField.PropertyAttributeSets)
77+
{
78+
if (converted.Declaration is VariableDeclaration variableDeclaration)
79+
{
80+
var getContent = $"{set.PropertyName} = {set.BackingField}";
81+
if (set.UsesSetAssignment)
82+
{
83+
getContent = $"{Tokens.Set} {getContent}";
84+
}
85+
AddContentBlock(NewContentTypes.MethodBlock, variableDeclaration.FieldToPropertyBlock(DeclarationType.PropertyGet, set.PropertyName, content: $"{_defaultIndent}{getContent}"));
86+
if (converted.IsReadOnly)
87+
{
88+
continue;
89+
}
90+
if (set.GenerateLetter)
91+
{
92+
AddContentBlock(NewContentTypes.MethodBlock, variableDeclaration.FieldToPropertyBlock(DeclarationType.PropertyLet, set.PropertyName, content: $"{_defaultIndent}{set.BackingField} = {set.ParameterName}"));
93+
}
94+
if (set.GenerateSetter)
95+
{
96+
AddContentBlock(NewContentTypes.MethodBlock, variableDeclaration.FieldToPropertyBlock(DeclarationType.PropertySet, set.PropertyName, content: $"{_defaultIndent}{Tokens.Set} {set.BackingField} = {set.ParameterName}"));
97+
}
98+
}
99+
}
73100
}
74101
}
75102

0 commit comments

Comments
 (0)