From 84d02e4ba8b2f7f10f55d003b109d356a1b26619 Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Fri, 7 Aug 2020 14:55:40 -0700 Subject: [PATCH 01/67] Conform to Split Refactoring structure Removes strategy objects and migrates the code to the EncapsulateFieldUseBackingFieldRefactoringAction and EncapsulateFieldUseUDTMemberRefactoringAction. Introduces PreviewProvider classes. --- .../EncapsulateField/EncapsulateFieldModel.cs | 18 +- .../EncapsulateFieldPreviewProvider.cs | 58 +++ .../EncapsulateFieldRefactoring.cs | 58 +-- .../EncapsulateFieldRefactoringAction.cs | 34 ++ ...apsulateFieldRefactoringActionImplBase.cs} | 331 ++++++++++-------- .../EncapsulateFieldRewriteSession.cs | 98 ------ ...teFieldUseBackingFieldRefactoringAction.cs | 77 ++++ ...ldUseBackingUDTMemberRefactoringAction.cs} | 46 ++- .../UseBackingFields.cs | 74 ---- .../Extensions/IModuleRewriterExtensions.cs | 13 - .../Extensions/StringExtensions.cs | 15 + .../EncapsulateFieldCandidate.cs | 15 +- .../EncapsulateFieldCommandTests.cs | 6 +- .../EncapsulateFIeldTestSupport.cs | 9 +- .../EncapsulateFieldTestComponentResolver.cs | 50 +++ 15 files changed, 477 insertions(+), 425 deletions(-) create mode 100644 Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldPreviewProvider.cs create mode 100644 Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringAction.cs rename Rubberduck.Refactorings/EncapsulateField/{EncapsulationStrategies/EncapsulateFieldStrategyBase.cs => EncapsulateFieldRefactoringActionImplBase.cs} (66%) delete mode 100644 Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRewriteSession.cs create mode 100644 Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingFieldRefactoringAction.cs rename Rubberduck.Refactorings/EncapsulateField/{EncapsulationStrategies/ConvertFieldsToUDTMembers.cs => EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs} (66%) delete mode 100644 Rubberduck.Refactorings/EncapsulateField/EncapsulationStrategies/UseBackingFields.cs create mode 100644 RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs index bdc19145c6..b990b7efdb 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs @@ -9,7 +9,6 @@ namespace Rubberduck.Refactorings.EncapsulateField { public class EncapsulateFieldModel : IRefactoringModel { - private readonly Func _previewDelegate; private QualifiedModuleName _targetQMN; private IDeclarationFinderProvider _declarationFinderProvider; private IEncapsulateFieldValidationsProvider _validationsProvider; @@ -21,15 +20,13 @@ public class EncapsulateFieldModel : IRefactoringModel private IDictionary)> _udtFieldToUdtDeclarationMap = new Dictionary)>(); public EncapsulateFieldModel( - Declaration target, - IEnumerable candidates, - IEnumerable objectStateUDTCandidates, - IObjectStateUDT stateUDTField, - Func previewDelegate, - IDeclarationFinderProvider declarationFinderProvider, + Declaration target, + IEnumerable candidates, + IEnumerable objectStateUDTCandidates, + IObjectStateUDT stateUDTField, + IDeclarationFinderProvider declarationFinderProvider, IEncapsulateFieldValidationsProvider validationsProvider) { - _previewDelegate = previewDelegate; _targetQMN = target.QualifiedModuleName; _newObjectStateUDT = stateUDTField; _declarationFinderProvider = declarationFinderProvider; @@ -45,7 +42,10 @@ public class EncapsulateFieldModel : IRefactoringModel public QualifiedModuleName QualifiedModuleName => _targetQMN; - public string PreviewRefactoring() => _previewDelegate(this); + public IRefactoringPreviewProvider PreviewProvider { set; get; } + + ////TODO: Remove method from model and have clients use PreviewProvider property directly + public string PreviewRefactoring() => PreviewProvider?.Preview(this) ?? string.Empty; public IEnumerable ObjectStateUDTCandidates => _objStateCandidates; diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldPreviewProvider.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldPreviewProvider.cs new file mode 100644 index 0000000000..68636c55ec --- /dev/null +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldPreviewProvider.cs @@ -0,0 +1,58 @@ +using Rubberduck.Parsing.Rewriter; +using Rubberduck.Refactorings.EncapsulateField.Extensions; +using Rubberduck.VBEditor; + +namespace Rubberduck.Refactorings.EncapsulateField +{ + public class EncapsulateFieldPreviewProvider : IRefactoringPreviewProvider + { + private readonly EncapsulateFieldUseBackingFieldPreviewProvider _useBackingFieldPreviewer; + private readonly EncapsulateFieldUseBackingUDTMemberPreviewProvider _useUDTMembmerPreviewer; + public EncapsulateFieldPreviewProvider( + EncapsulateFieldUseBackingFieldPreviewProvider useBackingFieldPreviewProvider, + EncapsulateFieldUseBackingUDTMemberPreviewProvider useUDTMemberPreviewProvide) + { + _useBackingFieldPreviewer = useBackingFieldPreviewProvider; + _useUDTMembmerPreviewer = useUDTMemberPreviewProvide; + } + + public string Preview(EncapsulateFieldModel model) + { + var preview = model.EncapsulateFieldStrategy == EncapsulateFieldStrategy.ConvertFieldsToUDTMembers + ? _useUDTMembmerPreviewer.Preview(model) + : _useBackingFieldPreviewer.Preview(model); + + return preview.LimitNewLines(3); + } + } + + public class EncapsulateFieldUseBackingFieldPreviewProvider : RefactoringPreviewProviderWrapperBase + { + public EncapsulateFieldUseBackingFieldPreviewProvider(EncapsulateFieldUseBackingFieldRefactoringAction refactoringAction, + IRewritingManager rewritingManager) + : base(refactoringAction, rewritingManager) + { + + } + + protected override QualifiedModuleName ComponentToShow(EncapsulateFieldModel model) + { + return model.QualifiedModuleName; + } + } + + public class EncapsulateFieldUseBackingUDTMemberPreviewProvider : RefactoringPreviewProviderWrapperBase + { + public EncapsulateFieldUseBackingUDTMemberPreviewProvider(EncapsulateFieldUseBackingUDTMemberRefactoringAction refactoringAction, + IRewritingManager rewritingManager) + : base(refactoringAction, rewritingManager) + { + + } + + protected override QualifiedModuleName ComponentToShow(EncapsulateFieldModel model) + { + return model.QualifiedModuleName; + } + } +} diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoring.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoring.cs index 78003eddce..0a10abde1e 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoring.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoring.cs @@ -4,9 +4,7 @@ using Rubberduck.Parsing.VBA; using Rubberduck.Refactorings.Exceptions; using Rubberduck.VBEditor; -using Rubberduck.SmartIndenter; using Rubberduck.VBEditor.Utility; -using System; namespace Rubberduck.Refactorings.EncapsulateField { @@ -20,24 +18,24 @@ public class EncapsulateFieldRefactoring : InteractiveRefactoringBase userInteraction, IRewritingManager rewritingManager, ISelectionProvider selectionProvider, - ISelectedDeclarationProvider selectedDeclarationProvider, - ICodeBuilder codeBuilder) + ISelectedDeclarationProvider selectedDeclarationProvider) :base(selectionProvider, userInteraction) { + _refactoringAction = refactoringAction; + _previewProvider = previewProvider; _declarationFinderProvider = declarationFinderProvider; _selectedDeclarationProvider = selectedDeclarationProvider; - _indenter = indenter; - _codeBuilder = codeBuilder; _rewritingManager = rewritingManager; } @@ -76,49 +74,27 @@ protected override EncapsulateFieldModel InitializeModel(Declaration target) builder.Candidates, builder.ObjectStateUDTCandidates, builder.DefaultObjectStateUDT, - PreviewRewrite, _declarationFinderProvider, - builder.ValidationsProvider); - - if (builder.ObjectStateUDT != null) + builder.ValidationsProvider) { - model.EncapsulateFieldStrategy = EncapsulateFieldStrategy.ConvertFieldsToUDTMembers; - model.ObjectStateUDTField = builder.ObjectStateUDT; - } + PreviewProvider = _previewProvider, + ObjectStateUDTField = builder.ObjectStateUDT, + EncapsulateFieldStrategy = builder.ObjectStateUDT != null ? EncapsulateFieldStrategy.ConvertFieldsToUDTMembers : EncapsulateFieldStrategy.UseBackingFields, + }; return model; } - protected override void RefactorImpl(EncapsulateFieldModel model) - { - var refactorRewriteSession = new EncapsulateFieldRewriteSession(_rewritingManager.CheckOutCodePaneSession()) as IEncapsulateFieldRewriteSession; - - refactorRewriteSession = RefactorRewrite(model, refactorRewriteSession); - - if (!refactorRewriteSession.TryRewrite()) - { - throw new RewriteFailedException(refactorRewriteSession.RewriteSession); - } - } - - private string PreviewRewrite(EncapsulateFieldModel model) + private EncapsulateFieldStrategy ApplyStrategy(IObjectStateUDT objStateUDT) { - var previewSession = new EncapsulateFieldRewriteSession(_rewritingManager.CheckOutCodePaneSession()) as IEncapsulateFieldRewriteSession; ; - - previewSession = RefactorRewrite(model, previewSession, true); - - return previewSession.CreatePreview(model.QualifiedModuleName); + return objStateUDT != null ? EncapsulateFieldStrategy.ConvertFieldsToUDTMembers : EncapsulateFieldStrategy.UseBackingFields; } - private IEncapsulateFieldRewriteSession RefactorRewrite(EncapsulateFieldModel model, IEncapsulateFieldRewriteSession refactorRewriteSession, bool asPreview = false) + protected override void RefactorImpl(EncapsulateFieldModel model) { - if (!model.SelectedFieldCandidates.Any()) { return refactorRewriteSession; } - - var strategy = model.EncapsulateFieldStrategy == EncapsulateFieldStrategy.ConvertFieldsToUDTMembers - ? new ConvertFieldsToUDTMembers(_declarationFinderProvider, model, _indenter, _codeBuilder) as IEncapsulateStrategy - : new UseBackingFields(_declarationFinderProvider, model, _indenter, _codeBuilder) as IEncapsulateStrategy; + if (!model.SelectedFieldCandidates.Any()) { return; } - return strategy.RefactorRewrite(refactorRewriteSession, asPreview); + _refactoringAction.Refactor(model); } } } diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringAction.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringAction.cs new file mode 100644 index 0000000000..beb44d75fd --- /dev/null +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringAction.cs @@ -0,0 +1,34 @@ +using System.Linq; + +namespace Rubberduck.Refactorings.EncapsulateField +{ + public class EncapsulateFieldRefactoringAction : IRefactoringAction + { + private readonly EncapsulateFieldUseBackingFieldRefactoringAction _useBackingField; + private readonly EncapsulateFieldUseBackingUDTMemberRefactoringAction _useBackingUDTMember; + + public EncapsulateFieldRefactoringAction( + EncapsulateFieldUseBackingFieldRefactoringAction encapsulateFieldUseBackingField, + EncapsulateFieldUseBackingUDTMemberRefactoringAction encapsulateFieldUseUDTMember) + { + _useBackingField = encapsulateFieldUseBackingField; + _useBackingUDTMember = encapsulateFieldUseUDTMember; + } + + public void Refactor(EncapsulateFieldModel model) + { + if (!model?.EncapsulationCandidates.Any() ?? true) + { + return; + } + + if (model.EncapsulateFieldStrategy == EncapsulateFieldStrategy.ConvertFieldsToUDTMembers) + { + _useBackingUDTMember.Refactor(model); + return; + } + + _useBackingField.Refactor(model); + } + } +} diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulationStrategies/EncapsulateFieldStrategyBase.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringActionImplBase.cs similarity index 66% rename from Rubberduck.Refactorings/EncapsulateField/EncapsulationStrategies/EncapsulateFieldStrategyBase.cs rename to Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringActionImplBase.cs index bf6c9f2d12..8cb0fb63d8 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulationStrategies/EncapsulateFieldStrategyBase.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringActionImplBase.cs @@ -1,205 +1,89 @@ using Antlr4.Runtime; using Rubberduck.Parsing; using Rubberduck.Parsing.Grammar; +using Rubberduck.Parsing.Rewriter; using Rubberduck.Parsing.Symbols; using Rubberduck.Parsing.VBA; using Rubberduck.Refactorings.Common; using Rubberduck.Refactorings.EncapsulateField.Extensions; -using Rubberduck.Resources; using Rubberduck.SmartIndenter; using Rubberduck.VBEditor; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Rubberduck.Refactorings.EncapsulateField { - - public struct PropertyAttributeSet + public abstract class EncapsulateFieldRefactoringActionImplBase : CodeOnlyRefactoringActionBase { - public string PropertyName { get; set; } - public string BackingField { get; set; } - public string AsTypeName { get; set; } - public string ParameterName { get; set; } - public bool GenerateLetter { get; set; } - public bool GenerateSetter { get; set; } - public bool UsesSetAssignment { get; set; } - public bool IsUDTProperty { get; set; } - public Declaration Declaration { get; set; } - } + private const string _defaultIndent = " "; //4 spaces + private static string _doubleSpace = $"{Environment.NewLine}{Environment.NewLine}"; - public interface IEncapsulateStrategy - { - IEncapsulateFieldRewriteSession RefactorRewrite(IEncapsulateFieldRewriteSession refactorRewriteSession, bool asPreview); - } + protected enum NewContentType + { + TypeDeclarationBlock, + DeclarationBlock, + MethodBlock, + PostContentMessage + }; - public abstract class EncapsulateFieldStrategyBase : IEncapsulateStrategy - { + protected readonly IDeclarationFinderProvider _declarationFinderProvider; protected readonly IIndenter _indenter; + protected readonly ICodeBuilder _codeBuilder; + protected QualifiedModuleName _targetQMN; - private readonly int? _codeSectionStartIndex; - protected const string _defaultIndent = " "; //4 spaces - protected ICodeBuilder _codeBuilder; + protected int? _codeSectionStartIndex; + protected Dictionary> _newContent { set; get; } protected Dictionary IdentifierReplacements { get; } = new Dictionary(); - protected enum NewContentTypes { TypeDeclarationBlock, DeclarationBlock, MethodBlock, PostContentMessage }; - protected Dictionary> _newContent { set; get; } - private static string DoubleSpace => $"{Environment.NewLine}{Environment.NewLine}"; - - protected IEnumerable SelectedFields { private set; get; } - - public EncapsulateFieldStrategyBase(IDeclarationFinderProvider declarationFinderProvider, EncapsulateFieldModel model, IIndenter indenter, ICodeBuilder codeBuilder) + public EncapsulateFieldRefactoringActionImplBase( + IDeclarationFinderProvider declarationFinderProvider, + IIndenter indenter, + IRewritingManager rewritingManager, + ICodeBuilder codeBuilder) + : base(rewritingManager) { - _targetQMN = model.QualifiedModuleName; + _declarationFinderProvider = declarationFinderProvider; _indenter = indenter; _codeBuilder = codeBuilder; - SelectedFields = model.SelectedFieldCandidates; - - _codeSectionStartIndex = declarationFinderProvider.DeclarationFinder - .Members(_targetQMN).Where(m => m.IsMember()) - .OrderBy(c => c.Selection) - .FirstOrDefault()?.Context.Start.TokenIndex ?? null; } - public IEncapsulateFieldRewriteSession RefactorRewrite(IEncapsulateFieldRewriteSession refactorRewriteSession, bool asPreview) + protected IEnumerable SelectedFields { set; get; } + + protected IRewriteSession RefactorImpl(EncapsulateFieldModel model, IRewriteSession rewriteSession) { - ModifyFields(refactorRewriteSession); + InitializeRefactoringAction(model); - ModifyReferences(refactorRewriteSession); + ModifyFields(rewriteSession); - InsertNewContent(refactorRewriteSession, asPreview); + ModifyReferences(rewriteSession); - return refactorRewriteSession; + InsertNewContent(rewriteSession); + + return rewriteSession; } - protected abstract void ModifyFields(IEncapsulateFieldRewriteSession rewriteSession); + protected abstract void ModifyFields(IRewriteSession rewriteSession); - protected abstract void ModifyReferences(IEncapsulateFieldRewriteSession refactorRewriteSession); + protected abstract void ModifyReferences(IRewriteSession refactorRewriteSession); protected abstract void LoadNewDeclarationBlocks(); - protected void RewriteReferences(IEncapsulateFieldRewriteSession refactorRewriteSession) + protected void RewriteReferences(IRewriteSession rewriteSession) { foreach (var replacement in IdentifierReplacements) { (ParserRuleContext Context, string Text) = replacement.Value; - var rewriter = refactorRewriteSession.CheckOutModuleRewriter(replacement.Key.QualifiedModuleName); + var rewriter = rewriteSession.CheckOutModuleRewriter(replacement.Key.QualifiedModuleName); rewriter.Replace(Context, Text); } } - protected void AddContentBlock(NewContentTypes contentType, string block) + protected void AddContentBlock(NewContentType contentType, string block) => _newContent[contentType].Add(block); - private void InsertNewContent(IEncapsulateFieldRewriteSession refactorRewriteSession, bool isPreview = false) - { - _newContent = new Dictionary> - { - { NewContentTypes.PostContentMessage, new List() }, - { NewContentTypes.DeclarationBlock, new List() }, - { NewContentTypes.MethodBlock, new List() }, - { NewContentTypes.TypeDeclarationBlock, new List() } - }; - - LoadNewDeclarationBlocks(); - - LoadNewPropertyBlocks(); - - if (isPreview) - { - AddContentBlock(NewContentTypes.PostContentMessage, RubberduckUI.EncapsulateField_PreviewMarker); - } - - var newContentBlock = string.Join(DoubleSpace, - (_newContent[NewContentTypes.TypeDeclarationBlock]) - .Concat(_newContent[NewContentTypes.DeclarationBlock]) - .Concat(_newContent[NewContentTypes.MethodBlock]) - .Concat(_newContent[NewContentTypes.PostContentMessage])) - .Trim(); - - var maxConsecutiveNewLines = 3; - var target = string.Join(string.Empty, Enumerable.Repeat(Environment.NewLine, maxConsecutiveNewLines).ToList()); - var replacement = string.Join(string.Empty, Enumerable.Repeat(Environment.NewLine, maxConsecutiveNewLines - 1).ToList()); - for (var counter = 1; counter < 10 && newContentBlock.Contains(target); counter++) - { - newContentBlock = newContentBlock.Replace(target, replacement); - } - - - var rewriter = refactorRewriteSession.CheckOutModuleRewriter(_targetQMN); - if (_codeSectionStartIndex.HasValue) - { - rewriter.InsertBefore(_codeSectionStartIndex.Value, $"{newContentBlock}{DoubleSpace}"); - } - else - { - rewriter.InsertAtEndOfFile($"{DoubleSpace}{newContentBlock}"); - } - } - - protected void LoadNewPropertyBlocks() - { - foreach (var propertyAttributes in SelectedFields.SelectMany(f => f.PropertyAttributeSets)) - { - AddPropertyCodeBlocks(propertyAttributes); - } - } - - private void AddPropertyCodeBlocks(PropertyAttributeSet propertyAttributes) - { - Debug.Assert(propertyAttributes.Declaration.DeclarationType.HasFlag(DeclarationType.Variable) || propertyAttributes.Declaration.DeclarationType.HasFlag(DeclarationType.UserDefinedTypeMember)); - - var getContent = $"{propertyAttributes.PropertyName} = {propertyAttributes.BackingField}"; - if (propertyAttributes.UsesSetAssignment) - { - getContent = $"{Tokens.Set} {getContent}"; - } - - if (propertyAttributes.AsTypeName.Equals(Tokens.Variant) && !propertyAttributes.Declaration.IsArray) - { - getContent = string.Join(Environment.NewLine, - $"{Tokens.If} IsObject({propertyAttributes.BackingField}) {Tokens.Then}", - $"{_defaultIndent}{Tokens.Set} {propertyAttributes.PropertyName} = {propertyAttributes.BackingField}", - Tokens.Else, - $"{_defaultIndent}{propertyAttributes.PropertyName} = {propertyAttributes.BackingField}", - $"{Tokens.End} {Tokens.If}", - Environment.NewLine); - } - - if (!_codeBuilder.TryBuildPropertyGetCodeBlock(propertyAttributes.Declaration, propertyAttributes.PropertyName, out var propertyGet, content: $"{_defaultIndent}{getContent}")) - { - throw new ArgumentException(); - } - AddContentBlock(NewContentTypes.MethodBlock, propertyGet); - - if (!(propertyAttributes.GenerateLetter || propertyAttributes.GenerateSetter)) - { - return; - } - - if (propertyAttributes.GenerateLetter) - { - if (!_codeBuilder.TryBuildPropertyLetCodeBlock(propertyAttributes.Declaration, propertyAttributes.PropertyName, out var propertyLet, content: $"{_defaultIndent}{propertyAttributes.BackingField} = {propertyAttributes.ParameterName}")) - { - throw new ArgumentException(); - } - AddContentBlock(NewContentTypes.MethodBlock, propertyLet); - } - - if (propertyAttributes.GenerateSetter) - { - if (!_codeBuilder.TryBuildPropertySetCodeBlock(propertyAttributes.Declaration, propertyAttributes.PropertyName, out var propertySet, content: $"{_defaultIndent}{Tokens.Set} {propertyAttributes.BackingField} = {propertyAttributes.ParameterName}")) - { - throw new ArgumentException(); - } - AddContentBlock(NewContentTypes.MethodBlock, propertySet); - } - } - protected virtual void LoadFieldReferenceContextReplacements(IEncapsulateFieldCandidate field) { if (field is IUserDefinedTypeCandidate udt && udt.TypeDeclarationIsPrivate) @@ -275,7 +159,19 @@ protected virtual void SetUDTMemberReferenceRewriteContent(IdentifierReference i } } - private void AddIdentifierReplacement( IdentifierReference idRef, ParserRuleContext context, string replacementText) + private void InitializeRefactoringAction(EncapsulateFieldModel model) + { + _targetQMN = model.QualifiedModuleName; + + _codeSectionStartIndex = _declarationFinderProvider.DeclarationFinder + .Members(model.QualifiedModuleName).Where(m => m.IsMember()) + .OrderBy(c => c.Selection) + .FirstOrDefault()?.Context.Start.TokenIndex ?? null; + + SelectedFields = model.SelectedFieldCandidates; + } + + private void AddIdentifierReplacement(IdentifierReference idRef, ParserRuleContext context, string replacementText) { if (IdentifierReplacements.ContainsKey(idRef)) { @@ -284,5 +180,132 @@ private void AddIdentifierReplacement( IdentifierReference idRef, ParserRuleCont } IdentifierReplacements.Add(idRef, (context, replacementText)); } + + private void InsertNewContent(IRewriteSession refactorRewriteSession) + { + _newContent = new Dictionary> + { + { NewContentType.PostContentMessage, new List() }, + { NewContentType.DeclarationBlock, new List() }, + { NewContentType.MethodBlock, new List() }, + { NewContentType.TypeDeclarationBlock, new List() } + }; + + LoadNewDeclarationBlocks(); + + LoadNewPropertyBlocks(); + + var newContentBlock = string.Join(_doubleSpace, + (_newContent[NewContentType.TypeDeclarationBlock]) + .Concat(_newContent[NewContentType.DeclarationBlock]) + .Concat(_newContent[NewContentType.MethodBlock]) + .Concat(_newContent[NewContentType.PostContentMessage])) + .Trim(); + + var maxConsecutiveNewLines = 3; + var target = string.Join(string.Empty, Enumerable.Repeat(Environment.NewLine, maxConsecutiveNewLines).ToList()); + var replacement = string.Join(string.Empty, Enumerable.Repeat(Environment.NewLine, maxConsecutiveNewLines - 1).ToList()); + for (var counter = 1; counter < 10 && newContentBlock.Contains(target); counter++) + { + newContentBlock = newContentBlock.Replace(target, replacement); + } + + + var rewriter = refactorRewriteSession.CheckOutModuleRewriter(_targetQMN); + if (_codeSectionStartIndex.HasValue) + { + rewriter.InsertBefore(_codeSectionStartIndex.Value, $"{newContentBlock}{_doubleSpace}"); + } + else + { + rewriter.InsertAtEndOfFile($"{_doubleSpace}{newContentBlock}"); + } + } + + protected void LoadNewPropertyBlocks() + { + foreach (var propertyAttributes in SelectedFields.SelectMany(f => f.PropertyAttributeSets)) + { + AddPropertyCodeBlocks(propertyAttributes); + } + } + /// + /// RemoveFields handles the special case of field declaration removal where + /// each field of a VariableListStmtContext is specified for removal. In this + /// special case the Parent context is removed rather than the individual declarations. + /// + protected static void RemoveFields(IEnumerable toRemove, IRewriteSession rewriteSession) + { + if (!toRemove.Any()) { return; } + + var fieldsByListContext = toRemove.Distinct().GroupBy(f => f.Context.GetAncestor()); + + var rewriter = rewriteSession.CheckOutModuleRewriter(toRemove.First().QualifiedModuleName); + foreach (var fieldsGroup in fieldsByListContext) + { + var variables = fieldsGroup.Key.children.Where(ch => ch is VBAParser.VariableSubStmtContext); + if (variables.Count() == fieldsGroup.Count()) + { + rewriter.Remove(fieldsGroup.Key.Parent); + continue; + } + + foreach (var target in fieldsGroup) + { + rewriter.Remove(target); + } + } + } + + private void AddPropertyCodeBlocks(PropertyAttributeSet propertyAttributes) + { + Debug.Assert(propertyAttributes.Declaration.DeclarationType.HasFlag(DeclarationType.Variable) || propertyAttributes.Declaration.DeclarationType.HasFlag(DeclarationType.UserDefinedTypeMember)); + + var getContent = $"{propertyAttributes.PropertyName} = {propertyAttributes.BackingField}"; + if (propertyAttributes.UsesSetAssignment) + { + getContent = $"{Tokens.Set} {getContent}"; + } + + if (propertyAttributes.AsTypeName.Equals(Tokens.Variant) && !propertyAttributes.Declaration.IsArray) + { + getContent = string.Join(Environment.NewLine, + $"{Tokens.If} IsObject({propertyAttributes.BackingField}) {Tokens.Then}", + $"{_defaultIndent}{Tokens.Set} {propertyAttributes.PropertyName} = {propertyAttributes.BackingField}", + Tokens.Else, + $"{_defaultIndent}{propertyAttributes.PropertyName} = {propertyAttributes.BackingField}", + $"{Tokens.End} {Tokens.If}", + Environment.NewLine); + } + + if (!_codeBuilder.TryBuildPropertyGetCodeBlock(propertyAttributes.Declaration, propertyAttributes.PropertyName, out var propertyGet, content: $"{_defaultIndent}{getContent}")) + { + throw new ArgumentException(); + } + AddContentBlock(NewContentType.MethodBlock, propertyGet); + + if (!(propertyAttributes.GenerateLetter || propertyAttributes.GenerateSetter)) + { + return; + } + + if (propertyAttributes.GenerateLetter) + { + if (!_codeBuilder.TryBuildPropertyLetCodeBlock(propertyAttributes.Declaration, propertyAttributes.PropertyName, out var propertyLet, content: $"{_defaultIndent}{propertyAttributes.BackingField} = {propertyAttributes.ParameterName}")) + { + throw new ArgumentException(); + } + AddContentBlock(NewContentType.MethodBlock, propertyLet); + } + + if (propertyAttributes.GenerateSetter) + { + if (!_codeBuilder.TryBuildPropertySetCodeBlock(propertyAttributes.Declaration, propertyAttributes.PropertyName, out var propertySet, content: $"{_defaultIndent}{Tokens.Set} {propertyAttributes.BackingField} = {propertyAttributes.ParameterName}")) + { + throw new ArgumentException(); + } + AddContentBlock(NewContentType.MethodBlock, propertySet); + } + } } } diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRewriteSession.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRewriteSession.cs deleted file mode 100644 index 107187c7fe..0000000000 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRewriteSession.cs +++ /dev/null @@ -1,98 +0,0 @@ -using Rubberduck.Parsing; -using Rubberduck.Parsing.Grammar; -using Rubberduck.Parsing.Rewriter; -using Rubberduck.Parsing.Symbols; -using Rubberduck.VBEditor; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Rubberduck.Refactorings.EncapsulateField.Extensions; - -namespace Rubberduck.Refactorings.EncapsulateField -{ - public interface IEncapsulateFieldRewriteSession - { - IExecutableRewriteSession RewriteSession { get; } - IModuleRewriter CheckOutModuleRewriter(QualifiedModuleName qmn); - bool TryRewrite(); - string CreatePreview(QualifiedModuleName qmn); - void Remove(Declaration target, IModuleRewriter rewriter); - } - - public class EncapsulateFieldRewriteSession : IEncapsulateFieldRewriteSession - { - private IExecutableRewriteSession _rewriteSession; - private Dictionary> RemovedVariables { set; get; } = new Dictionary>(); - - public EncapsulateFieldRewriteSession(IExecutableRewriteSession rewriteSession) - { - _rewriteSession = rewriteSession; - } - - public IExecutableRewriteSession RewriteSession => _rewriteSession; - - public IModuleRewriter CheckOutModuleRewriter(QualifiedModuleName qmn) - => _rewriteSession.CheckOutModuleRewriter(qmn); - - public bool TryRewrite() - { - ExecuteCachedRemoveRequests(); - - return _rewriteSession.TryRewrite(); - } - - public string CreatePreview(QualifiedModuleName qmn) - { - ExecuteCachedRemoveRequests(); - - var previewRewriter = _rewriteSession.CheckOutModuleRewriter(qmn); - - return previewRewriter.GetText(maxConsecutiveNewLines: 3); - } - - public void Remove(Declaration target, IModuleRewriter rewriter) - { - var varList = target.Context.GetAncestor(); - if (varList.children.Where(ch => ch is VBAParser.VariableSubStmtContext).Count() == 1) - { - rewriter.Remove(target); - return; - } - - if (!RemovedVariables.ContainsKey(varList)) - { - RemovedVariables.Add(varList, new HashSet()); - } - RemovedVariables[varList].Add(target); - } - - private void ExecuteCachedRemoveRequests() - { - foreach (var key in RemovedVariables.Keys) - { - if (RemovedVariables[key].Count == 0) - { - continue; - } - - var rewriter = RewriteSession.CheckOutModuleRewriter(RemovedVariables[key].First().QualifiedModuleName); - - var variables = key.children.Where(ch => ch is VBAParser.VariableSubStmtContext); - if (variables.Count() == RemovedVariables[key].Count) - { - rewriter.Remove(key.Parent); - } - else - { - foreach (var dec in RemovedVariables[key]) - { - rewriter.Remove(dec); - } - } - } - RemovedVariables = new Dictionary>(); - } - } - } diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingFieldRefactoringAction.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingFieldRefactoringAction.cs new file mode 100644 index 0000000000..df3ccd7113 --- /dev/null +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingFieldRefactoringAction.cs @@ -0,0 +1,77 @@ +using Rubberduck.Parsing.Grammar; +using Rubberduck.Parsing.Rewriter; +using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.VBA; +using Rubberduck.Refactorings.Common; +using Rubberduck.Refactorings.EncapsulateField.Extensions; +using Rubberduck.SmartIndenter; +using System.Linq; + +namespace Rubberduck.Refactorings.EncapsulateField +{ + public class EncapsulateFieldUseBackingFieldRefactoringAction : EncapsulateFieldRefactoringActionImplBase + { + public EncapsulateFieldUseBackingFieldRefactoringAction( + IDeclarationFinderProvider declarationFinderProvider, + IIndenter indenter, + IRewritingManager rewritingManager, + ICodeBuilder codeBuilder) + : base(declarationFinderProvider, indenter, rewritingManager, codeBuilder) + {} + + public override void Refactor(EncapsulateFieldModel model, IRewriteSession rewriteSession) + { + RefactorImpl(model, rewriteSession); + } + + protected override void ModifyFields(IRewriteSession rewriteSession) + { + var fieldsToDeleteAndReplace = SelectedFields.Where(f => IsFieldToDeleteAndReplace(f)); + var rewriter = rewriteSession.CheckOutModuleRewriter(_targetQMN); + + RemoveFields(fieldsToDeleteAndReplace.Select(f => f.Declaration), rewriteSession); + + foreach (var field in SelectedFields.Except(fieldsToDeleteAndReplace)) + { + rewriter.MakeImplicitDeclarationTypeExplicit(field.Declaration); + + if (!field.Declaration.HasPrivateAccessibility()) + { + rewriter.SetVariableVisiblity(field.Declaration, Accessibility.Private.TokenString()); + } + + if (!field.BackingIdentifier.Equals(field.Declaration.IdentifierName)) + { + rewriter.Rename(field.Declaration, field.BackingIdentifier); + } + } + } + + protected override void ModifyReferences(IRewriteSession rewriteSession) + { + foreach (var field in SelectedFields) + { + LoadFieldReferenceContextReplacements(field); + } + + RewriteReferences(rewriteSession); + } + + protected override void LoadNewDeclarationBlocks() + { + //Fields to create here were deleted in ModifyFields(...) + foreach (var field in SelectedFields.Where(f => IsFieldToDeleteAndReplace(f))) + { + var targetIdentifier = field.Declaration.Context.GetText().Replace(field.IdentifierName, field.BackingIdentifier); + var newField = field.Declaration.IsTypeSpecified + ? $"{Tokens.Private} {targetIdentifier}" + : $"{Tokens.Private} {targetIdentifier} {Tokens.As} {field.Declaration.AsTypeName}"; + + AddContentBlock(NewContentType.DeclarationBlock, newField); + } + } + + private static bool IsFieldToDeleteAndReplace(IEncapsulateFieldCandidate field) + => field.Declaration.IsDeclaredInList() && !field.Declaration.HasPrivateAccessibility(); + } +} diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulationStrategies/ConvertFieldsToUDTMembers.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs similarity index 66% rename from Rubberduck.Refactorings/EncapsulateField/EncapsulationStrategies/ConvertFieldsToUDTMembers.cs rename to Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs index 57289c1db1..713c025fd3 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulationStrategies/ConvertFieldsToUDTMembers.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs @@ -1,57 +1,51 @@ -using Antlr4.Runtime; -using Rubberduck.Parsing.Grammar; -using Rubberduck.Parsing.Rewriter; -using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.Rewriter; using Rubberduck.Parsing.VBA; -using Rubberduck.Refactorings.Common; -using Rubberduck.Refactorings.EncapsulateField.Extensions; using Rubberduck.SmartIndenter; -using Rubberduck.VBEditor; -using System; -using System.Collections.Generic; using System.Diagnostics; -using System.IO; using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Rubberduck.Refactorings.EncapsulateField { - public class ConvertFieldsToUDTMembers : EncapsulateFieldStrategyBase + public class EncapsulateFieldUseBackingUDTMemberRefactoringAction : EncapsulateFieldRefactoringActionImplBase { private IObjectStateUDT _stateUDTField; - public ConvertFieldsToUDTMembers(IDeclarationFinderProvider declarationFinderProvider, EncapsulateFieldModel model, IIndenter indenter, ICodeBuilder codeBuilder) - : base(declarationFinderProvider, model, indenter, codeBuilder) + public EncapsulateFieldUseBackingUDTMemberRefactoringAction( + IDeclarationFinderProvider declarationFinderProvider, + IIndenter indenter, + IRewritingManager rewritingManager, + ICodeBuilder codeBuilder) + : base(declarationFinderProvider, indenter, rewritingManager, codeBuilder) + {} + + public override void Refactor(EncapsulateFieldModel model, IRewriteSession rewriteSession) { _stateUDTField = model.ObjectStateUDTField; + + RefactorImpl(model, rewriteSession); } - protected override void ModifyFields(IEncapsulateFieldRewriteSession refactorRewriteSession) + protected override void ModifyFields(IRewriteSession rewriteSession) { - var rewriter = refactorRewriteSession.CheckOutModuleRewriter(_targetQMN); - - foreach (var field in SelectedFields) - { - refactorRewriteSession.Remove(field.Declaration, rewriter); - } + RemoveFields(SelectedFields.Select(sf => sf.Declaration), rewriteSession); if (_stateUDTField.IsExistingDeclaration) { _stateUDTField.AddMembers(SelectedFields.Cast()); + var rewriter = rewriteSession.CheckOutModuleRewriter(_targetQMN); rewriter.Replace(_stateUDTField.AsTypeDeclaration, _stateUDTField.TypeDeclarationBlock(_indenter)); } } - protected override void ModifyReferences(IEncapsulateFieldRewriteSession refactorRewriteSession) + protected override void ModifyReferences(IRewriteSession rewriteSession) { foreach (var field in SelectedFields) { LoadFieldReferenceContextReplacements(field); } - RewriteReferences(refactorRewriteSession); + RewriteReferences(rewriteSession); } protected override void LoadNewDeclarationBlocks() @@ -60,9 +54,9 @@ protected override void LoadNewDeclarationBlocks() _stateUDTField.AddMembers(SelectedFields.Cast()); - AddContentBlock(NewContentTypes.TypeDeclarationBlock, _stateUDTField.TypeDeclarationBlock(_indenter)); + AddContentBlock(NewContentType.TypeDeclarationBlock, _stateUDTField.TypeDeclarationBlock(_indenter)); - AddContentBlock(NewContentTypes.DeclarationBlock, _stateUDTField.FieldDeclarationBlock); + AddContentBlock(NewContentType.DeclarationBlock, _stateUDTField.FieldDeclarationBlock); return; } diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulationStrategies/UseBackingFields.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulationStrategies/UseBackingFields.cs deleted file mode 100644 index b4f5b16dfb..0000000000 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulationStrategies/UseBackingFields.cs +++ /dev/null @@ -1,74 +0,0 @@ -using Antlr4.Runtime; -using Rubberduck.Parsing.Grammar; -using Rubberduck.Parsing.Symbols; -using Rubberduck.Parsing.VBA; -using Rubberduck.Refactorings.Common; -using Rubberduck.Refactorings.EncapsulateField.Extensions; -using Rubberduck.SmartIndenter; -using Rubberduck.VBEditor; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Rubberduck.Refactorings.EncapsulateField -{ - public class UseBackingFields : EncapsulateFieldStrategyBase - { - public UseBackingFields(IDeclarationFinderProvider declarationFinderProvider, EncapsulateFieldModel model, IIndenter indenter, ICodeBuilder codeBuilder) - : base(declarationFinderProvider, model, indenter, codeBuilder) { } - - protected override void ModifyFields(IEncapsulateFieldRewriteSession refactorRewriteSession) - { - var rewriter = refactorRewriteSession.CheckOutModuleRewriter(_targetQMN); - - foreach (var field in SelectedFields) - { - if (field.Declaration.HasPrivateAccessibility() && field.BackingIdentifier.Equals(field.Declaration.IdentifierName)) - { - rewriter.MakeImplicitDeclarationTypeExplicit(field.Declaration); - continue; - } - - if (field.Declaration.IsDeclaredInList() && !field.Declaration.HasPrivateAccessibility()) - { - refactorRewriteSession.Remove(field.Declaration, rewriter); - continue; - } - - rewriter.Rename(field.Declaration, field.BackingIdentifier); - rewriter.SetVariableVisiblity(field.Declaration, Accessibility.Private.TokenString()); - rewriter.MakeImplicitDeclarationTypeExplicit(field.Declaration); - } - } - - protected override void ModifyReferences(IEncapsulateFieldRewriteSession refactorRewriteSession) - { - foreach (var field in SelectedFields) - { - LoadFieldReferenceContextReplacements(field); - } - - RewriteReferences(refactorRewriteSession); - } - - protected override void LoadNewDeclarationBlocks() - { - //New field declarations created here were removed from their list within ModifyFields(...) - var fieldsRequiringNewDeclaration = SelectedFields - .Where(field => field.Declaration.IsDeclaredInList() - && field.Declaration.Accessibility != Accessibility.Private); - - foreach (var field in fieldsRequiringNewDeclaration) - { - var targetIdentifier = field.Declaration.Context.GetText().Replace(field.IdentifierName, field.BackingIdentifier); - var newField = field.Declaration.IsTypeSpecified - ? $"{Tokens.Private} {targetIdentifier}" - : $"{Tokens.Private} {targetIdentifier} {Tokens.As} {field.Declaration.AsTypeName}"; - - AddContentBlock(NewContentTypes.DeclarationBlock, newField); - } - } - } -} diff --git a/Rubberduck.Refactorings/EncapsulateField/Extensions/IModuleRewriterExtensions.cs b/Rubberduck.Refactorings/EncapsulateField/Extensions/IModuleRewriterExtensions.cs index 85020fccdd..8a210bfeab 100644 --- a/Rubberduck.Refactorings/EncapsulateField/Extensions/IModuleRewriterExtensions.cs +++ b/Rubberduck.Refactorings/EncapsulateField/Extensions/IModuleRewriterExtensions.cs @@ -4,24 +4,11 @@ using Rubberduck.Parsing.Symbols; using Rubberduck.Refactorings.Common; using System; -using System.Linq; namespace Rubberduck.Refactorings.EncapsulateField.Extensions { public static class IModuleRewriterExtensions { - public static string GetText(this IModuleRewriter rewriter, int maxConsecutiveNewLines) - { - var result = rewriter.GetText(); - var target = string.Join(string.Empty, Enumerable.Repeat(Environment.NewLine, maxConsecutiveNewLines).ToList()); - var replacement = string.Join(string.Empty, Enumerable.Repeat(Environment.NewLine, maxConsecutiveNewLines - 1).ToList()); - for (var counter = 1; counter < 10 && result.Contains(target); counter++) - { - result = result.Replace(target, replacement); - } - return result; - } - public static void InsertAtEndOfFile(this IModuleRewriter rewriter, string content) { if (content == string.Empty) { return; } diff --git a/Rubberduck.Refactorings/EncapsulateField/Extensions/StringExtensions.cs b/Rubberduck.Refactorings/EncapsulateField/Extensions/StringExtensions.cs index f9ec88c9ff..66c6ac87c4 100644 --- a/Rubberduck.Refactorings/EncapsulateField/Extensions/StringExtensions.cs +++ b/Rubberduck.Refactorings/EncapsulateField/Extensions/StringExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; namespace Rubberduck.Refactorings.EncapsulateField.Extensions { @@ -21,5 +22,19 @@ public static string IncrementEncapsulationIdentifier(this string identifier) } return $"{identifier}_1"; ; } + + public static string LimitNewLines(this string content, int maxConsecutiveNewLines) + { + if (maxConsecutiveNewLines < 2) { throw new ArgumentOutOfRangeException(); } + + var target = string.Concat(Enumerable.Repeat(Environment.NewLine, maxConsecutiveNewLines + 1).ToList()); + var replacement = string.Concat(Enumerable.Repeat(Environment.NewLine, maxConsecutiveNewLines).ToList()); + + for (var counter = 1; counter < 100 && content.Contains(target); counter++) + { + content = content.Replace(target, replacement); + } + return content; + } } } diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidate.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidate.cs index 88cab6b018..ac2704ab5e 100644 --- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidate.cs +++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidate.cs @@ -5,12 +5,23 @@ using Rubberduck.Refactorings.EncapsulateField.Extensions; using Rubberduck.Resources; using Rubberduck.VBEditor; -using Rubberduck.VBEditor.SafeComWrappers; -using System; using System.Collections.Generic; namespace Rubberduck.Refactorings.EncapsulateField { + public struct PropertyAttributeSet + { + public string PropertyName { get; set; } + public string BackingField { get; set; } + public string AsTypeName { get; set; } + public string ParameterName { get; set; } + public bool GenerateLetter { get; set; } + public bool GenerateSetter { get; set; } + public bool UsesSetAssignment { get; set; } + public bool IsUDTProperty { get; set; } + public Declaration Declaration { get; set; } + } + public interface IEncapsulateFieldRefactoringElement { string IdentifierName { get; } diff --git a/RubberduckTests/Commands/RefactorCommands/EncapsulateFieldCommandTests.cs b/RubberduckTests/Commands/RefactorCommands/EncapsulateFieldCommandTests.cs index 2d9a377960..0c180b970b 100644 --- a/RubberduckTests/Commands/RefactorCommands/EncapsulateFieldCommandTests.cs +++ b/RubberduckTests/Commands/RefactorCommands/EncapsulateFieldCommandTests.cs @@ -7,12 +7,14 @@ using Rubberduck.Parsing.VBA; using Rubberduck.Refactorings; using Rubberduck.Refactorings.EncapsulateField; +using Rubberduck.SmartIndenter; using Rubberduck.UI.Command; using Rubberduck.UI.Command.Refactorings; using Rubberduck.UI.Command.Refactorings.Notifiers; using Rubberduck.VBEditor; using Rubberduck.VBEditor.SafeComWrappers.Abstract; using Rubberduck.VBEditor.Utility; +using RubberduckTests.Refactoring.EncapsulateField; namespace RubberduckTests.Commands.RefactorCommands { @@ -56,6 +58,8 @@ Sub Foo() protected override CommandBase TestCommand(IVBE vbe, RubberduckParserState state, IRewritingManager rewritingManager, ISelectionService selectionService) { + var resolver = new EncapsulateFieldTestComponentResolver(state, rewritingManager); + var msgBox = new Mock().Object; var factory = new Mock().Object; var selectedDeclarationProvider = new SelectedDeclarationProvider(selectionService, state); @@ -64,7 +68,7 @@ protected override CommandBase TestCommand(IVBE vbe, RubberduckParserState state .Setup(m => m.Invoke(It.IsAny())) .Callback((Action action) => action.Invoke()); var userInteraction = new RefactoringUserInteraction(factory, uiDispatcherMock.Object); - var refactoring = new EncapsulateFieldRefactoring(state, null, userInteraction, rewritingManager, selectionService, selectedDeclarationProvider, new CodeBuilder()); + var refactoring = new EncapsulateFieldRefactoring(resolver.Resolve(), resolver.Resolve(), state, userInteraction, rewritingManager, selectionService, selectedDeclarationProvider); var notifier = new EncapsulateFieldFailedNotifier(msgBox); var selectedDeclarationService = new SelectedDeclarationProvider(selectionService, state); return new RefactorEncapsulateFieldCommand(refactoring, notifier, state, selectionService, selectedDeclarationService); diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFIeldTestSupport.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFIeldTestSupport.cs index b7878733e0..1ac188c22b 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFIeldTestSupport.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFIeldTestSupport.cs @@ -117,9 +117,9 @@ public string RefactoredCode(CodeString codeString, Func userInteraction, ISelectionService selectionService) { - var indenter = CreateIndenter(); + var resolver = new EncapsulateFieldTestComponentResolver(state, rewritingManager); var selectedDeclarationProvider = new SelectedDeclarationProvider(selectionService, state); - return new EncapsulateFieldRefactoring(state, indenter, userInteraction, rewritingManager, selectionService, selectedDeclarationProvider, new CodeBuilder()); + return new EncapsulateFieldRefactoring(resolver.Resolve(), resolver.Resolve(), state, userInteraction, rewritingManager, selectionService, selectedDeclarationProvider); } public IEncapsulateFieldCandidate RetrieveEncapsulateFieldCandidate(string inputCode, string fieldName) @@ -161,11 +161,6 @@ public EncapsulateFieldModel RetrieveUserModifiedModelPriorToRefactoring(IVBE vb return presenterAdjustment(initialModel); } - public static IIndenter CreateIndenter(IVBE vbe = null) - { - return new Indenter(vbe, () => Settings.IndenterSettingsTests.GetMockIndenterSettings()); - } - protected override IRefactoring TestRefactoring( IRewritingManager rewritingManager, RubberduckParserState state, diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs new file mode 100644 index 0000000000..87ac213fa7 --- /dev/null +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs @@ -0,0 +1,50 @@ +using Rubberduck.Parsing.Rewriter; +using Rubberduck.Parsing.VBA; +using Rubberduck.Refactorings; +using Rubberduck.Refactorings.EncapsulateField; +using Rubberduck.SmartIndenter; +using Rubberduck.VBEditor.SafeComWrappers.Abstract; + +namespace RubberduckTests.Refactoring.EncapsulateField +{ + public class EncapsulateFieldTestComponentResolver + { + private static IDeclarationFinderProvider _declarationFinderProvider; + private static IRewritingManager _rewritingManager; + public EncapsulateFieldTestComponentResolver(IDeclarationFinderProvider declarationFinderProvider, IRewritingManager rewritingManager) + { + _declarationFinderProvider = declarationFinderProvider; + _rewritingManager = rewritingManager; + } + + public T Resolve() where T : class + { + return ResolveImpl(); + } + + private static T ResolveImpl() where T : class + { + switch (typeof(T).Name) + { + case nameof(EncapsulateFieldRefactoringAction): + return new EncapsulateFieldRefactoringAction(ResolveImpl(), ResolveImpl()) as T; + case nameof(EncapsulateFieldUseBackingFieldRefactoringAction): + return new EncapsulateFieldUseBackingFieldRefactoringAction(_declarationFinderProvider, CreateIndenter(), _rewritingManager, new CodeBuilder()) as T; + case nameof(EncapsulateFieldUseBackingUDTMemberRefactoringAction): + return new EncapsulateFieldUseBackingUDTMemberRefactoringAction(_declarationFinderProvider, CreateIndenter(), _rewritingManager, new CodeBuilder()) as T; + case nameof(EncapsulateFieldPreviewProvider): + return new EncapsulateFieldPreviewProvider(ResolveImpl(), ResolveImpl()) as T; + case nameof(EncapsulateFieldUseBackingFieldPreviewProvider): + return new EncapsulateFieldUseBackingFieldPreviewProvider(ResolveImpl(), _rewritingManager) as T; + case nameof(EncapsulateFieldUseBackingUDTMemberPreviewProvider): + return new EncapsulateFieldUseBackingUDTMemberPreviewProvider(ResolveImpl(), _rewritingManager) as T; + } + return null; + } + + private static IIndenter CreateIndenter(IVBE vbe = null) + { + return new Indenter(vbe, () => Settings.IndenterSettingsTests.GetMockIndenterSettings()); + } + } +} From d509ed6c8b339754ffa8c305011ce3a10ce98fb1 Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Fri, 7 Aug 2020 15:04:28 -0700 Subject: [PATCH 02/67] Modify to use IRefactoringPreviewProvider interface Removed PreviewRefactoring method from Model in favor of IRefactoringPreviewProvider property. --- .../Refactorings/EncapsulateField/EncapsulateFieldViewModel.cs | 2 +- .../EncapsulateField/EncapsulateFieldModel.cs | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/Rubberduck.Core/UI/Refactorings/EncapsulateField/EncapsulateFieldViewModel.cs b/Rubberduck.Core/UI/Refactorings/EncapsulateField/EncapsulateFieldViewModel.cs index 5275c21590..4316fc7550 100644 --- a/Rubberduck.Core/UI/Refactorings/EncapsulateField/EncapsulateFieldViewModel.cs +++ b/Rubberduck.Core/UI/Refactorings/EncapsulateField/EncapsulateFieldViewModel.cs @@ -198,7 +198,7 @@ public bool ConvertFieldsToUDTMembers private bool _selectionHasValidEncapsulationAttributes; public bool SelectionHasValidEncapsulationAttributes => _selectionHasValidEncapsulationAttributes; - public string PropertiesPreview => Model.PreviewRefactoring(); + public string PropertiesPreview => Model.PreviewProvider?.Preview(Model) ?? string.Empty; public CommandBase SelectAllCommand { get; } diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs index b990b7efdb..4d9893e95a 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs @@ -44,9 +44,6 @@ public class EncapsulateFieldModel : IRefactoringModel public IRefactoringPreviewProvider PreviewProvider { set; get; } - ////TODO: Remove method from model and have clients use PreviewProvider property directly - public string PreviewRefactoring() => PreviewProvider?.Preview(this) ?? string.Empty; - public IEnumerable ObjectStateUDTCandidates => _objStateCandidates; private EncapsulateFieldStrategy _encapsulationFieldStategy; From ba21c3f6cd96ed38b93283545bec387fbc1464ed Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Sat, 8 Aug 2020 13:27:56 -0700 Subject: [PATCH 03/67] Make Preview function virtual to allow overrides --- .../Abstract/RefactoringPreviewProviderWrapperBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rubberduck.Refactorings/Abstract/RefactoringPreviewProviderWrapperBase.cs b/Rubberduck.Refactorings/Abstract/RefactoringPreviewProviderWrapperBase.cs index 5d4be37753..1ddfbfd46b 100644 --- a/Rubberduck.Refactorings/Abstract/RefactoringPreviewProviderWrapperBase.cs +++ b/Rubberduck.Refactorings/Abstract/RefactoringPreviewProviderWrapperBase.cs @@ -20,7 +20,7 @@ public abstract class RefactoringPreviewProviderWrapperBase : IRefactori protected abstract QualifiedModuleName ComponentToShow(TModel model); - public string Preview(TModel model) + public virtual string Preview(TModel model) { var rewriteSession = RewriteSession(RewriteSessionCodeKind); _refactoringAction.Refactor(model, rewriteSession); From 78ab43583353e32e20c8d5686b36825c76af2722 Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Sat, 8 Aug 2020 13:33:27 -0700 Subject: [PATCH 04/67] Add model flag to insert new content label Currently, adding the label is only used in Previews --- .../EncapsulateField/EncapsulateFieldModel.cs | 2 ++ .../EncapsulateFieldPreviewProvider.cs | 35 +++++++++++++++++-- ...capsulateFieldRefactoringActionImplBase.cs | 31 ++++++++-------- 3 files changed, 50 insertions(+), 18 deletions(-) diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs index 4d9893e95a..ab8f89951b 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs @@ -40,6 +40,8 @@ public class EncapsulateFieldModel : IRefactoringModel _activeObjectStateUDT = ObjectStateUDTField; } + public bool IncludeNewContentMarker { set; get; } = false; + public QualifiedModuleName QualifiedModuleName => _targetQMN; public IRefactoringPreviewProvider PreviewProvider { set; get; } diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldPreviewProvider.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldPreviewProvider.cs index 68636c55ec..12f736adcb 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldPreviewProvider.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldPreviewProvider.cs @@ -1,6 +1,7 @@ using Rubberduck.Parsing.Rewriter; using Rubberduck.Refactorings.EncapsulateField.Extensions; using Rubberduck.VBEditor; +using System; namespace Rubberduck.Refactorings.EncapsulateField { @@ -31,8 +32,23 @@ public class EncapsulateFieldUseBackingFieldPreviewProvider : RefactoringPreview public EncapsulateFieldUseBackingFieldPreviewProvider(EncapsulateFieldUseBackingFieldRefactoringAction refactoringAction, IRewritingManager rewritingManager) : base(refactoringAction, rewritingManager) - { + {} + public override string Preview(EncapsulateFieldModel model) + { + var preview = string.Empty; + var initialFlagValue = model.IncludeNewContentMarker; + model.IncludeNewContentMarker = true; + try + { + preview = base.Preview(model); + } + catch (Exception e) { } + finally + { + model.IncludeNewContentMarker = initialFlagValue; + } + return preview; } protected override QualifiedModuleName ComponentToShow(EncapsulateFieldModel model) @@ -46,8 +62,23 @@ public class EncapsulateFieldUseBackingUDTMemberPreviewProvider : RefactoringPre public EncapsulateFieldUseBackingUDTMemberPreviewProvider(EncapsulateFieldUseBackingUDTMemberRefactoringAction refactoringAction, IRewritingManager rewritingManager) : base(refactoringAction, rewritingManager) - { + {} + public override string Preview(EncapsulateFieldModel model) + { + var preview = string.Empty; + var initialFlagValue = model.IncludeNewContentMarker; + model.IncludeNewContentMarker = true; + try + { + preview = base.Preview(model); + } + catch (Exception e) { } + finally + { + model.IncludeNewContentMarker = initialFlagValue; + } + return preview; } protected override QualifiedModuleName ComponentToShow(EncapsulateFieldModel model) diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringActionImplBase.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringActionImplBase.cs index 8cb0fb63d8..ec304a7ac0 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringActionImplBase.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringActionImplBase.cs @@ -6,6 +6,7 @@ using Rubberduck.Parsing.VBA; using Rubberduck.Refactorings.Common; using Rubberduck.Refactorings.EncapsulateField.Extensions; +using Rubberduck.Resources; using Rubberduck.SmartIndenter; using Rubberduck.VBEditor; using System; @@ -24,7 +25,7 @@ protected enum NewContentType { TypeDeclarationBlock, DeclarationBlock, - MethodBlock, + CodeSectionBlock, PostContentMessage }; @@ -60,7 +61,7 @@ protected IRewriteSession RefactorImpl(EncapsulateFieldModel model, IRewriteSess ModifyReferences(rewriteSession); - InsertNewContent(rewriteSession); + InsertNewContent(rewriteSession, model.IncludeNewContentMarker); return rewriteSession; } @@ -181,16 +182,21 @@ private void AddIdentifierReplacement(IdentifierReference idRef, ParserRuleConte IdentifierReplacements.Add(idRef, (context, replacementText)); } - private void InsertNewContent(IRewriteSession refactorRewriteSession) + private void InsertNewContent(IRewriteSession refactorRewriteSession, bool addNewContentMarker) { _newContent = new Dictionary> { { NewContentType.PostContentMessage, new List() }, { NewContentType.DeclarationBlock, new List() }, - { NewContentType.MethodBlock, new List() }, + { NewContentType.CodeSectionBlock, new List() }, { NewContentType.TypeDeclarationBlock, new List() } }; + if (addNewContentMarker) + { + AddContentBlock(NewContentType.PostContentMessage, RubberduckUI.EncapsulateField_PreviewMarker); + } + LoadNewDeclarationBlocks(); LoadNewPropertyBlocks(); @@ -198,18 +204,11 @@ private void InsertNewContent(IRewriteSession refactorRewriteSession) var newContentBlock = string.Join(_doubleSpace, (_newContent[NewContentType.TypeDeclarationBlock]) .Concat(_newContent[NewContentType.DeclarationBlock]) - .Concat(_newContent[NewContentType.MethodBlock]) + .Concat(_newContent[NewContentType.CodeSectionBlock]) .Concat(_newContent[NewContentType.PostContentMessage])) .Trim(); - var maxConsecutiveNewLines = 3; - var target = string.Join(string.Empty, Enumerable.Repeat(Environment.NewLine, maxConsecutiveNewLines).ToList()); - var replacement = string.Join(string.Empty, Enumerable.Repeat(Environment.NewLine, maxConsecutiveNewLines - 1).ToList()); - for (var counter = 1; counter < 10 && newContentBlock.Contains(target); counter++) - { - newContentBlock = newContentBlock.Replace(target, replacement); - } - + newContentBlock = newContentBlock.LimitNewLines(3); var rewriter = refactorRewriteSession.CheckOutModuleRewriter(_targetQMN); if (_codeSectionStartIndex.HasValue) @@ -282,7 +281,7 @@ private void AddPropertyCodeBlocks(PropertyAttributeSet propertyAttributes) { throw new ArgumentException(); } - AddContentBlock(NewContentType.MethodBlock, propertyGet); + AddContentBlock(NewContentType.CodeSectionBlock, propertyGet); if (!(propertyAttributes.GenerateLetter || propertyAttributes.GenerateSetter)) { @@ -295,7 +294,7 @@ private void AddPropertyCodeBlocks(PropertyAttributeSet propertyAttributes) { throw new ArgumentException(); } - AddContentBlock(NewContentType.MethodBlock, propertyLet); + AddContentBlock(NewContentType.CodeSectionBlock, propertyLet); } if (propertyAttributes.GenerateSetter) @@ -304,7 +303,7 @@ private void AddPropertyCodeBlocks(PropertyAttributeSet propertyAttributes) { throw new ArgumentException(); } - AddContentBlock(NewContentType.MethodBlock, propertySet); + AddContentBlock(NewContentType.CodeSectionBlock, propertySet); } } } From e122aecb911cc7034d10a61c6ec4320c8193e91a Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Sun, 16 Aug 2020 13:15:25 -0700 Subject: [PATCH 05/67] Remove used-once extension methods Removes the EncapsulateField-specific IModuleRewriterExtensions file. Includes some formatting changes as well. --- ...capsulateFieldRefactoringActionImplBase.cs | 86 ++++++++----------- ...teFieldUseBackingFieldRefactoringAction.cs | 77 ++++++++++++----- ...eldUseBackingUDTMemberRefactoringAction.cs | 20 ++--- .../Extensions/IModuleRewriterExtensions.cs | 52 ----------- .../Extensions/StringExtensions.cs | 5 +- 5 files changed, 102 insertions(+), 138 deletions(-) delete mode 100644 Rubberduck.Refactorings/EncapsulateField/Extensions/IModuleRewriterExtensions.cs diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringActionImplBase.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringActionImplBase.cs index 6b35866908..c6646d8382 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringActionImplBase.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringActionImplBase.cs @@ -40,11 +40,11 @@ protected enum NewContentType protected Dictionary IdentifierReplacements { get; } = new Dictionary(); public EncapsulateFieldRefactoringActionImplBase( - IDeclarationFinderProvider declarationFinderProvider, - IIndenter indenter, - IRewritingManager rewritingManager, - ICodeBuilder codeBuilder) - : base(rewritingManager) + IDeclarationFinderProvider declarationFinderProvider, + IIndenter indenter, + IRewritingManager rewritingManager, + ICodeBuilder codeBuilder) + : base(rewritingManager) { _declarationFinderProvider = declarationFinderProvider; _indenter = indenter; @@ -68,8 +68,6 @@ protected IRewriteSession RefactorImpl(EncapsulateFieldModel model, IRewriteSess protected abstract void ModifyFields(IRewriteSession rewriteSession); - protected abstract void ModifyReferences(IRewriteSession refactorRewriteSession); - protected abstract void LoadNewDeclarationBlocks(); protected void RewriteReferences(IRewriteSession rewriteSession) @@ -85,6 +83,16 @@ protected void RewriteReferences(IRewriteSession rewriteSession) protected void AddContentBlock(NewContentType contentType, string block) => _newContent[contentType].Add(block); + protected void ModifyReferences(IRewriteSession rewriteSession) + { + foreach (var field in SelectedFields) + { + LoadFieldReferenceContextReplacements(field); + } + + RewriteReferences(rewriteSession); + } + protected virtual void LoadFieldReferenceContextReplacements(IEncapsulateFieldCandidate field) { if (field is IUserDefinedTypeCandidate udt && udt.TypeDeclarationIsPrivate) @@ -115,12 +123,12 @@ protected virtual void LoadFieldReferenceContextReplacements(IEncapsulateFieldCa protected bool IsExternalReferenceRequiringModuleQualification(IdentifierReference idRef) { var isLHSOfMemberAccess = - (idRef.Context.Parent is VBAParser.MemberAccessExprContext - || idRef.Context.Parent is VBAParser.WithMemberAccessExprContext) - && !(idRef.Context == idRef.Context.Parent.GetChild(0)); + (idRef.Context.Parent is VBAParser.MemberAccessExprContext + || idRef.Context.Parent is VBAParser.WithMemberAccessExprContext) + && !(idRef.Context == idRef.Context.Parent.GetChild(0)); return idRef.QualifiedModuleName != idRef.Declaration.QualifiedModuleName - && !isLHSOfMemberAccess; + && !isLHSOfMemberAccess; } protected virtual void SetReferenceRewriteContent(IdentifierReference idRef, string replacementText) @@ -202,14 +210,19 @@ private void InsertNewContent(IRewriteSession refactorRewriteSession, bool addNe LoadNewPropertyBlocks(); var newContentBlock = string.Join(_doubleSpace, - (_newContent[NewContentType.TypeDeclarationBlock]) - .Concat(_newContent[NewContentType.DeclarationBlock]) - .Concat(_newContent[NewContentType.CodeSectionBlock]) - .Concat(_newContent[NewContentType.PostContentMessage])) - .Trim(); + (_newContent[NewContentType.TypeDeclarationBlock]) + .Concat(_newContent[NewContentType.DeclarationBlock]) + .Concat(_newContent[NewContentType.CodeSectionBlock]) + .Concat(_newContent[NewContentType.PostContentMessage])) + .Trim(); newContentBlock = newContentBlock.LimitNewlines(3); + if (string.IsNullOrEmpty(newContentBlock)) + { + return; + } + var rewriter = refactorRewriteSession.CheckOutModuleRewriter(_targetQMN); if (_codeSectionStartIndex.HasValue) { @@ -217,7 +230,7 @@ private void InsertNewContent(IRewriteSession refactorRewriteSession, bool addNe } else { - rewriter.InsertAtEndOfFile($"{_doubleSpace}{newContentBlock}"); + rewriter.InsertBefore(rewriter.TokenStream.Size - 1, $"{_doubleSpace}{newContentBlock}"); } } @@ -228,33 +241,6 @@ protected void LoadNewPropertyBlocks() AddPropertyCodeBlocks(propertyAttributes); } } - /// - /// RemoveFields handles the special case of field declaration removal where - /// each field of a VariableListStmtContext is specified for removal. In this - /// special case the Parent context is removed rather than the individual declarations. - /// - protected static void RemoveFields(IEnumerable toRemove, IRewriteSession rewriteSession) - { - if (!toRemove.Any()) { return; } - - var fieldsByListContext = toRemove.Distinct().GroupBy(f => f.Context.GetAncestor()); - - var rewriter = rewriteSession.CheckOutModuleRewriter(toRemove.First().QualifiedModuleName); - foreach (var fieldsGroup in fieldsByListContext) - { - var variables = fieldsGroup.Key.children.Where(ch => ch is VBAParser.VariableSubStmtContext); - if (variables.Count() == fieldsGroup.Count()) - { - rewriter.Remove(fieldsGroup.Key.Parent); - continue; - } - - foreach (var target in fieldsGroup) - { - rewriter.Remove(target); - } - } - } private void AddPropertyCodeBlocks(PropertyAttributeSet propertyAttributes) { @@ -269,12 +255,12 @@ private void AddPropertyCodeBlocks(PropertyAttributeSet propertyAttributes) if (propertyAttributes.AsTypeName.Equals(Tokens.Variant) && !propertyAttributes.Declaration.IsArray) { getContent = string.Join(Environment.NewLine, - $"{Tokens.If} IsObject({propertyAttributes.BackingField}) {Tokens.Then}", - $"{_defaultIndent}{Tokens.Set} {propertyAttributes.PropertyName} = {propertyAttributes.BackingField}", - Tokens.Else, - $"{_defaultIndent}{propertyAttributes.PropertyName} = {propertyAttributes.BackingField}", - $"{Tokens.End} {Tokens.If}", - Environment.NewLine); + $"{Tokens.If} IsObject({propertyAttributes.BackingField}) {Tokens.Then}", + $"{_defaultIndent}{Tokens.Set} {propertyAttributes.PropertyName} = {propertyAttributes.BackingField}", + Tokens.Else, + $"{_defaultIndent}{propertyAttributes.PropertyName} = {propertyAttributes.BackingField}", + $"{Tokens.End} {Tokens.If}", + Environment.NewLine); } if (!_codeBuilder.TryBuildPropertyGetCodeBlock(propertyAttributes.Declaration, propertyAttributes.PropertyName, out var propertyGet, content: $"{_defaultIndent}{getContent}")) diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingFieldRefactoringAction.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingFieldRefactoringAction.cs index df3ccd7113..5cf95823fd 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingFieldRefactoringAction.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingFieldRefactoringAction.cs @@ -1,10 +1,12 @@ -using Rubberduck.Parsing.Grammar; +using Rubberduck.Parsing; +using Rubberduck.Parsing.Grammar; using Rubberduck.Parsing.Rewriter; using Rubberduck.Parsing.Symbols; using Rubberduck.Parsing.VBA; using Rubberduck.Refactorings.Common; -using Rubberduck.Refactorings.EncapsulateField.Extensions; using Rubberduck.SmartIndenter; +using System; +using System.Collections.Generic; using System.Linq; namespace Rubberduck.Refactorings.EncapsulateField @@ -12,11 +14,11 @@ namespace Rubberduck.Refactorings.EncapsulateField public class EncapsulateFieldUseBackingFieldRefactoringAction : EncapsulateFieldRefactoringActionImplBase { public EncapsulateFieldUseBackingFieldRefactoringAction( - IDeclarationFinderProvider declarationFinderProvider, - IIndenter indenter, - IRewritingManager rewritingManager, - ICodeBuilder codeBuilder) - : base(declarationFinderProvider, indenter, rewritingManager, codeBuilder) + IDeclarationFinderProvider declarationFinderProvider, + IIndenter indenter, + IRewritingManager rewritingManager, + ICodeBuilder codeBuilder) + : base(declarationFinderProvider, indenter, rewritingManager, codeBuilder) {} public override void Refactor(EncapsulateFieldModel model, IRewriteSession rewriteSession) @@ -26,35 +28,70 @@ public override void Refactor(EncapsulateFieldModel model, IRewriteSession rewri protected override void ModifyFields(IRewriteSession rewriteSession) { - var fieldsToDeleteAndReplace = SelectedFields.Where(f => IsFieldToDeleteAndReplace(f)); + var fieldDeclarationsToDeleteAndReplace = SelectedFields.Where(f => IsFieldToDeleteAndReplace(f)); var rewriter = rewriteSession.CheckOutModuleRewriter(_targetQMN); - RemoveFields(fieldsToDeleteAndReplace.Select(f => f.Declaration), rewriteSession); + rewriter.RemoveVariables(fieldDeclarationsToDeleteAndReplace.Select(f => f.Declaration).Cast()); - foreach (var field in SelectedFields.Except(fieldsToDeleteAndReplace)) + var fieldDeclaraionsToRetain = SelectedFields.Except(fieldDeclarationsToDeleteAndReplace).ToList(); + + if (fieldDeclaraionsToRetain.Any()) { - rewriter.MakeImplicitDeclarationTypeExplicit(field.Declaration); + MakeImplicitDeclarationTypeExplicit(fieldDeclaraionsToRetain, rewriter); + + + SetPrivateVariableVisiblity(fieldDeclaraionsToRetain, rewriter); + + Rename(fieldDeclaraionsToRetain, rewriter); + } + } - if (!field.Declaration.HasPrivateAccessibility()) + private static void MakeImplicitDeclarationTypeExplicit(IEnumerable fields, IModuleRewriter rewriter) + { + foreach (var element in fields.Select(f => f.Declaration)) + { + if (!element.Context.TryGetChildContext(out _)) { - rewriter.SetVariableVisiblity(field.Declaration, Accessibility.Private.TokenString()); + rewriter.InsertAfter(element.Context.Stop.TokenIndex, $" {Tokens.As} {element.AsTypeName}"); } + } + } - if (!field.BackingIdentifier.Equals(field.Declaration.IdentifierName)) + private static void SetPrivateVariableVisiblity(IEnumerable fields, IModuleRewriter rewriter) + { + var visibility = Accessibility.Private.TokenString(); + foreach (var element in fields.Where(f => !f.Declaration.HasPrivateAccessibility()).Select(f => f.Declaration)) + { + if (!element.IsVariable()) + { + throw new ArgumentException(); + } + + var variableStmtContext = element.Context.GetAncestor(); + var visibilityContext = variableStmtContext.GetChild(); + + if (visibilityContext != null) { - rewriter.Rename(field.Declaration, field.BackingIdentifier); + rewriter.Replace(visibilityContext, visibility); + continue; } + rewriter.InsertBefore(element.Context.Start.TokenIndex, $"{visibility} "); } } - protected override void ModifyReferences(IRewriteSession rewriteSession) + private static void Rename(IEnumerable fields, IModuleRewriter rewriter) { - foreach (var field in SelectedFields) + var fieldsToRename = fields.Where(f => !f.BackingIdentifier.Equals(f.Declaration.IdentifierName)); + + foreach (var field in fieldsToRename) { - LoadFieldReferenceContextReplacements(field); - } + if (!(field.Declaration.Context is IIdentifierContext context)) + { + throw new ArgumentException(); + } - RewriteReferences(rewriteSession); + rewriter.Replace(context.IdentifierTokens, field.BackingIdentifier); + } } protected override void LoadNewDeclarationBlocks() diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs index db9ea690fd..80ed2672c9 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs @@ -13,11 +13,11 @@ public class EncapsulateFieldUseBackingUDTMemberRefactoringAction : EncapsulateF private IObjectStateUDT _stateUDTField; public EncapsulateFieldUseBackingUDTMemberRefactoringAction( - IDeclarationFinderProvider declarationFinderProvider, - IIndenter indenter, - IRewritingManager rewritingManager, - ICodeBuilder codeBuilder) - : base(declarationFinderProvider, indenter, rewritingManager, codeBuilder) + IDeclarationFinderProvider declarationFinderProvider, + IIndenter indenter, + IRewritingManager rewritingManager, + ICodeBuilder codeBuilder) + : base(declarationFinderProvider, indenter, rewritingManager, codeBuilder) {} public override void Refactor(EncapsulateFieldModel model, IRewriteSession rewriteSession) @@ -42,16 +42,6 @@ protected override void ModifyFields(IRewriteSession refactorRewriteSession) } } - protected override void ModifyReferences(IRewriteSession rewriteSession) - { - foreach (var field in SelectedFields) - { - LoadFieldReferenceContextReplacements(field); - } - - RewriteReferences(rewriteSession); - } - protected override void LoadNewDeclarationBlocks() { if (_stateUDTField.IsExistingDeclaration) { return; } diff --git a/Rubberduck.Refactorings/EncapsulateField/Extensions/IModuleRewriterExtensions.cs b/Rubberduck.Refactorings/EncapsulateField/Extensions/IModuleRewriterExtensions.cs deleted file mode 100644 index 8a210bfeab..0000000000 --- a/Rubberduck.Refactorings/EncapsulateField/Extensions/IModuleRewriterExtensions.cs +++ /dev/null @@ -1,52 +0,0 @@ -using Rubberduck.Parsing; -using Rubberduck.Parsing.Grammar; -using Rubberduck.Parsing.Rewriter; -using Rubberduck.Parsing.Symbols; -using Rubberduck.Refactorings.Common; -using System; - -namespace Rubberduck.Refactorings.EncapsulateField.Extensions -{ - public static class IModuleRewriterExtensions - { - public static void InsertAtEndOfFile(this IModuleRewriter rewriter, string content) - { - if (content == string.Empty) { return; } - - rewriter.InsertBefore(rewriter.TokenStream.Size - 1, content); - } - - public static void MakeImplicitDeclarationTypeExplicit(this IModuleRewriter rewriter, Declaration element) - { - if (!element.Context.TryGetChildContext(out _)) - { - rewriter.InsertAfter(element.Context.Stop.TokenIndex, $" {Tokens.As} {element.AsTypeName}"); - } - } - - public static void Rename(this IModuleRewriter rewriter, Declaration target, string newName) - { - if (!(target.Context is IIdentifierContext context)) - { - throw new ArgumentException(); - } - - rewriter.Replace(context.IdentifierTokens, newName); - } - - public static void SetVariableVisiblity(this IModuleRewriter rewriter, Declaration element, string visibility) - { - if (!element.IsVariable()) { throw new ArgumentException(); } - - var variableStmtContext = element.Context.GetAncestor(); - var visibilityContext = variableStmtContext.GetChild(); - - if (visibilityContext != null) - { - rewriter.Replace(visibilityContext, visibility); - return; - } - rewriter.InsertBefore(element.Context.Start.TokenIndex, $"{visibility} "); - } - } -} diff --git a/Rubberduck.Refactorings/EncapsulateField/Extensions/StringExtensions.cs b/Rubberduck.Refactorings/EncapsulateField/Extensions/StringExtensions.cs index 31a2a18aa2..27c38dbb4d 100644 --- a/Rubberduck.Refactorings/EncapsulateField/Extensions/StringExtensions.cs +++ b/Rubberduck.Refactorings/EncapsulateField/Extensions/StringExtensions.cs @@ -11,7 +11,10 @@ public static bool IsEquivalentVBAIdentifierTo(this string lhs, string identifie public static string IncrementEncapsulationIdentifier(this string identifier) { var fragments = identifier.Split('_'); - if (fragments.Length == 1) { return $"{identifier}_1"; } + if (fragments.Length == 1) + { + return $"{identifier}_1"; + } var lastFragment = fragments[fragments.Length - 1]; if (long.TryParse(lastFragment, out var number)) From 2190c108c1839166e060a1712068597ccb651097 Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Wed, 19 Aug 2020 14:31:01 -0700 Subject: [PATCH 06/67] Add UserDefinedType declaration block capability Creates UDT text block based upon VariableDeclarations --- Rubberduck.Refactorings/Common/CodeBuilder.cs | 172 ++++++++++------- RubberduckTests/CodeBuilderTests.cs | 180 +++++++++++++----- 2 files changed, 240 insertions(+), 112 deletions(-) diff --git a/Rubberduck.Refactorings/Common/CodeBuilder.cs b/Rubberduck.Refactorings/Common/CodeBuilder.cs index c8e7932b0e..9d9991a1c6 100644 --- a/Rubberduck.Refactorings/Common/CodeBuilder.cs +++ b/Rubberduck.Refactorings/Common/CodeBuilder.cs @@ -1,4 +1,5 @@ using Rubberduck.Common; +using Rubberduck.Parsing; using Rubberduck.Parsing.Grammar; using Rubberduck.Parsing.Symbols; using System; @@ -20,9 +21,9 @@ public interface ICodeBuilder /// /// Main body content/logic of the member string BuildMemberBlockFromPrototype(ModuleBodyElementDeclaration declaration, - string content = null, - string accessibility = null, - string newIdentifier = null); + string content = null, + string accessibility = null, + string newIdentifier = null); /// /// Returns the argument list for the input ModuleBodyElementDeclaration with the following improvements: @@ -38,10 +39,10 @@ public interface ICodeBuilder /// Member body content. Formatting is the responsibility of the caller /// Defaults to 'Value' unless otherwise specified bool TryBuildPropertyGetCodeBlock(Declaration prototype, - string propertyIdentifier, - out string codeBlock, - string accessibility = null, - string content = null); + string propertyIdentifier, + out string codeBlock, + string accessibility = null, + string content = null); /// /// Generates a Property Let codeblock based on the prototype declaration @@ -50,11 +51,11 @@ public interface ICodeBuilder /// Member body content. Formatting is the responsibility of the caller /// Defaults to 'Value' unless otherwise specified bool TryBuildPropertyLetCodeBlock(Declaration prototype, - string propertyIdentifier, - out string codeBlock, - string accessibility = null, - string content = null, - string parameterIdentifier = null); + string propertyIdentifier, + out string codeBlock, + string accessibility = null, + string content = null, + string parameterIdentifier = null); /// /// Generates a Property Set codeblock based on the prototype declaration @@ -63,16 +64,29 @@ public interface ICodeBuilder /// Member body content. Formatting is the responsibility of the caller /// Defaults to 'Value' unless otherwise specified bool TryBuildPropertySetCodeBlock(Declaration prototype, - string propertyIdentifier, - out string codeBlock, - string accessibility = null, - string content = null, - string parameterIdentifier = null); + string propertyIdentifier, + out string codeBlock, + string accessibility = null, + string content = null, + string parameterIdentifier = null); + /// /// Generates a default RHS property parameter IdentifierName /// /// Let/Set Property IdentifierName string BuildPropertyRhsParameterName(string propertyIdentifier); + + /// + /// Generates a UserDefinedType (UDT) declaration using a VariableDeclaration as the prototype for + /// creating the UserDefinedTypeMember. + /// + /// At least one VariableDeclaration must be provided and + /// all UDTMemberIdentifiers must be unique + /// + /// Collection of prototypes and their required identifier. Must have 1 or more elements + string BuildUserDefinedTypeDeclaration(string udtIdentifier, IEnumerable<(VariableDeclaration Field, string UDTMemberIdentifier)> memberPrototypes, Accessibility accessibility = Accessibility.Private); + + string UDTMemberDeclaration(string identifier, string typeName, string indention = null); } public class CodeBuilder : ICodeBuilder @@ -81,9 +95,9 @@ public string BuildPropertyRhsParameterName(string propertyIdentifier) => string.Format(Resources.Refactorings.Refactorings.CodeBuilder_DefaultPropertyRHSParamFormat, propertyIdentifier.ToLowerCaseFirstLetter()); public string BuildMemberBlockFromPrototype(ModuleBodyElementDeclaration declaration, - string content = null, - string accessibility = null, - string newIdentifier = null) + string content = null, + string accessibility = null, + string newIdentifier = null) { var elements = new List() @@ -91,7 +105,7 @@ public string BuildPropertyRhsParameterName(string propertyIdentifier) ImprovedFullMemberSignatureInternal(declaration, accessibility, newIdentifier), Environment.NewLine, string.IsNullOrEmpty(content) ? null : $"{content}{Environment.NewLine}", - ProcedureEndStatement(declaration.DeclarationType), + EndStatement(declaration.DeclarationType), Environment.NewLine, }; return string.Concat(elements); @@ -124,8 +138,8 @@ public bool TryBuildPropertySetCodeBlock(Declaration prototype, string propertyI var asType = prototype.IsArray ? $"{Tokens.Variant}" : IsEnumField(prototype) && prototype.AsTypeDeclaration.Accessibility.Equals(Accessibility.Private) - ? $"{Tokens.Long}" - : $"{prototype.AsTypeName}"; + ? $"{Tokens.Long}" + : $"{prototype.AsTypeName}"; var asTypeClause = $"{Tokens.As} {asType}"; @@ -134,8 +148,8 @@ public bool TryBuildPropertySetCodeBlock(Declaration prototype, string propertyI var letSetParamExpression = $"{paramMechanism} {propertyValueParam} {asTypeClause}"; codeBlock = letSetGetType.HasFlag(DeclarationType.PropertyGet) - ? string.Join(Environment.NewLine, $"{accessibility ?? Tokens.Public} {ProcedureTypeStatement(letSetGetType)} {propertyIdentifier}() {asTypeClause}", content, ProcedureEndStatement(letSetGetType)) - : string.Join(Environment.NewLine, $"{accessibility ?? Tokens.Public} {ProcedureTypeStatement(letSetGetType)} {propertyIdentifier}({letSetParamExpression})", content, ProcedureEndStatement(letSetGetType)); + ? string.Join(Environment.NewLine, $"{accessibility ?? Tokens.Public} {TypeToken(letSetGetType)} {propertyIdentifier}() {asTypeClause}", content, EndStatement(letSetGetType)) + : string.Join(Environment.NewLine, $"{accessibility ?? Tokens.Public} {TypeToken(letSetGetType)} {propertyIdentifier}({letSetParamExpression})", content, EndStatement(letSetGetType)); return true; } @@ -145,17 +159,17 @@ public string ImprovedFullMemberSignature(ModuleBodyElementDeclaration declarati private string ImprovedFullMemberSignatureInternal(ModuleBodyElementDeclaration declaration, string accessibility = null, string newIdentifier = null) { var accessibilityToken = declaration.Accessibility.Equals(Accessibility.Implicit) - ? Tokens.Public - : $"{declaration.Accessibility.ToString()}"; + ? Tokens.Public + : $"{declaration.Accessibility.ToString()}"; var asTypeName = string.IsNullOrEmpty(declaration.AsTypeName) - ? string.Empty - : $" {Tokens.As} {declaration.AsTypeName}"; - + ? string.Empty + : $" {Tokens.As} {declaration.AsTypeName}"; + var elements = new List() { accessibility ?? accessibilityToken, - $" {ProcedureTypeStatement(declaration.DeclarationType)} ", + $" {TypeToken(declaration.DeclarationType)} ", newIdentifier ?? declaration.IdentifierName, $"({ImprovedArgumentList(declaration)})", asTypeName @@ -172,10 +186,10 @@ public string ImprovedArgumentList(ModuleBodyElementDeclaration declaration) arguments = parameterizedDeclaration.Parameters .OrderBy(parameter => parameter.Selection) .Select(parameter => BuildParameterDeclaration( - parameter, - parameter.Equals(parameterizedDeclaration.Parameters.LastOrDefault()) - && declaration.DeclarationType.HasFlag(DeclarationType.Property) - && !declaration.DeclarationType.Equals(DeclarationType.PropertyGet))); + parameter, + parameter.Equals(parameterizedDeclaration.Parameters.LastOrDefault()) + && declaration.DeclarationType.HasFlag(DeclarationType.Property) + && !declaration.DeclarationType.Equals(DeclarationType.PropertyGet))); } return $"{string.Join(", ", arguments)}"; } @@ -191,8 +205,8 @@ private static string BuildParameterDeclaration(ParameterDeclaration parameter, : parameter.IsByRef ? Tokens.ByRef : Tokens.ByVal; if (forceExplicitByValAccess - && (string.IsNullOrEmpty(paramMechanism) || paramMechanism.Equals(Tokens.ByRef)) - && !IsUserDefinedType(parameter)) + && (string.IsNullOrEmpty(paramMechanism) || paramMechanism.Equals(Tokens.ByRef)) + && !IsUserDefinedType(parameter)) { paramMechanism = Tokens.ByVal; } @@ -214,50 +228,72 @@ private static string BuildParameterDeclaration(ParameterDeclaration parameter, } private static string FormatOptionalElement(string element) - => string.IsNullOrEmpty(element) ? string.Empty : $"{element} "; + => string.IsNullOrEmpty(element) ? string.Empty : $"{element} "; private static string FormatAsTypeName(string AsTypeName) - => string.IsNullOrEmpty(AsTypeName) ? string.Empty : $"As {AsTypeName} "; + => string.IsNullOrEmpty(AsTypeName) ? string.Empty : $"As {AsTypeName} "; private static string FormatDefaultValue(string DefaultValue) - => string.IsNullOrEmpty(DefaultValue) ? string.Empty : $"= {DefaultValue}"; + => string.IsNullOrEmpty(DefaultValue) ? string.Empty : $"= {DefaultValue}"; + + private static Dictionary _declarationTypeTokens + = new Dictionary() + { + [DeclarationType.Function] = (Tokens.Function, $"{Tokens.End} {Tokens.Function}"), + [DeclarationType.Procedure] = (Tokens.Sub, $"{Tokens.End} {Tokens.Sub}"), + [DeclarationType.PropertyGet] = ($"{Tokens.Property} {Tokens.Get}", $"{Tokens.End} {Tokens.Property}"), + [DeclarationType.PropertyLet] = ($"{Tokens.Property} {Tokens.Let}", $"{Tokens.End} {Tokens.Property}"), + [DeclarationType.PropertySet] = ($"{Tokens.Property} {Tokens.Set}", $"{Tokens.End} {Tokens.Property}"), + }; + + private static string EndStatement(DeclarationType declarationType) + => _declarationTypeTokens[declarationType].EndStatement; + + private static string TypeToken(DeclarationType declarationType) + => _declarationTypeTokens[declarationType].TypeToken; - private static string ProcedureEndStatement(DeclarationType declarationType) + public string BuildUserDefinedTypeDeclaration(string udtIdentifier, IEnumerable<(VariableDeclaration Field, string UDTMemberIdentifier)> memberPrototypes, Accessibility accessibility = Accessibility.Private) { - switch (declarationType) + if (!memberPrototypes.Any()) { - case DeclarationType.Function: - return $"{Tokens.End} {Tokens.Function}"; - case DeclarationType.Procedure: - return $"{Tokens.End} {Tokens.Sub}"; - case DeclarationType.PropertyGet: - case DeclarationType.PropertyLet: - case DeclarationType.PropertySet: - return $"{Tokens.End} {Tokens.Property}"; - default: - throw new ArgumentException(); + throw new ArgumentOutOfRangeException(); } + + var hasDuplicateMemberNames = memberPrototypes.Select(pr => pr.UDTMemberIdentifier.ToUpperInvariant()) + .GroupBy(uc => uc).Any(g => g.Count() > 1); + if (hasDuplicateMemberNames) + { + throw new ArgumentException(); + } + + var newMemberTokenPairs = memberPrototypes.Select(m => (GetDeclarationIdentifier(m.Field, m.UDTMemberIdentifier), m.Field.AsTypeName)) + .Cast<(string Identifier, string AsTypeName)>(); + + var blockLines = new List(); + + blockLines.Add($"{accessibility.TokenString()} {Tokens.Type} {udtIdentifier}"); + + blockLines.AddRange(newMemberTokenPairs.Select(m => UDTMemberDeclaration(m.Identifier, m.AsTypeName))); + + blockLines.Add($"{Tokens.End} {Tokens.Type}"); + + return string.Join(Environment.NewLine, blockLines); } - private static string ProcedureTypeStatement(DeclarationType declarationType) + private static string GetDeclarationIdentifier(Declaration field, string udtMemberIdentifier) { - switch (declarationType) + if (field.IsArray) { - case DeclarationType.Function: - return Tokens.Function; - case DeclarationType.Procedure: - return Tokens.Sub; - case DeclarationType.PropertyGet: - return $"{Tokens.Property} {Tokens.Get}"; - case DeclarationType.PropertyLet: - return $"{Tokens.Property} {Tokens.Let}"; - case DeclarationType.PropertySet: - return $"{Tokens.Property} {Tokens.Set}"; - default: - throw new ArgumentException(); + return field.Context.TryGetChildContext(out var ctxt) + ? $"{udtMemberIdentifier}({ctxt.GetText()})" + : $"{udtMemberIdentifier}()"; } + return udtMemberIdentifier; } + public string UDTMemberDeclaration(string identifier, string typeName, string indention = null) + => $"{indention ?? " "}{identifier} {Tokens.As} {typeName}"; + private static bool IsEnumField(VariableDeclaration declaration) => IsMemberVariable(declaration) && (declaration.AsTypeDeclaration?.DeclarationType.Equals(DeclarationType.Enumeration) ?? false); @@ -267,10 +303,10 @@ private static bool IsEnumField(Declaration declaration) && (declaration.AsTypeDeclaration?.DeclarationType.Equals(DeclarationType.Enumeration) ?? false); private static bool IsUserDefinedType(Declaration declaration) - => (declaration.AsTypeDeclaration?.DeclarationType.Equals(DeclarationType.UserDefinedType) ?? false); + => (declaration.AsTypeDeclaration?.DeclarationType.Equals(DeclarationType.UserDefinedType) ?? false); private static bool IsMemberVariable(Declaration declaration) => declaration.DeclarationType.HasFlag(DeclarationType.Variable) - && !declaration.ParentDeclaration.DeclarationType.HasFlag(DeclarationType.Member); + && !declaration.ParentDeclaration.DeclarationType.HasFlag(DeclarationType.Member); } } diff --git a/RubberduckTests/CodeBuilderTests.cs b/RubberduckTests/CodeBuilderTests.cs index a4f32ada81..619a91a50e 100644 --- a/RubberduckTests/CodeBuilderTests.cs +++ b/RubberduckTests/CodeBuilderTests.cs @@ -4,6 +4,7 @@ using Rubberduck.Refactorings; using RubberduckTests.Mocks; using System; +using System.Collections.Generic; using System.Linq; namespace RubberduckTests @@ -46,10 +47,10 @@ End Enum Private fuzz As ETestType2 "; var result = ParseAndTest(inputCode, - targetIdentifier, - declarationType, - testParams, - PropertyGetBlockFromPrototypeTest); + targetIdentifier, + declarationType, + testParams, + PropertyGetBlockFromPrototypeTest); StringAssert.Contains($"Property Get {testParams.Identifier}() As {typeName}", result); } @@ -87,10 +88,10 @@ End Enum Private fuzz As ETestType2 "; var result = ParseAndTest(inputCode, - targetIdentifier, - declarationType, - testParams, - PropertyGetBlockFromPrototypeTest); + targetIdentifier, + declarationType, + testParams, + PropertyGetBlockFromPrototypeTest); StringAssert.Contains($"{accessibility} Property Get {testParams.Identifier}() As {typeName}", result); } @@ -130,15 +131,14 @@ End Enum Private fuzz As TTestType2 "; var result = ParseAndTest(inputCode, - targetIdentifier, - declarationType, - testParams, - PropertyGetBlockFromPrototypeTest); + targetIdentifier, + declarationType, + testParams, + PropertyGetBlockFromPrototypeTest); StringAssert.Contains(content, result); } - [TestCase("fizz", DeclarationType.Variable, "Integer", "Bazz = fizz")] [Category(nameof(CodeBuilder))] public void PropertyBlockFromPrototype_PropertyGetChangeParamName(string targetIdentifier, DeclarationType declarationType, string typeName, string content) @@ -149,10 +149,10 @@ public void PropertyBlockFromPrototype_PropertyGetChangeParamName(string targetI Private fizz As Integer "; var result = ParseAndTest(inputCode, - targetIdentifier, - declarationType, - testParams, - PropertyGetBlockFromPrototypeTest); + targetIdentifier, + declarationType, + testParams, + PropertyGetBlockFromPrototypeTest); StringAssert.Contains("Property Get Bazz() As Integer", result); } @@ -190,10 +190,10 @@ End Enum Private fuzz As ETestType2 "; var result = ParseAndTest(inputCode, - targetIdentifier, - declarationType, - testParams, - PropertyLetBlockFromPrototypeTest); + targetIdentifier, + declarationType, + testParams, + PropertyLetBlockFromPrototypeTest); StringAssert.Contains($"Property Let {testParams.Identifier}(ByVal {Param(testParams.Identifier)} As {typeName})", result); } @@ -215,10 +215,10 @@ End Type "; var result = ParseAndTest(inputCode, - targetIdentifier, - declarationType, - testParams, - PropertySetBlockFromPrototypeTest); + targetIdentifier, + declarationType, + testParams, + PropertySetBlockFromPrototypeTest); StringAssert.Contains($"Property Set {testParams.Identifier}(ByVal {Param(testParams.Identifier)} As {typeName})", result); } @@ -238,10 +238,10 @@ public void MemberBlockFromPrototype_AppliesByVal(DeclarationType declarationTyp End {procType.endStmt} "; var result = ParseAndTest(inputCode, - procedureIdentifier, - declarationType, - new MemberBlockFromPrototypeTestParams(), - MemberBlockFromPrototypeTest); + procedureIdentifier, + declarationType, + new MemberBlockFromPrototypeTestParams(), + MemberBlockFromPrototypeTest); var expected = declarationType.HasFlag(DeclarationType.Property) ? "(arg1 As Long, ByVal arg2 As String)" @@ -265,9 +265,9 @@ public void ImprovedArgumentList_AppliesByVal(DeclarationType declarationType) End {procType.endStmt} "; var result = ParseAndTest(inputCode, - procedureIdentifier, - declarationType, - ImprovedArgumentListTest); + procedureIdentifier, + declarationType, + ImprovedArgumentListTest); var expected = declarationType.HasFlag(DeclarationType.Property) ? "arg1 As Long, ByVal arg2 As String" @@ -276,7 +276,6 @@ public void ImprovedArgumentList_AppliesByVal(DeclarationType declarationType) StringAssert.AreEqualIgnoringCase(expected, result); } - [TestCase(DeclarationType.PropertyGet)] [TestCase(DeclarationType.Function)] [Category(nameof(CodeBuilder))] @@ -291,13 +290,106 @@ public void ImprovedArgumentList_FunctionTypes(DeclarationType declarationType) End {procType.endStmt} "; var result = ParseAndTest(inputCode, - procedureIdentifier, - declarationType, - ImprovedArgumentListTest); + procedureIdentifier, + declarationType, + ImprovedArgumentListTest); StringAssert.AreEqualIgnoringCase($"arg1 As Long, arg2 As String", result); } + [Test] + [Category(nameof(CodeBuilder))] + public void UDT_CreateFromFields() + { + var inputCode = +@" + Public field1 As Long + Public field2 As String"; + + var expected = +@"Private Type TestUDT + Field1 As Long + Field2 As String +End Type"; + var actual = CodeBuilderUDTResult(inputCode, "field1", "field2"); + StringAssert.AreEqualIgnoringCase(expected, actual); + } + + [TestCase("Duplicate", "Duplicate")] + [TestCase("Duplicate", "DuplicATE")] + [Category(nameof(CodeBuilder))] + public void UDT_DuplicateMemberIdentifiers_Throws(string field1Identifier, string field2Identifier) + { + var inputCode = "Public field1 : Public field2"; + var vbe = MockVbeBuilder.BuildFromSingleStandardModule(inputCode, out _).Object; + var state = MockParser.CreateAndParse(vbe); + using (state) + { + var targets = state.DeclarationFinder.UserDeclarations(DeclarationType.Variable) + .OfType() + .Where(d => new string[] { "field1", "field2" }.Contains(d.IdentifierName)); + + var inputPairs = new List<(VariableDeclaration, string)>(); + inputPairs.Add((targets.First(), field1Identifier)); + inputPairs.Add((targets.Last(), field2Identifier)); + ICodeBuilder codeBuilder = new CodeBuilder(); + Assert.Throws(() => { codeBuilder.BuildUserDefinedTypeDeclaration("TestUDT", inputPairs); }); + } + + } + + [Test] + [Category(nameof(CodeBuilder))] + public void UDT_EmptyMembersList_Throws() + { + Assert.Throws(() => { new CodeBuilder().BuildUserDefinedTypeDeclaration("TestUDT", Enumerable.Empty<(VariableDeclaration, string)>()); }); + } + + [Test] + [Category(nameof(CodeBuilder))] + public void UDT_ImplicitTypeMadeExplicit() + { + var inputCode = "Public field1"; + var actual = CodeBuilderUDTResult(inputCode, "field1"); + StringAssert.Contains("Field1 As Variant", actual); + } + + [TestCase("()", "Long")] + [TestCase("(50)", "Long")] + [TestCase("(1 To 10)", "Long")] + [TestCase("()", "")] + [TestCase("(50)", "")] + [TestCase("(1 To 10)", "")] + [Category(nameof(CodeBuilder))] + public void UDT_FromArrayField(string dimensions, string type) + { + var field = "field1"; + + var inputCode = string.IsNullOrEmpty(type) + ? $"Public {field}{dimensions}" + : $"Public {field}{dimensions} As {type}"; + + var expectedType = string.IsNullOrEmpty(type) + ? "Variant" + : type; + + var actual = CodeBuilderUDTResult(inputCode, field); + StringAssert.Contains($"Field1{dimensions} As {expectedType}", actual); + } + + private string CodeBuilderUDTResult(string inputCode, params string[] fields) + { + var vbe = MockVbeBuilder.BuildFromSingleStandardModule(inputCode, out _).Object; + var state = MockParser.CreateAndParse(vbe); + using (state) + { + var targets = state.DeclarationFinder.DeclarationsWithType(DeclarationType.Variable) + .Where(d => fields.Contains(d.IdentifierName)) + .Select(field => (field as VariableDeclaration, field.IdentifierName.CapitalizeFirstLetter())); + return new CodeBuilder().BuildUserDefinedTypeDeclaration("TestUDT", targets); + } + } + private string ParseAndTest(string inputCode, string targetIdentifier, DeclarationType declarationType, Func theTest) { var vbe = MockVbeBuilder.BuildFromSingleStandardModule(inputCode, out _).Object; @@ -305,8 +397,8 @@ private string ParseAndTest(string inputCode, string targetIdentifier, Declar using (state) { var target = state.DeclarationFinder.DeclarationsWithType(declarationType) - .Where(d => d.IdentifierName == targetIdentifier).OfType() - .Single(); + .Where(d => d.IdentifierName == targetIdentifier).OfType() + .Single(); return theTest(target); } } @@ -318,8 +410,8 @@ private string ParseAndTest(string inputCode, string targetIdentifier, Declar using (state) { var target = state.DeclarationFinder.DeclarationsWithType(declarationType) - .Where(d => d.IdentifierName == targetIdentifier).OfType() - .Single(); + .Where(d => d.IdentifierName == targetIdentifier).OfType() + .Single(); return theTest(target, testParams); } } @@ -331,8 +423,8 @@ private string ParseAndTest(string inputCode, string targetIdentifier, Declar using (state) { var target = state.DeclarationFinder.DeclarationsWithType(declarationType) - .Where(d => d.IdentifierName == targetIdentifier).OfType() - .Single(); + .Where(d => d.IdentifierName == targetIdentifier).OfType() + .Single(); return theTest(target, testParams); } } @@ -356,10 +448,10 @@ private string ParseAndTest(string inputCode, string targetIdentifier, Declar } private static string ImprovedArgumentListTest(ModuleBodyElementDeclaration mbed) - => new CodeBuilder().ImprovedArgumentList(mbed); + => new CodeBuilder().ImprovedArgumentList(mbed); private static string MemberBlockFromPrototypeTest(ModuleBodyElementDeclaration mbed, MemberBlockFromPrototypeTestParams testParams) - => new CodeBuilder().BuildMemberBlockFromPrototype(mbed, testParams.Accessibility, testParams.Content, testParams.NewIdentifier); + => new CodeBuilder().BuildMemberBlockFromPrototype(mbed, testParams.Accessibility, testParams.Content, testParams.NewIdentifier); private (string procType, string endStmt) ProcedureTypeIdentifier(DeclarationType declarationType) { From f4fd27412b732759041f02709f8e565ad1c84d1c Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Wed, 19 Aug 2020 15:27:03 -0700 Subject: [PATCH 07/67] Add DeclareFieldsAsUDTMembersRefactoringAction --- .../DeclareFieldsAsUDTMembersModel.cs | 46 ++++++ ...lareFieldsAsUDTMembersRefactoringAction.cs | 67 +++++++++ ...ieldsAsUDTMembersRefactoringActionTests.cs | 142 ++++++++++++++++++ 3 files changed, 255 insertions(+) create mode 100644 Rubberduck.Refactorings/DeclareFieldsAsUDTMembers/DeclareFieldsAsUDTMembersModel.cs create mode 100644 Rubberduck.Refactorings/DeclareFieldsAsUDTMembers/DeclareFieldsAsUDTMembersRefactoringAction.cs create mode 100644 RubberduckTests/Refactoring/DeclareFieldsAsUDTMembers/DeclareFieldsAsUDTMembersRefactoringActionTests.cs diff --git a/Rubberduck.Refactorings/DeclareFieldsAsUDTMembers/DeclareFieldsAsUDTMembersModel.cs b/Rubberduck.Refactorings/DeclareFieldsAsUDTMembers/DeclareFieldsAsUDTMembersModel.cs new file mode 100644 index 0000000000..ced3ac45a0 --- /dev/null +++ b/Rubberduck.Refactorings/DeclareFieldsAsUDTMembers/DeclareFieldsAsUDTMembersModel.cs @@ -0,0 +1,46 @@ +using Rubberduck.Parsing.Symbols; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Rubberduck.Refactorings.MoveFieldsToUDT +{ + public class DeclareFieldsAsUDTMembersModel : IRefactoringModel + { + private Dictionary> _targets { get; } = new Dictionary>(); + + public DeclareFieldsAsUDTMembersModel() + {} + + public IReadOnlyCollection UserDefinedTypeTargets => _targets.Keys; + + public IEnumerable<(VariableDeclaration Field, string userDefinedTypeMemberIdentifier)> this[Declaration udt] + => _targets[udt].Select(pr => (pr.Field, pr.UDTMemberIdentifier)); + + public void AssignFieldToUserDefinedType(Declaration udt, VariableDeclaration field, string udtMemberIdentifierName = null) + { + if (!udt.DeclarationType.HasFlag(DeclarationType.UserDefinedType)) + { + throw new ArgumentException(); + } + + if (!(_targets.TryGetValue(udt, out var memberPrototypes))) + { + _targets.Add(udt, new List<(VariableDeclaration, string)>()); + } + else + { + var hasDuplicateMemberNames = memberPrototypes + .Select(pr => pr.UDTMemberIdentifier?.ToUpperInvariant() ?? pr.Field.IdentifierName) + .GroupBy(uc => uc).Any(g => g.Count() > 1); + + if (hasDuplicateMemberNames) + { + throw new ArgumentException(); + } + } + + _targets[udt].Add((field, udtMemberIdentifierName ?? field.IdentifierName)); + } + } +} diff --git a/Rubberduck.Refactorings/DeclareFieldsAsUDTMembers/DeclareFieldsAsUDTMembersRefactoringAction.cs b/Rubberduck.Refactorings/DeclareFieldsAsUDTMembers/DeclareFieldsAsUDTMembersRefactoringAction.cs new file mode 100644 index 0000000000..462fb038a7 --- /dev/null +++ b/Rubberduck.Refactorings/DeclareFieldsAsUDTMembers/DeclareFieldsAsUDTMembersRefactoringAction.cs @@ -0,0 +1,67 @@ +using Rubberduck.Parsing; +using Rubberduck.Parsing.Grammar; +using Rubberduck.Parsing.Rewriter; +using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.VBA; +using System; +using System.Collections.Generic; +using System.Linq; +namespace Rubberduck.Refactorings.MoveFieldsToUDT +{ + public class DeclareFieldsAsUDTMembersRefactoringAction : CodeOnlyRefactoringActionBase + { + private readonly IDeclarationFinderProvider _declarationFinderProvider; + private readonly ICodeBuilder _codeBuilder; + + public DeclareFieldsAsUDTMembersRefactoringAction(IDeclarationFinderProvider declarationFinderProvider,IRewritingManager rewritingManager, ICodeBuilder codeBuilder) + : base(rewritingManager) + { + _declarationFinderProvider = declarationFinderProvider; + _codeBuilder = codeBuilder; + } + + public override void Refactor(DeclareFieldsAsUDTMembersModel model, IRewriteSession rewriteSession) + { + if (model.UserDefinedTypeTargets.Any( udt => !(udt.Context is VBAParser.UdtDeclarationContext))) + { + throw new ArgumentException(); + } + + foreach (var udt in model.UserDefinedTypeTargets) + { + InsertNewMembersBlock(BuildNewMembersBlock(udt, model[udt]), + GetInsertionIndex(udt.Context as VBAParser.UdtDeclarationContext), + rewriteSession.CheckOutModuleRewriter(udt.QualifiedModuleName)); + } + } + + private string BuildNewMembersBlock(Declaration udt, IEnumerable<(VariableDeclaration Field, string UDTMemberIdentifier)> newMemberPairs) + { + var indentation = DetermineIndentationFromLastMember(udt); + + var newMemberStatements = GenerateUserDefinedMemberDeclarations(newMemberPairs, indentation); + + return string.Concat(newMemberStatements); + } + + private string DetermineIndentationFromLastMember(Declaration udt) + { + var lastMember = _declarationFinderProvider.DeclarationFinder + .UserDeclarations(DeclarationType.UserDefinedTypeMember) + .Where(utm => udt == utm.ParentDeclaration) + .Last(); + + lastMember.Context.TryGetPrecedingContext(out var endOfStatementContextPrototype); + return endOfStatementContextPrototype.GetText(); + } + + private IEnumerable GenerateUserDefinedMemberDeclarations(IEnumerable<(VariableDeclaration Field, string UDTMemberIdentifier)> newMemberPairs, string indentation) + => newMemberPairs.Select(pr => _codeBuilder.UDTMemberDeclaration(pr.UDTMemberIdentifier, pr.Field.AsTypeName, indentation)); + + private static void InsertNewMembersBlock(string newMembersBlock, int insertionIndex, IModuleRewriter rewriter) + => rewriter.InsertBefore(insertionIndex, $"{newMembersBlock}"); + + private int GetInsertionIndex(VBAParser.UdtDeclarationContext udtContext) + => udtContext.END_TYPE().Symbol.TokenIndex - 1; + } +} diff --git a/RubberduckTests/Refactoring/DeclareFieldsAsUDTMembers/DeclareFieldsAsUDTMembersRefactoringActionTests.cs b/RubberduckTests/Refactoring/DeclareFieldsAsUDTMembers/DeclareFieldsAsUDTMembersRefactoringActionTests.cs new file mode 100644 index 0000000000..d91eebc7c6 --- /dev/null +++ b/RubberduckTests/Refactoring/DeclareFieldsAsUDTMembers/DeclareFieldsAsUDTMembersRefactoringActionTests.cs @@ -0,0 +1,142 @@ +using NUnit.Framework; +using Rubberduck.Parsing.Rewriter; +using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.VBA; +using Rubberduck.Refactorings; +using Rubberduck.Refactorings.MoveFieldsToUDT; +using System.Collections.Generic; +using System.Linq; + +namespace RubberduckTests.Refactoring.MoveFieldsToUDT +{ + [TestFixture] + public class DeclareFieldsAsUDTMembersRefactoringActionTests : RefactoringActionTestBase + { + [TestCase(4)] + [TestCase(2)] + [Category("Refactorings")] + [Category(nameof(DeclareFieldsAsUDTMembersRefactoringAction))] + public void FormatSingleExistingMember(int indentionLevel) + { + var indention = string.Concat(Enumerable.Repeat(" ", indentionLevel)); + + string inputCode = +$@" +Option Explicit + +Private mTest As Long + +Private Type TestType +{indention}FirstValue As String +End Type +"; + var expectedUDT = +$@" +Private Type TestType +{indention}FirstValue As String +{indention}Test As Long +End Type +"; + + var results = ExecuteTest(inputCode, "TestType", ("mTest", "Test")); + StringAssert.Contains(expectedUDT, results); + } + + [TestCase(4)] + [TestCase(2)] + [Category("Refactorings")] + [Category(nameof(DeclareFieldsAsUDTMembersRefactoringAction))] + public void FormatMatchesLastMemberIndent(int indentionLevel) + { + var indention = string.Concat(Enumerable.Repeat(" ", indentionLevel)); + var indentionFirstMember = string.Concat(Enumerable.Repeat(" ", 10)); + + string inputCode = +$@" +Option Explicit + +Private mTest As Long + +Private Type TestType +{indentionFirstMember}FirstValue As String +{indention}SecondValue As Double +End Type +"; + var expectedUDT = +$@" +Private Type TestType +{indentionFirstMember}FirstValue As String +{indention}SecondValue As Double +{indention}Test As Long +End Type +"; + + var results = ExecuteTest(inputCode, "TestType", ("mTest", "Test")); + StringAssert.Contains(expectedUDT, results); + } + + [Test] + [Category("Refactorings")] + [Category(nameof(DeclareFieldsAsUDTMembersRefactoringAction))] + public void FormatPreservesComment() + { + var indention = string.Concat(Enumerable.Repeat(" ", 2)); + + string inputCode = +$@" +Option Explicit + +Private mTest As Long + +Private Type TestType +{indention}FirstValue As String +{indention}SecondValue As Double 'This is a comment +End Type +"; + var expectedUDT = +$@" +Private Type TestType +{indention}FirstValue As String +{indention}SecondValue As Double 'This is a comment +{indention}Test As Long +End Type +"; + + var results = ExecuteTest(inputCode, "TestType", ("mTest", "Test")); + StringAssert.Contains(expectedUDT, results); + } + + private string ExecuteTest(string inputCode, string udtIdentifier, params (string, string)[] fieldConversions) + { + return RefactoredCode(inputCode, state => TestModel(state, udtIdentifier, fieldConversions)); + } + + private DeclareFieldsAsUDTMembersModel TestModel(RubberduckParserState state, string udtIdentifier, params (string fieldID, string udtMemberID)[] fieldConversions) + { + var udtDeclaration = GetUniquelyNamedDeclaration(state, DeclarationType.UserDefinedType, udtIdentifier); + var conversions = new List<(VariableDeclaration field, string udtMemberID)>(); + foreach (var (fieldID, udtMemberID) in fieldConversions) + { + var fieldDeclaration = GetUniquelyNamedDeclaration(state, DeclarationType.Variable, fieldID) as VariableDeclaration; + conversions.Add((fieldDeclaration, udtMemberID)); + } + + var model = new DeclareFieldsAsUDTMembersModel(); + foreach ((VariableDeclaration field, string udtMemberID) in conversions) + { + model.AssignFieldToUserDefinedType(udtDeclaration, field, udtMemberID); + } + return model; + } + + private static Declaration GetUniquelyNamedDeclaration(IDeclarationFinderProvider declarationFinderProvider, DeclarationType declarationType, string identifier) + { + return declarationFinderProvider.DeclarationFinder.UserDeclarations(declarationType).Single(d => d.IdentifierName.Equals(identifier)); + } + + protected override IRefactoringAction TestBaseRefactoring(RubberduckParserState state, IRewritingManager rewritingManager) + { + return new DeclareFieldsAsUDTMembersRefactoringAction(state, rewritingManager, new CodeBuilder()); + } + } +} From 315323a489e7a4d8e07f63aaea0a82b4d71f1908 Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Wed, 19 Aug 2020 15:39:09 -0700 Subject: [PATCH 08/67] Use DeclareFieldsAsUDTMembersRefactoringAction --- ...eldUseBackingUDTMemberRefactoringAction.cs | 24 +++++++++++---- .../EncapsulateField/ObjectStateUDT.cs | 30 +++++-------------- .../EncapsulateFieldTestComponentResolver.cs | 5 +++- 3 files changed, 29 insertions(+), 30 deletions(-) diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs index 80ed2672c9..e5cca1c4ec 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs @@ -2,6 +2,7 @@ using Rubberduck.Parsing.Symbols; using Rubberduck.Parsing.VBA; using Rubberduck.Refactorings.Common; +using Rubberduck.Refactorings.MoveFieldsToUDT; using Rubberduck.SmartIndenter; using System.Diagnostics; using System.Linq; @@ -11,14 +12,18 @@ namespace Rubberduck.Refactorings.EncapsulateField public class EncapsulateFieldUseBackingUDTMemberRefactoringAction : EncapsulateFieldRefactoringActionImplBase { private IObjectStateUDT _stateUDTField; + private readonly DeclareFieldsAsUDTMembersRefactoringAction _convertFieldToUDTMemberRefactoringAction; public EncapsulateFieldUseBackingUDTMemberRefactoringAction( + DeclareFieldsAsUDTMembersRefactoringAction convertFieldToUDTMemberRefactoringAction, IDeclarationFinderProvider declarationFinderProvider, IIndenter indenter, IRewritingManager rewritingManager, ICodeBuilder codeBuilder) : base(declarationFinderProvider, indenter, rewritingManager, codeBuilder) - {} + { + _convertFieldToUDTMemberRefactoringAction = convertFieldToUDTMemberRefactoringAction; + } public override void Refactor(EncapsulateFieldModel model, IRewriteSession rewriteSession) { @@ -27,24 +32,31 @@ public override void Refactor(EncapsulateFieldModel model, IRewriteSession rewri RefactorImpl(model, rewriteSession); } - protected override void ModifyFields(IRewriteSession refactorRewriteSession) + protected override void ModifyFields(IRewriteSession rewriteSession) { - var rewriter = refactorRewriteSession.CheckOutModuleRewriter(_targetQMN); + var rewriter = rewriteSession.CheckOutModuleRewriter(_targetQMN); rewriter.RemoveVariables(SelectedFields.Select(f => f.Declaration) .Cast()); if (_stateUDTField.IsExistingDeclaration) { - _stateUDTField.AddMembers(SelectedFields.Cast()); + var model = new DeclareFieldsAsUDTMembersModel(); - rewriter.Replace(_stateUDTField.AsTypeDeclaration, _stateUDTField.TypeDeclarationBlock(_indenter)); + foreach (var field in SelectedFields) + { + model.AssignFieldToUserDefinedType(_stateUDTField.AsTypeDeclaration, field.Declaration as VariableDeclaration, field.PropertyIdentifier); + } + _convertFieldToUDTMemberRefactoringAction.Refactor(model, rewriteSession); } } protected override void LoadNewDeclarationBlocks() { - if (_stateUDTField.IsExistingDeclaration) { return; } + if (_stateUDTField.IsExistingDeclaration) + { + return; + } _stateUDTField.AddMembers(SelectedFields.Cast()); diff --git a/Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT.cs b/Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT.cs index 611fd72b37..398ed211cd 100644 --- a/Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT.cs +++ b/Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT.cs @@ -6,9 +6,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Rubberduck.Refactorings.EncapsulateField.Extensions; using Rubberduck.Resources; namespace Rubberduck.Refactorings.EncapsulateField @@ -30,10 +27,11 @@ public interface IObjectStateUDT : IEncapsulateFieldRefactoringElement //newly inserted declaration public class ObjectStateUDT : IObjectStateUDT { - private static string _defaultNewFieldName = RubberduckUI.EncapsulateField_DefaultObjectStateUDTFieldName; // EncapsulateFieldResources.DefaultStateUDTFieldName; + private static string _defaultNewFieldName = RubberduckUI.EncapsulateField_DefaultObjectStateUDTFieldName; private List _convertedMembers; private readonly IUserDefinedTypeCandidate _wrappedUDT; + private readonly ICodeBuilder _codeBuilder; private int _hashCode; public ObjectStateUDT(IUserDefinedTypeCandidate udt) @@ -60,6 +58,7 @@ private ObjectStateUDT(string typeIdentifier) FieldIdentifier = _defaultNewFieldName; TypeIdentifier = typeIdentifier; _convertedMembers = new List(); + _codeBuilder = new CodeBuilder(); } public string IdentifierName => _wrappedUDT?.IdentifierName ?? FieldIdentifier; @@ -97,7 +96,6 @@ public IEnumerable ExistingMembers } } - private QualifiedModuleName _qmn; public QualifiedModuleName QualifiedModuleName { @@ -132,11 +130,10 @@ public string FieldDeclarationBlock public string TypeDeclarationBlock(IIndenter indenter = null) { - if (indenter != null) - { - return string.Join(Environment.NewLine, indenter?.Indent(BlockLines(Accessibility.Private) ?? BlockLines(Accessibility.Private), true)); - } - return string.Join(Environment.NewLine, BlockLines(Accessibility.Private)); + var udtMembers = _convertedMembers.Where(m => m.Declaration is VariableDeclaration) + .Select(m => (m.Declaration as VariableDeclaration, m.BackingIdentifier)); + + return _codeBuilder.BuildUserDefinedTypeDeclaration(AsTypeName, udtMembers); } public override bool Equals(object obj) @@ -153,18 +150,5 @@ public override bool Equals(object obj) } public override int GetHashCode() => _hashCode; - - private IEnumerable BlockLines(Accessibility accessibility) - { - var blockLines = new List(); - - blockLines.Add($"{accessibility.TokenString()} {Tokens.Type} {TypeIdentifier}"); - - _convertedMembers.ForEach(m => blockLines.Add($"{m.UDTMemberDeclaration}")); - - blockLines.Add($"{Tokens.End} {Tokens.Type}"); - - return blockLines; - } } } diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs index 87ac213fa7..76268af9e5 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs @@ -1,6 +1,7 @@ using Rubberduck.Parsing.Rewriter; using Rubberduck.Parsing.VBA; using Rubberduck.Refactorings; +using Rubberduck.Refactorings.MoveFieldsToUDT; using Rubberduck.Refactorings.EncapsulateField; using Rubberduck.SmartIndenter; using Rubberduck.VBEditor.SafeComWrappers.Abstract; @@ -31,7 +32,9 @@ public EncapsulateFieldTestComponentResolver(IDeclarationFinderProvider declarat case nameof(EncapsulateFieldUseBackingFieldRefactoringAction): return new EncapsulateFieldUseBackingFieldRefactoringAction(_declarationFinderProvider, CreateIndenter(), _rewritingManager, new CodeBuilder()) as T; case nameof(EncapsulateFieldUseBackingUDTMemberRefactoringAction): - return new EncapsulateFieldUseBackingUDTMemberRefactoringAction(_declarationFinderProvider, CreateIndenter(), _rewritingManager, new CodeBuilder()) as T; + return new EncapsulateFieldUseBackingUDTMemberRefactoringAction(ResolveImpl(), _declarationFinderProvider, CreateIndenter(), _rewritingManager, new CodeBuilder()) as T; + case nameof(DeclareFieldsAsUDTMembersRefactoringAction): + return new DeclareFieldsAsUDTMembersRefactoringAction(_declarationFinderProvider, _rewritingManager, new CodeBuilder()) as T; case nameof(EncapsulateFieldPreviewProvider): return new EncapsulateFieldPreviewProvider(ResolveImpl(), ResolveImpl()) as T; case nameof(EncapsulateFieldUseBackingFieldPreviewProvider): From 03dd9f9b1669f64f281f5b8f07a8632eab66498a Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Wed, 19 Aug 2020 17:20:48 -0700 Subject: [PATCH 09/67] Remove Strategies Folder --- Rubberduck.Refactorings/Rubberduck.Refactorings.csproj | 3 --- 1 file changed, 3 deletions(-) diff --git a/Rubberduck.Refactorings/Rubberduck.Refactorings.csproj b/Rubberduck.Refactorings/Rubberduck.Refactorings.csproj index 3bf59f88f1..78a137d982 100644 --- a/Rubberduck.Refactorings/Rubberduck.Refactorings.csproj +++ b/Rubberduck.Refactorings/Rubberduck.Refactorings.csproj @@ -23,7 +23,4 @@ 4.6.4 - - - \ No newline at end of file From b72b631290129720639f9567b57ca3d90fc0e9e4 Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Wed, 26 Aug 2020 13:58:32 -0700 Subject: [PATCH 10/67] Add ReplaceDeclarationIdentifier refactoring action Supports renaming a Declaration independent of its IdentifierReferences --- .../ReplaceDeclarationIdentifierModel.cs | 24 +++++++++++++++ ...eDeclarationIdentifierRefactoringAction.cs | 29 +++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 Rubberduck.Refactorings/ReplaceDeclarationIdentifier/ReplaceDeclarationIdentifierModel.cs create mode 100644 Rubberduck.Refactorings/ReplaceDeclarationIdentifier/ReplaceDeclarationIdentifierRefactoringAction.cs diff --git a/Rubberduck.Refactorings/ReplaceDeclarationIdentifier/ReplaceDeclarationIdentifierModel.cs b/Rubberduck.Refactorings/ReplaceDeclarationIdentifier/ReplaceDeclarationIdentifierModel.cs new file mode 100644 index 0000000000..ebc92cd069 --- /dev/null +++ b/Rubberduck.Refactorings/ReplaceDeclarationIdentifier/ReplaceDeclarationIdentifierModel.cs @@ -0,0 +1,24 @@ +using Rubberduck.Parsing.Symbols; +using System.Collections.Generic; +using System.Linq; + +namespace Rubberduck.Refactorings.ReplaceDeclarationIdentifier +{ + public class ReplaceDeclarationIdentifierModel : IRefactoringModel + { + private List<(Declaration, string)> _targetNewNamePairs; + + public ReplaceDeclarationIdentifierModel(Declaration target, string newName) + : this((target, newName)) { } + + public ReplaceDeclarationIdentifierModel(params (Declaration, string)[] targetNewNamePairs) + : this(targetNewNamePairs.ToList()) { } + + public ReplaceDeclarationIdentifierModel(IEnumerable<(Declaration, string)> targetNewNamePairs) + { + _targetNewNamePairs = targetNewNamePairs.ToList(); + } + + public IReadOnlyCollection<(Declaration, string)> TargetNewNamePairs => _targetNewNamePairs; + } +} diff --git a/Rubberduck.Refactorings/ReplaceDeclarationIdentifier/ReplaceDeclarationIdentifierRefactoringAction.cs b/Rubberduck.Refactorings/ReplaceDeclarationIdentifier/ReplaceDeclarationIdentifierRefactoringAction.cs new file mode 100644 index 0000000000..d8b6f0c056 --- /dev/null +++ b/Rubberduck.Refactorings/ReplaceDeclarationIdentifier/ReplaceDeclarationIdentifierRefactoringAction.cs @@ -0,0 +1,29 @@ +using Rubberduck.Parsing.Grammar; +using Rubberduck.Parsing.Rewriter; +using Rubberduck.Parsing.Symbols; + +namespace Rubberduck.Refactorings.ReplaceDeclarationIdentifier +{ + /// + /// Supports Renaming a Declaration independent of its IdentifierReferences. + /// To replace Declarations and its IdentifierReferences in a single call use RenameRefactoringAction + /// + public class ReplaceDeclarationIdentifierRefactoringAction : CodeOnlyRefactoringActionBase + { + public ReplaceDeclarationIdentifierRefactoringAction(IRewritingManager rewritingManager) + : base(rewritingManager) { } + + public override void Refactor(ReplaceDeclarationIdentifierModel model, IRewriteSession rewriteSession) + { + foreach ((Declaration target, string Name) in model.TargetNewNamePairs) + { + var rewriter = rewriteSession.CheckOutModuleRewriter(target.QualifiedName.QualifiedModuleName); + + if (target.Context is IIdentifierContext context) + { + rewriter.Replace(context.IdentifierTokens, Name); + } + } + } + } +} From 7b55d9be6cee3aa430a07a6f12dfbcb185c06026 Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Wed, 26 Aug 2020 14:02:55 -0700 Subject: [PATCH 11/67] Add ReplaceReferencesRefactoringAction Supports renaming an IdentifierReference independent of its Declaration. Renamed ReplaceFieldReferencesRefactoringActionTests to ReplaceReferencesRefactoringActionTests to match the refactoring action class name. Modified namespace accordingly. --- .../ReplaceReferencesModel.cs | 25 + .../ReplaceReferencesRefactoringAction.cs | 63 +++ ...ReplaceReferencesRefactoringActionTests.cs | 501 ++++++++++++++++++ 3 files changed, 589 insertions(+) create mode 100644 Rubberduck.Refactorings/ReplaceReferences/ReplaceReferencesModel.cs create mode 100644 Rubberduck.Refactorings/ReplaceReferences/ReplaceReferencesRefactoringAction.cs create mode 100644 RubberduckTests/Refactoring/ReplaceReferences/ReplaceReferencesRefactoringActionTests.cs diff --git a/Rubberduck.Refactorings/ReplaceReferences/ReplaceReferencesModel.cs b/Rubberduck.Refactorings/ReplaceReferences/ReplaceReferencesModel.cs new file mode 100644 index 0000000000..205c0d9ef0 --- /dev/null +++ b/Rubberduck.Refactorings/ReplaceReferences/ReplaceReferencesModel.cs @@ -0,0 +1,25 @@ +using Rubberduck.Parsing.Symbols; +using System.Collections.Generic; +using System.Linq; + +namespace Rubberduck.Refactorings.ReplaceReferences +{ + public class ReplaceReferencesModel :IRefactoringModel + { + private Dictionary _fieldTargets = new Dictionary(); + + public bool ModuleQualifyExternalReferences { set; get; } = false; + + public void AssignFieldReferenceReplacementExpression(IdentifierReference fieldReference, string replacementIdentifier) + { + if (_fieldTargets.ContainsKey(fieldReference)) + { + _fieldTargets[fieldReference] = replacementIdentifier; + return; + } + _fieldTargets.Add(fieldReference, replacementIdentifier); + } + public IReadOnlyList<(IdentifierReference IdentifierReference, string NewName)> FieldReferenceReplacementPairs + => _fieldTargets.Select(t => (t.Key, t.Value)).ToList(); + } +} diff --git a/Rubberduck.Refactorings/ReplaceReferences/ReplaceReferencesRefactoringAction.cs b/Rubberduck.Refactorings/ReplaceReferences/ReplaceReferencesRefactoringAction.cs new file mode 100644 index 0000000000..8bc14a9984 --- /dev/null +++ b/Rubberduck.Refactorings/ReplaceReferences/ReplaceReferencesRefactoringAction.cs @@ -0,0 +1,63 @@ +using System.Linq; +using Antlr4.Runtime; +using Rubberduck.Parsing.Grammar; +using Rubberduck.Parsing.Rewriter; +using Rubberduck.Parsing.Symbols; + +namespace Rubberduck.Refactorings.ReplaceReferences +{ + /// + /// Supports Renaming an IdentifierReference independent of its Declaration. + /// To replace Declarations and its IdentifierReferences in a single call use RenameRefactoringAction + /// + public class ReplaceReferencesRefactoringAction : CodeOnlyRefactoringActionBase + { + public ReplaceReferencesRefactoringAction(IRewritingManager rewritingManager) + : base(rewritingManager) + { } + + public override void Refactor(ReplaceReferencesModel model, IRewriteSession rewriteSession) + { + var replacementPairByQualifiedModuleName = model.FieldReferenceReplacementPairs + .Where(pr => + pr.IdentifierReference.Context.GetText() != Tokens.Me + && !pr.IdentifierReference.IsArrayAccess + && !pr.IdentifierReference.IsDefaultMemberAccess) + .GroupBy(r => r.IdentifierReference.QualifiedModuleName); + + foreach (var replacements in replacementPairByQualifiedModuleName) + { + var rewriter = rewriteSession.CheckOutModuleRewriter(replacements.Key); + foreach ((IdentifierReference identifierReference, string newIdentifier) in replacements) + { + (ParserRuleContext context, string replacementName) = BuildReferenceReplacementString(identifierReference, newIdentifier, model.ModuleQualifyExternalReferences); + rewriter.Replace(context, replacementName); + } + } + } + + private (ParserRuleContext context, string replacementName) BuildReferenceReplacementString(IdentifierReference identifierReference, string NewName, bool moduleQualify) + { + var replacementExpression = moduleQualify && CanBeModuleQualified(identifierReference) + ? $"{identifierReference.Declaration.QualifiedModuleName.ComponentName}.{NewName}" + : NewName; + + return (identifierReference.Context, replacementExpression); + } + + private static bool CanBeModuleQualified(IdentifierReference idRef) + { + if (idRef.QualifiedModuleName == idRef.Declaration.QualifiedModuleName) + { + return false; + } + + var isLHSOfMemberAccess = + (idRef.Context.Parent is VBAParser.MemberAccessExprContext + || idRef.Context.Parent is VBAParser.WithMemberAccessExprContext) + && !(idRef.Context == idRef.Context.Parent.GetChild(0)); + + return !isLHSOfMemberAccess; + } + } +} diff --git a/RubberduckTests/Refactoring/ReplaceReferences/ReplaceReferencesRefactoringActionTests.cs b/RubberduckTests/Refactoring/ReplaceReferences/ReplaceReferencesRefactoringActionTests.cs new file mode 100644 index 0000000000..93f1c7e6a9 --- /dev/null +++ b/RubberduckTests/Refactoring/ReplaceReferences/ReplaceReferencesRefactoringActionTests.cs @@ -0,0 +1,501 @@ +using NUnit.Framework; +using Rubberduck.Parsing.Rewriter; +using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.VBA; +using Rubberduck.Refactorings; +using Rubberduck.Refactorings.ReplaceReferences; +using Rubberduck.Refactorings.ReplacePrivateUDTMemberReferences; +using Rubberduck.VBEditor.SafeComWrappers; +using RubberduckTests.Mocks; +using System.Linq; + +namespace RubberduckTests.Refactoring.RenameReferences +{ + [TestFixture] + public class ReplaceReferencesRefactoringActionTests : RefactoringActionTestBase + { + [Test] + [Category("Refactorings")] + [Category("Encapsulate Field")] + [Category(nameof(ReplaceReferencesRefactoringAction))] + public void ValueField() + { + string inputCode = +$@" +Option Explicit + +Public mTest As Long + +Private mValue As Long + +Public Sub Fizz(arg As Long) + mValue = mTest + arg +End Sub + +Public Function RetrieveTest() As Long + RetrieveTest = mTest +End Function +"; + + string externalModule = "ExternalModule"; + string externalCode = +$@" +Option Explicit + +Private mValue As Long + +Public Sub Fazz(arg As Long) + mValue = mTest * arg +End Sub +"; + var vbe = MockVbeBuilder.BuildFromStdModules((MockVbeBuilder.TestModuleName, inputCode), (externalModule, externalCode)); + var results = RefactoredCode(vbe.Object, state => TestModel(state, ("mTest", "mNewName", "RetrieveTest()"))); + + StringAssert.Contains($"RetrieveTest = mNewName", results[MockVbeBuilder.TestModuleName]); + StringAssert.Contains($"Public mTest As Long", results[MockVbeBuilder.TestModuleName]); + + StringAssert.Contains($"mValue = {MockVbeBuilder.TestModuleName}.RetrieveTest()", results[externalModule]); + } + + [Test] + [Category("Refactorings")] + [Category("Encapsulate Field")] + [Category(nameof(ReplaceReferencesRefactoringAction))] + public void ExternalReferences() + { + var sourceModuleName = "SourceModule"; + var referenceExpression = $"{sourceModuleName}."; + var sourceModuleCode = +$@" + +Public this As Long"; + + var procedureModuleReferencingCode = +$@"Option Explicit + +Private Const bar As Long = 7 + +Public Sub Bar() + {referenceExpression}this = bar +End Sub + +Public Sub Foo() + With {sourceModuleName} + .this = bar + End With +End Sub +"; + + var vbe = MockVbeBuilder.BuildFromStdModules((sourceModuleName, sourceModuleCode), (MockVbeBuilder.TestModuleName, procedureModuleReferencingCode)); + var actualModuleCode = RefactoredCode(vbe.Object, state => TestModel(state, ("this", "test", "MyProperty"))); + + var referencingModuleCode = actualModuleCode[MockVbeBuilder.TestModuleName]; + StringAssert.Contains($"{sourceModuleName}.MyProperty = ", referencingModuleCode); + StringAssert.DoesNotContain($"{sourceModuleName}.{sourceModuleName}.MyProperty = ", referencingModuleCode); + StringAssert.Contains($" .MyProperty = bar", referencingModuleCode); + } + + [Test] + [Category("Refactorings")] + [Category("Encapsulate Field")] + [Category(nameof(ReplaceReferencesRefactoringAction))] + public void ArrayReferences2() + { + var sourceModuleName = "SourceModule"; + string inputCode = + @"Private Sub Foo() + ReDim arr(0 To 1) + arr(1) = arr(0) +End Sub"; + string expectedCode = + @"Private Sub Foo() + ReDim bar(0 To 1) + bar(1) = bar(0) +End Sub"; + + var vbe = MockVbeBuilder.BuildFromStdModules((sourceModuleName, inputCode)); + var actualModuleCode = RefactoredCode(vbe.Object, state => TestModel(state, ("arr", "bar", null))); + + var results = actualModuleCode[sourceModuleName]; + Assert.AreEqual(expectedCode, results); + } + + [Test] + [Category("Refactorings")] + [Category("Encapsulate Field")] + [Category(nameof(ReplaceReferencesRefactoringAction))] + public void ArrayReferences() + { + var sourceModuleName = "SourceModule"; + string inputCode = +$@" +Option Explicit + +Private myArray() As Integer + +Private Sub InitializeArray(size As Long) + Redim myArray(size) + Dim idx As Long + For idx = 1 To size + myArray(idx) = idx + Next idx +End Sub +"; + string expectedCode = +$@" +Option Explicit + +Private myArray() As Integer + +Private Sub InitializeArray(size As Long) + Redim renamedArray(size) + Dim idx As Long + For idx = 1 To size + renamedArray(idx) = idx + Next idx +End Sub +"; + + var vbe = MockVbeBuilder.BuildFromStdModules((sourceModuleName, inputCode)); + var actualModuleCode = RefactoredCode(vbe.Object, state => TestModel(state, ("myArray", "renamedArray", "renamedArray"))); + + var results = actualModuleCode[sourceModuleName]; + Assert.AreEqual(expectedCode, results); + } + + [Test] + [Category("Refactorings")] + [Category("Encapsulate Field")] + [Category(nameof(ReplaceReferencesRefactoringAction))] + public void ClassModuleReferences() + { + var sourceModuleName = "SourceModule"; + var referenceExpression = $"{sourceModuleName}."; + var sourceModuleCode = +$@" + +Public this As Long"; + + string classModuleReferencingCode = +$@"Option Explicit + +Private Const bar As Long = 7 + +Public Sub Bar() + {referenceExpression}this = bar +End Sub + +Public Sub Foo() + With {sourceModuleName} + .this = bar + End With +End Sub +"; + + var vbe = MockVbeBuilder.BuildFromModules((sourceModuleName, sourceModuleCode, ComponentType.StandardModule), (MockVbeBuilder.TestModuleName, classModuleReferencingCode, ComponentType.ClassModule)); + var actualModuleCode = RefactoredCode(vbe.Object, state => TestModel(state, ("this", "test", "MyProperty"))); + + var referencingModuleCode = actualModuleCode[MockVbeBuilder.TestModuleName]; + StringAssert.Contains($"{sourceModuleName}.MyProperty = ", referencingModuleCode); + StringAssert.DoesNotContain($"{sourceModuleName}.{sourceModuleName}.MyProperty = ", referencingModuleCode); + StringAssert.Contains($" .MyProperty = bar", referencingModuleCode); + } + + [Test] + [Category("Refactorings")] + [Category("Encapsulate Field")] + [Category(nameof(ReplaceReferencesRefactoringAction))] + public void UDTField_MemberAccess() + { + string inputCode = +$@" +Private Type TBar + FirstVal As String + SecondVal As Long + ThirdVal As Byte +End Type + +Private myBar As TBar +Private myFoo As TBar + +Public Function GetOne() As String + GetOne = myBar.FirstVal +End Function + +Public Function GetTwo() As Long + GetTwo = myBar.ThirdVal +End Function + +Public Function GetThree() As Long + GetThree = myFoo.ThirdVal +End Function + +"; + + var vbe = MockVbeBuilder.BuildFromStdModules((MockVbeBuilder.TestModuleName, inputCode)); + var state = MockParser.CreateAndParse(vbe.Object); + using (state) + { + var target = state.DeclarationFinder.UserDeclarations(DeclarationType.Variable) + .OfType() + .Where(d => "myBar" == d.IdentifierName) + .Single(); + + var udtMembers = state.DeclarationFinder.UserDeclarations(DeclarationType.UserDefinedTypeMember) + .Where(d => "TBar" == d.ParentDeclaration.IdentifierName); + + var test = new UserDefinedTypeInstance(target, udtMembers); + var refs = test.UDTMemberReferences; + Assert.AreEqual(2, refs.Select(rf => rf.IdentifierName).Count()); + } + } + + [Test] + [Category("Refactorings")] + [Category("Encapsulate Field")] + [Category(nameof(ReplaceReferencesRefactoringAction))] + public void UDTField_MemberAccessMultipleInstances() + { + string inputCode = +$@" +Private Type TBar + FirstVal As String + SecondVal As Long + ThirdVal As Byte +End Type + +Private myBar As TBar +Private myFoo As TBar + +Public Function GetOne() As String + GetOne = myBar.FirstVal +End Function + +Public Function GetTwo() As Long + GetTwo = myBar.ThirdVal +End Function + +Public Function GetThree() As Long + GetThree = myFoo.ThirdVal +End Function + +"; + + var vbe = MockVbeBuilder.BuildFromStdModules((MockVbeBuilder.TestModuleName, inputCode)); + var state = MockParser.CreateAndParse(vbe.Object); + using (state) + { + var myBarTarget = state.DeclarationFinder.UserDeclarations(DeclarationType.Variable) + .OfType() + .Where(d => "myBar" == d.IdentifierName) + .Single(); + + var myFooTarget = state.DeclarationFinder.UserDeclarations(DeclarationType.Variable) + .OfType() + .Where(d => "myFoo" == d.IdentifierName) + .Single(); + + var udtMembers = state.DeclarationFinder.UserDeclarations(DeclarationType.UserDefinedTypeMember) + .Where(d => "TBar" == d.ParentDeclaration.IdentifierName); + + var myBarRefs = new UserDefinedTypeInstance(myBarTarget, udtMembers); + var refs = myBarRefs.UDTMemberReferences; + Assert.AreEqual(2, refs.Select(rf => rf.IdentifierName).Count()); + + var myFooRefs = new UserDefinedTypeInstance(myFooTarget, udtMembers); + var fooRefs = myBarRefs.UDTMemberReferences; + Assert.AreEqual(2, fooRefs.Select(rf => rf.IdentifierName).Count()); + } + } + + [Test] + [Category("Refactorings")] + [Category("Encapsulate Field")] + [Category(nameof(ReplaceReferencesRefactoringAction))] + public void UDTField_WithMemberAccess() + { + string inputCode = +$@" +Private Type TBar + FirstVal As String + SecondVal As Long + ThirdVal As Byte +End Type + +Private myBar As TBar +Private myFoo As TBar + +Public Function GetOne() As String + With myBar + GetOne = .FirstVal + End With +End Function + +Public Function GetTwo() As Long + With myBar + GetTwo = .SecondVal + End With +End Function + +Public Function GetThree() As Long + With myFoo + GetThree = .ThirdVal + End With +End Function + +"; + + var vbe = MockVbeBuilder.BuildFromStdModules((MockVbeBuilder.TestModuleName, inputCode)); + var state = MockParser.CreateAndParse(vbe.Object); + using (state) + { + var target = state.DeclarationFinder.UserDeclarations(DeclarationType.Variable) + .OfType() + .Where(d => "myBar" == d.IdentifierName) + .Single(); + + var udtMembers = state.DeclarationFinder.UserDeclarations(DeclarationType.UserDefinedTypeMember) + .Where(d => "TBar" == d.ParentDeclaration.IdentifierName); + + var test = new UserDefinedTypeInstance(target, udtMembers); + var refs = test.UDTMemberReferences; + Assert.AreEqual(2, refs.Select(rf => rf.IdentifierName).Count()); + } + } + + [Test] + [Category("Refactorings")] + [Category("Encapsulate Field")] + [Category(nameof(ReplaceReferencesRefactoringAction))] + public void ClassModuleUDTField_ExternalReferences() + { + var className = "TestClass"; + var classInputCode = +$@" + +Public this As TBar +"; + + var classInstanceName = "theClass"; + var proceduralModuleName = MockVbeBuilder.TestModuleName; + var procedureModuleReferencingCode = +$@"Option Explicit + +Public Type TBar + First As String + Second As Long +End Type + +Private {classInstanceName} As {className} +Private Const foo As String = ""Foo"" +Private Const bar As Long = 7 + +Public Sub Initialize() + Set {classInstanceName} = New {className} +End Sub + +Public Sub Foo() + {classInstanceName}.this.First = foo +End Sub + +Public Sub Bar() + {classInstanceName}.this.Second = bar +End Sub + +Public Sub FooBar() + With {classInstanceName} + .this.First = foo + .this.Second = bar + End With +End Sub +"; + + var vbe = MockVbeBuilder.BuildFromModules((className, classInputCode, ComponentType.ClassModule), + (proceduralModuleName, procedureModuleReferencingCode, ComponentType.StandardModule)); + + var actualModuleCode = RefactoredCode(vbe.Object, state => TestModel(state, ("this", "MyType", "MyType"))); + + var referencingModuleCode = actualModuleCode[proceduralModuleName]; + StringAssert.Contains($"{classInstanceName}.MyType.First = ", referencingModuleCode); + StringAssert.Contains($"{classInstanceName}.MyType.Second = ", referencingModuleCode); + StringAssert.Contains($" .MyType.Second = ", referencingModuleCode); + } + + [TestCase("DeclaringModule.this", "DeclaringModule.TheFirst.First")] + [TestCase("this", "DeclaringModule.TheFirst.First")] + [Category("Encapsulate Field")] + [Category("Refactorings")] + [Category(nameof(ReplaceReferencesRefactoringAction))] + public void PublicUDT_ExternalFieldReferences(string memberAccessExpression, string expectedExpression) + { + var moduleName = "DeclaringModule"; + var inputCode = +$@" +Public this As TBazz + +Public Property Let TheFirst(ByVal RHS As String) + 'this.First = RHS +End Property +"; + + var referencingModuleName = MockVbeBuilder.TestModuleName; + var referencingCode = +$@"Option Explicit + +Public Type TBazz + First As String +End Type + +Public Sub Fizz() + {memberAccessExpression}.First = ""Fizz"" +End Sub +"; + + var vbe = MockVbeBuilder.BuildFromModules((moduleName, inputCode, ComponentType.StandardModule), + (referencingModuleName, referencingCode, ComponentType.StandardModule)); + + var actualModuleCode = RefactoredCode(vbe.Object, state => TestModel(state, ("this", "TheFirst", "TheFirst"))); + + var referencingModuleCode = actualModuleCode[referencingModuleName]; + StringAssert.Contains($"{expectedExpression} = ", referencingModuleCode); + } + + private ReplaceReferencesModel TestModel(RubberduckParserState state, params (string fieldID, string internalName, string externalName)[] fieldConversions) + { + var model = new ReplaceReferencesModel() + { + ModuleQualifyExternalReferences = true, + }; + + var fields = state.DeclarationFinder.UserDeclarations(DeclarationType.Variable) + .Select(v => v as VariableDeclaration); + + foreach (var (fieldID, internalName, externalName) in fieldConversions) + { + var fieldDeclaration = GetUniquelyNamedDeclaration(state, DeclarationType.Variable, fieldID); + foreach (var reference in fieldDeclaration.References) + { + var replacementExpression = fieldDeclaration.QualifiedModuleName != reference.QualifiedModuleName + ? externalName + : internalName; + + model.AssignFieldReferenceReplacementExpression(reference, replacementExpression); + } + } + return model; + } + + private static bool IsExternalReference(IdentifierReference identifierReference) + => identifierReference.QualifiedModuleName != identifierReference.Declaration.QualifiedModuleName; + + private static Declaration GetUniquelyNamedDeclaration(IDeclarationFinderProvider declarationFinderProvider, DeclarationType declarationType, string identifier) + { + return declarationFinderProvider.DeclarationFinder.UserDeclarations(declarationType).Single(d => d.IdentifierName.Equals(identifier)); + } + + protected override IRefactoringAction TestBaseRefactoring(RubberduckParserState state, IRewritingManager rewritingManager) + { + return new ReplaceReferencesRefactoringAction(rewritingManager); + } + } +} From 925a245dabc4a732c2572417abf13e5dcec3118b Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Wed, 26 Aug 2020 18:20:43 -0700 Subject: [PATCH 12/67] Add CodeBlockInsertRefactoringAction --- .../CodeBlockInsert/CodeBlockInsertModel.cs | 53 ++++++++ .../CodeBlockInsertRefactoringAction.cs | 116 +++++++++++++++++ .../EncapsulateFieldPreviewProvider.cs | 6 +- .../Extensions/StringExtensions.cs | 19 --- .../CodeBlockInsertRefactoringActionTests.cs | 122 ++++++++++++++++++ 5 files changed, 294 insertions(+), 22 deletions(-) create mode 100644 Rubberduck.Refactorings/CodeBlockInsert/CodeBlockInsertModel.cs create mode 100644 Rubberduck.Refactorings/CodeBlockInsert/CodeBlockInsertRefactoringAction.cs create mode 100644 RubberduckTests/Refactoring/CodeBlockInsert/CodeBlockInsertRefactoringActionTests.cs diff --git a/Rubberduck.Refactorings/CodeBlockInsert/CodeBlockInsertModel.cs b/Rubberduck.Refactorings/CodeBlockInsert/CodeBlockInsertModel.cs new file mode 100644 index 0000000000..9989d1484b --- /dev/null +++ b/Rubberduck.Refactorings/CodeBlockInsert/CodeBlockInsertModel.cs @@ -0,0 +1,53 @@ +using Rubberduck.Refactorings.EncapsulateField; +using Rubberduck.VBEditor; +using System.Collections.Generic; +using System.Linq; + +namespace Rubberduck.Refactorings.CodeBlockInsert +{ + public enum NewContentType + { + TypeDeclarationBlock, + DeclarationBlock, + CodeSectionBlock, + PostContentMessage + } + + public class CodeBlockInsertModel : IRefactoringModel + { + public CodeBlockInsertModel() + { + _selectedFields = new List(); + + NewContent = new Dictionary> + { + { NewContentType.PostContentMessage, new List() }, + { NewContentType.DeclarationBlock, new List() }, + { NewContentType.CodeSectionBlock, new List() }, + { NewContentType.TypeDeclarationBlock, new List() } + }; + } + + public QualifiedModuleName QualifiedModuleName { set; get; } + + public bool IncludeComments { set; get; } + + public Dictionary> NewContent { set; get; } + + public void AddContentBlock(NewContentType contentType, string block) + => NewContent[contentType].Add(block); + + public int NewLineLimit { set; get; } = 2; + + private List _selectedFields; + public IEnumerable SelectedFieldCandidates + { + set => _selectedFields = value.ToList(); + get => _selectedFields; + } + + public int? CodeSectionStartIndex { set; get; } = null; + + public int? NewContentInsertionIndex => CodeSectionStartIndex; + } +} diff --git a/Rubberduck.Refactorings/CodeBlockInsert/CodeBlockInsertRefactoringAction.cs b/Rubberduck.Refactorings/CodeBlockInsert/CodeBlockInsertRefactoringAction.cs new file mode 100644 index 0000000000..79ff2fe008 --- /dev/null +++ b/Rubberduck.Refactorings/CodeBlockInsert/CodeBlockInsertRefactoringAction.cs @@ -0,0 +1,116 @@ +using Rubberduck.Parsing.Rewriter; +using Rubberduck.Parsing.VBA; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Rubberduck.Refactorings.CodeBlockInsert +{ + /// + /// Inserts new content into a ModuleDeclarationSection of CodeSection + /// based on the presence or absence of existing CodeSection content. + /// + /// If there is an existing member Declaration, then the + /// new content will be inserted above the existing member. + /// + public class CodeBlockInsertRefactoringAction : CodeOnlyRefactoringActionBase + { + private static string _doubleSpace = $"{Environment.NewLine}{Environment.NewLine}"; + + private readonly IDeclarationFinderProvider _declarationFinderProvider; + private readonly ICodeBuilder _codeBuilder; + + public CodeBlockInsertRefactoringAction(IDeclarationFinderProvider declarationFinderProvider, IRewritingManager rewritingManager, ICodeBuilder codeBuilder) + : base(rewritingManager) + { + _declarationFinderProvider = declarationFinderProvider; + _codeBuilder = codeBuilder; + } + + public override void Refactor(CodeBlockInsertModel model, IRewriteSession rewriteSession) + { + var newDeclarationSectionBlock = BuildBlock(model, NewContentType.TypeDeclarationBlock, NewContentType.DeclarationBlock); + + var newCodeSectionBlock = BuildBlock(model, NewContentType.CodeSectionBlock); + + var aggregatedBlocks = new List(); + if (!string.IsNullOrEmpty(newDeclarationSectionBlock)) + { + aggregatedBlocks.Add(newDeclarationSectionBlock); + } + + if (!string.IsNullOrEmpty(newCodeSectionBlock)) + { + aggregatedBlocks.Add(newCodeSectionBlock); + } + + if (aggregatedBlocks.Count == 0) + { + return; + } + + var rewriter = rewriteSession.CheckOutModuleRewriter(model.QualifiedModuleName); + + var allNewContent = string.Join(_doubleSpace, aggregatedBlocks); + + var comments = string.Join(_doubleSpace, model.NewContent[NewContentType.PostContentMessage]); + if (!string.IsNullOrEmpty(comments) && model.IncludeComments) + { + allNewContent = $"{allNewContent}{Environment.NewLine}{comments}"; + } + + InsertBlock(allNewContent, model.NewContentInsertionIndex, rewriter); + } + + private static void InsertBlock(string content, int? insertionIndex, IModuleRewriter rewriter) + { + if (string.IsNullOrEmpty(content)) + { + return; + } + + if (insertionIndex.HasValue) + { + rewriter.InsertBefore(insertionIndex.Value, $"{content}{_doubleSpace}"); + return; + } + rewriter.InsertBefore(rewriter.TokenStream.Size - 1, $"{_doubleSpace}{content}"); + } + + private string BuildBlock(CodeBlockInsertModel model, params NewContentType[] newContentTypes) + { + var block = string.Empty; + foreach (var newContentType in newContentTypes) + { + var newContent = string.Join(_doubleSpace, + (model.NewContent[newContentType])); + if (!string.IsNullOrEmpty(newContent)) + { + block = string.IsNullOrEmpty(block) + ? newContent + : $"{block}{_doubleSpace}{newContent}"; + } + } + return LimitNewLines(block.Trim(), model.NewLineLimit); + } + + private static string LimitNewLines(string content, int maxConsecutiveNewlines = 2) + { + var target = string.Concat(Enumerable.Repeat(Environment.NewLine, maxConsecutiveNewlines + 1).ToList()); + var replacement = string.Concat(Enumerable.Repeat(Environment.NewLine, maxConsecutiveNewlines).ToList()); + var guard = 0; + var maxAttempts = 100; + while (++guard < maxAttempts && content.Contains(target)) + { + content = content.Replace(target, replacement); + } + + if (guard >= maxAttempts) + { + throw new FormatException($"Unable to limit consecutive '{Environment.NewLine}' strings to {maxConsecutiveNewlines}"); + } + return content; + } + + } +} diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldPreviewProvider.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldPreviewProvider.cs index 4d56d30029..bbe6909fd1 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldPreviewProvider.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldPreviewProvider.cs @@ -20,10 +20,10 @@ public class EncapsulateFieldPreviewProvider : IRefactoringPreviewProvider= maxAttempts) - { - throw new FormatException($"Unable to limit consecutive '{Environment.NewLine}' strings to {maxConsecutiveNewlines}"); - } - return content; - } } } diff --git a/RubberduckTests/Refactoring/CodeBlockInsert/CodeBlockInsertRefactoringActionTests.cs b/RubberduckTests/Refactoring/CodeBlockInsert/CodeBlockInsertRefactoringActionTests.cs new file mode 100644 index 0000000000..dab8b604a3 --- /dev/null +++ b/RubberduckTests/Refactoring/CodeBlockInsert/CodeBlockInsertRefactoringActionTests.cs @@ -0,0 +1,122 @@ +using NUnit.Framework; +using Rubberduck.Parsing.Rewriter; +using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.VBA; +using Rubberduck.Refactorings; +using Rubberduck.Refactorings.CodeBlockInsert; +using RubberduckTests.Mocks; +using System.Linq; + +namespace RubberduckTests.Refactoring.CodeBlockInsert +{ + [TestFixture] + public class CodeBlockInsertRefactoringActionTests : RefactoringActionTestBase + { + private static string _declarationSectionMisplaced = "DeclarationSection content in wrong location"; + private static string _codeSectionMisplaced = "CodeSection content in wrong location"; + private static string _commentMisplaced = "PostContentMessage content in wrong location"; + + private static string _declarationContent = "'This is the DeclarationSection"; + private static string _codeContent = "'This is the CodeSection"; + private static string _commentContent = "'This is a comment"; + + [Test] + [Category("Refactorings")] + [Category(nameof(CodeBlockInsertRefactoringAction))] + public void CorrectOrder() + { + string inputCode = +$@" +Option Explicit + +"; + var results = ExecuteTest(inputCode, (_declarationContent, _codeContent, _commentContent)); + var declarationsIndex = results.IndexOf(_declarationContent); + var codeIndex = results.IndexOf(_codeContent); + var commentIndex = results.IndexOf(_commentContent); + Assert.Greater(codeIndex, declarationsIndex, _codeSectionMisplaced); + Assert.Greater(commentIndex, codeIndex, _commentMisplaced); + } + + [Test] + [Category("Refactorings")] + [Category(nameof(CodeBlockInsertRefactoringAction))] + public void CorrectOrder_NoMembers() + { + string inputCode = +$@" +Option Explicit + +Private mTest As Long + +"; + var results = ExecuteTest(inputCode, (_declarationContent, _codeContent, _commentContent)); + var existingDeclarationIndex = results.IndexOf("Private mTest As Long"); + var declarationsIndex = results.IndexOf(_declarationContent); + var codeIndex = results.IndexOf(_codeContent); + var commentIndex = results.IndexOf(_commentContent); + Assert.Greater(declarationsIndex, existingDeclarationIndex, _declarationSectionMisplaced); + Assert.Greater(codeIndex, declarationsIndex, _codeSectionMisplaced); + Assert.Greater(commentIndex, codeIndex, _commentMisplaced); + } + + + [Test] + [Category("Refactorings")] + [Category(nameof(CodeBlockInsertRefactoringAction))] + public void CorrectOrder_ExistingMember() + { + string inputCode = +$@" +Option Explicit + +Private mTest As Long + +Public Sub Test() +End Sub +"; + var results = ExecuteTest(inputCode, (_declarationContent, _codeContent, _commentContent)); + var existingDeclarationIndex = results.IndexOf("Private mTest As Long"); + var existingMemberIndex = results.IndexOf("Sub Test()"); + var declarationsIndex = results.IndexOf(_declarationContent); + var codeIndex = results.IndexOf(_codeContent); + var commentIndex = results.IndexOf(_commentContent); + Assert.Greater(declarationsIndex, existingDeclarationIndex, _declarationSectionMisplaced); + Assert.Greater(codeIndex, declarationsIndex, _codeSectionMisplaced); + Assert.Greater(existingMemberIndex, codeIndex, _codeSectionMisplaced); + Assert.Greater(existingMemberIndex, commentIndex, _commentMisplaced); + } + + private string ExecuteTest(string inputCode, params (string, string, string)[] content) + { + return RefactoredCode(inputCode, state => TestModel(state, content)); + } + + private CodeBlockInsertModel TestModel(RubberduckParserState state, params (string declarationSection, string codeSection, string comment)[] blocks) + { + var members = state.DeclarationFinder.UserDeclarations(DeclarationType.Member); + + var module = state.DeclarationFinder.MatchName(MockVbeBuilder.TestModuleName).Single(); + var model = new CodeBlockInsertModel() + { + QualifiedModuleName = module.QualifiedModuleName, + CodeSectionStartIndex = members + .OrderBy(m => m?.Context.Start.TokenIndex) + .FirstOrDefault()?.Context.Start.TokenIndex, + }; + + foreach ((string declaration, string code, string comment) in blocks) + { + model.AddContentBlock(NewContentType.DeclarationBlock, declaration); + model.AddContentBlock(NewContentType.CodeSectionBlock, code); + model.AddContentBlock(NewContentType.PostContentMessage, comment); + } + return model; + } + + protected override IRefactoringAction TestBaseRefactoring(RubberduckParserState state, IRewritingManager rewritingManager) + { + return new CodeBlockInsertRefactoringAction(state, rewritingManager, new CodeBuilder()); + } + } +} \ No newline at end of file From c86a359c95917a6e2e4f23fc410bc854817a7ec5 Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Sat, 29 Aug 2020 09:08:32 -0700 Subject: [PATCH 13/67] Add ReplacePrivateUDTMemberReferencesRefactoringAction --- ...DTMemberReferenceReplacementExpressions.cs | 21 +++ .../ReplacePrivateUDTMemberReferencesModel.cs | 42 +++++ ...ePrivateUDTMemberReferencesModelFactory.cs | 43 +++++ ...ateUDTMemberReferencesRefactoringAction.cs | 97 ++++++++++ .../UserDefinedTypeInstance.cs | 68 +++++++ ...TMemberReferencesRefactoringActionTests.cs | 169 ++++++++++++++++++ .../UserDefinedTypeInstanceTests.cs | 136 ++++++++++++++ 7 files changed, 576 insertions(+) create mode 100644 Rubberduck.Refactorings/ReplacePrivateUDTMemberReferences/PrivateUDTMemberReferenceReplacementExpressions.cs create mode 100644 Rubberduck.Refactorings/ReplacePrivateUDTMemberReferences/ReplacePrivateUDTMemberReferencesModel.cs create mode 100644 Rubberduck.Refactorings/ReplacePrivateUDTMemberReferences/ReplacePrivateUDTMemberReferencesModelFactory.cs create mode 100644 Rubberduck.Refactorings/ReplacePrivateUDTMemberReferences/ReplacePrivateUDTMemberReferencesRefactoringAction.cs create mode 100644 Rubberduck.Refactorings/ReplacePrivateUDTMemberReferences/UserDefinedTypeInstance.cs create mode 100644 RubberduckTests/Refactoring/ReplacePrivateUDTMemberReferences/ReplacePrivateUDTMemberReferencesRefactoringActionTests.cs create mode 100644 RubberduckTests/Refactoring/ReplacePrivateUDTMemberReferences/UserDefinedTypeInstanceTests.cs diff --git a/Rubberduck.Refactorings/ReplacePrivateUDTMemberReferences/PrivateUDTMemberReferenceReplacementExpressions.cs b/Rubberduck.Refactorings/ReplacePrivateUDTMemberReferences/PrivateUDTMemberReferenceReplacementExpressions.cs new file mode 100644 index 0000000000..8dc33094fd --- /dev/null +++ b/Rubberduck.Refactorings/ReplacePrivateUDTMemberReferences/PrivateUDTMemberReferenceReplacementExpressions.cs @@ -0,0 +1,21 @@ + +namespace Rubberduck.Refactorings.ReplacePrivateUDTMemberReferences +{ + public struct PrivateUDTMemberReferenceReplacementExpressions + { + public PrivateUDTMemberReferenceReplacementExpressions(string memberAccessExpression) + { + MemberAccesExpression = memberAccessExpression; + _localReferenceExpression = memberAccessExpression; + } + + public string MemberAccesExpression { set; get; } + + private string _localReferenceExpression; + public string LocalReferenceExpression + { + set => _localReferenceExpression = value; + get => _localReferenceExpression ?? MemberAccesExpression; + } + } +} diff --git a/Rubberduck.Refactorings/ReplacePrivateUDTMemberReferences/ReplacePrivateUDTMemberReferencesModel.cs b/Rubberduck.Refactorings/ReplacePrivateUDTMemberReferences/ReplacePrivateUDTMemberReferencesModel.cs new file mode 100644 index 0000000000..a07cfd0d47 --- /dev/null +++ b/Rubberduck.Refactorings/ReplacePrivateUDTMemberReferences/ReplacePrivateUDTMemberReferencesModel.cs @@ -0,0 +1,42 @@ +using Rubberduck.Parsing.Symbols; +using System.Collections.Generic; +using System.Linq; + +namespace Rubberduck.Refactorings.ReplacePrivateUDTMemberReferences +{ + public class ReplacePrivateUDTMemberReferencesModel : IRefactoringModel + { + private Dictionary<(VariableDeclaration, Declaration), PrivateUDTMemberReferenceReplacementExpressions> _udtTargets + = new Dictionary<(VariableDeclaration, Declaration), PrivateUDTMemberReferenceReplacementExpressions>(); + + private Dictionary _fieldToUserDefinedTypeInstance; + + public ReplacePrivateUDTMemberReferencesModel(Dictionary fieldToUserDefinedTypeInstance, IEnumerable userDefinedTypeMembers) + { + _fieldToUserDefinedTypeInstance = fieldToUserDefinedTypeInstance; + _udtMembers = userDefinedTypeMembers.ToList(); + } + + public IReadOnlyCollection Targets => _fieldToUserDefinedTypeInstance.Keys; + + private List _udtMembers; + public IReadOnlyCollection UDTMembers => _udtMembers; + + public UserDefinedTypeInstance UserDefinedTypeInstance(VariableDeclaration field) + => _fieldToUserDefinedTypeInstance[field]; + + public void AssignUDTMemberReferenceExpressions(VariableDeclaration field, Declaration udtMember, PrivateUDTMemberReferenceReplacementExpressions expressions) + { + _udtTargets.Add((field,udtMember), expressions); + } + + public (bool HasValue, string Expression) LocalReferenceExpression(VariableDeclaration field, Declaration udtMember) + { + if (_udtTargets.TryGetValue((field, udtMember), out var result)) + { + return (true, result.LocalReferenceExpression); + } + return (false, null); + } + } +} diff --git a/Rubberduck.Refactorings/ReplacePrivateUDTMemberReferences/ReplacePrivateUDTMemberReferencesModelFactory.cs b/Rubberduck.Refactorings/ReplacePrivateUDTMemberReferences/ReplacePrivateUDTMemberReferencesModelFactory.cs new file mode 100644 index 0000000000..8dd77dcb0c --- /dev/null +++ b/Rubberduck.Refactorings/ReplacePrivateUDTMemberReferences/ReplacePrivateUDTMemberReferencesModelFactory.cs @@ -0,0 +1,43 @@ +using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.VBA; +using System.Collections.Generic; +using System.Linq; + +namespace Rubberduck.Refactorings.ReplacePrivateUDTMemberReferences +{ + public interface IReplacePrivateUDTMemberReferencesModelFactory + { + ReplacePrivateUDTMemberReferencesModel Create(IEnumerable targets ); + } + + public class ReplacePrivateUDTMemberReferencesModelFactory : IReplacePrivateUDTMemberReferencesModelFactory + { + private readonly IDeclarationFinderProvider _declarationFinderProvider; + public ReplacePrivateUDTMemberReferencesModelFactory(IDeclarationFinderProvider declarationFinderProvider) + { + _declarationFinderProvider = declarationFinderProvider; + } + + public ReplacePrivateUDTMemberReferencesModel Create(IEnumerable targets) + { + var allUDTMembers = new List(); + var fieldsToUDTMembers = new Dictionary>(); + foreach (var target in targets) + { + var udtMembers = _declarationFinderProvider.DeclarationFinder.UserDeclarations(DeclarationType.UserDefinedTypeMember) + .Where(udtm => udtm.ParentDeclaration == target.AsTypeDeclaration); + + allUDTMembers.AddRange(udtMembers); + fieldsToUDTMembers.Add(target as VariableDeclaration, udtMembers); + } + + var fieldToUDTInstance = new Dictionary(); + foreach (var fieldToUDTMembers in fieldsToUDTMembers) + { + fieldToUDTInstance.Add(fieldToUDTMembers.Key, new UserDefinedTypeInstance(fieldToUDTMembers.Key, fieldToUDTMembers.Value)); + } + + return new ReplacePrivateUDTMemberReferencesModel(fieldToUDTInstance, allUDTMembers.Distinct()); + } + } +} diff --git a/Rubberduck.Refactorings/ReplacePrivateUDTMemberReferences/ReplacePrivateUDTMemberReferencesRefactoringAction.cs b/Rubberduck.Refactorings/ReplacePrivateUDTMemberReferences/ReplacePrivateUDTMemberReferencesRefactoringAction.cs new file mode 100644 index 0000000000..9a01651945 --- /dev/null +++ b/Rubberduck.Refactorings/ReplacePrivateUDTMemberReferences/ReplacePrivateUDTMemberReferencesRefactoringAction.cs @@ -0,0 +1,97 @@ +using Antlr4.Runtime; +using Rubberduck.Parsing; +using Rubberduck.Parsing.Grammar; +using Rubberduck.Parsing.Rewriter; +using Rubberduck.Parsing.Symbols; +using System.Collections.Generic; +using System.Linq; + +namespace Rubberduck.Refactorings.ReplacePrivateUDTMemberReferences +{ + /// + /// Replaces UserDefinedTypeMember IdentifierReferences of a Private UserDefinedType + /// with a Property IdentifierReference. + /// + public class ReplacePrivateUDTMemberReferencesRefactoringAction : CodeOnlyRefactoringActionBase + { + private Dictionary IdentifierReplacements { get; } = new Dictionary(); + + public ReplacePrivateUDTMemberReferencesRefactoringAction(IRewritingManager rewritingManager) + : base(rewritingManager) + { } + + public override void Refactor(ReplacePrivateUDTMemberReferencesModel model, IRewriteSession rewriteSession) + { + if (!(model.UDTMembers?.Any() ?? false)) + { + return; + } + + foreach (var target in model.Targets) + { + SetRewriteContent(target, model); + } + + RewriteReferences(rewriteSession); + } + + private void SetRewriteContent(VariableDeclaration target, ReplacePrivateUDTMemberReferencesModel model) + { + var udtInstance = model.UserDefinedTypeInstance(target); + foreach (var idRef in udtInstance.UDTMemberReferences) + { + var internalExpression = model.LocalReferenceExpression(target, idRef.Declaration); + if (internalExpression.HasValue) + { + SetUDTMemberReferenceRewriteContent(target, idRef, internalExpression.Expression); + } + } + } + + private void SetUDTMemberReferenceRewriteContent(VariableDeclaration instanceField, IdentifierReference idRef, string replacementText, bool moduleQualify = false) + { + if (idRef.Context.TryGetAncestor(out var maec)) + { + if (maec.TryGetChildContext(out var childMaec)) + { + if (childMaec.TryGetChildContext(out var smp)) + { + AddIdentifierReplacement(idRef, maec, $"{smp.GetText()}.{replacementText}"); + } + } + else if (maec.TryGetChildContext(out var wm)) + { + AddIdentifierReplacement(idRef, maec, $".{replacementText}"); + } + else + { + AddIdentifierReplacement(idRef, maec, replacementText); + } + } + else if (idRef.Context.TryGetAncestor(out var wmac)) + { + AddIdentifierReplacement(idRef, wmac, replacementText); + } + } + + private void AddIdentifierReplacement(IdentifierReference idRef, ParserRuleContext context, string replacementText) + { + if (IdentifierReplacements.ContainsKey(idRef)) + { + IdentifierReplacements[idRef] = (context, replacementText); + return; + } + IdentifierReplacements.Add(idRef, (context, replacementText)); + } + + private void RewriteReferences(IRewriteSession rewriteSession) + { + foreach (var replacement in IdentifierReplacements) + { + (ParserRuleContext Context, string Text) = replacement.Value; + var rewriter = rewriteSession.CheckOutModuleRewriter(replacement.Key.QualifiedModuleName); + rewriter.Replace(Context, Text); + } + } + } +} diff --git a/Rubberduck.Refactorings/ReplacePrivateUDTMemberReferences/UserDefinedTypeInstance.cs b/Rubberduck.Refactorings/ReplacePrivateUDTMemberReferences/UserDefinedTypeInstance.cs new file mode 100644 index 0000000000..2bf16aba0d --- /dev/null +++ b/Rubberduck.Refactorings/ReplacePrivateUDTMemberReferences/UserDefinedTypeInstance.cs @@ -0,0 +1,68 @@ +using Antlr4.Runtime; +using Rubberduck.Parsing; +using Rubberduck.Parsing.Grammar; +using Rubberduck.Parsing.Symbols; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; + +namespace Rubberduck.Refactorings.ReplacePrivateUDTMemberReferences +{ + public class UserDefinedTypeInstance + { + public UserDefinedTypeInstance(VariableDeclaration field, IEnumerable udtMembers) + { + if (!(field.AsTypeDeclaration?.DeclarationType.HasFlag(DeclarationType.UserDefinedType) ?? false)) + { + throw new ArgumentException(); + } + + InstanceField = field; + _udtMemberReferences = udtMembers.SelectMany(m => m.References) + .Where(rf => IsRelatedReference(rf, InstanceField.References)).ToList(); + } + + public VariableDeclaration InstanceField { get; } + + public string UserDefinedTypeIdentifier => InstanceField.AsTypeDeclaration.IdentifierName; + + private List _udtMemberReferences; + public IReadOnlyCollection UDTMemberReferences => _udtMemberReferences; + + private bool IsRelatedReference(IdentifierReference idRef, IEnumerable fieldReferences) + { + if (idRef.Context.TryGetAncestor(out var wmac)) + { + var goalContext = wmac.GetAncestor(); + return fieldReferences.Any(rf => HasSameAncestor(rf, goalContext)); + } + else if (idRef.Context.TryGetAncestor(out var memberAccessExprContext)) + { + return fieldReferences.Any(rf => HasSameAncestor(rf, memberAccessExprContext)); + } + throw new ArgumentOutOfRangeException(); + } + + private bool HasSameAncestor(IdentifierReference idRef, ParserRuleContext goalContext) where T : ParserRuleContext + { + Debug.Assert(goalContext != null); + Debug.Assert(goalContext is VBAParser.MemberAccessExprContext || goalContext is VBAParser.WithStmtContext); + + var guard = 0; + var accessExprContext = idRef.Context.GetAncestor(); + while (accessExprContext != null && ++guard < 100) + { + var prCtxt = accessExprContext as ParserRuleContext; + if (prCtxt == goalContext) + { + return true; + } + accessExprContext = accessExprContext.GetAncestor(); + } + + Debug.Assert(guard < 100); + return false; + } + } +} diff --git a/RubberduckTests/Refactoring/ReplacePrivateUDTMemberReferences/ReplacePrivateUDTMemberReferencesRefactoringActionTests.cs b/RubberduckTests/Refactoring/ReplacePrivateUDTMemberReferences/ReplacePrivateUDTMemberReferencesRefactoringActionTests.cs new file mode 100644 index 0000000000..07203596d0 --- /dev/null +++ b/RubberduckTests/Refactoring/ReplacePrivateUDTMemberReferences/ReplacePrivateUDTMemberReferencesRefactoringActionTests.cs @@ -0,0 +1,169 @@ +using NUnit.Framework; +using Rubberduck.Common; +using Rubberduck.Parsing.Rewriter; +using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.VBA; +using Rubberduck.Refactorings; +using Rubberduck.Refactorings.ReplacePrivateUDTMemberReferences; +using RubberduckTests.Mocks; +using RubberduckTests.Refactoring; +using System; +using System.Linq; + +namespace RubberduckTests.ReplacePrivateUDTMemberReferences +{ + [TestFixture] + public class ReplacePrivateUDTMemberReferencesRefactoringActionTests : RefactoringActionTestBase + { + [Test] + [Category("Refactorings")] + [Category("Encapsulate Field")] + [Category(nameof(ReplacePrivateUDTMemberReferencesRefactoringAction))] + public void RenameFieldReferences() + { + string inputCode = +$@" +Private Type TBazz + FirstValue As String + SecondValue As Long +End Type + +Private myBazz As TBazz + +Public Sub Fizz(newValue As String) + myBazz.FirstValue = newValue +End Sub + +Public Sub Bazz(newValue As String) + myBazz.SecondValue = newValue +End Sub +"; + + var vbe = MockVbeBuilder.BuildFromStdModules((MockVbeBuilder.TestModuleName, inputCode)); + + var testParam1 = new PrivateUDTExpressions("myBazz", "FirstValue") + { + InternalName = "TheFirst", + }; + var testParam2 = new PrivateUDTExpressions("myBazz", "SecondValue") + { + InternalName = "TheSecond", + }; + + var results = RefactoredCode(vbe.Object, state => TestModel(state, false, testParam1, testParam2)); + StringAssert.Contains(" TheFirst = newValue", results[MockVbeBuilder.TestModuleName]); + StringAssert.Contains(" TheSecond = newValue", results[MockVbeBuilder.TestModuleName]); + } + + [Test] + [Category("Refactorings")] + [Category("Encapsulate Field")] + [Category(nameof(ReplacePrivateUDTMemberReferencesRefactoringAction))] + public void RenameFieldReferences_WithMemberAccess() + { + string inputCode = +$@" +Private Type TBazz + FirstValue As String + SecondValue As Long +End Type + +Private myBazz As TBazz + +Public Sub Fizz(newValue As String) + With myBazz + .FirstValue = newValue + End With +End Sub + +Public Sub Bazz(newValue As String) + With myBazz + .SecondValue = newValue + End With +End Sub +"; + + var vbe = MockVbeBuilder.BuildFromStdModules((MockVbeBuilder.TestModuleName, inputCode)); + + var testParam1 = new PrivateUDTExpressions("myBazz", "FirstValue") + { + InternalName = "TheFirst", + }; + var testParam2 = new PrivateUDTExpressions("myBazz", "SecondValue") + { + InternalName = "TheSecond", + }; + + var results = RefactoredCode(vbe.Object, state => TestModel(state, false, testParam1, testParam2)); + StringAssert.Contains($" With myBazz{Environment.NewLine}", results[MockVbeBuilder.TestModuleName]); + StringAssert.Contains(" TheFirst = newValue", results[MockVbeBuilder.TestModuleName]); + StringAssert.Contains(" TheSecond = newValue", results[MockVbeBuilder.TestModuleName]); + } + + private ReplacePrivateUDTMemberReferencesModel TestModel(RubberduckParserState state, bool moduleQualify = true, params PrivateUDTExpressions[] fieldConversions) + { + var fields = state.DeclarationFinder.UserDeclarations(DeclarationType.Variable) + .Where(d => d.AsTypeDeclaration?.DeclarationType.HasFlag(DeclarationType.UserDefinedType) ?? false) + .Select(v => v as VariableDeclaration); + + var factory = new ReplacePrivateUDTMemberReferencesModelFactory(state); + + var model = factory.Create(fields); + + foreach (var fieldConversion in fieldConversions) + { + var fieldDeclaration = fields.Single(f => f.IdentifierName == fieldConversion.FieldID); + var udtMember = model.UDTMembers + .Single(udtm => udtm.ParentDeclaration == fieldDeclaration.AsTypeDeclaration + && udtm.IdentifierName == fieldConversion.UDTMemberID); + + var expressions = new PrivateUDTMemberReferenceReplacementExpressions(fieldConversion.InternalName); + + model.AssignUDTMemberReferenceExpressions(fieldDeclaration as VariableDeclaration, udtMember, expressions); + } + return model; + } + + private static bool IsExternalReference(IdentifierReference identifierReference) + => identifierReference.QualifiedModuleName != identifierReference.Declaration.QualifiedModuleName; + + private static Declaration GetUniquelyNamedDeclaration(IDeclarationFinderProvider declarationFinderProvider, DeclarationType declarationType, string identifier) + { + return declarationFinderProvider.DeclarationFinder.UserDeclarations(declarationType).Single(d => d.IdentifierName.Equals(identifier)); + } + + protected override IRefactoringAction TestBaseRefactoring(RubberduckParserState state, IRewritingManager rewritingManager) + { + return new ReplacePrivateUDTMemberReferencesRefactoringAction(rewritingManager); + } + + private struct PrivateUDTExpressions + { + public PrivateUDTExpressions(string fieldID, string udtMemberIdentifier) + { + FieldID = fieldID; + UDTMemberID = udtMemberIdentifier; + _externalName = null; + _internalName = null; + } + + public string FieldID { set; get; } + + public string UDTMemberID {set; get;} + + private string _internalName; + public string InternalName + { + set => _internalName = value; + get => _internalName ?? FieldID.CapitalizeFirstLetter(); + } + + private string _externalName; + public string ExternalName + { + set => _externalName = value; + get => _externalName ?? FieldID.CapitalizeFirstLetter(); + } + } +} +} diff --git a/RubberduckTests/Refactoring/ReplacePrivateUDTMemberReferences/UserDefinedTypeInstanceTests.cs b/RubberduckTests/Refactoring/ReplacePrivateUDTMemberReferences/UserDefinedTypeInstanceTests.cs new file mode 100644 index 0000000000..f5e9f64956 --- /dev/null +++ b/RubberduckTests/Refactoring/ReplacePrivateUDTMemberReferences/UserDefinedTypeInstanceTests.cs @@ -0,0 +1,136 @@ +using NUnit.Framework; +using Rubberduck.Parsing.Symbols; +using Rubberduck.Refactorings.ReplacePrivateUDTMemberReferences; +using Rubberduck.VBEditor.SafeComWrappers; +using RubberduckTests.Mocks; +using System.Linq; + +namespace RubberduckTests.Refactoring.ReplacePrivateUDTMemberReferences +{ + [TestFixture] + public class UserDefinedTypeInstanceTests + { + [Test] + [Category("Refactorings")] + [Category("Encapsulate Field")] + [Category(nameof(ReplacePrivateUDTMemberReferencesRefactoringAction))] + [Category(nameof(UserDefinedTypeInstanceTests))] + public void GetsCorrectReferenceCount() + { + string inputCode = +$@" +Private Type TBar + First As String + Second As String +End Type + +Private bizz_ As TBar + +Private fizz_ As TBar + +Public Sub Fizz(newValue As String) + With bizz_ + .First = newValue + End With +End Sub + +Public Sub Buzz(newValue As String) + With bizz_ + .Second = newValue + End With +End Sub + +Public Sub Fizz1(newValue As String) + bizz_.First = newValue +End Sub + +Public Sub Buzz1(newValue As String) + bizz_.Second = newValue +End Sub + +Public Sub Tazz(newValue As String) + fizz_.First = newValue + fizz_.Second = newValue +End Sub +"; + + var vbe = MockVbeBuilder.BuildFromModules((MockVbeBuilder.TestModuleName, inputCode, ComponentType.StandardModule)); + using (var state = MockParser.CreateAndParse(vbe.Object)) + { + var bizz_Target = state.DeclarationFinder.UserDeclarations(DeclarationType.Variable) + .Where(d => d.IdentifierName == "bizz_") + .Single(); + + var udtMembers = state.DeclarationFinder.UserDeclarations(DeclarationType.UserDefinedTypeMember); + + var sut = new UserDefinedTypeInstance(bizz_Target as VariableDeclaration, udtMembers); + Assert.AreEqual(4, sut.UDTMemberReferences.Count()); + } + } + + [Test] + [Category("Refactorings")] + [Category("Encapsulate Field")] + [Category(nameof(ReplacePrivateUDTMemberReferencesRefactoringAction))] + [Category(nameof(UserDefinedTypeInstanceTests))] + public void GetsCorrectReferenceCount_ClassAccessor() + { + string className = "TestClass"; + string classCode = +$@" +Public this As TBar +"; + + string classInstance = "theClass"; + string moduleName = MockVbeBuilder.TestModuleName; + string moduleCode = +$@" +Public Type TBar + First As String + Second As String +End Type + +Private {classInstance} As {className} + +Public Sub Initialize() + Set {classInstance} = New {className} +End Sub + +'Public Sub Fizz(newValue As String) +' With {classInstance} +' .this.First = newValue +' End With +'End Sub + +'Public Sub Buzz(newValue As String) +' With {classInstance} +' .this.Second = newValue +' End With +'End Sub + +Public Sub Fizz1(newValue As String) + {classInstance}.this.First = newValue +End Sub + +Public Sub Buzz1(newValue As String) + {classInstance}.this.Second = newValue +End Sub + +"; + + var vbe = MockVbeBuilder.BuildFromModules((moduleName, moduleCode, ComponentType.StandardModule), + (className, classCode, ComponentType.ClassModule)); + using (var state = MockParser.CreateAndParse(vbe.Object)) + { + var this_Target = state.DeclarationFinder.UserDeclarations(DeclarationType.Variable) + .Where(d => d.IdentifierName == "this") + .Single(); + + var udtMembers = state.DeclarationFinder.UserDeclarations(DeclarationType.UserDefinedTypeMember); + + var sut = new UserDefinedTypeInstance(this_Target as VariableDeclaration, udtMembers); + Assert.AreEqual(2, sut.UDTMemberReferences.Count()); + } + } + } +} From e29f3b25db75b930fe5b95b24c3b1eadf63ce409 Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Sat, 29 Aug 2020 09:41:14 -0700 Subject: [PATCH 14/67] Add EncapsulateFieldInsertNewCodeRefactoringAction --- .../EncapsulateFieldInsertNewCodeModel.cs | 38 ++++++ ...lateFieldInsertNewCodeRefactoringAction.cs | 128 ++++++++++++++++++ 2 files changed, 166 insertions(+) create mode 100644 Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCodeModel.cs create mode 100644 Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCodeRefactoringAction.cs diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCodeModel.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCodeModel.cs new file mode 100644 index 0000000000..a4496f7b7c --- /dev/null +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCodeModel.cs @@ -0,0 +1,38 @@ +using Rubberduck.VBEditor; +using Rubberduck.Refactorings.CodeBlockInsert; +using System.Collections.Generic; +using System.Linq; + +namespace Rubberduck.Refactorings.EncapsulateField +{ + public class EncapsulateFieldInsertNewCodeModel : IRefactoringModel + { + public EncapsulateFieldInsertNewCodeModel(IEnumerable selectedFieldCandidates) + { + _selectedCandidates = selectedFieldCandidates.ToList(); + if (_selectedCandidates.Any()) + { + QualifiedModuleName = _selectedCandidates.Select(f => f.QualifiedModuleName).First(); + } + } + + public bool IncludeNewContentMarker { set; get; } = false; + + public QualifiedModuleName QualifiedModuleName { get; } = new QualifiedModuleName(); + + private Dictionary> _newContent { set; get; } + public Dictionary> NewContent + { + set => _newContent = value; + get => _newContent; + } + + private List _selectedCandidates; + public IEnumerable SelectedFieldCandidates + => _selectedCandidates; + //{ + // //set => _selectedCandidates = value.ToList(); + // get => _selectedCandidates; + //} + } +} diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCodeRefactoringAction.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCodeRefactoringAction.cs new file mode 100644 index 0000000000..fb206552c7 --- /dev/null +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCodeRefactoringAction.cs @@ -0,0 +1,128 @@ +using Rubberduck.Parsing.Grammar; +using Rubberduck.Parsing.Rewriter; +using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.VBA; +using Rubberduck.Refactorings.Common; +using Rubberduck.Resources; +using Rubberduck.Refactorings.CodeBlockInsert; +using System; +using System.Diagnostics; +using System.Linq; + +namespace Rubberduck.Refactorings.EncapsulateField +{ + public class EncapsulateFieldInsertNewCodeRefactoringAction : CodeOnlyRefactoringActionBase + { + private const string FourSpaces = " "; + + private readonly IDeclarationFinderProvider _declarationFinderProvider; + private readonly IRewritingManager _rewritingManager; + private readonly ICodeBuilder _codeBuilder; + private readonly ICodeOnlyRefactoringAction _codeBlockInsertRefactoringAction; + public EncapsulateFieldInsertNewCodeRefactoringAction( + CodeBlockInsertRefactoringAction codeBlockInsertRefactoringAction, + IDeclarationFinderProvider declarationFinderProvider, + IRewritingManager rewritingManager, + ICodeBuilder codeBuilder) + : base(rewritingManager) + { + _declarationFinderProvider = declarationFinderProvider; + _rewritingManager = rewritingManager; + _codeBuilder = codeBuilder; + _codeBlockInsertRefactoringAction = codeBlockInsertRefactoringAction; + } + + public override void Refactor(EncapsulateFieldInsertNewCodeModel model, IRewriteSession rewriteSession) + { + var codeSectionStartIndex = _declarationFinderProvider.DeclarationFinder + .Members(model.QualifiedModuleName).Where(m => m.IsMember()) + .OrderBy(c => c.Selection) + .FirstOrDefault()?.Context.Start.TokenIndex; + + var codeBlockInsertModel = new CodeBlockInsertModel() + { + QualifiedModuleName = model.QualifiedModuleName, + SelectedFieldCandidates = model.SelectedFieldCandidates, + NewContent = model.NewContent, + CodeSectionStartIndex = codeSectionStartIndex, + IncludeComments = model.IncludeNewContentMarker + }; + + LoadNewPropertyBlocks(codeBlockInsertModel, _codeBuilder, rewriteSession); + + _codeBlockInsertRefactoringAction.Refactor(codeBlockInsertModel, rewriteSession); + } + + public void LoadNewPropertyBlocks(CodeBlockInsertModel model, ICodeBuilder codeBuilder, IRewriteSession rewriteSession) + { + if (model.IncludeComments) + { + model.AddContentBlock(NewContentType.PostContentMessage, RubberduckUI.EncapsulateField_PreviewMarker); + } + + foreach (var propertyAttributes in model.SelectedFieldCandidates.SelectMany(f => f.PropertyAttributeSets)) + { + Debug.Assert(propertyAttributes.Declaration.DeclarationType.HasFlag(DeclarationType.Variable) || propertyAttributes.Declaration.DeclarationType.HasFlag(DeclarationType.UserDefinedTypeMember)); + + LoadPropertyGetCodeBlock(model, propertyAttributes, codeBuilder); + + if (propertyAttributes.GenerateLetter) + { + LoadPropertyLetCodeBlock(model, propertyAttributes, codeBuilder); + } + + if (propertyAttributes.GenerateSetter) + { + LoadPropertySetCodeBlock(model, propertyAttributes, codeBuilder); + } + } + } + + private static void LoadPropertyLetCodeBlock(CodeBlockInsertModel model, PropertyAttributeSet propertyAttributes, ICodeBuilder codeBuilder) + { + var letterContent = $"{FourSpaces}{propertyAttributes.BackingField} = {propertyAttributes.ParameterName}"; + if (!codeBuilder.TryBuildPropertyLetCodeBlock(propertyAttributes.Declaration, propertyAttributes.PropertyName, out var propertyLet, content: letterContent)) + { + throw new ArgumentException(); + } + model.AddContentBlock(NewContentType.CodeSectionBlock, propertyLet); + } + + private static void LoadPropertySetCodeBlock(CodeBlockInsertModel model, PropertyAttributeSet propertyAttributes, ICodeBuilder codeBuilder) + { + var setterContent = $"{FourSpaces}{Tokens.Set} {propertyAttributes.BackingField} = {propertyAttributes.ParameterName}"; + if (!codeBuilder.TryBuildPropertySetCodeBlock(propertyAttributes.Declaration, propertyAttributes.PropertyName, out var propertySet, content: setterContent)) + { + throw new ArgumentException(); + } + model.AddContentBlock(NewContentType.CodeSectionBlock, propertySet); + } + + private static void LoadPropertyGetCodeBlock(CodeBlockInsertModel model, PropertyAttributeSet propertyAttributes, ICodeBuilder codeBuilder) + { + var getterContent = $"{propertyAttributes.PropertyName} = {propertyAttributes.BackingField}"; + if (propertyAttributes.UsesSetAssignment) + { + getterContent = $"{Tokens.Set} {getterContent}"; + } + + if (propertyAttributes.AsTypeName.Equals(Tokens.Variant) && !propertyAttributes.Declaration.IsArray) + { + getterContent = string.Join(Environment.NewLine, + $"{Tokens.If} IsObject({propertyAttributes.BackingField}) {Tokens.Then}", + $"{FourSpaces}{Tokens.Set} {propertyAttributes.PropertyName} = {propertyAttributes.BackingField}", + Tokens.Else, + $"{FourSpaces}{propertyAttributes.PropertyName} = {propertyAttributes.BackingField}", + $"{Tokens.End} {Tokens.If}", + Environment.NewLine); + } + + if (!codeBuilder.TryBuildPropertyGetCodeBlock(propertyAttributes.Declaration, propertyAttributes.PropertyName, out var propertyGet, content: $"{FourSpaces}{getterContent}")) + { + throw new ArgumentException(); + } + + model.AddContentBlock(NewContentType.CodeSectionBlock, propertyGet); + } + } +} From ab8d942e52ee18c5ea1e79dd7d589d1afcb85e3c Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Sat, 29 Aug 2020 09:55:59 -0700 Subject: [PATCH 15/67] Integrate RefactoringActions Removes EncapsulateFieldRefactoringAction base class to make UseBackingField and UseBackingUDTMember refactoring actions independent. Common functionality has been moved to new refactoringActions that are constructor injected into UseBackingField and UseBackingUDTMember refactoring actions. --- .../DeclareFieldsAsUDTMembersModel.cs | 2 +- ...lareFieldsAsUDTMembersRefactoringAction.cs | 2 +- .../EncapsulateField/EncapsulateFieldModel.cs | 32 +- ...capsulateFieldRefactoringActionImplBase.cs | 296 ------------------ ...apsulateFieldRefactoringActionsProvider.cs | 69 ++++ ...teFieldUseBackingFieldRefactoringAction.cs | 190 ++++++++--- ...eldUseBackingUDTMemberRefactoringAction.cs | 154 +++++---- ...ieldsAsUDTMembersRefactoringActionTests.cs | 2 +- .../EncapsulateFieldTestComponentResolver.cs | 69 +++- .../EncapsulatedUDTFieldTests.cs | 44 ++- 10 files changed, 423 insertions(+), 437 deletions(-) delete mode 100644 Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringActionImplBase.cs create mode 100644 Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringActionsProvider.cs diff --git a/Rubberduck.Refactorings/DeclareFieldsAsUDTMembers/DeclareFieldsAsUDTMembersModel.cs b/Rubberduck.Refactorings/DeclareFieldsAsUDTMembers/DeclareFieldsAsUDTMembersModel.cs index ced3ac45a0..23f75fd2f2 100644 --- a/Rubberduck.Refactorings/DeclareFieldsAsUDTMembers/DeclareFieldsAsUDTMembersModel.cs +++ b/Rubberduck.Refactorings/DeclareFieldsAsUDTMembers/DeclareFieldsAsUDTMembersModel.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; -namespace Rubberduck.Refactorings.MoveFieldsToUDT +namespace Rubberduck.Refactorings.DeclareFieldsAsUDTMembers { public class DeclareFieldsAsUDTMembersModel : IRefactoringModel { diff --git a/Rubberduck.Refactorings/DeclareFieldsAsUDTMembers/DeclareFieldsAsUDTMembersRefactoringAction.cs b/Rubberduck.Refactorings/DeclareFieldsAsUDTMembers/DeclareFieldsAsUDTMembersRefactoringAction.cs index 462fb038a7..d704bf0684 100644 --- a/Rubberduck.Refactorings/DeclareFieldsAsUDTMembers/DeclareFieldsAsUDTMembersRefactoringAction.cs +++ b/Rubberduck.Refactorings/DeclareFieldsAsUDTMembers/DeclareFieldsAsUDTMembersRefactoringAction.cs @@ -6,7 +6,7 @@ using System; using System.Collections.Generic; using System.Linq; -namespace Rubberduck.Refactorings.MoveFieldsToUDT +namespace Rubberduck.Refactorings.DeclareFieldsAsUDTMembers { public class DeclareFieldsAsUDTMembersRefactoringAction : CodeOnlyRefactoringActionBase { diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs index ab8f89951b..5c662e85ac 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs @@ -1,9 +1,9 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using Rubberduck.Parsing.Symbols; using Rubberduck.Parsing.VBA; using Rubberduck.VBEditor; +using Rubberduck.Refactorings.CodeBlockInsert; namespace Rubberduck.Refactorings.EncapsulateField { @@ -19,6 +19,8 @@ public class EncapsulateFieldModel : IRefactoringModel private IDictionary)> _udtFieldToUdtDeclarationMap = new Dictionary)>(); + private Dictionary> _newContent { set; get; } + public EncapsulateFieldModel( Declaration target, IEnumerable candidates, @@ -54,7 +56,10 @@ public EncapsulateFieldStrategy EncapsulateFieldStrategy get => _encapsulationFieldStategy; set { - if (_encapsulationFieldStategy == value) { return; } + if (_encapsulationFieldStategy == value) + { + return; + } _encapsulationFieldStategy = value; @@ -96,27 +101,36 @@ public IEnumerable SelectedFieldCandidates public IEnumerable UDTFieldCandidates => EncapsulationCandidates - .Where(v => v is IUserDefinedTypeCandidate) - .Cast(); + .Where(v => v is IUserDefinedTypeCandidate) + .Cast(); public IEnumerable SelectedUDTFieldCandidates => SelectedFieldCandidates - .Where(v => v is IUserDefinedTypeCandidate) - .Cast(); + .Where(v => v is IUserDefinedTypeCandidate) + .Cast(); public IEncapsulateFieldCandidate this[string encapsulatedFieldTargetID] => EncapsulationCandidates.Where(c => c.TargetID.Equals(encapsulatedFieldTargetID)).Single(); public IEncapsulateFieldCandidate this[Declaration fieldDeclaration] => EncapsulationCandidates.Where(c => c.Declaration == fieldDeclaration).Single(); - + + public void AddContentBlock(NewContentType contentType, string block) + => _newContent[contentType].Add(block); + + public Dictionary> NewContent + { + set => _newContent = value; + get => _newContent; + } + private IObjectStateUDT _activeObjectStateUDT; public IObjectStateUDT ObjectStateUDTField { get { _activeObjectStateUDT = ObjectStateUDTCandidates - .SingleOrDefault(os => os.IsSelected) ?? _newObjectStateUDT; + .SingleOrDefault(os => os.IsSelected) ?? _newObjectStateUDT; return _activeObjectStateUDT; } diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringActionImplBase.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringActionImplBase.cs deleted file mode 100644 index c6646d8382..0000000000 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringActionImplBase.cs +++ /dev/null @@ -1,296 +0,0 @@ -using Antlr4.Runtime; -using Rubberduck.Parsing; -using Rubberduck.Parsing.Grammar; -using Rubberduck.Parsing.Rewriter; -using Rubberduck.Parsing.Symbols; -using Rubberduck.Parsing.VBA; -using Rubberduck.Refactorings.Common; -using Rubberduck.Refactorings.EncapsulateField.Extensions; -using Rubberduck.Resources; -using Rubberduck.SmartIndenter; -using Rubberduck.VBEditor; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; - -namespace Rubberduck.Refactorings.EncapsulateField -{ - public abstract class EncapsulateFieldRefactoringActionImplBase : CodeOnlyRefactoringActionBase - { - private const string _defaultIndent = " "; //4 spaces - private static string _doubleSpace = $"{Environment.NewLine}{Environment.NewLine}"; - - protected enum NewContentType - { - TypeDeclarationBlock, - DeclarationBlock, - CodeSectionBlock, - PostContentMessage - }; - - protected readonly IDeclarationFinderProvider _declarationFinderProvider; - protected readonly IIndenter _indenter; - protected readonly ICodeBuilder _codeBuilder; - - protected QualifiedModuleName _targetQMN; - protected int? _codeSectionStartIndex; - - protected Dictionary> _newContent { set; get; } - protected Dictionary IdentifierReplacements { get; } = new Dictionary(); - - public EncapsulateFieldRefactoringActionImplBase( - IDeclarationFinderProvider declarationFinderProvider, - IIndenter indenter, - IRewritingManager rewritingManager, - ICodeBuilder codeBuilder) - : base(rewritingManager) - { - _declarationFinderProvider = declarationFinderProvider; - _indenter = indenter; - _codeBuilder = codeBuilder; - } - - protected IEnumerable SelectedFields { set; get; } - - protected IRewriteSession RefactorImpl(EncapsulateFieldModel model, IRewriteSession rewriteSession) - { - InitializeRefactoringAction(model); - - ModifyFields(rewriteSession); - - ModifyReferences(rewriteSession); - - InsertNewContent(rewriteSession, model.IncludeNewContentMarker); - - return rewriteSession; - } - - protected abstract void ModifyFields(IRewriteSession rewriteSession); - - protected abstract void LoadNewDeclarationBlocks(); - - protected void RewriteReferences(IRewriteSession rewriteSession) - { - foreach (var replacement in IdentifierReplacements) - { - (ParserRuleContext Context, string Text) = replacement.Value; - var rewriter = rewriteSession.CheckOutModuleRewriter(replacement.Key.QualifiedModuleName); - rewriter.Replace(Context, Text); - } - } - - protected void AddContentBlock(NewContentType contentType, string block) - => _newContent[contentType].Add(block); - - protected void ModifyReferences(IRewriteSession rewriteSession) - { - foreach (var field in SelectedFields) - { - LoadFieldReferenceContextReplacements(field); - } - - RewriteReferences(rewriteSession); - } - - protected virtual void LoadFieldReferenceContextReplacements(IEncapsulateFieldCandidate field) - { - if (field is IUserDefinedTypeCandidate udt && udt.TypeDeclarationIsPrivate) - { - foreach (var member in udt.Members) - { - foreach (var idRef in member.FieldContextReferences) - { - var replacementText = member.IdentifierForReference(idRef); - SetUDTMemberReferenceRewriteContent(idRef, replacementText); - } - } - } - else - { - foreach (var idRef in field.Declaration.References) - { - var replacementText = field.IdentifierForReference(idRef); - if (IsExternalReferenceRequiringModuleQualification(idRef)) - { - replacementText = $"{field.QualifiedModuleName.ComponentName}.{replacementText}"; - } - SetReferenceRewriteContent(idRef, replacementText); - } - } - } - - protected bool IsExternalReferenceRequiringModuleQualification(IdentifierReference idRef) - { - var isLHSOfMemberAccess = - (idRef.Context.Parent is VBAParser.MemberAccessExprContext - || idRef.Context.Parent is VBAParser.WithMemberAccessExprContext) - && !(idRef.Context == idRef.Context.Parent.GetChild(0)); - - return idRef.QualifiedModuleName != idRef.Declaration.QualifiedModuleName - && !isLHSOfMemberAccess; - } - - protected virtual void SetReferenceRewriteContent(IdentifierReference idRef, string replacementText) - { - if (idRef.Context is VBAParser.IndexExprContext idxExpression) - { - AddIdentifierReplacement(idRef, idxExpression.children.ElementAt(0) as ParserRuleContext, replacementText); - } - else if (idRef.Context is VBAParser.UnrestrictedIdentifierContext - || idRef.Context is VBAParser.SimpleNameExprContext) - { - AddIdentifierReplacement(idRef, idRef.Context, replacementText); - } - else if (idRef.Context.TryGetAncestor(out var wmac)) - { - AddIdentifierReplacement(idRef, wmac.GetChild(), replacementText); - } - else if (idRef.Context.TryGetAncestor(out var maec)) - { - AddIdentifierReplacement(idRef, maec, replacementText); - } - } - - protected virtual void SetUDTMemberReferenceRewriteContent(IdentifierReference idRef, string replacementText) - { - if (idRef.Context is VBAParser.IndexExprContext idxExpression) - { - AddIdentifierReplacement(idRef, idxExpression.children.ElementAt(0) as ParserRuleContext, replacementText); - } - else if (idRef.Context.TryGetAncestor(out var wmac)) - { - AddIdentifierReplacement(idRef, wmac.GetChild(), replacementText); - } - else if (idRef.Context.TryGetAncestor(out var maec)) - { - AddIdentifierReplacement(idRef, maec, replacementText); - } - } - - private void InitializeRefactoringAction(EncapsulateFieldModel model) - { - _targetQMN = model.QualifiedModuleName; - - _codeSectionStartIndex = _declarationFinderProvider.DeclarationFinder - .Members(model.QualifiedModuleName).Where(m => m.IsMember()) - .OrderBy(c => c.Selection) - .FirstOrDefault()?.Context.Start.TokenIndex ?? null; - - SelectedFields = model.SelectedFieldCandidates; - } - - private void AddIdentifierReplacement(IdentifierReference idRef, ParserRuleContext context, string replacementText) - { - if (IdentifierReplacements.ContainsKey(idRef)) - { - IdentifierReplacements[idRef] = (context, replacementText); - return; - } - IdentifierReplacements.Add(idRef, (context, replacementText)); - } - - private void InsertNewContent(IRewriteSession refactorRewriteSession, bool addNewContentMarker) - { - _newContent = new Dictionary> - { - { NewContentType.PostContentMessage, new List() }, - { NewContentType.DeclarationBlock, new List() }, - { NewContentType.CodeSectionBlock, new List() }, - { NewContentType.TypeDeclarationBlock, new List() } - }; - - if (addNewContentMarker) - { - AddContentBlock(NewContentType.PostContentMessage, RubberduckUI.EncapsulateField_PreviewMarker); - } - - LoadNewDeclarationBlocks(); - - LoadNewPropertyBlocks(); - - var newContentBlock = string.Join(_doubleSpace, - (_newContent[NewContentType.TypeDeclarationBlock]) - .Concat(_newContent[NewContentType.DeclarationBlock]) - .Concat(_newContent[NewContentType.CodeSectionBlock]) - .Concat(_newContent[NewContentType.PostContentMessage])) - .Trim(); - - newContentBlock = newContentBlock.LimitNewlines(3); - - if (string.IsNullOrEmpty(newContentBlock)) - { - return; - } - - var rewriter = refactorRewriteSession.CheckOutModuleRewriter(_targetQMN); - if (_codeSectionStartIndex.HasValue) - { - rewriter.InsertBefore(_codeSectionStartIndex.Value, $"{newContentBlock}{_doubleSpace}"); - } - else - { - rewriter.InsertBefore(rewriter.TokenStream.Size - 1, $"{_doubleSpace}{newContentBlock}"); - } - } - - protected void LoadNewPropertyBlocks() - { - foreach (var propertyAttributes in SelectedFields.SelectMany(f => f.PropertyAttributeSets)) - { - AddPropertyCodeBlocks(propertyAttributes); - } - } - - private void AddPropertyCodeBlocks(PropertyAttributeSet propertyAttributes) - { - Debug.Assert(propertyAttributes.Declaration.DeclarationType.HasFlag(DeclarationType.Variable) || propertyAttributes.Declaration.DeclarationType.HasFlag(DeclarationType.UserDefinedTypeMember)); - - var getContent = $"{propertyAttributes.PropertyName} = {propertyAttributes.BackingField}"; - if (propertyAttributes.UsesSetAssignment) - { - getContent = $"{Tokens.Set} {getContent}"; - } - - if (propertyAttributes.AsTypeName.Equals(Tokens.Variant) && !propertyAttributes.Declaration.IsArray) - { - getContent = string.Join(Environment.NewLine, - $"{Tokens.If} IsObject({propertyAttributes.BackingField}) {Tokens.Then}", - $"{_defaultIndent}{Tokens.Set} {propertyAttributes.PropertyName} = {propertyAttributes.BackingField}", - Tokens.Else, - $"{_defaultIndent}{propertyAttributes.PropertyName} = {propertyAttributes.BackingField}", - $"{Tokens.End} {Tokens.If}", - Environment.NewLine); - } - - if (!_codeBuilder.TryBuildPropertyGetCodeBlock(propertyAttributes.Declaration, propertyAttributes.PropertyName, out var propertyGet, content: $"{_defaultIndent}{getContent}")) - { - throw new ArgumentException(); - } - AddContentBlock(NewContentType.CodeSectionBlock, propertyGet); - - if (!(propertyAttributes.GenerateLetter || propertyAttributes.GenerateSetter)) - { - return; - } - - if (propertyAttributes.GenerateLetter) - { - if (!_codeBuilder.TryBuildPropertyLetCodeBlock(propertyAttributes.Declaration, propertyAttributes.PropertyName, out var propertyLet, content: $"{_defaultIndent}{propertyAttributes.BackingField} = {propertyAttributes.ParameterName}")) - { - throw new ArgumentException(); - } - AddContentBlock(NewContentType.CodeSectionBlock, propertyLet); - } - - if (propertyAttributes.GenerateSetter) - { - if (!_codeBuilder.TryBuildPropertySetCodeBlock(propertyAttributes.Declaration, propertyAttributes.PropertyName, out var propertySet, content: $"{_defaultIndent}{Tokens.Set} {propertyAttributes.BackingField} = {propertyAttributes.ParameterName}")) - { - throw new ArgumentException(); - } - AddContentBlock(NewContentType.CodeSectionBlock, propertySet); - } - } - } -} diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringActionsProvider.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringActionsProvider.cs new file mode 100644 index 0000000000..d9d387f4bf --- /dev/null +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringActionsProvider.cs @@ -0,0 +1,69 @@ +using Rubberduck.Parsing.Rewriter; +using Rubberduck.Parsing.VBA; +using Rubberduck.Refactorings.DeclareFieldsAsUDTMembers; +using Rubberduck.Refactorings.ReplaceDeclarationIdentifier; +using Rubberduck.Refactorings.ReplaceReferences; +using Rubberduck.Refactorings.ReplacePrivateUDTMemberReferences; +using Rubberduck.Refactorings.CodeBlockInsert; + +namespace Rubberduck.Refactorings.EncapsulateField +{ + public interface IEncapsulateFieldRefactoringActionsProvider + { + ICodeOnlyRefactoringAction ReplaceReferences { get; } + ICodeOnlyRefactoringAction ReplaceUDTMemberReferences { get; } + ICodeOnlyRefactoringAction ReplaceDeclarationIdentifiers { get; } + ICodeOnlyRefactoringAction CodeBlockInsert { get; } + ICodeOnlyRefactoringAction DeclareFieldsAsUDTMembers { get; } + ICodeOnlyRefactoringAction EncapsulateFieldInsertNewCode { get; } + } + + public class EncapsulateFieldRefactoringActionsProvider : IEncapsulateFieldRefactoringActionsProvider + { + private readonly IDeclarationFinderProvider _declarationFinderProvider; + private readonly IRewritingManager _rewritingManager; + private readonly ReplaceReferencesRefactoringAction _replaceReferences; + private readonly ReplaceDeclarationIdentifierRefactoringAction _replaceDeclarationIdentifiers; + private readonly CodeBlockInsertRefactoringAction _codeBlockInsertRefactoringAction; + private readonly ReplacePrivateUDTMemberReferencesRefactoringAction _replaceUDTMemberReferencesRefactoringAction; + private readonly DeclareFieldsAsUDTMembersRefactoringAction _declareFieldsAsUDTMembersRefactoringAction; + private readonly EncapsulateFieldInsertNewCodeRefactoringAction _encapsulateFieldInsertNewCodeRefactoringAction; + + public EncapsulateFieldRefactoringActionsProvider(IDeclarationFinderProvider declarationFinderProvider, + IRewritingManager rewritingManager, + ReplaceReferencesRefactoringAction replaceReferencesRefactoringAction, + ReplacePrivateUDTMemberReferencesRefactoringAction replaceUDTMemberReferencesRefactoringAction, + ReplaceDeclarationIdentifierRefactoringAction replaceDeclarationIdentifierRefactoringAction, + DeclareFieldsAsUDTMembersRefactoringAction declareFieldsAsUDTMembersRefactoringAction, + EncapsulateFieldInsertNewCodeRefactoringAction encapsulateFieldInsertNewCodeRefactoringAction, + CodeBlockInsertRefactoringAction codeBlockInsertRefactoringAction) + { + _declarationFinderProvider = declarationFinderProvider; + _rewritingManager = rewritingManager; + _replaceReferences = replaceReferencesRefactoringAction; + _replaceUDTMemberReferencesRefactoringAction = replaceUDTMemberReferencesRefactoringAction; + _replaceDeclarationIdentifiers = replaceDeclarationIdentifierRefactoringAction; + _declareFieldsAsUDTMembersRefactoringAction = declareFieldsAsUDTMembersRefactoringAction; + _codeBlockInsertRefactoringAction = codeBlockInsertRefactoringAction; + _encapsulateFieldInsertNewCodeRefactoringAction = encapsulateFieldInsertNewCodeRefactoringAction; + } + + public ICodeOnlyRefactoringAction ReplaceReferences + => _replaceReferences; + + public ICodeOnlyRefactoringAction ReplaceDeclarationIdentifiers + => _replaceDeclarationIdentifiers; + + public ICodeOnlyRefactoringAction CodeBlockInsert + => _codeBlockInsertRefactoringAction; + + public ICodeOnlyRefactoringAction ReplaceUDTMemberReferences + => _replaceUDTMemberReferencesRefactoringAction; + + public ICodeOnlyRefactoringAction DeclareFieldsAsUDTMembers + => _declareFieldsAsUDTMembersRefactoringAction; + + public ICodeOnlyRefactoringAction EncapsulateFieldInsertNewCode + => _encapsulateFieldInsertNewCodeRefactoringAction; + } +} diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingFieldRefactoringAction.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingFieldRefactoringAction.cs index 5cf95823fd..2c1676f2bb 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingFieldRefactoringAction.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingFieldRefactoringAction.cs @@ -1,59 +1,173 @@ -using Rubberduck.Parsing; +using Rubberduck.Common; +using Rubberduck.Parsing; using Rubberduck.Parsing.Grammar; using Rubberduck.Parsing.Rewriter; using Rubberduck.Parsing.Symbols; using Rubberduck.Parsing.VBA; using Rubberduck.Refactorings.Common; -using Rubberduck.SmartIndenter; +using Rubberduck.Refactorings.ReplaceDeclarationIdentifier; +using Rubberduck.Refactorings.ReplaceReferences; +using Rubberduck.Refactorings.ReplacePrivateUDTMemberReferences; +using Rubberduck.Refactorings.CodeBlockInsert; using System; using System.Collections.Generic; using System.Linq; namespace Rubberduck.Refactorings.EncapsulateField { - public class EncapsulateFieldUseBackingFieldRefactoringAction : EncapsulateFieldRefactoringActionImplBase + public class EncapsulateFieldUseBackingFieldRefactoringAction : CodeOnlyRefactoringActionBase { + private readonly IDeclarationFinderProvider _declarationFinderProvider; + private readonly ICodeOnlyRefactoringAction _replaceUDTMemberReferencesRefactoringAction; + private readonly ICodeOnlyRefactoringAction _replaceReferencesRefactoringAction; + private readonly ICodeOnlyRefactoringAction _replaceDeclarationIdentifiers; + private readonly ICodeOnlyRefactoringAction _encapsulateFieldInsertNewCodeRefactoringAction; + private readonly IReplacePrivateUDTMemberReferencesModelFactory _replaceUDTMemberReferencesModelFactory; + public EncapsulateFieldUseBackingFieldRefactoringAction( + IEncapsulateFieldRefactoringActionsProvider refactoringActionsProvider, + IReplacePrivateUDTMemberReferencesModelFactory replaceUDTMemberReferencesModelFactory, IDeclarationFinderProvider declarationFinderProvider, - IIndenter indenter, - IRewritingManager rewritingManager, - ICodeBuilder codeBuilder) - : base(declarationFinderProvider, indenter, rewritingManager, codeBuilder) - {} + IRewritingManager rewritingManager) + :base(rewritingManager) + { + _declarationFinderProvider = declarationFinderProvider; + _replaceUDTMemberReferencesRefactoringAction = refactoringActionsProvider.ReplaceUDTMemberReferences; + _replaceReferencesRefactoringAction = refactoringActionsProvider.ReplaceReferences; + _replaceDeclarationIdentifiers = refactoringActionsProvider.ReplaceDeclarationIdentifiers; + _encapsulateFieldInsertNewCodeRefactoringAction = refactoringActionsProvider.EncapsulateFieldInsertNewCode; + _replaceUDTMemberReferencesModelFactory = replaceUDTMemberReferencesModelFactory; + } public override void Refactor(EncapsulateFieldModel model, IRewriteSession rewriteSession) { - RefactorImpl(model, rewriteSession); + if (!model.SelectedFieldCandidates.Any()) + { + return; + } + + model.NewContent = new Dictionary> + { + { NewContentType.PostContentMessage, new List() }, + { NewContentType.DeclarationBlock, new List() }, + { NewContentType.CodeSectionBlock, new List() }, + { NewContentType.TypeDeclarationBlock, new List() } + }; + + ModifyFields(model, rewriteSession); + + ModifyReferences(model, rewriteSession); + + InsertNewContent(model, rewriteSession); } - protected override void ModifyFields(IRewriteSession rewriteSession) + private void ModifyFields(EncapsulateFieldModel model, IRewriteSession rewriteSession) { - var fieldDeclarationsToDeleteAndReplace = SelectedFields.Where(f => IsFieldToDeleteAndReplace(f)); - var rewriter = rewriteSession.CheckOutModuleRewriter(_targetQMN); + var fieldDeclarationsToDeleteAndReplace = model.SelectedFieldCandidates + .Where(f => f.Declaration.IsDeclaredInList() + && !f.Declaration.HasPrivateAccessibility()) + .ToList(); + + var rewriter = rewriteSession.CheckOutModuleRewriter(model.QualifiedModuleName); + rewriter.RemoveVariables(fieldDeclarationsToDeleteAndReplace.Select(f => f.Declaration) + .Cast()); - rewriter.RemoveVariables(fieldDeclarationsToDeleteAndReplace.Select(f => f.Declaration).Cast()); + foreach (var field in fieldDeclarationsToDeleteAndReplace) + { + var targetIdentifier = field.Declaration.Context.GetText().Replace(field.IdentifierName, field.BackingIdentifier); + var newField = field.Declaration.IsTypeSpecified + ? $"{Tokens.Private} {targetIdentifier}" + : $"{Tokens.Private} {targetIdentifier} {Tokens.As} {field.Declaration.AsTypeName}"; - var fieldDeclaraionsToRetain = SelectedFields.Except(fieldDeclarationsToDeleteAndReplace).ToList(); + model.AddContentBlock(NewContentType.DeclarationBlock, newField); + } - if (fieldDeclaraionsToRetain.Any()) + var retainedFieldDeclarations = model.SelectedFieldCandidates.Except(fieldDeclarationsToDeleteAndReplace).ToList(); + + if (retainedFieldDeclarations.Any()) { - MakeImplicitDeclarationTypeExplicit(fieldDeclaraionsToRetain, rewriter); + MakeImplicitDeclarationTypeExplicit(retainedFieldDeclarations, rewriter); + + SetPrivateVariableVisiblity(retainedFieldDeclarations, rewriter); + + Rename(retainedFieldDeclarations, rewriteSession); + } + } + + private void ModifyReferences(EncapsulateFieldModel model, IRewriteSession rewriteSession) + { + var privateUdtInstances = model.SelectedFieldCandidates + .Where(f => (f.Declaration.AsTypeDeclaration?.DeclarationType.HasFlag(DeclarationType.UserDefinedType) ?? false) + && f.Declaration.AsTypeDeclaration.Accessibility == Accessibility.Private); + + ReplaceEncapsulatedPrivateUserDefinedTypeMemberReferences(privateUdtInstances, rewriteSession); + ReplaceEncapsulatedFieldReferences(model.SelectedFieldCandidates.Except(privateUdtInstances), rewriteSession); + } + + private void InsertNewContent(EncapsulateFieldModel model, IRewriteSession rewriteSession) + { + var encapsulateFieldInsertNewCodeModel = new EncapsulateFieldInsertNewCodeModel(model.SelectedFieldCandidates) + { + NewContent = model.NewContent, + IncludeNewContentMarker = model.IncludeNewContentMarker + }; + _encapsulateFieldInsertNewCodeRefactoringAction.Refactor(encapsulateFieldInsertNewCodeModel, rewriteSession); + } - SetPrivateVariableVisiblity(fieldDeclaraionsToRetain, rewriter); + private void ReplaceEncapsulatedFieldReferences(IEnumerable fieldCandidates, IRewriteSession rewriteSession) + { + var model = new ReplaceReferencesModel() + { + ModuleQualifyExternalReferences = true + }; + foreach (var field in fieldCandidates) + { + foreach (var idRef in field.Declaration.References) + { + var replacementExpression = idRef.QualifiedModuleName == field.QualifiedModuleName + ? field.Declaration.IsArray ? field.BackingIdentifier : field.PropertyIdentifier + : field.PropertyIdentifier; - Rename(fieldDeclaraionsToRetain, rewriter); + model.AssignFieldReferenceReplacementExpression(idRef, replacementExpression); + } } + + _replaceReferencesRefactoringAction.Refactor(model, rewriteSession); } - private static void MakeImplicitDeclarationTypeExplicit(IEnumerable fields, IModuleRewriter rewriter) + private void ReplaceEncapsulatedPrivateUserDefinedTypeMemberReferences(IEnumerable udtFieldCandidates, IRewriteSession rewriteSession) { - foreach (var element in fields.Select(f => f.Declaration)) + if (!udtFieldCandidates.Any()) { - if (!element.Context.TryGetChildContext(out _)) + return; + } + + var replacePrivateUDTMemberReferencesModel = _replaceUDTMemberReferencesModelFactory.Create(udtFieldCandidates.Select(f => f.Declaration).Cast()); + + foreach (var udtfield in udtFieldCandidates) + { + foreach (var udtMember in replacePrivateUDTMemberReferencesModel.UDTMembers) { - rewriter.InsertAfter(element.Context.Stop.TokenIndex, $" {Tokens.As} {element.AsTypeName}"); + var udtExpressions = new PrivateUDTMemberReferenceReplacementExpressions($"{udtfield.IdentifierName}.{udtMember.IdentifierName}") + { + LocalReferenceExpression = udtMember.IdentifierName.CapitalizeFirstLetter(), + }; + + replacePrivateUDTMemberReferencesModel.AssignUDTMemberReferenceExpressions(udtfield.Declaration as VariableDeclaration, udtMember, udtExpressions); } + _replaceUDTMemberReferencesRefactoringAction.Refactor(replacePrivateUDTMemberReferencesModel, rewriteSession); + } + } + + private static void MakeImplicitDeclarationTypeExplicit(IEnumerable fields, IModuleRewriter rewriter) + { + var fieldsToChange = fields.Where(f => !f.Declaration.Context.TryGetChildContext(out _)) + .Select(f => f.Declaration); + + foreach (var field in fieldsToChange) + { + rewriter.InsertAfter(field.Context.Stop.TokenIndex, $" {Tokens.As} {field.AsTypeName}"); } } @@ -79,36 +193,12 @@ private static void SetPrivateVariableVisiblity(IEnumerable fields, IModuleRewriter rewriter) + private void Rename(IEnumerable fields, IRewriteSession rewriteSession) { - var fieldsToRename = fields.Where(f => !f.BackingIdentifier.Equals(f.Declaration.IdentifierName)); + var fieldToNewNamePairs = fields.Where(f => !f.BackingIdentifier.Equals(f.Declaration.IdentifierName, StringComparison.InvariantCultureIgnoreCase)) + .Select(f => (f.Declaration, f.BackingIdentifier)); - foreach (var field in fieldsToRename) - { - if (!(field.Declaration.Context is IIdentifierContext context)) - { - throw new ArgumentException(); - } - - rewriter.Replace(context.IdentifierTokens, field.BackingIdentifier); - } - } - - protected override void LoadNewDeclarationBlocks() - { - //Fields to create here were deleted in ModifyFields(...) - foreach (var field in SelectedFields.Where(f => IsFieldToDeleteAndReplace(f))) - { - var targetIdentifier = field.Declaration.Context.GetText().Replace(field.IdentifierName, field.BackingIdentifier); - var newField = field.Declaration.IsTypeSpecified - ? $"{Tokens.Private} {targetIdentifier}" - : $"{Tokens.Private} {targetIdentifier} {Tokens.As} {field.Declaration.AsTypeName}"; - - AddContentBlock(NewContentType.DeclarationBlock, newField); - } + _replaceDeclarationIdentifiers.Refactor(new ReplaceDeclarationIdentifierModel(fieldToNewNamePairs), rewriteSession); } - - private static bool IsFieldToDeleteAndReplace(IEncapsulateFieldCandidate field) - => field.Declaration.IsDeclaredInList() && !field.Declaration.HasPrivateAccessibility(); } } diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs index e5cca1c4ec..89e241afe9 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs @@ -1,111 +1,153 @@ -using Rubberduck.Parsing.Rewriter; +using Rubberduck.Common; +using Rubberduck.Parsing.Grammar; +using Rubberduck.Parsing.Rewriter; using Rubberduck.Parsing.Symbols; using Rubberduck.Parsing.VBA; using Rubberduck.Refactorings.Common; -using Rubberduck.Refactorings.MoveFieldsToUDT; -using Rubberduck.SmartIndenter; -using System.Diagnostics; +using Rubberduck.Refactorings.DeclareFieldsAsUDTMembers; +using Rubberduck.Refactorings.ReplaceReferences; +using Rubberduck.Refactorings.ReplacePrivateUDTMemberReferences; +using Rubberduck.Refactorings.CodeBlockInsert; +using System.Collections.Generic; using System.Linq; namespace Rubberduck.Refactorings.EncapsulateField { - public class EncapsulateFieldUseBackingUDTMemberRefactoringAction : EncapsulateFieldRefactoringActionImplBase + public class EncapsulateFieldUseBackingUDTMemberRefactoringAction : CodeOnlyRefactoringActionBase { - private IObjectStateUDT _stateUDTField; - private readonly DeclareFieldsAsUDTMembersRefactoringAction _convertFieldToUDTMemberRefactoringAction; + private readonly IDeclarationFinderProvider _declarationFinderProvider; + private readonly ICodeOnlyRefactoringAction _declareFieldAsUDTMemberRefactoringAction; + private readonly ICodeOnlyRefactoringAction _replaceUDTMemberReferencesRefactoringAction; + private readonly ICodeOnlyRefactoringAction _replaceFieldReferencesRefactoringAction; + private readonly ICodeOnlyRefactoringAction _encapsulateFieldInsertNewCodeRefactoringAction; + private readonly ICodeBuilder _codeBuilder; + private readonly IReplacePrivateUDTMemberReferencesModelFactory _replaceUDTMemberReferencesModelFactory; public EncapsulateFieldUseBackingUDTMemberRefactoringAction( - DeclareFieldsAsUDTMembersRefactoringAction convertFieldToUDTMemberRefactoringAction, + IEncapsulateFieldRefactoringActionsProvider refactoringActionsProvider, + IReplacePrivateUDTMemberReferencesModelFactory replaceUDTMemberReferencesModelFactory, IDeclarationFinderProvider declarationFinderProvider, - IIndenter indenter, IRewritingManager rewritingManager, ICodeBuilder codeBuilder) - : base(declarationFinderProvider, indenter, rewritingManager, codeBuilder) + : base(rewritingManager) { - _convertFieldToUDTMemberRefactoringAction = convertFieldToUDTMemberRefactoringAction; + _declarationFinderProvider = declarationFinderProvider; + _declareFieldAsUDTMemberRefactoringAction = refactoringActionsProvider.DeclareFieldsAsUDTMembers; + _replaceUDTMemberReferencesRefactoringAction = refactoringActionsProvider.ReplaceUDTMemberReferences; + _replaceFieldReferencesRefactoringAction = refactoringActionsProvider.ReplaceReferences; + _encapsulateFieldInsertNewCodeRefactoringAction = refactoringActionsProvider.EncapsulateFieldInsertNewCode; + _codeBuilder = codeBuilder; + _replaceUDTMemberReferencesModelFactory = replaceUDTMemberReferencesModelFactory; } public override void Refactor(EncapsulateFieldModel model, IRewriteSession rewriteSession) { - _stateUDTField = model.ObjectStateUDTField; + if (!model.SelectedFieldCandidates.Any()) + { + return; + } + + model.NewContent = new Dictionary> + { + { NewContentType.PostContentMessage, new List() }, + { NewContentType.DeclarationBlock, new List() }, + { NewContentType.CodeSectionBlock, new List() }, + { NewContentType.TypeDeclarationBlock, new List() } + }; + + ModifyFields(model, rewriteSession); - RefactorImpl(model, rewriteSession); + ModifyReferences(model, rewriteSession); + + InsertNewContent(model, rewriteSession); } - protected override void ModifyFields(IRewriteSession rewriteSession) + private void ModifyFields(EncapsulateFieldModel encapsulateFieldModel, IRewriteSession rewriteSession) { - var rewriter = rewriteSession.CheckOutModuleRewriter(_targetQMN); + var rewriter = rewriteSession.CheckOutModuleRewriter(encapsulateFieldModel.QualifiedModuleName); - rewriter.RemoveVariables(SelectedFields.Select(f => f.Declaration) + rewriter.RemoveVariables(encapsulateFieldModel.SelectedFieldCandidates.Select(f => f.Declaration) .Cast()); - if (_stateUDTField.IsExistingDeclaration) + if (encapsulateFieldModel.ObjectStateUDTField.IsExistingDeclaration) { var model = new DeclareFieldsAsUDTMembersModel(); - foreach (var field in SelectedFields) + foreach (var field in encapsulateFieldModel.SelectedFieldCandidates) { - model.AssignFieldToUserDefinedType(_stateUDTField.AsTypeDeclaration, field.Declaration as VariableDeclaration, field.PropertyIdentifier); + model.AssignFieldToUserDefinedType(encapsulateFieldModel.ObjectStateUDTField.AsTypeDeclaration, field.Declaration as VariableDeclaration, field.PropertyIdentifier); } - _convertFieldToUDTMemberRefactoringAction.Refactor(model, rewriteSession); + _declareFieldAsUDTMemberRefactoringAction.Refactor(model, rewriteSession); } - } - - protected override void LoadNewDeclarationBlocks() - { - if (_stateUDTField.IsExistingDeclaration) + else { - return; - } + var newUDTMembers = encapsulateFieldModel.SelectedFieldCandidates + .Select(m => (m.Declaration as VariableDeclaration, m.BackingIdentifier)); - _stateUDTField.AddMembers(SelectedFields.Cast()); + var typeDeclarationBlock = _codeBuilder.BuildUserDefinedTypeDeclaration(encapsulateFieldModel.ObjectStateUDTField.AsTypeName, newUDTMembers); - AddContentBlock(NewContentType.TypeDeclarationBlock, _stateUDTField.TypeDeclarationBlock(_indenter)); + encapsulateFieldModel.AddContentBlock(NewContentType.TypeDeclarationBlock, typeDeclarationBlock); - AddContentBlock(NewContentType.DeclarationBlock, _stateUDTField.FieldDeclarationBlock); - return; + encapsulateFieldModel.AddContentBlock(NewContentType.DeclarationBlock, $"{Accessibility.Private} {encapsulateFieldModel.ObjectStateUDTField.IdentifierName} {Tokens.As} {encapsulateFieldModel.ObjectStateUDTField.AsTypeName}"); + } } - protected override void LoadFieldReferenceContextReplacements(IEncapsulateFieldCandidate field) + private void ModifyReferences(EncapsulateFieldModel model, IRewriteSession rewriteSession) { - Debug.Assert(field is IConvertToUDTMember); + var udtFields = model.SelectedFieldCandidates + .Where(f => (f.Declaration.AsTypeDeclaration?.DeclarationType.HasFlag(DeclarationType.UserDefinedType) ?? false) + && f.Declaration.AsTypeDeclaration.Accessibility == Accessibility.Private); - var converted = field as IConvertToUDTMember; - if (converted.WrappedCandidate is IUserDefinedTypeCandidate udt && udt.TypeDeclarationIsPrivate) + if (udtFields.Any()) { - foreach (var member in udt.Members) + var replaceUDTMemberReferencesModel = _replaceUDTMemberReferencesModelFactory.Create(udtFields.Select(f => f.Declaration).Cast()); + + foreach (var udtfield in udtFields) { - foreach (var idRef in member.FieldContextReferences) + foreach (var udtMember in replaceUDTMemberReferencesModel.UDTMembers) { - var replacementText = member.IdentifierForReference(idRef); - if (IsExternalReferenceRequiringModuleQualification(idRef)) + var localReplacement = udtfield.Declaration.IsArray + ? $"{udtfield.IdentifierName}.{udtMember.IdentifierName.CapitalizeFirstLetter()}" + : udtMember.IdentifierName.CapitalizeFirstLetter(); + + var udtExpressions = new PrivateUDTMemberReferenceReplacementExpressions($"{udtfield.IdentifierName}.{udtMember.IdentifierName}") { - replacementText = $"{udt.QualifiedModuleName.ComponentName}.{replacementText}"; - } + LocalReferenceExpression = udtMember.IdentifierName.CapitalizeFirstLetter(), + }; - SetUDTMemberReferenceRewriteContent(idRef, replacementText); + replaceUDTMemberReferencesModel.AssignUDTMemberReferenceExpressions(udtfield.Declaration as VariableDeclaration, udtMember, udtExpressions); } + _replaceUDTMemberReferencesRefactoringAction.Refactor(replaceUDTMemberReferencesModel, rewriteSession); } } - else + + var modelReplaceField = new ReplaceReferencesModel() + { + ModuleQualifyExternalReferences = true, + }; + + foreach (var field in model.SelectedFieldCandidates.Except(udtFields)) { foreach (var idRef in field.Declaration.References) { - var replacementText = converted.IdentifierForReference(idRef); - - if (IsExternalReferenceRequiringModuleQualification(idRef)) - { - replacementText = $"{converted.QualifiedModuleName.ComponentName}.{replacementText}"; - } - - if (converted.Declaration.IsArray) - { - replacementText = $"{_stateUDTField.FieldIdentifier}.{replacementText}"; - } - - SetReferenceRewriteContent(idRef, replacementText); + var replacementExpression = idRef.QualifiedModuleName == field.QualifiedModuleName + ? field.Declaration.IsArray ? $"{model.ObjectStateUDTField.FieldIdentifier}.{field.BackingIdentifier}" : field.PropertyIdentifier + : field.PropertyIdentifier; + modelReplaceField.AssignFieldReferenceReplacementExpression(idRef, replacementExpression); } + } + _replaceFieldReferencesRefactoringAction.Refactor(modelReplaceField, rewriteSession); + } + + private void InsertNewContent(EncapsulateFieldModel model, IRewriteSession rewriteSession) + { + var encapsulateFieldInsertNewCodeModel = new EncapsulateFieldInsertNewCodeModel(model.SelectedFieldCandidates) + { + NewContent = model.NewContent, + IncludeNewContentMarker = model.IncludeNewContentMarker + }; + _encapsulateFieldInsertNewCodeRefactoringAction.Refactor(encapsulateFieldInsertNewCodeModel, rewriteSession); } } } diff --git a/RubberduckTests/Refactoring/DeclareFieldsAsUDTMembers/DeclareFieldsAsUDTMembersRefactoringActionTests.cs b/RubberduckTests/Refactoring/DeclareFieldsAsUDTMembers/DeclareFieldsAsUDTMembersRefactoringActionTests.cs index d91eebc7c6..94321c9810 100644 --- a/RubberduckTests/Refactoring/DeclareFieldsAsUDTMembers/DeclareFieldsAsUDTMembersRefactoringActionTests.cs +++ b/RubberduckTests/Refactoring/DeclareFieldsAsUDTMembers/DeclareFieldsAsUDTMembersRefactoringActionTests.cs @@ -3,7 +3,7 @@ using Rubberduck.Parsing.Symbols; using Rubberduck.Parsing.VBA; using Rubberduck.Refactorings; -using Rubberduck.Refactorings.MoveFieldsToUDT; +using Rubberduck.Refactorings.DeclareFieldsAsUDTMembers; using System.Collections.Generic; using System.Linq; diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs index 76268af9e5..c692e47076 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs @@ -1,10 +1,14 @@ using Rubberduck.Parsing.Rewriter; using Rubberduck.Parsing.VBA; using Rubberduck.Refactorings; -using Rubberduck.Refactorings.MoveFieldsToUDT; +using Rubberduck.Refactorings.DeclareFieldsAsUDTMembers; using Rubberduck.Refactorings.EncapsulateField; using Rubberduck.SmartIndenter; using Rubberduck.VBEditor.SafeComWrappers.Abstract; +using Rubberduck.Refactorings.ReplaceReferences; +using Rubberduck.Refactorings.ReplacePrivateUDTMemberReferences; +using Rubberduck.Refactorings.ReplaceDeclarationIdentifier; +using Rubberduck.Refactorings.CodeBlockInsert; namespace RubberduckTests.Refactoring.EncapsulateField { @@ -28,19 +32,68 @@ public EncapsulateFieldTestComponentResolver(IDeclarationFinderProvider declarat switch (typeof(T).Name) { case nameof(EncapsulateFieldRefactoringAction): - return new EncapsulateFieldRefactoringAction(ResolveImpl(), ResolveImpl()) as T; + return new EncapsulateFieldRefactoringAction( + ResolveImpl(), + ResolveImpl()) as T; + case nameof(ReplaceReferencesRefactoringAction): + return new ReplaceReferencesRefactoringAction(_rewritingManager) as T; + case nameof(ReplaceDeclarationIdentifierRefactoringAction): + return new ReplaceDeclarationIdentifierRefactoringAction(_rewritingManager) as T; + case nameof(CodeBlockInsertRefactoringAction): + return new CodeBlockInsertRefactoringAction(_declarationFinderProvider, + _rewritingManager, + new CodeBuilder()) as T; + case nameof(EncapsulateFieldInsertNewCodeRefactoringAction): + return new EncapsulateFieldInsertNewCodeRefactoringAction( + ResolveImpl(), + _declarationFinderProvider, + _rewritingManager, + new CodeBuilder()) as T; + case nameof(ReplacePrivateUDTMemberReferencesRefactoringAction): + return new ReplacePrivateUDTMemberReferencesRefactoringAction(_rewritingManager) as T; + case nameof(IEncapsulateFieldRefactoringActionsProvider): + case nameof(EncapsulateFieldRefactoringActionsProvider): + return new EncapsulateFieldRefactoringActionsProvider( + _declarationFinderProvider, + _rewritingManager, + ResolveImpl(), + ResolveImpl(), + ResolveImpl(), + ResolveImpl(), + ResolveImpl(), + ResolveImpl()) as T; case nameof(EncapsulateFieldUseBackingFieldRefactoringAction): - return new EncapsulateFieldUseBackingFieldRefactoringAction(_declarationFinderProvider, CreateIndenter(), _rewritingManager, new CodeBuilder()) as T; + return new EncapsulateFieldUseBackingFieldRefactoringAction( + ResolveImpl(), + ResolveImpl(), + _declarationFinderProvider, + _rewritingManager) as T; case nameof(EncapsulateFieldUseBackingUDTMemberRefactoringAction): - return new EncapsulateFieldUseBackingUDTMemberRefactoringAction(ResolveImpl(), _declarationFinderProvider, CreateIndenter(), _rewritingManager, new CodeBuilder()) as T; + return new EncapsulateFieldUseBackingUDTMemberRefactoringAction( + ResolveImpl(), + ResolveImpl(), + _declarationFinderProvider, + _rewritingManager, + new CodeBuilder()) as T; + case nameof(IReplacePrivateUDTMemberReferencesModelFactory): + return new ReplacePrivateUDTMemberReferencesModelFactory(_declarationFinderProvider) as T; case nameof(DeclareFieldsAsUDTMembersRefactoringAction): - return new DeclareFieldsAsUDTMembersRefactoringAction(_declarationFinderProvider, _rewritingManager, new CodeBuilder()) as T; + return new DeclareFieldsAsUDTMembersRefactoringAction( + _declarationFinderProvider, + _rewritingManager, + new CodeBuilder()) as T; case nameof(EncapsulateFieldPreviewProvider): - return new EncapsulateFieldPreviewProvider(ResolveImpl(), ResolveImpl()) as T; + return new EncapsulateFieldPreviewProvider( + ResolveImpl(), + ResolveImpl()) as T; case nameof(EncapsulateFieldUseBackingFieldPreviewProvider): - return new EncapsulateFieldUseBackingFieldPreviewProvider(ResolveImpl(), _rewritingManager) as T; + return new EncapsulateFieldUseBackingFieldPreviewProvider( + ResolveImpl(), + _rewritingManager) as T; case nameof(EncapsulateFieldUseBackingUDTMemberPreviewProvider): - return new EncapsulateFieldUseBackingUDTMemberPreviewProvider(ResolveImpl(), _rewritingManager) as T; + return new EncapsulateFieldUseBackingUDTMemberPreviewProvider( + ResolveImpl(), + _rewritingManager) as T; } return null; } diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulatedUDTFieldTests.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulatedUDTFieldTests.cs index ed82a61707..570168a7f5 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/EncapsulatedUDTFieldTests.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulatedUDTFieldTests.cs @@ -201,11 +201,25 @@ End Sub "; var presenterAction = Support.UserAcceptsDefaults(); + var expectedWithThis = +@" + With this + First = arg1 + Second = arg2 + End With +"; + + var expectedWithThat = +@" + With that + .First = arg1 + .Second = arg2 + End With +"; + var actualCode = Support.RefactoredCode(inputCode.ToCodeString(), presenterAction); - StringAssert.DoesNotContain($" First = arg1", actualCode); - StringAssert.DoesNotContain($" Second = arg2", actualCode); - StringAssert.Contains($" .First = arg1", actualCode); - StringAssert.Contains($" .Second = arg2", actualCode); + StringAssert.Contains(expectedWithThis, actualCode); + StringAssert.Contains(expectedWithThat, actualCode); StringAssert.Contains("With this", actualCode); } @@ -757,8 +771,8 @@ End Sub [Category("Encapsulate Field")] public void ClassModuleUDTFieldSelection_ExternalReferences_StdModule() { - var sourceModuleName = "SourceModule"; - var sourceClassName = "theClass"; + var classModuleName = "SourceModule"; + var classInstanceName = "theClass"; var sourceModuleCode = $@" @@ -772,24 +786,24 @@ public void ClassModuleUDTFieldSelection_ExternalReferences_StdModule() Second As Long End Type -Private {sourceClassName} As {sourceModuleName} +Private {classInstanceName} As {classModuleName} Private Const foo As String = ""Foo"" Private Const bar As Long = 7 Public Sub Initialize() - Set {sourceClassName} = New {sourceModuleName} + Set {classInstanceName} = New {classModuleName} End Sub Public Sub Foo() - {sourceClassName}.this.First = foo + {classInstanceName}.this.First = foo End Sub Public Sub Bar() - {sourceClassName}.this.Second = bar + {classInstanceName}.this.Second = bar End Sub Public Sub FooBar() - With {sourceClassName} + With {classInstanceName} .this.First = foo .this.Second = bar End With @@ -804,17 +818,17 @@ End Sub var sourceCodeString = sourceModuleCode.ToCodeString(); var actualModuleCode = RefactoredCode( - sourceModuleName, + classModuleName, sourceCodeString.CaretPosition.ToOneBased(), presenterAction, null, false, ("StdModule", procedureModuleReferencingCode, ComponentType.StandardModule), - (sourceModuleName, sourceCodeString.Code, ComponentType.ClassModule)); + (classModuleName, sourceCodeString.Code, ComponentType.ClassModule)); var referencingModuleCode = actualModuleCode["StdModule"]; - StringAssert.Contains($"{sourceClassName}.MyType.First = ", referencingModuleCode); - StringAssert.Contains($"{sourceClassName}.MyType.Second = ", referencingModuleCode); + StringAssert.Contains($"{classInstanceName}.MyType.First = ", referencingModuleCode); + StringAssert.Contains($"{classInstanceName}.MyType.Second = ", referencingModuleCode); StringAssert.Contains($" .MyType.Second = ", referencingModuleCode); } From ceaf5322f905794d768e506295db70411aded81b Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Thu, 3 Sep 2020 11:25:40 -0700 Subject: [PATCH 16/67] Update EncapsulateFieldNewCodeRefactoringAction Relocate and functional updates --- .../EncapsulateFieldInsertNewCodeModel.cs | 22 +++++++++---------- ...lateFieldInsertNewCodeRefactoringAction.cs | 3 ++- 2 files changed, 13 insertions(+), 12 deletions(-) rename Rubberduck.Refactorings/EncapsulateField/{ => EncapsulateFieldInsertNewCode}/EncapsulateFieldInsertNewCodeModel.cs (65%) rename Rubberduck.Refactorings/EncapsulateField/{ => EncapsulateFieldInsertNewCode}/EncapsulateFieldInsertNewCodeRefactoringAction.cs (98%) diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCodeModel.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeModel.cs similarity index 65% rename from Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCodeModel.cs rename to Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeModel.cs index a4496f7b7c..76b3cf7d87 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCodeModel.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeModel.cs @@ -2,8 +2,9 @@ using Rubberduck.Refactorings.CodeBlockInsert; using System.Collections.Generic; using System.Linq; +using Rubberduck.Refactorings.EncapsulateField; -namespace Rubberduck.Refactorings.EncapsulateField +namespace Rubberduck.Refactorings.EncapsulateFieldInsertNewCode { public class EncapsulateFieldInsertNewCodeModel : IRefactoringModel { @@ -14,25 +15,24 @@ public EncapsulateFieldInsertNewCodeModel(IEnumerable f.QualifiedModuleName).First(); } + + NewContent = new Dictionary> + { + { NewContentType.PostContentMessage, new List() }, + { NewContentType.DeclarationBlock, new List() }, + { NewContentType.CodeSectionBlock, new List() }, + { NewContentType.TypeDeclarationBlock, new List() } + }; } public bool IncludeNewContentMarker { set; get; } = false; public QualifiedModuleName QualifiedModuleName { get; } = new QualifiedModuleName(); - private Dictionary> _newContent { set; get; } - public Dictionary> NewContent - { - set => _newContent = value; - get => _newContent; - } + public Dictionary> NewContent { set; get; } private List _selectedCandidates; public IEnumerable SelectedFieldCandidates => _selectedCandidates; - //{ - // //set => _selectedCandidates = value.ToList(); - // get => _selectedCandidates; - //} } } diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCodeRefactoringAction.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeRefactoringAction.cs similarity index 98% rename from Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCodeRefactoringAction.cs rename to Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeRefactoringAction.cs index fb206552c7..214b4a4365 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCodeRefactoringAction.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeRefactoringAction.cs @@ -8,8 +8,9 @@ using System; using System.Diagnostics; using System.Linq; +using Rubberduck.Refactorings.EncapsulateField; -namespace Rubberduck.Refactorings.EncapsulateField +namespace Rubberduck.Refactorings.EncapsulateFieldInsertNewCode { public class EncapsulateFieldInsertNewCodeRefactoringAction : CodeOnlyRefactoringActionBase { From 93977fa5eeb158a21f85a15930656e1899a88dcc Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Thu, 3 Sep 2020 11:30:01 -0700 Subject: [PATCH 17/67] Add EncapsulateFieldCandidateFactory --- .../EncapsulateFieldCandidateFactory.cs | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldCandidateFactory.cs diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldCandidateFactory.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldCandidateFactory.cs new file mode 100644 index 0000000000..24c6c88630 --- /dev/null +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldCandidateFactory.cs @@ -0,0 +1,91 @@ +using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.VBA; +using Rubberduck.Refactorings.Common; +using Rubberduck.Refactorings.EncapsulateField; +using System.Collections.Generic; +using System.Linq; + +namespace Rubberduck.Refactorings +{ + public interface IEncapsulateFieldCandidateFactory + { + IEncapsulateFieldCandidate Create(Declaration target); + } + public class EncapsulateFieldCandidateFactory : IEncapsulateFieldCandidateFactory + { + private readonly IDeclarationFinderProvider _declarationFinderProvider; + private readonly ICodeBuilder _codeBuilder; + + private readonly IValidateVBAIdentifiers _defaultNameValidator; + private readonly IValidateVBAIdentifiers _udtNameValidator; + private readonly IValidateVBAIdentifiers _udtMemberNameValidator; + private readonly IValidateVBAIdentifiers _udtMemberArrayNameValidator; + + public EncapsulateFieldCandidateFactory(IDeclarationFinderProvider declarationFinderProvider, ICodeBuilder codeBuilder) + { + _declarationFinderProvider = declarationFinderProvider; + _codeBuilder = codeBuilder; + + _defaultNameValidator = EncapsulateFieldValidationsProvider.NameOnlyValidator(NameValidators.Default); + _udtNameValidator = EncapsulateFieldValidationsProvider.NameOnlyValidator(NameValidators.UserDefinedType); + _udtMemberNameValidator = EncapsulateFieldValidationsProvider.NameOnlyValidator(NameValidators.UserDefinedTypeMember); + _udtMemberArrayNameValidator = EncapsulateFieldValidationsProvider.NameOnlyValidator(NameValidators.UserDefinedTypeMemberArray); + } + + public IEncapsulateFieldCandidate Create(Declaration target) + { + var candidate= CreateCandidate(target, _defaultNameValidator); + return candidate; + } + + private IEncapsulateFieldCandidate CreateCandidate(Declaration target, IValidateVBAIdentifiers validator) + { + if (target.IsUserDefinedType()) + { + var udtField = new UserDefinedTypeCandidate(target, _udtNameValidator, _codeBuilder.BuildPropertyRhsParameterName) as IUserDefinedTypeCandidate; + + (Declaration udtDeclaration, IEnumerable udtMembers) = GetUDTAndMembersForField(udtField); + + foreach (var udtMemberDeclaration in udtMembers) + { + var udtMemberValidator = _udtMemberNameValidator; + if (udtMemberDeclaration.IsArray) + { + udtMemberValidator = _udtMemberArrayNameValidator; + } + var candidateUDTMember = new UserDefinedTypeMemberCandidate(CreateCandidate(udtMemberDeclaration, udtMemberValidator), udtField, _codeBuilder.BuildPropertyRhsParameterName) as IUserDefinedTypeMemberCandidate; + + udtField.AddMember(candidateUDTMember); + } + + var udtVariablesOfSameType = _declarationFinderProvider.DeclarationFinder.UserDeclarations(DeclarationType.Variable) + .Where(v => v.AsTypeDeclaration == udtDeclaration); + + udtField.CanBeObjectStateUDT = udtField.TypeDeclarationIsPrivate + && udtField.Declaration.HasPrivateAccessibility() + && udtVariablesOfSameType.Count() == 1; + + return udtField; + } + else if (target.IsArray) + { + var arrayCandidate = new ArrayCandidate(target, validator, _codeBuilder.BuildPropertyRhsParameterName); + return arrayCandidate; + } + + var candidate = new EncapsulateFieldCandidate(target, validator, _codeBuilder.BuildPropertyRhsParameterName); + return candidate; + } + + private (Declaration TypeDeclaration, IEnumerable Members) GetUDTAndMembersForField(IUserDefinedTypeCandidate udtField) + { + var userDefinedTypeDeclaration = udtField.Declaration.AsTypeDeclaration; + + var udtMembers = _declarationFinderProvider.DeclarationFinder + .UserDeclarations(DeclarationType.UserDefinedTypeMember) + .Where(utm => userDefinedTypeDeclaration == utm.ParentDeclaration); + + return (userDefinedTypeDeclaration, udtMembers); + } + } +} From 96087d766940bcebfef472d5e21263cf71bf6562 Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Thu, 3 Sep 2020 11:33:49 -0700 Subject: [PATCH 18/67] Add EncapsulateFieldUseBackingFieldModelFactory Some changes in folder organization as well --- .../EncapsulateFieldUseBackingFieldModel.cs | 51 +++++++++++++ ...psulateFieldUseBackingFieldModelFactory.cs | 74 +++++++++++++++++++ ...lateFieldUseBackingFieldPreviewProvider.cs | 37 ++++++++++ ...teFieldUseBackingFieldRefactoringAction.cs | 22 ++---- 4 files changed, 170 insertions(+), 14 deletions(-) create mode 100644 Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldModel.cs create mode 100644 Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldModelFactory.cs create mode 100644 Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldPreviewProvider.cs rename Rubberduck.Refactorings/EncapsulateField/{ => EncapsulateFieldUseBackingField}/EncapsulateFieldUseBackingFieldRefactoringAction.cs (91%) diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldModel.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldModel.cs new file mode 100644 index 0000000000..a859b3ee4f --- /dev/null +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldModel.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; +using System.Linq; +using Rubberduck.Parsing.VBA; +using Rubberduck.VBEditor; +using Rubberduck.Refactorings.CodeBlockInsert; +using Rubberduck.Refactorings.EncapsulateField; + +namespace Rubberduck.Refactorings.EncapsulateFieldUseBackingField +{ + public class EncapsulateFieldUseBackingFieldModel : IRefactoringModel + { + private readonly IDeclarationFinderProvider _declarationFinderProvider; + + public EncapsulateFieldUseBackingFieldModel(IEnumerable candidates, + IDeclarationFinderProvider declarationFinderProvider) + { + _declarationFinderProvider = declarationFinderProvider; + + EncapsulationCandidates = candidates.ToList(); + + ResetNewContent(); + } + + public void ResetNewContent() + { + NewContent = new Dictionary> + { + { NewContentType.PostContentMessage, new List() }, + { NewContentType.DeclarationBlock, new List() }, + { NewContentType.CodeSectionBlock, new List() }, + { NewContentType.TypeDeclarationBlock, new List() } + }; + } + + public IEncapsulateFieldConflictFinder ConflictFinder { set; get; } + + public bool IncludeNewContentMarker { set; get; } = false; + + public List EncapsulationCandidates { get; } + + public IEnumerable SelectedFieldCandidates + => EncapsulationCandidates.Where(v => v.EncapsulateFlag); + + public void AddContentBlock(NewContentType contentType, string block) + => NewContent[contentType].Add(block); + + public Dictionary> NewContent { set; get; } + + public QualifiedModuleName QualifiedModuleName => EncapsulationCandidates.First().QualifiedModuleName; + } +} diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldModelFactory.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldModelFactory.cs new file mode 100644 index 0000000000..274762aab4 --- /dev/null +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldModelFactory.cs @@ -0,0 +1,74 @@ +using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.VBA; +using Rubberduck.Refactorings.Common; +using Rubberduck.Refactorings.EncapsulateField; +using Rubberduck.Refactorings.EncapsulateFieldUseBackingField; +using Rubberduck.VBEditor; +using System.Collections.Generic; +using System.Linq; + +namespace Rubberduck.Refactorings +{ + public interface IEncapsulateFieldUseBackingFieldModelFactory : IEncapsulateFieldModelsFactory + { } + + public class EncapsulateFieldUseBackingFieldModelFactory : IEncapsulateFieldUseBackingFieldModelFactory + { + private readonly IDeclarationFinderProvider _declarationFinderProvider; + private readonly IEncapsulateFieldCandidateFactory _fieldCandidateFactory; + + public EncapsulateFieldUseBackingFieldModelFactory(IDeclarationFinderProvider declarationFinderProvider, + IEncapsulateFieldCandidateFactory fieldCandidateFactory) + { + _declarationFinderProvider = declarationFinderProvider; + _fieldCandidateFactory = fieldCandidateFactory; + } + + public EncapsulateFieldUseBackingFieldModel Create(QualifiedModuleName qmn) + { + var fields = _declarationFinderProvider.DeclarationFinder.UserDeclarations(DeclarationType.Variable) + .Where(v => v.ParentDeclaration is ModuleDeclaration + && !v.IsWithEvents); + + var candidates = fields.Select(f => _fieldCandidateFactory.Create(f)); + + var objectStateUDTCandidates = candidates.Where(c => c is IUserDefinedTypeCandidate udt && udt.CanBeObjectStateUDT) + .Select(udtc => new ObjectStateUDT(udtc as IUserDefinedTypeCandidate)); + + return Create(candidates, objectStateUDTCandidates); + } + + public EncapsulateFieldUseBackingFieldModel Create(IEnumerable candidates, IEnumerable objectStateUDTCandidates) + { + var fieldCandidates = new List(candidates); + var objectStateFieldCandidates = new List(objectStateUDTCandidates); + var udtMemberCandidates = new List(); + + fieldCandidates.ForEach(c => LoadUDTMembers(udtMemberCandidates, c)); + + var conflictsFinder = new UseBackingFieldsStrategyConflictFinder(_declarationFinderProvider, candidates, udtMemberCandidates); + fieldCandidates.ForEach(c => c.ConflictFinder = conflictsFinder); + + return new EncapsulateFieldUseBackingFieldModel(candidates, _declarationFinderProvider) + { + ConflictFinder = conflictsFinder + }; + } + + private void LoadUDTMembers(List udtMembers, IEncapsulateFieldCandidate candidate) + { + if (candidate is IUserDefinedTypeCandidate udtCandidate) + { + foreach (var member in udtCandidate.Members) + { + udtMembers.Add(member); + if (member.WrappedCandidate is IUserDefinedTypeCandidate childUDT + && childUDT.Declaration.AsTypeDeclaration.HasPrivateAccessibility()) + { + LoadUDTMembers(udtMembers, childUDT); + } + } + } + } + } +} diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldPreviewProvider.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldPreviewProvider.cs new file mode 100644 index 0000000000..a68c440bbe --- /dev/null +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldPreviewProvider.cs @@ -0,0 +1,37 @@ +using Rubberduck.Parsing.Rewriter; +using Rubberduck.VBEditor; +using System; + +namespace Rubberduck.Refactorings.EncapsulateFieldUseBackingField +{ + public class EncapsulateFieldUseBackingFieldPreviewProvider : RefactoringPreviewProviderWrapperBase + { + public EncapsulateFieldUseBackingFieldPreviewProvider(EncapsulateFieldUseBackingFieldRefactoringAction refactoringAction, + IRewritingManager rewritingManager) + : base(refactoringAction, rewritingManager) + { } + + public override string Preview(EncapsulateFieldUseBackingFieldModel model) + { + var preview = string.Empty; + var initialFlagValue = model.IncludeNewContentMarker; + model.IncludeNewContentMarker = true; + try + { + model.ResetNewContent(); + preview = base.Preview(model); + } + catch (Exception) { } + finally + { + model.IncludeNewContentMarker = initialFlagValue; + } + return preview; + } + + protected override QualifiedModuleName ComponentToShow(EncapsulateFieldUseBackingFieldModel model) + { + return model.QualifiedModuleName; + } + } +} diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingFieldRefactoringAction.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldRefactoringAction.cs similarity index 91% rename from Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingFieldRefactoringAction.cs rename to Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldRefactoringAction.cs index 2c1676f2bb..3797933245 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingFieldRefactoringAction.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldRefactoringAction.cs @@ -12,10 +12,12 @@ using System; using System.Collections.Generic; using System.Linq; +using Rubberduck.Refactorings.EncapsulateField; +using Rubberduck.Refactorings.EncapsulateFieldInsertNewCode; -namespace Rubberduck.Refactorings.EncapsulateField +namespace Rubberduck.Refactorings.EncapsulateFieldUseBackingField { - public class EncapsulateFieldUseBackingFieldRefactoringAction : CodeOnlyRefactoringActionBase + public class EncapsulateFieldUseBackingFieldRefactoringAction : CodeOnlyRefactoringActionBase { private readonly IDeclarationFinderProvider _declarationFinderProvider; private readonly ICodeOnlyRefactoringAction _replaceUDTMemberReferencesRefactoringAction; @@ -39,21 +41,13 @@ public class EncapsulateFieldUseBackingFieldRefactoringAction : CodeOnlyRefactor _replaceUDTMemberReferencesModelFactory = replaceUDTMemberReferencesModelFactory; } - public override void Refactor(EncapsulateFieldModel model, IRewriteSession rewriteSession) + public override void Refactor(EncapsulateFieldUseBackingFieldModel model, IRewriteSession rewriteSession) { if (!model.SelectedFieldCandidates.Any()) { return; } - model.NewContent = new Dictionary> - { - { NewContentType.PostContentMessage, new List() }, - { NewContentType.DeclarationBlock, new List() }, - { NewContentType.CodeSectionBlock, new List() }, - { NewContentType.TypeDeclarationBlock, new List() } - }; - ModifyFields(model, rewriteSession); ModifyReferences(model, rewriteSession); @@ -61,7 +55,7 @@ public override void Refactor(EncapsulateFieldModel model, IRewriteSession rewri InsertNewContent(model, rewriteSession); } - private void ModifyFields(EncapsulateFieldModel model, IRewriteSession rewriteSession) + private void ModifyFields(EncapsulateFieldUseBackingFieldModel model, IRewriteSession rewriteSession) { var fieldDeclarationsToDeleteAndReplace = model.SelectedFieldCandidates .Where(f => f.Declaration.IsDeclaredInList() @@ -94,7 +88,7 @@ private void ModifyFields(EncapsulateFieldModel model, IRewriteSession rewriteSe } } - private void ModifyReferences(EncapsulateFieldModel model, IRewriteSession rewriteSession) + private void ModifyReferences(EncapsulateFieldUseBackingFieldModel model, IRewriteSession rewriteSession) { var privateUdtInstances = model.SelectedFieldCandidates .Where(f => (f.Declaration.AsTypeDeclaration?.DeclarationType.HasFlag(DeclarationType.UserDefinedType) ?? false) @@ -105,7 +99,7 @@ private void ModifyReferences(EncapsulateFieldModel model, IRewriteSession rewri ReplaceEncapsulatedFieldReferences(model.SelectedFieldCandidates.Except(privateUdtInstances), rewriteSession); } - private void InsertNewContent(EncapsulateFieldModel model, IRewriteSession rewriteSession) + private void InsertNewContent(EncapsulateFieldUseBackingFieldModel model, IRewriteSession rewriteSession) { var encapsulateFieldInsertNewCodeModel = new EncapsulateFieldInsertNewCodeModel(model.SelectedFieldCandidates) { From 371ad27e0a12d0c925351d63c322874fb53e9507 Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Thu, 3 Sep 2020 11:35:24 -0700 Subject: [PATCH 19/67] Add EncapsulateFieldUseBackingUDTMemberModelFactory Some changes in folder organization as well --- ...ncapsulateFieldUseBackingUDTMemberModel.cs | 113 ++++++++++++++++++ ...ateFieldUseBackingUDTMemberModelFactory.cs | 112 +++++++++++++++++ ...FieldUseBackingUDTMemberPreviewProvider.cs | 36 ++++++ ...eldUseBackingUDTMemberRefactoringAction.cs | 14 ++- 4 files changed, 269 insertions(+), 6 deletions(-) create mode 100644 Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModel.cs create mode 100644 Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModelFactory.cs create mode 100644 Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberPreviewProvider.cs rename Rubberduck.Refactorings/EncapsulateField/{ => EncapsulateFieldUseBackingUDTMember}/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs (91%) diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModel.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModel.cs new file mode 100644 index 0000000000..51e4ad3282 --- /dev/null +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModel.cs @@ -0,0 +1,113 @@ +using System.Collections.Generic; +using System.Linq; +using Rubberduck.Parsing.VBA; +using Rubberduck.VBEditor; +using Rubberduck.Refactorings.CodeBlockInsert; +using Rubberduck.Refactorings.EncapsulateField; +using System; + +namespace Rubberduck.Refactorings.EncapsulateFieldUseBackingUDTMember +{ + public class EncapsulateFieldUseBackingUDTMemberModel : IRefactoringModel + { + private readonly IObjectStateUDT _defaultObjectStateUDT; + private readonly IDeclarationFinderProvider _declarationFinderProvider; + private readonly string _defaultObjectStateUDTTypeName; + private readonly IObjectStateUDT _preExistingObjectStateUDT; + + private List _convertedFields; + private List _objStateCandidates; + + public EncapsulateFieldUseBackingUDTMemberModel(IEnumerable candidates, + IObjectStateUDT defaultObjectStateUserDefinedType, + IEnumerable objectStateUserDefinedTypeCandidates, + IDeclarationFinderProvider declarationFinderProvider) + { + _convertedFields = new List(candidates); + _declarationFinderProvider = declarationFinderProvider; + _defaultObjectStateUDT = defaultObjectStateUserDefinedType; + + QualifiedModuleName = candidates.First().QualifiedModuleName; + _defaultObjectStateUDTTypeName = $"T{QualifiedModuleName.ComponentName}"; + + _objStateCandidates = new List(); + + if (objectStateUserDefinedTypeCandidates.Any()) + { + _objStateCandidates.AddRange(objectStateUserDefinedTypeCandidates.Distinct()); + + _preExistingObjectStateUDT = objectStateUserDefinedTypeCandidates + .FirstOrDefault(os => os.AsTypeDeclaration.IdentifierName.StartsWith(_defaultObjectStateUDTTypeName, StringComparison.InvariantCultureIgnoreCase)); + + if (_preExistingObjectStateUDT != null) + { + HasPreExistingObjectStateUDT = true; + _defaultObjectStateUDT.IsSelected = false; + _preExistingObjectStateUDT.IsSelected = true; + _convertedFields.ForEach(c => c.ObjectStateUDT = _preExistingObjectStateUDT); + } + } + + _objStateCandidates.Add(_defaultObjectStateUDT); + + ResetNewContent(); + + _convertedFields.ForEach(c => c.ObjectStateUDT = ObjectStateUDTField); + } + + private void ResetNewContent() + { + NewContent = new Dictionary> + { + { NewContentType.PostContentMessage, new List() }, + { NewContentType.DeclarationBlock, new List() }, + { NewContentType.CodeSectionBlock, new List() }, + { NewContentType.TypeDeclarationBlock, new List() } + }; + } + + public bool HasPreExistingObjectStateUDT { get; } + + public IEncapsulateFieldConflictFinder ConflictFinder { set; get; } + + public bool IncludeNewContentMarker { set; get; } = false; + + public IReadOnlyCollection EncapsulationCandidates + => _convertedFields.Cast().ToList(); + + public IEnumerable SelectedFieldCandidates + => EncapsulationCandidates.Where(v => v.EncapsulateFlag); + + public void AddContentBlock(NewContentType contentType, string block) + => NewContent[contentType].Add(block); + + public Dictionary> NewContent { set; get; } + + public QualifiedModuleName QualifiedModuleName { get; } + + public IEnumerable ObjectStateUDTCandidates => _objStateCandidates; + + public IObjectStateUDT ObjectStateUDTField + { + get => _objStateCandidates.SingleOrDefault(os => os.IsSelected) + ?? _defaultObjectStateUDT; + + set + { + if (value is null) + { + _objStateCandidates.ForEach(osc => osc.IsSelected = (osc == _defaultObjectStateUDT)); + return; + } + + var matchingCandidate = _objStateCandidates + .SingleOrDefault(os => os.FieldIdentifier.Equals(value.FieldIdentifier)) + ?? _defaultObjectStateUDT; + + _objStateCandidates.ForEach(osc => osc.IsSelected = (osc == matchingCandidate)); + + _convertedFields.ForEach(cf => cf.ObjectStateUDT = matchingCandidate); + } + } + } +} diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModelFactory.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModelFactory.cs new file mode 100644 index 0000000000..b173aea29d --- /dev/null +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModelFactory.cs @@ -0,0 +1,112 @@ +using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.VBA; +using Rubberduck.Refactorings.Common; +using Rubberduck.Refactorings.EncapsulateField; +using Rubberduck.Refactorings.EncapsulateFieldUseBackingUDTMember; +using Rubberduck.VBEditor; +using System.Collections.Generic; +using System.Linq; + +namespace Rubberduck.Refactorings +{ + public interface IEncapsulateFieldUseBackingUDTMemberModelFactory : IEncapsulateFieldModelsFactory + { } + + public class EncapsulateFieldUseBackingUDTMemberModelFactory : IEncapsulateFieldUseBackingUDTMemberModelFactory + { + private readonly IDeclarationFinderProvider _declarationFinderProvider; + private readonly IEncapsulateFieldCandidateFactory _fieldCandidateFactory; + + public EncapsulateFieldUseBackingUDTMemberModelFactory(IDeclarationFinderProvider declarationFinderProvider, IEncapsulateFieldCandidateFactory fieldCandidateFactory) + { + _declarationFinderProvider = declarationFinderProvider; + _fieldCandidateFactory = fieldCandidateFactory; + } + + public EncapsulateFieldUseBackingUDTMemberModel Create(QualifiedModuleName qmn) { + var fields = _declarationFinderProvider.DeclarationFinder.UserDeclarations(DeclarationType.Variable) + .Where(v => v.ParentDeclaration is ModuleDeclaration + && !v.IsWithEvents); + + var candidates = fields.Select(f => _fieldCandidateFactory.Create(f)); + + var objectStateUDTCandidates = candidates.Where(c => c is IUserDefinedTypeCandidate udt + && udt.CanBeObjectStateUDT) + .Select(udtc => new ObjectStateUDT(udtc as IUserDefinedTypeCandidate)); + + return Create(candidates, objectStateUDTCandidates); + } + + public EncapsulateFieldUseBackingUDTMemberModel Create(IEnumerable candidates, IEnumerable objectStateUDTCandidates) + { + var fieldCandidates = new List(candidates); + var objectStateFieldCandidates = new List(objectStateUDTCandidates); + + var udtMemberCandidates = new List(); + fieldCandidates.ForEach(c => LoadUDTMembers(udtMemberCandidates, c)); + + var conflictsFinder = new ConvertFieldsToUDTMembersStrategyConflictFinder(_declarationFinderProvider, candidates, udtMemberCandidates, objectStateUDTCandidates); + fieldCandidates.ForEach(c => c.ConflictFinder = conflictsFinder); + + var defaultObjectStateUDT = CreateStateUDTField(candidates.First().QualifiedModuleName); + conflictsFinder.AssignNoConflictIdentifiers(defaultObjectStateUDT, _declarationFinderProvider); + + var convertedToUDTMemberCandidates = new List(); + foreach (var field in candidates) + { + if (field is ConvertToUDTMember cm) + { + convertedToUDTMemberCandidates.Add(cm); + continue; + } + convertedToUDTMemberCandidates.Add(new ConvertToUDTMember(field, defaultObjectStateUDT)); + } + + return new EncapsulateFieldUseBackingUDTMemberModel(convertedToUDTMemberCandidates, defaultObjectStateUDT, objectStateUDTCandidates, _declarationFinderProvider) + { + ConflictFinder = conflictsFinder + }; + } + + private IObjectStateUDT CreateStateUDTField(QualifiedModuleName qualifiedModuleName) + { + var stateUDT = new ObjectStateUDT(qualifiedModuleName) as IObjectStateUDT; + stateUDT.IsSelected = true; + + return stateUDT; + } + + private void ResolveConflict(IEncapsulateFieldConflictFinder conflictFinder, IEncapsulateFieldCandidate candidate) + { + conflictFinder.AssignNoConflictIdentifiers(candidate); + if (candidate is IUserDefinedTypeCandidate udtCandidate) + { + foreach (var member in udtCandidate.Members) + { + conflictFinder.AssignNoConflictIdentifiers(member); + if (member.WrappedCandidate is IUserDefinedTypeCandidate childUDT + && childUDT.Declaration.AsTypeDeclaration.HasPrivateAccessibility()) + { + ResolveConflict(conflictFinder, childUDT); + } + } + } + } + + private void LoadUDTMembers(List udtMembers, IEncapsulateFieldCandidate candidate) + { + if (candidate is IUserDefinedTypeCandidate udtCandidate) + { + foreach (var member in udtCandidate.Members) + { + udtMembers.Add(member); + if (member.WrappedCandidate is IUserDefinedTypeCandidate childUDT + && childUDT.Declaration.AsTypeDeclaration.HasPrivateAccessibility()) + { + LoadUDTMembers(udtMembers, childUDT); + } + } + } + } + } +} diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberPreviewProvider.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberPreviewProvider.cs new file mode 100644 index 0000000000..2cb236913e --- /dev/null +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberPreviewProvider.cs @@ -0,0 +1,36 @@ +using Rubberduck.Parsing.Rewriter; +using Rubberduck.VBEditor; +using System; + +namespace Rubberduck.Refactorings.EncapsulateFieldUseBackingUDTMember +{ + public class EncapsulateFieldUseBackingUDTMemberPreviewProvider : RefactoringPreviewProviderWrapperBase + { + public EncapsulateFieldUseBackingUDTMemberPreviewProvider(EncapsulateFieldUseBackingUDTMemberRefactoringAction refactoringAction, + IRewritingManager rewritingManager) + : base(refactoringAction, rewritingManager) + { } + + public override string Preview(EncapsulateFieldUseBackingUDTMemberModel model) + { + var preview = string.Empty; + var initialFlagValue = model.IncludeNewContentMarker; + model.IncludeNewContentMarker = true; + try + { + preview = base.Preview(model); + } + catch (Exception e) { } + finally + { + model.IncludeNewContentMarker = initialFlagValue; + } + return preview; + } + + protected override QualifiedModuleName ComponentToShow(EncapsulateFieldUseBackingUDTMemberModel model) + { + return model.QualifiedModuleName; + } + } +} diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs similarity index 91% rename from Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs rename to Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs index 89e241afe9..f41cc7032e 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs @@ -10,10 +10,12 @@ using Rubberduck.Refactorings.CodeBlockInsert; using System.Collections.Generic; using System.Linq; +using Rubberduck.Refactorings.EncapsulateField; +using Rubberduck.Refactorings.EncapsulateFieldInsertNewCode; -namespace Rubberduck.Refactorings.EncapsulateField +namespace Rubberduck.Refactorings.EncapsulateFieldUseBackingUDTMember { - public class EncapsulateFieldUseBackingUDTMemberRefactoringAction : CodeOnlyRefactoringActionBase + public class EncapsulateFieldUseBackingUDTMemberRefactoringAction : CodeOnlyRefactoringActionBase { private readonly IDeclarationFinderProvider _declarationFinderProvider; private readonly ICodeOnlyRefactoringAction _declareFieldAsUDTMemberRefactoringAction; @@ -40,7 +42,7 @@ public class EncapsulateFieldUseBackingUDTMemberRefactoringAction : CodeOnlyRefa _replaceUDTMemberReferencesModelFactory = replaceUDTMemberReferencesModelFactory; } - public override void Refactor(EncapsulateFieldModel model, IRewriteSession rewriteSession) + public override void Refactor(EncapsulateFieldUseBackingUDTMemberModel model, IRewriteSession rewriteSession) { if (!model.SelectedFieldCandidates.Any()) { @@ -62,7 +64,7 @@ public override void Refactor(EncapsulateFieldModel model, IRewriteSession rewri InsertNewContent(model, rewriteSession); } - private void ModifyFields(EncapsulateFieldModel encapsulateFieldModel, IRewriteSession rewriteSession) + private void ModifyFields(EncapsulateFieldUseBackingUDTMemberModel encapsulateFieldModel, IRewriteSession rewriteSession) { var rewriter = rewriteSession.CheckOutModuleRewriter(encapsulateFieldModel.QualifiedModuleName); @@ -92,7 +94,7 @@ private void ModifyFields(EncapsulateFieldModel encapsulateFieldModel, IRewriteS } } - private void ModifyReferences(EncapsulateFieldModel model, IRewriteSession rewriteSession) + private void ModifyReferences(EncapsulateFieldUseBackingUDTMemberModel model, IRewriteSession rewriteSession) { var udtFields = model.SelectedFieldCandidates .Where(f => (f.Declaration.AsTypeDeclaration?.DeclarationType.HasFlag(DeclarationType.UserDefinedType) ?? false) @@ -140,7 +142,7 @@ private void ModifyReferences(EncapsulateFieldModel model, IRewriteSession rewri _replaceFieldReferencesRefactoringAction.Refactor(modelReplaceField, rewriteSession); } - private void InsertNewContent(EncapsulateFieldModel model, IRewriteSession rewriteSession) + private void InsertNewContent(EncapsulateFieldUseBackingUDTMemberModel model, IRewriteSession rewriteSession) { var encapsulateFieldInsertNewCodeModel = new EncapsulateFieldInsertNewCodeModel(model.SelectedFieldCandidates) { From 8f18aceb7531569b16db62bd271dbbece1aa3ca2 Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Thu, 3 Sep 2020 11:36:33 -0700 Subject: [PATCH 20/67] Add EncapsulateFieldModelFactory --- .../EncapsulateFieldModelFactory.cs | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModelFactory.cs diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModelFactory.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModelFactory.cs new file mode 100644 index 0000000000..7ee4cd091b --- /dev/null +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModelFactory.cs @@ -0,0 +1,71 @@ +using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.VBA; +using Rubberduck.Refactorings.Common; +using Rubberduck.Refactorings.EncapsulateField; +using Rubberduck.VBEditor; +using System.Collections.Generic; +using System.Linq; + +namespace Rubberduck.Refactorings +{ + public interface IEncapsulateFieldModelsFactory + { + T Create(QualifiedModuleName qmn); + T Create(IEnumerable candidates, IEnumerable objectStateUDTCandidates); + } + + public interface IEncapsulateFieldModelFactory + { + EncapsulateFieldModel Create(Declaration target); + } + + public class EncapsulateFieldModelFactory : IEncapsulateFieldModelFactory + { + private readonly IDeclarationFinderProvider _declarationFinderProvider; + private readonly IEncapsulateFieldCandidateFactory _encapsulateFieldCandidateFactory; + private readonly IEncapsulateFieldUseBackingUDTMemberModelFactory _useBackingUDTMemberModelFactory; + private readonly IEncapsulateFieldUseBackingFieldModelFactory _useBackingFieldModelFactory; + + public EncapsulateFieldModelFactory(IDeclarationFinderProvider declarationFinderProvider, + IEncapsulateFieldCandidateFactory encapsulateFieldCandidateFactory, + IEncapsulateFieldUseBackingUDTMemberModelFactory encapsulateFieldUseBackingUDTMemberModelFactory, + IEncapsulateFieldUseBackingFieldModelFactory encapsulateFieldUseBackingFieldModelFactory) + { + _declarationFinderProvider = declarationFinderProvider; + _encapsulateFieldCandidateFactory = encapsulateFieldCandidateFactory; + _useBackingUDTMemberModelFactory = encapsulateFieldUseBackingUDTMemberModelFactory; + _useBackingFieldModelFactory = encapsulateFieldUseBackingFieldModelFactory; + } + + public EncapsulateFieldModel Create(Declaration target) + { + var fields = _declarationFinderProvider.DeclarationFinder + .Members(target.QualifiedModuleName) + .Where(v => v.IsMemberVariable() && !v.IsWithEvents); + + var candidates = fields.Select(fd => _encapsulateFieldCandidateFactory.Create(fd)) + .ToList(); + + var objectStateUDTCandidates = candidates.Where(c => c is IUserDefinedTypeCandidate udt + && udt.CanBeObjectStateUDT) + .Select(udtc => new ObjectStateUDT(udtc as IUserDefinedTypeCandidate)) + .ToList(); + + var initialStrategy = objectStateUDTCandidates + .Any(os => os.AsTypeDeclaration.IdentifierName.StartsWith($"T{target.QualifiedModuleName.ComponentName}", System.StringComparison.InvariantCultureIgnoreCase)) + ? EncapsulateFieldStrategy.ConvertFieldsToUDTMembers + : EncapsulateFieldStrategy.UseBackingFields; + + var selected = candidates.Single(c => c.Declaration == target); + selected.EncapsulateFlag = true; + + var udtModel = _useBackingUDTMemberModelFactory.Create(candidates, objectStateUDTCandidates); + var backingModel = _useBackingFieldModelFactory.Create(candidates, objectStateUDTCandidates); + + return new EncapsulateFieldModel(backingModel, udtModel) + { + EncapsulateFieldStrategy = initialStrategy + }; + } + } +} From 9ca03c8209fd00f24cabb37c4dda9469081a026f Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Thu, 3 Sep 2020 11:38:58 -0700 Subject: [PATCH 21/67] Update EncapsulateFieldRefactoringActionsProvider.cs Adds EncapsulateFieldInsertNewCodeAction --- .../EncapsulateFieldRefactoringActionsProvider.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringActionsProvider.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringActionsProvider.cs index d9d387f4bf..dae155f004 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringActionsProvider.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringActionsProvider.cs @@ -5,6 +5,7 @@ using Rubberduck.Refactorings.ReplaceReferences; using Rubberduck.Refactorings.ReplacePrivateUDTMemberReferences; using Rubberduck.Refactorings.CodeBlockInsert; +using Rubberduck.Refactorings.EncapsulateFieldInsertNewCode; namespace Rubberduck.Refactorings.EncapsulateField { From d5d8e7daf3fb2f675c203c6b856130895c80031b Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Thu, 3 Sep 2020 11:42:36 -0700 Subject: [PATCH 22/67] Change namespace ReplacePrivateUDTMemberReferencesModelFactory --- .../ReplacePrivateUDTMemberReferencesModelFactory.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Rubberduck.Refactorings/ReplacePrivateUDTMemberReferences/ReplacePrivateUDTMemberReferencesModelFactory.cs b/Rubberduck.Refactorings/ReplacePrivateUDTMemberReferences/ReplacePrivateUDTMemberReferencesModelFactory.cs index 8dd77dcb0c..67bbde6014 100644 --- a/Rubberduck.Refactorings/ReplacePrivateUDTMemberReferences/ReplacePrivateUDTMemberReferencesModelFactory.cs +++ b/Rubberduck.Refactorings/ReplacePrivateUDTMemberReferences/ReplacePrivateUDTMemberReferencesModelFactory.cs @@ -1,9 +1,10 @@ using Rubberduck.Parsing.Symbols; using Rubberduck.Parsing.VBA; +using Rubberduck.Refactorings.ReplacePrivateUDTMemberReferences; using System.Collections.Generic; using System.Linq; -namespace Rubberduck.Refactorings.ReplacePrivateUDTMemberReferences +namespace Rubberduck.Refactorings { public interface IReplacePrivateUDTMemberReferencesModelFactory { From 8b265a837f921d2175f87f45ec53859c10391615 Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Thu, 3 Sep 2020 11:44:42 -0700 Subject: [PATCH 23/67] Separate name validation and conflict detection --- ...ieldsToUDTMembersStrategyConflictFinder.cs | 41 ++++++++- .../EncapsulateFieldConflictFinderBase.cs | 38 ++++---- .../EncapsulateFieldValidationsProvider.cs | 92 +------------------ .../UseBackingFieldsStrategyConflictFinder.cs | 2 +- 4 files changed, 67 insertions(+), 106 deletions(-) diff --git a/Rubberduck.Refactorings/EncapsulateField/Validations/ConvertFieldsToUDTMembersStrategyConflictFinder.cs b/Rubberduck.Refactorings/EncapsulateField/Validations/ConvertFieldsToUDTMembersStrategyConflictFinder.cs index b6c6b0ee5d..0f6e4c9f7e 100644 --- a/Rubberduck.Refactorings/EncapsulateField/Validations/ConvertFieldsToUDTMembersStrategyConflictFinder.cs +++ b/Rubberduck.Refactorings/EncapsulateField/Validations/ConvertFieldsToUDTMembersStrategyConflictFinder.cs @@ -9,8 +9,27 @@ namespace Rubberduck.Refactorings.EncapsulateField { - public class ConvertFieldsToUDTMembersStrategyConflictFinder : EncapsulateFieldConflictFinderBase + public interface IConvertFieldsToUDTMembersStrategyConflictFinder : IEncapsulateFieldConflictFinder { + IObjectStateUDT AssignNoConflictIdentifiers(IObjectStateUDT stateUDT, IDeclarationFinderProvider declarationFinderProvider); + } + + public class ConvertFieldsToUDTMembersStrategyConflictFinder : EncapsulateFieldConflictFinderBase, IConvertFieldsToUDTMembersStrategyConflictFinder + { + private static DeclarationType[] _udtTypeIdentifierNonConflictTypes = new DeclarationType[] + { + DeclarationType.Project, + DeclarationType.Module, + DeclarationType.Property, + DeclarationType.Function, + DeclarationType.Procedure, + DeclarationType.Variable, + DeclarationType.Constant, + DeclarationType.UserDefinedTypeMember, + DeclarationType.EnumerationMember, + DeclarationType.Parameter + }; + private IEnumerable _objectStateUDTs; public ConvertFieldsToUDTMembersStrategyConflictFinder(IDeclarationFinderProvider declarationFinderProvider, IEnumerable candidates, IEnumerable udtCandidates, IEnumerable objectStateUDTs) : base(declarationFinderProvider, candidates, udtCandidates) @@ -33,6 +52,26 @@ public override bool TryValidateEncapsulationAttributes(IEncapsulateFieldCandida return !ConflictsWithExistingUDTMembers(objectStateUDT, field.BackingIdentifier); } + public IObjectStateUDT AssignNoConflictIdentifiers(IObjectStateUDT stateUDT, IDeclarationFinderProvider declarationFinderProvider) + { + var members = declarationFinderProvider.DeclarationFinder.Members(stateUDT.QualifiedModuleName); + var guard = 0; + while (guard++ < 10 && members.Any(m => m.IdentifierName.IsEquivalentVBAIdentifierTo(stateUDT.FieldIdentifier))) + { + stateUDT.FieldIdentifier = stateUDT.FieldIdentifier.IncrementEncapsulationIdentifier(); + } + + members = declarationFinderProvider.DeclarationFinder.Members(stateUDT.QualifiedModuleName) + .Where(m => !_udtTypeIdentifierNonConflictTypes.Any(nct => m.DeclarationType.HasFlag(nct))); + + guard = 0; + while (guard++ < 10 && members.Any(m => m.IdentifierName.IsEquivalentVBAIdentifierTo(stateUDT.TypeIdentifier))) + { + stateUDT.TypeIdentifier = stateUDT.TypeIdentifier.IncrementEncapsulationIdentifier(); + } + return stateUDT; + } + public override IEncapsulateFieldCandidate AssignNoConflictIdentifiers(IEncapsulateFieldCandidate candidate) { candidate = base.AssignNoConflictIdentifier(candidate, DeclarationType.Property); diff --git a/Rubberduck.Refactorings/EncapsulateField/Validations/EncapsulateFieldConflictFinderBase.cs b/Rubberduck.Refactorings/EncapsulateField/Validations/EncapsulateFieldConflictFinderBase.cs index 674e77cc0e..472bd3a7d6 100644 --- a/Rubberduck.Refactorings/EncapsulateField/Validations/EncapsulateFieldConflictFinderBase.cs +++ b/Rubberduck.Refactorings/EncapsulateField/Validations/EncapsulateFieldConflictFinderBase.cs @@ -4,12 +4,9 @@ using Rubberduck.Refactorings.EncapsulateField.Extensions; using Rubberduck.Resources; using Rubberduck.VBEditor; -using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Rubberduck.Refactorings.EncapsulateField { @@ -21,23 +18,26 @@ public interface IEncapsulateFieldConflictFinder bool TryValidateEncapsulationAttributes(IEncapsulateFieldCandidate field, out string errorMessage); } - public abstract class EncapsulateFieldConflictFinderBase : IEncapsulateFieldConflictFinder + public abstract class EncapsulateFieldConflictFinderBase { protected readonly IDeclarationFinderProvider _declarationFinderProvider; protected List _fieldCandidates { set; get; } = new List(); protected List _udtMemberCandidates { set; get; } = new List(); - public EncapsulateFieldConflictFinderBase(IDeclarationFinderProvider declarationFinderProvider, IEnumerable candidates, IEnumerable udtCandidates) + public EncapsulateFieldConflictFinderBase(IDeclarationFinderProvider declarationFinderProvider, IEnumerable candidates, IEnumerable udtMemberCandidates) { _declarationFinderProvider = declarationFinderProvider; _fieldCandidates.AddRange(candidates); - _udtMemberCandidates.AddRange(udtCandidates); + _udtMemberCandidates.AddRange(udtMemberCandidates); } public virtual bool TryValidateEncapsulationAttributes(IEncapsulateFieldCandidate field, out string errorMessage) { errorMessage = string.Empty; - if (!field.EncapsulateFlag) { return true; } + if (!field.EncapsulateFlag) + { + return true; + } if (!field.NameValidator.IsValidVBAIdentifier(field.PropertyIdentifier, out errorMessage)) { @@ -101,18 +101,22 @@ protected virtual bool InternalHasConflictingIdentifier(IEncapsulateFieldCandida errorMessage = string.Empty; var potentialDeclarationIdentifierConflicts = new List(); - potentialDeclarationIdentifierConflicts.AddRange(PotentialConflictIdentifiers(field, declarationType)); + var membersAndLocalReferencesMatches = PotentialConflictIdentifiers(field, declarationType); + potentialDeclarationIdentifierConflicts.AddRange(membersAndLocalReferencesMatches); + var propertiesOfOtherFieldCandidates = _fieldCandidates.Where(fc => fc.TargetID != field.TargetID).Select(fc => fc.PropertyIdentifier); if (ignoreEncapsulationFlags) { - potentialDeclarationIdentifierConflicts.AddRange(_fieldCandidates.Where(fc => fc.TargetID != field.TargetID).Select(fc => fc.PropertyIdentifier)); + potentialDeclarationIdentifierConflicts.AddRange(propertiesOfOtherFieldCandidates); } else { potentialDeclarationIdentifierConflicts.AddRange(FlaggedCandidates.Where(fc => fc.TargetID != field.TargetID).Select(fc => fc.PropertyIdentifier)); } - potentialDeclarationIdentifierConflicts.AddRange(_udtMemberCandidates.Where(udtm => udtm.TargetID != field.TargetID && udtm.EncapsulateFlag).Select(udtm => udtm.PropertyIdentifier)); + var udtMemberNameConflictCandidates = _udtMemberCandidates.Where(udtm => udtm.TargetID != field.TargetID && udtm.EncapsulateFlag).Select(udtm => udtm.PropertyIdentifier); + + potentialDeclarationIdentifierConflicts.AddRange(udtMemberNameConflictCandidates); var identifierToCompare = IdentifierToCompare(field, declarationType); @@ -134,10 +138,12 @@ protected string IdentifierToCompare(IEncapsulateFieldCandidate field, Declarati protected bool HasConflictingIdentifierIgnoreEncapsulationFlag(IEncapsulateFieldCandidate field, DeclarationType declarationType, out string errorMessage) => InternalHasConflictingIdentifier(field, declarationType, true, out errorMessage); - //The refactoring only inserts new code elements with the following Accessibilities: - //Variables => Private - //Properties => Public - //UDTs => Private + /// + ///The refactoring only inserts new code elements with the following Accessibilities: + ///Variables => Private + ///Properties => Public + ///UDTs => Private + /// private bool IsAlwaysIgnoreNameConflictType(Declaration d, DeclarationType toEnapsulateDeclarationType) { //5.3.1.6 Each and must have a procedure @@ -179,8 +185,8 @@ private bool IsAlwaysIgnoreNameConflictType(Declaration d, DeclarationType toEna NeverCauseNameConflictTypes.Remove(DeclarationType.EnumerationMember); } return d.IsLocalVariable() - || d.IsLocalConstant() - || NeverCauseNameConflictTypes.Contains(d.DeclarationType); + || d.IsLocalConstant() + || NeverCauseNameConflictTypes.Contains(d.DeclarationType); } private List PotentialConflictIdentifiers(IEncapsulateFieldCandidate candidate, DeclarationType declarationType) diff --git a/Rubberduck.Refactorings/EncapsulateField/Validations/EncapsulateFieldValidationsProvider.cs b/Rubberduck.Refactorings/EncapsulateField/Validations/EncapsulateFieldValidationsProvider.cs index 0b755c87ca..c917ecd94b 100644 --- a/Rubberduck.Refactorings/EncapsulateField/Validations/EncapsulateFieldValidationsProvider.cs +++ b/Rubberduck.Refactorings/EncapsulateField/Validations/EncapsulateFieldValidationsProvider.cs @@ -1,14 +1,5 @@ using Rubberduck.Parsing.Symbols; -using Rubberduck.Parsing.VBA; -using Rubberduck.Refactorings.Common; -using Rubberduck.Refactorings.EncapsulateField.Extensions; -using Rubberduck.Resources; -using Rubberduck.VBEditor; -using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Rubberduck.Refactorings.EncapsulateField { @@ -20,14 +11,9 @@ public enum NameValidators UserDefinedTypeMemberArray } - public interface IEncapsulateFieldValidationsProvider + public class EncapsulateFieldValidationsProvider { - IEncapsulateFieldConflictFinder ConflictDetector(EncapsulateFieldStrategy strategy, IDeclarationFinderProvider declarationFinderProvider); - } - - public class EncapsulateFieldValidationsProvider : IEncapsulateFieldValidationsProvider - { - private static Dictionary _nameOnlyValidators = new Dictionary() + private static readonly Dictionary _nameOnlyValidators = new Dictionary() { [NameValidators.Default] = new IdentifierOnlyValidator(DeclarationType.Variable, false), [NameValidators.UserDefinedType] = new IdentifierOnlyValidator(DeclarationType.UserDefinedType, false), @@ -35,80 +21,10 @@ public class EncapsulateFieldValidationsProvider : IEncapsulateFieldValidationsP [NameValidators.UserDefinedTypeMemberArray] = new IdentifierOnlyValidator(DeclarationType.UserDefinedTypeMember, true), }; - private static DeclarationType[] _udtTypeIdentifierNonConflictTypes = new DeclarationType[] - { - DeclarationType.Project, - DeclarationType.Module, - DeclarationType.Property, - DeclarationType.Function, - DeclarationType.Procedure, - DeclarationType.Variable, - DeclarationType.Constant, - DeclarationType.UserDefinedTypeMember, - DeclarationType.EnumerationMember, - DeclarationType.Parameter - }; - - - private List _candidates; - private List _udtMemberCandidates; - private List _objectStateUDTs; - - public EncapsulateFieldValidationsProvider(IEnumerable candidates, IEnumerable objectStateUDTCandidates) - { - _udtMemberCandidates = new List(); - _objectStateUDTs = objectStateUDTCandidates.ToList(); - _candidates = candidates.ToList(); - var udtCandidates = candidates.Where(c => c is IUserDefinedTypeCandidate).Cast(); - - foreach (var udtCandidate in candidates.Where(c => c is IUserDefinedTypeCandidate).Cast()) - { - LoadUDTMemberCandidates(udtCandidate); - } - } - - private void LoadUDTMemberCandidates(IUserDefinedTypeCandidate udtCandidate) - { - foreach (var member in udtCandidate.Members) - { - if (member.WrappedCandidate is IUserDefinedTypeCandidate udt) - { - LoadUDTMemberCandidates(udt); - } - _udtMemberCandidates.Add(member); - } - } + public EncapsulateFieldValidationsProvider() + {} public static IValidateVBAIdentifiers NameOnlyValidator(NameValidators validatorType) => _nameOnlyValidators[validatorType]; - - public static IObjectStateUDT AssignNoConflictIdentifiers(IObjectStateUDT stateUDT, IDeclarationFinderProvider declarationFinderProvider) - { - var members = declarationFinderProvider.DeclarationFinder.Members(stateUDT.QualifiedModuleName); - var guard = 0; - while (guard++ < 10 && members.Any(m => m.IdentifierName.IsEquivalentVBAIdentifierTo(stateUDT.FieldIdentifier))) - { - stateUDT.FieldIdentifier = stateUDT.FieldIdentifier.IncrementEncapsulationIdentifier(); - } - - members = declarationFinderProvider.DeclarationFinder.Members(stateUDT.QualifiedModuleName) - .Where(m => !_udtTypeIdentifierNonConflictTypes.Any(nct => m.DeclarationType.HasFlag(nct))); - - guard = 0; - while (guard++ < 10 && members.Any(m => m.IdentifierName.IsEquivalentVBAIdentifierTo(stateUDT.TypeIdentifier))) - { - stateUDT.TypeIdentifier = stateUDT.TypeIdentifier.IncrementEncapsulationIdentifier(); - } - return stateUDT; - } - - public IEncapsulateFieldConflictFinder ConflictDetector(EncapsulateFieldStrategy strategy, IDeclarationFinderProvider declarationFinderProvider) - { - if (strategy == EncapsulateFieldStrategy.UseBackingFields) - { - return new UseBackingFieldsStrategyConflictFinder(declarationFinderProvider, _candidates, _udtMemberCandidates); - } - return new ConvertFieldsToUDTMembersStrategyConflictFinder(declarationFinderProvider, _candidates, _udtMemberCandidates, _objectStateUDTs); - } } } diff --git a/Rubberduck.Refactorings/EncapsulateField/Validations/UseBackingFieldsStrategyConflictFinder.cs b/Rubberduck.Refactorings/EncapsulateField/Validations/UseBackingFieldsStrategyConflictFinder.cs index e324c401ca..24d99967bd 100644 --- a/Rubberduck.Refactorings/EncapsulateField/Validations/UseBackingFieldsStrategyConflictFinder.cs +++ b/Rubberduck.Refactorings/EncapsulateField/Validations/UseBackingFieldsStrategyConflictFinder.cs @@ -8,7 +8,7 @@ namespace Rubberduck.Refactorings.EncapsulateField { - public class UseBackingFieldsStrategyConflictFinder : EncapsulateFieldConflictFinderBase + public class UseBackingFieldsStrategyConflictFinder : EncapsulateFieldConflictFinderBase, IEncapsulateFieldConflictFinder { public UseBackingFieldsStrategyConflictFinder(IDeclarationFinderProvider declarationFinderProvider, IEnumerable candidates, IEnumerable udtCandidates) : base(declarationFinderProvider, candidates, udtCandidates) { } From 11b8bd6ef000d2c90dfd7c70ee9753e958fc88cd Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Thu, 3 Sep 2020 11:46:09 -0700 Subject: [PATCH 24/67] Add registration of EncapsulateFieldRefactoring factories --- .../Root/RubberduckIoCInstaller.cs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/Rubberduck.Main/Root/RubberduckIoCInstaller.cs b/Rubberduck.Main/Root/RubberduckIoCInstaller.cs index 7a66399bb0..d86f04efcc 100644 --- a/Rubberduck.Main/Root/RubberduckIoCInstaller.cs +++ b/Rubberduck.Main/Root/RubberduckIoCInstaller.cs @@ -380,7 +380,14 @@ private void RegisterSpecialFactories(IWindsorContainer container) container.Register(Component.For() .ImplementedBy() .LifestyleSingleton()); + + container.Register(Component.For() + .ImplementedBy() + .LifestyleSingleton()); + RegisterUnreachableCaseFactories(container); + + RegisterEncapsulateFieldRefactoringFactories(container); } private void RegisterUnreachableCaseFactories(IWindsorContainer container) @@ -390,6 +397,22 @@ private void RegisterUnreachableCaseFactories(IWindsorContainer container) .LifestyleSingleton()); } + private void RegisterEncapsulateFieldRefactoringFactories(IWindsorContainer container) + { + container.Register(Component.For() + .ImplementedBy() + .LifestyleSingleton()); + container.Register(Component.For() + .ImplementedBy() + .LifestyleSingleton()); + container.Register(Component.For() + .ImplementedBy() + .LifestyleSingleton()); + container.Register(Component.For() + .ImplementedBy() + .LifestyleSingleton()); + } + private void RegisterQuickFixes(IWindsorContainer container, Assembly[] assembliesToRegister) { From 9fde732bf559c48e5c4aa3a5932af18c67d7579c Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Thu, 3 Sep 2020 11:49:40 -0700 Subject: [PATCH 25/67] Integrate new factories and models --- .../EncapsulateFieldElementsBuilder.cs | 163 -------------- .../EncapsulateField/EncapsulateFieldModel.cs | 210 ++++-------------- .../EncapsulateFieldPreviewProvider.cs | 75 +------ .../EncapsulateFieldRefactoring.cs | 47 ++-- .../EncapsulateFieldRefactoringAction.cs | 13 +- .../FieldCandidates/ArrayCandidate.cs | 2 +- .../ConvertToUDTMemberCandidate.cs | 6 +- .../EncapsulateFieldCandidate.cs | 18 +- .../UserDefinedTypeCandidate.cs | 10 +- .../UserDefinedTypeMemberCandidate.cs | 19 +- .../EncapsulateField/ObjectStateUDT.cs | 72 ++---- 11 files changed, 135 insertions(+), 500 deletions(-) delete mode 100644 Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldElementsBuilder.cs diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldElementsBuilder.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldElementsBuilder.cs deleted file mode 100644 index aff59a9627..0000000000 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldElementsBuilder.cs +++ /dev/null @@ -1,163 +0,0 @@ -using Rubberduck.Parsing.Grammar; -using Rubberduck.Parsing.Symbols; -using Rubberduck.Parsing.VBA; -using Rubberduck.Refactorings.Common; -using Rubberduck.VBEditor; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; - -namespace Rubberduck.Refactorings.EncapsulateField -{ - public class EncapsulateFieldElementsBuilder - { - private readonly IDeclarationFinderProvider _declarationFinderProvider; - private QualifiedModuleName _targetQMN; - private string _defaultObjectStateUDTTypeName; - private ICodeBuilder _codeBuilder; - - public EncapsulateFieldElementsBuilder(IDeclarationFinderProvider declarationFinderProvider, QualifiedModuleName targetQMN) - { - _declarationFinderProvider = declarationFinderProvider; - _targetQMN = targetQMN; - _defaultObjectStateUDTTypeName = $"T{_targetQMN.ComponentName}"; - _codeBuilder = new CodeBuilder(); - CreateRefactoringElements(); - } - - public IObjectStateUDT DefaultObjectStateUDT { private set; get; } - - public IObjectStateUDT ObjectStateUDT { private set; get; } - - public IEncapsulateFieldValidationsProvider ValidationsProvider { private set; get; } - - public IEnumerable Candidates { private set; get; } - - public IEnumerable ObjectStateUDTCandidates { private set; get; } = new List(); - - private void CreateRefactoringElements() - { - var fieldDeclarations = _declarationFinderProvider.DeclarationFinder - .Members(_targetQMN) - .Where(v => v.IsMemberVariable() && !v.IsWithEvents); - - var defaultNamesValidator = EncapsulateFieldValidationsProvider.NameOnlyValidator(NameValidators.Default); - - var candidates = new List(); - foreach (var fieldDeclaration in fieldDeclarations) - { - Debug.Assert(!fieldDeclaration.DeclarationType.Equals(DeclarationType.UserDefinedTypeMember)); - - var fieldEncapsulationCandidate = CreateCandidate(fieldDeclaration, defaultNamesValidator); - - candidates.Add(fieldEncapsulationCandidate); - } - - Candidates = candidates; - - ObjectStateUDTCandidates = BuildObjectStateUDTCandidates(candidates).ToList(); - - ObjectStateUDT = ObjectStateUDTCandidates.FirstOrDefault(os => os.AsTypeDeclaration.IdentifierName.StartsWith(_defaultObjectStateUDTTypeName, StringComparison.InvariantCultureIgnoreCase)); - - DefaultObjectStateUDT = CreateStateUDTField(); - DefaultObjectStateUDT.IsSelected = true; - if (ObjectStateUDT != null) - { - ObjectStateUDT.IsSelected = true; - DefaultObjectStateUDT.IsSelected = false; - } - - ObjectStateUDTCandidates = ObjectStateUDTCandidates.Concat(new IObjectStateUDT[] { DefaultObjectStateUDT }); - - ValidationsProvider = new EncapsulateFieldValidationsProvider(Candidates, ObjectStateUDTCandidates); - - var conflictsFinder = ValidationsProvider.ConflictDetector(EncapsulateFieldStrategy.UseBackingFields, _declarationFinderProvider); - foreach (var candidate in candidates) - { - candidate.ConflictFinder = conflictsFinder; - } - } - - private IEnumerable BuildObjectStateUDTCandidates(IEnumerable candidates) - { - var udtCandidates = candidates.Where(c => c is IUserDefinedTypeCandidate udt - && udt.CanBeObjectStateUDT); - - var objectStateUDTs = new List(); - foreach (var udt in udtCandidates) - { - objectStateUDTs.Add(new ObjectStateUDT(udt as IUserDefinedTypeCandidate)); - } - - var objectStateUDT = objectStateUDTs.FirstOrDefault(os => os.AsTypeDeclaration.IdentifierName.StartsWith(_defaultObjectStateUDTTypeName, StringComparison.InvariantCultureIgnoreCase)); - - return objectStateUDTs; - } - - private IObjectStateUDT CreateStateUDTField() - { - var stateUDT = new ObjectStateUDT(_targetQMN) as IObjectStateUDT; - - EncapsulateFieldValidationsProvider.AssignNoConflictIdentifiers(stateUDT, _declarationFinderProvider); - - stateUDT.IsSelected = true; - - return stateUDT; - } - - private IEncapsulateFieldCandidate CreateCandidate(Declaration target, IValidateVBAIdentifiers validator) - { - if (target.IsUserDefinedType()) - { - var udtValidator = EncapsulateFieldValidationsProvider.NameOnlyValidator(NameValidators.UserDefinedType); - var udtField = new UserDefinedTypeCandidate(target, udtValidator, _codeBuilder.BuildPropertyRhsParameterName) as IUserDefinedTypeCandidate; - - (Declaration udtDeclaration, IEnumerable udtMembers) = GetUDTAndMembersForField(udtField); - - udtField.TypeDeclarationIsPrivate = udtDeclaration.HasPrivateAccessibility(); - - udtField.NameValidator = udtValidator; - - foreach (var udtMemberDeclaration in udtMembers) - { - var udtMemberValidator = EncapsulateFieldValidationsProvider.NameOnlyValidator(NameValidators.UserDefinedTypeMember); - if (udtMemberDeclaration.IsArray) - { - udtMemberValidator = EncapsulateFieldValidationsProvider.NameOnlyValidator(NameValidators.UserDefinedTypeMemberArray); - } - var candidateUDTMember = new UserDefinedTypeMemberCandidate(CreateCandidate(udtMemberDeclaration, udtMemberValidator), udtField, _codeBuilder.BuildPropertyRhsParameterName) as IUserDefinedTypeMemberCandidate; - - udtField.AddMember(candidateUDTMember); - } - - var udtVariablesOfSameType = _declarationFinderProvider.DeclarationFinder.UserDeclarations(DeclarationType.Variable) - .Where(v => v.AsTypeDeclaration == udtDeclaration); - - udtField.CanBeObjectStateUDT = udtField.TypeDeclarationIsPrivate - && udtField.Declaration.HasPrivateAccessibility() - && udtVariablesOfSameType.Count() == 1; - - return udtField; - } - else if (target.IsArray) - { - return new ArrayCandidate(target, validator, _codeBuilder.BuildPropertyRhsParameterName); - } - - var candidate = new EncapsulateFieldCandidate(target, validator, _codeBuilder.BuildPropertyRhsParameterName); - return candidate; - } - - private (Declaration TypeDeclaration, IEnumerable Members) GetUDTAndMembersForField(IUserDefinedTypeCandidate udtField) - { - var userDefinedTypeDeclaration = udtField.Declaration.AsTypeDeclaration; - - var udtMembers = _declarationFinderProvider.DeclarationFinder - .UserDeclarations(DeclarationType.UserDefinedTypeMember) - .Where(utm => userDefinedTypeDeclaration == utm.ParentDeclaration); - - return (userDefinedTypeDeclaration, udtMembers); - } - } -} diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs index 5c662e85ac..7f885a1c29 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs @@ -1,207 +1,91 @@ -using System.Collections.Generic; +using Rubberduck.Refactorings.Common; +using Rubberduck.Refactorings.EncapsulateFieldUseBackingField; +using Rubberduck.Refactorings.EncapsulateFieldUseBackingUDTMember; +using System.Collections.Generic; using System.Linq; -using Rubberduck.Parsing.Symbols; -using Rubberduck.Parsing.VBA; -using Rubberduck.VBEditor; -using Rubberduck.Refactorings.CodeBlockInsert; namespace Rubberduck.Refactorings.EncapsulateField { public class EncapsulateFieldModel : IRefactoringModel { - private QualifiedModuleName _targetQMN; - private IDeclarationFinderProvider _declarationFinderProvider; - private IEncapsulateFieldValidationsProvider _validationsProvider; - private IObjectStateUDT _newObjectStateUDT; - - private List _convertedFields; - private HashSet _objStateCandidates; - - private IDictionary)> _udtFieldToUdtDeclarationMap = new Dictionary)>(); - - private Dictionary> _newContent { set; get; } - - public EncapsulateFieldModel( - Declaration target, - IEnumerable candidates, - IEnumerable objectStateUDTCandidates, - IObjectStateUDT stateUDTField, - IDeclarationFinderProvider declarationFinderProvider, - IEncapsulateFieldValidationsProvider validationsProvider) + public EncapsulateFieldModel(EncapsulateFieldUseBackingFieldModel backingFieldModel, + EncapsulateFieldUseBackingUDTMemberModel udtModel) { - _targetQMN = target.QualifiedModuleName; - _newObjectStateUDT = stateUDTField; - _declarationFinderProvider = declarationFinderProvider; - _validationsProvider = validationsProvider; - - _useBackingFieldCandidates = candidates.ToList(); - _objStateCandidates = new HashSet(objectStateUDTCandidates); - _objStateCandidates.Add(_newObjectStateUDT); - - EncapsulateFieldStrategy = EncapsulateFieldStrategy.UseBackingFields; - _activeObjectStateUDT = ObjectStateUDTField; + EncapsulateFieldUseBackingFieldModel = backingFieldModel; + EncapsulateFieldUseBackingUDTMemberModel = udtModel; + ResetConflictDetection(EncapsulateFieldStrategy.UseBackingFields); } - public bool IncludeNewContentMarker { set; get; } = false; + public EncapsulateFieldUseBackingUDTMemberModel EncapsulateFieldUseBackingUDTMemberModel { get; } - public QualifiedModuleName QualifiedModuleName => _targetQMN; + public EncapsulateFieldUseBackingFieldModel EncapsulateFieldUseBackingFieldModel { get; } public IRefactoringPreviewProvider PreviewProvider { set; get; } - - public IEnumerable ObjectStateUDTCandidates => _objStateCandidates; - private EncapsulateFieldStrategy _encapsulationFieldStategy; + public IEnumerable ObjectStateUDTCandidates => EncapsulateFieldUseBackingUDTMemberModel.ObjectStateUDTCandidates; + + private EncapsulateFieldStrategy _strategy; public EncapsulateFieldStrategy EncapsulateFieldStrategy { - get => _encapsulationFieldStategy; set { - if (_encapsulationFieldStategy == value) + if (_strategy == value) { return; } + _strategy = value; + ResetConflictDetection(_strategy); + } + get => _strategy; + } - _encapsulationFieldStategy = value; + private void ResetConflictDetection(EncapsulateFieldStrategy strategy) + { + var conflictFinder = + strategy == EncapsulateFieldStrategy.UseBackingFields + ? EncapsulateFieldUseBackingFieldModel.ConflictFinder + : EncapsulateFieldUseBackingUDTMemberModel.ConflictFinder; - if (_encapsulationFieldStategy == EncapsulateFieldStrategy.UseBackingFields) - { - UpdateFieldCandidatesForUseBackingFieldsStrategy(); - return; - } - UpdateFieldCandidatesForConvertFieldsToUDTMembersStrategy(); + foreach (var candidate in EncapsulateFieldUseBackingFieldModel.EncapsulationCandidates) + { + candidate.ConflictFinder = conflictFinder; + ResolveConflict(conflictFinder, candidate); } - } - public IEncapsulateFieldValidationsProvider ValidationsProvider => _validationsProvider; + return; + } - private List _useBackingFieldCandidates; - public List EncapsulationCandidates + private void ResolveConflict(IEncapsulateFieldConflictFinder conflictFinder, IEncapsulateFieldCandidate candidate) { - get + conflictFinder.AssignNoConflictIdentifiers(candidate); + if (candidate is IUserDefinedTypeCandidate udtCandidate) { - if (EncapsulateFieldStrategy == EncapsulateFieldStrategy.UseBackingFields) + foreach (var member in udtCandidate.Members) { - return _useBackingFieldCandidates; - } - - if (_convertedFields is null) - { - _convertedFields = new List(); - foreach (var field in _useBackingFieldCandidates) + conflictFinder.AssignNoConflictIdentifiers(member); + if (member.WrappedCandidate is IUserDefinedTypeCandidate childUDT + && childUDT.Declaration.AsTypeDeclaration.HasPrivateAccessibility()) { - _convertedFields.Add(new ConvertToUDTMember(field, ObjectStateUDTField)); + ResolveConflict(conflictFinder, childUDT); } } - return _convertedFields; } - } + } + + public IReadOnlyCollection EncapsulationCandidates => EncapsulateFieldStrategy == EncapsulateFieldStrategy.UseBackingFields + ? EncapsulateFieldUseBackingFieldModel.EncapsulationCandidates + : EncapsulateFieldUseBackingUDTMemberModel.EncapsulationCandidates; public IEnumerable SelectedFieldCandidates => EncapsulationCandidates.Where(v => v.EncapsulateFlag); - public IEnumerable UDTFieldCandidates - => EncapsulationCandidates - .Where(v => v is IUserDefinedTypeCandidate) - .Cast(); - - public IEnumerable SelectedUDTFieldCandidates - => SelectedFieldCandidates - .Where(v => v is IUserDefinedTypeCandidate) - .Cast(); - public IEncapsulateFieldCandidate this[string encapsulatedFieldTargetID] => EncapsulationCandidates.Where(c => c.TargetID.Equals(encapsulatedFieldTargetID)).Single(); - public IEncapsulateFieldCandidate this[Declaration fieldDeclaration] - => EncapsulationCandidates.Where(c => c.Declaration == fieldDeclaration).Single(); - - public void AddContentBlock(NewContentType contentType, string block) - => _newContent[contentType].Add(block); - - public Dictionary> NewContent - { - set => _newContent = value; - get => _newContent; - } - - private IObjectStateUDT _activeObjectStateUDT; public IObjectStateUDT ObjectStateUDTField { - get - { - _activeObjectStateUDT = ObjectStateUDTCandidates - .SingleOrDefault(os => os.IsSelected) ?? _newObjectStateUDT; - - return _activeObjectStateUDT; - } - set - { - if (_activeObjectStateUDT.FieldIdentifier == (value?.FieldIdentifier ?? string.Empty)) - { - return; - } - - foreach (var objectStateUDT in ObjectStateUDTCandidates) - { - objectStateUDT.IsSelected = false; - } - - _activeObjectStateUDT = - ObjectStateUDTCandidates.SingleOrDefault(os => os.FieldIdentifier.Equals(value?.FieldIdentifier ?? null)) - ?? _newObjectStateUDT; - - _activeObjectStateUDT.IsSelected = true; - - if (EncapsulateFieldStrategy == EncapsulateFieldStrategy.ConvertFieldsToUDTMembers) - { - foreach (var field in EncapsulationCandidates) - { - if (field is IConvertToUDTMember convertedField) - { - convertedField.ObjectStateUDT = _activeObjectStateUDT; - convertedField.ConflictFinder = _validationsProvider.ConflictDetector(EncapsulateFieldStrategy, _declarationFinderProvider); - convertedField.ConflictFinder.AssignNoConflictIdentifiers(convertedField); - } - } - } - } - } - - private void UpdateFieldCandidatesForUseBackingFieldsStrategy() - { - foreach (var candidate in EncapsulationCandidates) - { - switch (candidate) - { - case IUserDefinedTypeCandidate udt: - candidate.NameValidator = EncapsulateFieldValidationsProvider.NameOnlyValidator(NameValidators.UserDefinedType); - break; - case IUserDefinedTypeMemberCandidate udtm: - candidate.NameValidator = candidate.Declaration.IsArray - ? EncapsulateFieldValidationsProvider.NameOnlyValidator(NameValidators.UserDefinedTypeMemberArray) - : EncapsulateFieldValidationsProvider.NameOnlyValidator(NameValidators.UserDefinedTypeMember); - break; - default: - candidate.NameValidator = EncapsulateFieldValidationsProvider.NameOnlyValidator(NameValidators.Default); - break; - } - candidate.ConflictFinder = _validationsProvider.ConflictDetector(EncapsulateFieldStrategy, _declarationFinderProvider); - candidate.ConflictFinder.AssignNoConflictIdentifiers(candidate); - } - } - - private void UpdateFieldCandidatesForConvertFieldsToUDTMembersStrategy() - { - foreach (var candidate in EncapsulationCandidates.Cast()) - { - candidate.ObjectStateUDT = ObjectStateUDTField; - candidate.NameValidator = candidate.Declaration.IsArray - ? EncapsulateFieldValidationsProvider.NameOnlyValidator(NameValidators.UserDefinedTypeMemberArray) - : EncapsulateFieldValidationsProvider.NameOnlyValidator(NameValidators.UserDefinedTypeMember); - - candidate.ConflictFinder = _validationsProvider.ConflictDetector(EncapsulateFieldStrategy, _declarationFinderProvider); - candidate.ConflictFinder.AssignNoConflictIdentifiers(candidate); - } + get => EncapsulateFieldUseBackingUDTMemberModel.ObjectStateUDTField; + set => EncapsulateFieldUseBackingUDTMemberModel.ObjectStateUDTField = value; } } } diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldPreviewProvider.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldPreviewProvider.cs index bbe6909fd1..9e80fdab42 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldPreviewProvider.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldPreviewProvider.cs @@ -1,7 +1,6 @@ -using Rubberduck.Parsing.Rewriter; -using Rubberduck.Refactorings.EncapsulateField.Extensions; -using Rubberduck.VBEditor; -using System; +using Rubberduck.Parsing.VBA; +using Rubberduck.Refactorings.EncapsulateFieldUseBackingField; +using Rubberduck.Refactorings.EncapsulateFieldUseBackingUDTMember; namespace Rubberduck.Refactorings.EncapsulateField { @@ -9,10 +8,12 @@ public class EncapsulateFieldPreviewProvider : IRefactoringPreviewProvider - { - public EncapsulateFieldUseBackingFieldPreviewProvider(EncapsulateFieldUseBackingFieldRefactoringAction refactoringAction, - IRewritingManager rewritingManager) - : base(refactoringAction, rewritingManager) - {} - - public override string Preview(EncapsulateFieldModel model) - { - var preview = string.Empty; - var initialFlagValue = model.IncludeNewContentMarker; - model.IncludeNewContentMarker = true; - try - { - preview = base.Preview(model); - } - catch (Exception e) { } - finally - { - model.IncludeNewContentMarker = initialFlagValue; - } - return preview; - } - - protected override QualifiedModuleName ComponentToShow(EncapsulateFieldModel model) - { - return model.QualifiedModuleName; - } - } - - public class EncapsulateFieldUseBackingUDTMemberPreviewProvider : RefactoringPreviewProviderWrapperBase - { - public EncapsulateFieldUseBackingUDTMemberPreviewProvider(EncapsulateFieldUseBackingUDTMemberRefactoringAction refactoringAction, - IRewritingManager rewritingManager) - : base(refactoringAction, rewritingManager) - {} - - public override string Preview(EncapsulateFieldModel model) - { - var preview = string.Empty; - var initialFlagValue = model.IncludeNewContentMarker; - model.IncludeNewContentMarker = true; - try - { - preview = base.Preview(model); - } - catch (Exception e) { } - finally - { - model.IncludeNewContentMarker = initialFlagValue; - } - return preview; - } - - protected override QualifiedModuleName ComponentToShow(EncapsulateFieldModel model) - { - return model.QualifiedModuleName; - } - } } diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoring.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoring.cs index 0a10abde1e..0927c1e171 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoring.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoring.cs @@ -21,22 +21,25 @@ public class EncapsulateFieldRefactoring : InteractiveRefactoringBase userInteraction, - IRewritingManager rewritingManager, - ISelectionProvider selectionProvider, - ISelectedDeclarationProvider selectedDeclarationProvider) - :base(selectionProvider, userInteraction) + EncapsulateFieldRefactoringAction refactoringAction, + EncapsulateFieldPreviewProvider previewProvider, + IEncapsulateFieldModelFactory encapsulateFieldModelFactory, + IDeclarationFinderProvider declarationFinderProvider, + RefactoringUserInteraction userInteraction, + IRewritingManager rewritingManager, + ISelectionProvider selectionProvider, + ISelectedDeclarationProvider selectedDeclarationProvider) + :base(selectionProvider, userInteraction) { _refactoringAction = refactoringAction; _previewProvider = previewProvider; _declarationFinderProvider = declarationFinderProvider; _selectedDeclarationProvider = selectedDeclarationProvider; _rewritingManager = rewritingManager; + _modelFactory = encapsulateFieldModelFactory; } protected override Declaration FindTargetDeclaration(QualifiedSelection targetSelection) @@ -64,35 +67,19 @@ protected override EncapsulateFieldModel InitializeModel(Declaration target) throw new InvalidDeclarationTypeException(target); } - var builder = new EncapsulateFieldElementsBuilder(_declarationFinderProvider, target.QualifiedModuleName); + var model = _modelFactory.Create(target); - var selected = builder.Candidates.Single(c => c.Declaration == target); - selected.EncapsulateFlag = true; - - var model = new EncapsulateFieldModel( - target, - builder.Candidates, - builder.ObjectStateUDTCandidates, - builder.DefaultObjectStateUDT, - _declarationFinderProvider, - builder.ValidationsProvider) - { - PreviewProvider = _previewProvider, - ObjectStateUDTField = builder.ObjectStateUDT, - EncapsulateFieldStrategy = builder.ObjectStateUDT != null ? EncapsulateFieldStrategy.ConvertFieldsToUDTMembers : EncapsulateFieldStrategy.UseBackingFields, - }; + model.PreviewProvider = _previewProvider; return model; } - private EncapsulateFieldStrategy ApplyStrategy(IObjectStateUDT objStateUDT) - { - return objStateUDT != null ? EncapsulateFieldStrategy.ConvertFieldsToUDTMembers : EncapsulateFieldStrategy.UseBackingFields; - } - protected override void RefactorImpl(EncapsulateFieldModel model) { - if (!model.SelectedFieldCandidates.Any()) { return; } + if (!model.SelectedFieldCandidates.Any()) + { + return; + } _refactoringAction.Refactor(model); } diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringAction.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringAction.cs index beb44d75fd..252d20fe69 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringAction.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringAction.cs @@ -1,4 +1,7 @@ -using System.Linq; +using Rubberduck.Parsing.VBA; +using Rubberduck.Refactorings.EncapsulateFieldUseBackingField; +using Rubberduck.Refactorings.EncapsulateFieldUseBackingUDTMember; +using System.Linq; namespace Rubberduck.Refactorings.EncapsulateField { @@ -6,11 +9,13 @@ public class EncapsulateFieldRefactoringAction : IRefactoringAction rf.QualifiedModuleName != QualifiedModuleName - && rf.Context.TryGetAncestor(out _))) + && rf.Context.TryGetAncestor(out _))) { errorMessage = string.Format(RubberduckUI.EncapsulateField_ArrayHasExternalRedimFormat, IdentifierName); return true; diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ConvertToUDTMemberCandidate.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ConvertToUDTMemberCandidate.cs index bb2720dd04..fd1504435c 100644 --- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ConvertToUDTMemberCandidate.cs +++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ConvertToUDTMemberCandidate.cs @@ -89,8 +89,10 @@ public bool IsReadOnly public IValidateVBAIdentifiers NameValidator { - set => _wrapped.NameValidator = value; - get => _wrapped.NameValidator; + set { } + get => _wrapped.Declaration.IsArray + ? EncapsulateFieldValidationsProvider.NameOnlyValidator(NameValidators.UserDefinedTypeMemberArray) + : EncapsulateFieldValidationsProvider.NameOnlyValidator(NameValidators.UserDefinedTypeMember); } public IEncapsulateFieldConflictFinder ConflictFinder diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidate.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidate.cs index 9e80cd065e..65b70dcc34 100644 --- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidate.cs +++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidate.cs @@ -114,8 +114,15 @@ public virtual string BackingIdentifier public virtual IEncapsulateFieldConflictFinder ConflictFinder { set; get; } - public virtual bool TryValidateEncapsulationAttributes(out string errorMessage) - => ConflictFinder.TryValidateEncapsulationAttributes(this, out errorMessage); + public virtual bool TryValidateEncapsulationAttributes(out string errorMessage) + { + errorMessage = string.Empty; + if (ConflictFinder is null) + { + return true; + } + return ConflictFinder.TryValidateEncapsulationAttributes(this, out errorMessage); + } public virtual string TargetID => _target?.IdentifierName ?? IdentifierName; @@ -124,14 +131,17 @@ public virtual bool EncapsulateFlag { set { - var valueChanged = _encapsulateFlag != value; + if (_encapsulateFlag == value) + { + return; + } _encapsulateFlag = value; if (!_encapsulateFlag) { PropertyIdentifier = _fieldAndProperty.DefaultPropertyName; } - else if (valueChanged) + else if (ConflictFinder != null) { ConflictFinder.AssignNoConflictIdentifiers(this); } diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeCandidate.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeCandidate.cs index 96d8871512..5ca521fc75 100644 --- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeCandidate.cs +++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeCandidate.cs @@ -10,7 +10,7 @@ public interface IUserDefinedTypeCandidate : IEncapsulateFieldCandidate { IEnumerable Members { get; } void AddMember(IUserDefinedTypeMemberCandidate member); - bool TypeDeclarationIsPrivate { set; get; } + bool TypeDeclarationIsPrivate { get; } bool CanBeObjectStateUDT { set; get; } bool IsSelectedObjectStateUDT { set; get; } } @@ -20,6 +20,7 @@ public class UserDefinedTypeCandidate : EncapsulateFieldCandidate, IUserDefinedT public UserDefinedTypeCandidate(Declaration declaration, IValidateVBAIdentifiers identifierValidator, Func parameterNameBuilder) : base(declaration, identifierValidator, parameterNameBuilder) { + TypeDeclarationIsPrivate = declaration.HasPrivateAccessibility(); } public void AddMember(IUserDefinedTypeMemberCandidate member) @@ -39,12 +40,7 @@ public bool TypeDeclarationIsPrivate public bool IsSelectedObjectStateUDT { set; get; } - private bool _canBeObjectStateUDT; - public bool CanBeObjectStateUDT - { - set => _canBeObjectStateUDT = value; - get => _canBeObjectStateUDT; - } + public bool CanBeObjectStateUDT { set; get; } public override string BackingIdentifier { diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeMemberCandidate.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeMemberCandidate.cs index 93c089c4e0..6b5bf7ca86 100644 --- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeMemberCandidate.cs +++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeMemberCandidate.cs @@ -40,10 +40,7 @@ public UserDefinedTypeMemberCandidate(IEncapsulateFieldCandidate candidate, IUse public string BackingIdentifier { - get - { - return _wrappedCandidate.IdentifierName; - } + get => _wrappedCandidate.IdentifierName; set { } } @@ -176,17 +173,19 @@ public bool EncapsulateFlag return; } var valueChanged = _encapsulateFlag != value; - _encapsulateFlag = value; - if (!_encapsulateFlag) + + PropertyIdentifier = _wrappedCandidate.PropertyIdentifier; + if (_encapsulateFlag && valueChanged && ConflictFinder != null) { - _wrappedCandidate.EncapsulateFlag = value; - PropertyIdentifier = _wrappedCandidate.PropertyIdentifier; + ConflictFinder.AssignNoConflictIdentifiers(this); } - else if (valueChanged) + + if (!_encapsulateFlag) { - ConflictFinder.AssignNoConflictIdentifiers(this); + _wrappedCandidate.EncapsulateFlag = value; } + } get => _encapsulateFlag; } diff --git a/Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT.cs b/Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT.cs index 398ed211cd..c775bb70fa 100644 --- a/Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT.cs +++ b/Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT.cs @@ -1,30 +1,33 @@ -using Rubberduck.Parsing.Grammar; -using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.Symbols; using Rubberduck.Common; -using Rubberduck.SmartIndenter; using Rubberduck.VBEditor; using System; using System.Collections.Generic; using System.Linq; using Rubberduck.Resources; +using Rubberduck.Parsing.Grammar; namespace Rubberduck.Refactorings.EncapsulateField { public interface IObjectStateUDT : IEncapsulateFieldRefactoringElement { string TypeIdentifier { set; get; } - string FieldIdentifier { set; get; } - string TypeDeclarationBlock(IIndenter indenter = null); string FieldDeclarationBlock { get; } - void AddMembers(IEnumerable fields); + string FieldIdentifier { set; get; } bool IsExistingDeclaration { get; } Declaration AsTypeDeclaration { get; } bool IsSelected { set; get; } IEnumerable ExistingMembers { get; } } - //ObjectStateUDT can be an existing UDT (Private only) selected by the user, or a - //newly inserted declaration + /// + /// ObjectStateUDT is a Private UserDefinedType whose UserDefinedTypeMembers represent + /// object state in lieu of (or in addition to) a set of Private fields. + /// + /// + /// Within the EncapsulateField refactoring, the ObjectStateUDT can be an existing + /// UserDefinedType or an identifier that will be used to generate a new UserDefinedType + /// public class ObjectStateUDT : IObjectStateUDT { private static string _defaultNewFieldName = RubberduckUI.EncapsulateField_DefaultObjectStateUDTFieldName; @@ -59,8 +62,12 @@ private ObjectStateUDT(string typeIdentifier) TypeIdentifier = typeIdentifier; _convertedMembers = new List(); _codeBuilder = new CodeBuilder(); + _convertedMembers = new List(); } + public string FieldDeclarationBlock + => $"{Accessibility.Private} {IdentifierName} {Tokens.As} {AsTypeName}"; + public string IdentifierName => _wrappedUDT?.IdentifierName ?? FieldIdentifier; public string AsTypeName => _wrappedUDT?.AsTypeName ?? TypeIdentifier; @@ -74,27 +81,19 @@ public bool IsSelected if (_wrappedUDT != null) { _wrappedUDT.IsSelectedObjectStateUDT = value; - } - - if (_isSelected && IsExistingDeclaration) - { - _wrappedUDT.EncapsulateFlag = false; + if (_isSelected && IsExistingDeclaration) + { + _wrappedUDT.EncapsulateFlag = false; + } } } get => _isSelected; } - public IEnumerable ExistingMembers - { - get - { - if (IsExistingDeclaration) - { - return _wrappedUDT.Members; - } - return Enumerable.Empty(); - } - } + public IEnumerable ExistingMembers + => IsExistingDeclaration + ? _wrappedUDT.Members + : Enumerable.Empty(); private QualifiedModuleName _qmn; public QualifiedModuleName QualifiedModuleName @@ -111,31 +110,6 @@ public QualifiedModuleName QualifiedModuleName public string FieldIdentifier { set; get; } - public void AddMembers(IEnumerable fields) - { - _convertedMembers = new List(); - if (IsExistingDeclaration) - { - foreach (var member in _wrappedUDT.Members) - { - var convertedMember = new ConvertToUDTMember(member, this) { EncapsulateFlag = false }; - _convertedMembers.Add(convertedMember); - } - } - _convertedMembers.AddRange(fields); - } - - public string FieldDeclarationBlock - => $"{Accessibility.Private} {IdentifierName} {Tokens.As} {AsTypeName}"; - - public string TypeDeclarationBlock(IIndenter indenter = null) - { - var udtMembers = _convertedMembers.Where(m => m.Declaration is VariableDeclaration) - .Select(m => (m.Declaration as VariableDeclaration, m.BackingIdentifier)); - - return _codeBuilder.BuildUserDefinedTypeDeclaration(AsTypeName, udtMembers); - } - public override bool Equals(object obj) { if (obj is IObjectStateUDT stateUDT && stateUDT.FieldIdentifier == FieldIdentifier) From f468e8e2971dfe247226772b86b917b9c6c437fc Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Fri, 4 Sep 2020 06:44:03 -0700 Subject: [PATCH 26/67] Simplify Identifier validations Using VBAIdentifierValidator directly and removed wrapper classes. Renamed Validations folder to ConflictDetection. --- ...ieldsToUDTMembersStrategyConflictFinder.cs | 3 -- .../EncapsulateFieldConflictFinderBase.cs | 6 +++- .../UseBackingFieldsStrategyConflictFinder.cs | 3 -- .../EncapsulateFieldCandidateFactory.cs | 29 +++--------------- .../FieldCandidates/ArrayCandidate.cs | 4 +-- .../ConvertToUDTMemberCandidate.cs | 8 ----- .../EncapsulateFieldCandidate.cs | 8 ++--- .../EncapsulationIdentifiers.cs | 8 +++-- .../UserDefinedTypeCandidate.cs | 18 ++--------- .../UserDefinedTypeMemberCandidate.cs | 6 ---- .../EncapsulateFieldValidationsProvider.cs | 30 ------------------- .../Validations/IdentifierOnlyValidator.cs | 29 ------------------ 12 files changed, 20 insertions(+), 132 deletions(-) rename Rubberduck.Refactorings/EncapsulateField/{Validations => ConflictDetection}/ConvertFieldsToUDTMembersStrategyConflictFinder.cs (98%) rename Rubberduck.Refactorings/EncapsulateField/{Validations => ConflictDetection}/EncapsulateFieldConflictFinderBase.cs (96%) rename Rubberduck.Refactorings/EncapsulateField/{Validations => ConflictDetection}/UseBackingFieldsStrategyConflictFinder.cs (93%) delete mode 100644 Rubberduck.Refactorings/EncapsulateField/Validations/EncapsulateFieldValidationsProvider.cs delete mode 100644 Rubberduck.Refactorings/EncapsulateField/Validations/IdentifierOnlyValidator.cs diff --git a/Rubberduck.Refactorings/EncapsulateField/Validations/ConvertFieldsToUDTMembersStrategyConflictFinder.cs b/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/ConvertFieldsToUDTMembersStrategyConflictFinder.cs similarity index 98% rename from Rubberduck.Refactorings/EncapsulateField/Validations/ConvertFieldsToUDTMembersStrategyConflictFinder.cs rename to Rubberduck.Refactorings/EncapsulateField/ConflictDetection/ConvertFieldsToUDTMembersStrategyConflictFinder.cs index 0f6e4c9f7e..41f06a0af1 100644 --- a/Rubberduck.Refactorings/EncapsulateField/Validations/ConvertFieldsToUDTMembersStrategyConflictFinder.cs +++ b/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/ConvertFieldsToUDTMembersStrategyConflictFinder.cs @@ -1,11 +1,8 @@ using Rubberduck.Parsing.Symbols; using Rubberduck.Parsing.VBA; using Rubberduck.Refactorings.EncapsulateField.Extensions; -using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Rubberduck.Refactorings.EncapsulateField { diff --git a/Rubberduck.Refactorings/EncapsulateField/Validations/EncapsulateFieldConflictFinderBase.cs b/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldConflictFinderBase.cs similarity index 96% rename from Rubberduck.Refactorings/EncapsulateField/Validations/EncapsulateFieldConflictFinderBase.cs rename to Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldConflictFinderBase.cs index 472bd3a7d6..324e27adb6 100644 --- a/Rubberduck.Refactorings/EncapsulateField/Validations/EncapsulateFieldConflictFinderBase.cs +++ b/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldConflictFinderBase.cs @@ -39,7 +39,11 @@ public virtual bool TryValidateEncapsulationAttributes(IEncapsulateFieldCandidat return true; } - if (!field.NameValidator.IsValidVBAIdentifier(field.PropertyIdentifier, out errorMessage)) + var declarationType = field is IConvertToUDTMember udtMember + ? DeclarationType.UserDefinedTypeMember + : field.Declaration.DeclarationType; + + if (VBAIdentifierValidator.TryMatchInvalidIdentifierCriteria(field.PropertyIdentifier, declarationType, out errorMessage, field.Declaration.IsArray)) { return false; } diff --git a/Rubberduck.Refactorings/EncapsulateField/Validations/UseBackingFieldsStrategyConflictFinder.cs b/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/UseBackingFieldsStrategyConflictFinder.cs similarity index 93% rename from Rubberduck.Refactorings/EncapsulateField/Validations/UseBackingFieldsStrategyConflictFinder.cs rename to Rubberduck.Refactorings/EncapsulateField/ConflictDetection/UseBackingFieldsStrategyConflictFinder.cs index 24d99967bd..04224dd792 100644 --- a/Rubberduck.Refactorings/EncapsulateField/Validations/UseBackingFieldsStrategyConflictFinder.cs +++ b/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/UseBackingFieldsStrategyConflictFinder.cs @@ -1,10 +1,7 @@ using Rubberduck.Parsing.Symbols; using Rubberduck.Parsing.VBA; -using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Rubberduck.Refactorings.EncapsulateField { diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldCandidateFactory.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldCandidateFactory.cs index 24c6c88630..4d161fee2b 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldCandidateFactory.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldCandidateFactory.cs @@ -16,44 +16,23 @@ public class EncapsulateFieldCandidateFactory : IEncapsulateFieldCandidateFactor private readonly IDeclarationFinderProvider _declarationFinderProvider; private readonly ICodeBuilder _codeBuilder; - private readonly IValidateVBAIdentifiers _defaultNameValidator; - private readonly IValidateVBAIdentifiers _udtNameValidator; - private readonly IValidateVBAIdentifiers _udtMemberNameValidator; - private readonly IValidateVBAIdentifiers _udtMemberArrayNameValidator; - public EncapsulateFieldCandidateFactory(IDeclarationFinderProvider declarationFinderProvider, ICodeBuilder codeBuilder) { _declarationFinderProvider = declarationFinderProvider; _codeBuilder = codeBuilder; - - _defaultNameValidator = EncapsulateFieldValidationsProvider.NameOnlyValidator(NameValidators.Default); - _udtNameValidator = EncapsulateFieldValidationsProvider.NameOnlyValidator(NameValidators.UserDefinedType); - _udtMemberNameValidator = EncapsulateFieldValidationsProvider.NameOnlyValidator(NameValidators.UserDefinedTypeMember); - _udtMemberArrayNameValidator = EncapsulateFieldValidationsProvider.NameOnlyValidator(NameValidators.UserDefinedTypeMemberArray); } public IEncapsulateFieldCandidate Create(Declaration target) - { - var candidate= CreateCandidate(target, _defaultNameValidator); - return candidate; - } - - private IEncapsulateFieldCandidate CreateCandidate(Declaration target, IValidateVBAIdentifiers validator) { if (target.IsUserDefinedType()) { - var udtField = new UserDefinedTypeCandidate(target, _udtNameValidator, _codeBuilder.BuildPropertyRhsParameterName) as IUserDefinedTypeCandidate; + var udtField = new UserDefinedTypeCandidate(target, _codeBuilder.BuildPropertyRhsParameterName) as IUserDefinedTypeCandidate; (Declaration udtDeclaration, IEnumerable udtMembers) = GetUDTAndMembersForField(udtField); foreach (var udtMemberDeclaration in udtMembers) { - var udtMemberValidator = _udtMemberNameValidator; - if (udtMemberDeclaration.IsArray) - { - udtMemberValidator = _udtMemberArrayNameValidator; - } - var candidateUDTMember = new UserDefinedTypeMemberCandidate(CreateCandidate(udtMemberDeclaration, udtMemberValidator), udtField, _codeBuilder.BuildPropertyRhsParameterName) as IUserDefinedTypeMemberCandidate; + var candidateUDTMember = new UserDefinedTypeMemberCandidate(Create(udtMemberDeclaration), udtField, _codeBuilder.BuildPropertyRhsParameterName) as IUserDefinedTypeMemberCandidate; udtField.AddMember(candidateUDTMember); } @@ -69,11 +48,11 @@ private IEncapsulateFieldCandidate CreateCandidate(Declaration target, IValidate } else if (target.IsArray) { - var arrayCandidate = new ArrayCandidate(target, validator, _codeBuilder.BuildPropertyRhsParameterName); + var arrayCandidate = new ArrayCandidate(target, _codeBuilder.BuildPropertyRhsParameterName); return arrayCandidate; } - var candidate = new EncapsulateFieldCandidate(target, validator, _codeBuilder.BuildPropertyRhsParameterName); + var candidate = new EncapsulateFieldCandidate(target, _codeBuilder.BuildPropertyRhsParameterName); return candidate; } diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ArrayCandidate.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ArrayCandidate.cs index d76ada1962..985a89beb1 100644 --- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ArrayCandidate.cs +++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ArrayCandidate.cs @@ -16,8 +16,8 @@ public interface IArrayCandidate : IEncapsulateFieldCandidate public class ArrayCandidate : EncapsulateFieldCandidate, IArrayCandidate { private string _subscripts; - public ArrayCandidate(Declaration declaration, IValidateVBAIdentifiers validator, Func parameterNameBuilder) - :base(declaration, validator, parameterNameBuilder) + public ArrayCandidate(Declaration declaration, Func parameterNameBuilder) + :base(declaration, parameterNameBuilder) { ImplementLet = false; ImplementSet = false; diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ConvertToUDTMemberCandidate.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ConvertToUDTMemberCandidate.cs index fd1504435c..2010acc65e 100644 --- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ConvertToUDTMemberCandidate.cs +++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ConvertToUDTMemberCandidate.cs @@ -87,14 +87,6 @@ public bool IsReadOnly public string ParameterName => _wrapped.ParameterName; - public IValidateVBAIdentifiers NameValidator - { - set { } - get => _wrapped.Declaration.IsArray - ? EncapsulateFieldValidationsProvider.NameOnlyValidator(NameValidators.UserDefinedTypeMemberArray) - : EncapsulateFieldValidationsProvider.NameOnlyValidator(NameValidators.UserDefinedTypeMember); - } - public IEncapsulateFieldConflictFinder ConflictFinder { set => _wrapped.ConflictFinder = value; diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidate.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidate.cs index 65b70dcc34..fae1741bb7 100644 --- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidate.cs +++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidate.cs @@ -43,7 +43,6 @@ public interface IEncapsulateFieldCandidate : IEncapsulateFieldRefactoringElemen bool ImplementSet { get; } bool IsReadOnly { set; get; } string ParameterName { get; } - IValidateVBAIdentifiers NameValidator { set; get; } IEncapsulateFieldConflictFinder ConflictFinder { set; get; } bool TryValidateEncapsulationAttributes(out string errorMessage); string IdentifierForReference(IdentifierReference idRef); @@ -60,13 +59,12 @@ public class EncapsulateFieldCandidate : IEncapsulateFieldCandidate protected EncapsulationIdentifiers _fieldAndProperty; private Func _parameterNameBuilder; - public EncapsulateFieldCandidate(Declaration declaration, IValidateVBAIdentifiers identifierValidator, Func parameterNameBuilder) + public EncapsulateFieldCandidate(Declaration declaration, Func parameterNameBuilder) { _target = declaration; - NameValidator = identifierValidator; _parameterNameBuilder = parameterNameBuilder; - _fieldAndProperty = new EncapsulationIdentifiers(declaration.IdentifierName, identifierValidator); + _fieldAndProperty = new EncapsulationIdentifiers(declaration.IdentifierName); IdentifierName = declaration.IdentifierName; PropertyAsTypeName = declaration.AsTypeName; _qmn = declaration.QualifiedModuleName; @@ -110,8 +108,6 @@ public virtual string BackingIdentifier public string BackingAsTypeName => Declaration.AsTypeName; - public virtual IValidateVBAIdentifiers NameValidator { set; get; } - public virtual IEncapsulateFieldConflictFinder ConflictFinder { set; get; } public virtual bool TryValidateEncapsulationAttributes(out string errorMessage) diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulationIdentifiers.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulationIdentifiers.cs index bb29bdb253..3edb6503bd 100644 --- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulationIdentifiers.cs +++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulationIdentifiers.cs @@ -1,6 +1,8 @@ using Rubberduck.Common; using System.Collections.Generic; using Rubberduck.Refactorings.EncapsulateField.Extensions; +using Rubberduck.Refactorings.Common; +using Rubberduck.Parsing.Symbols; namespace Rubberduck.Refactorings.EncapsulateField { @@ -9,7 +11,7 @@ public class EncapsulationIdentifiers private KeyValuePair _fieldAndProperty; private string _targetIdentifier; - public EncapsulationIdentifiers(string field, IValidateVBAIdentifiers identifierValidator) + public EncapsulationIdentifiers(string field) { _targetIdentifier = field; @@ -18,7 +20,7 @@ public EncapsulationIdentifiers(string field, IValidateVBAIdentifiers identifier if (field.TryMatchHungarianNotationCriteria(out var nonHungarianName)) { - if (identifierValidator.IsValidVBAIdentifier(nonHungarianName, out _)) + if (!VBAIdentifierValidator.TryMatchInvalidIdentifierCriteria(nonHungarianName, DeclarationType.Variable, out _)) { DefaultPropertyName = nonHungarianName; DefaultNewFieldName = field; @@ -27,7 +29,7 @@ public EncapsulationIdentifiers(string field, IValidateVBAIdentifiers identifier else if (field.StartsWith("m_")) { var propertyName = field.Substring(2).CapitalizeFirstLetter(); - if (identifierValidator.IsValidVBAIdentifier(propertyName, out _)) + if (!VBAIdentifierValidator.TryMatchInvalidIdentifierCriteria(propertyName, DeclarationType.Property, out _)) { DefaultPropertyName = propertyName; DefaultNewFieldName = field; diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeCandidate.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeCandidate.cs index 5ca521fc75..edcda870df 100644 --- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeCandidate.cs +++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeCandidate.cs @@ -17,8 +17,8 @@ public interface IUserDefinedTypeCandidate : IEncapsulateFieldCandidate public class UserDefinedTypeCandidate : EncapsulateFieldCandidate, IUserDefinedTypeCandidate { - public UserDefinedTypeCandidate(Declaration declaration, IValidateVBAIdentifiers identifierValidator, Func parameterNameBuilder) - : base(declaration, identifierValidator, parameterNameBuilder) + public UserDefinedTypeCandidate(Declaration declaration, Func parameterNameBuilder) + : base(declaration, parameterNameBuilder) { TypeDeclarationIsPrivate = declaration.HasPrivateAccessibility(); } @@ -48,20 +48,6 @@ public override string BackingIdentifier set => _fieldAndProperty.Field = value; } - private IValidateVBAIdentifiers _namesValidator; - public override IValidateVBAIdentifiers NameValidator - { - set - { - _namesValidator = value; - foreach (var member in Members) - { - member.NameValidator = value; - } - } - get => _namesValidator; - } - private IEncapsulateFieldConflictFinder _conflictsValidator; public override IEncapsulateFieldConflictFinder ConflictFinder { diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeMemberCandidate.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeMemberCandidate.cs index 6b5bf7ca86..edc7346134 100644 --- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeMemberCandidate.cs +++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeMemberCandidate.cs @@ -48,12 +48,6 @@ public string BackingIdentifier public IUserDefinedTypeCandidate UDTField { private set; get; } - public IValidateVBAIdentifiers NameValidator - { - set => _wrappedCandidate.NameValidator = value; - get => _wrappedCandidate.NameValidator; - } - public IEncapsulateFieldConflictFinder ConflictFinder { set => _wrappedCandidate.ConflictFinder = value; diff --git a/Rubberduck.Refactorings/EncapsulateField/Validations/EncapsulateFieldValidationsProvider.cs b/Rubberduck.Refactorings/EncapsulateField/Validations/EncapsulateFieldValidationsProvider.cs deleted file mode 100644 index c917ecd94b..0000000000 --- a/Rubberduck.Refactorings/EncapsulateField/Validations/EncapsulateFieldValidationsProvider.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Rubberduck.Parsing.Symbols; -using System.Collections.Generic; - -namespace Rubberduck.Refactorings.EncapsulateField -{ - public enum NameValidators - { - Default, - UserDefinedType, - UserDefinedTypeMember, - UserDefinedTypeMemberArray - } - - public class EncapsulateFieldValidationsProvider - { - private static readonly Dictionary _nameOnlyValidators = new Dictionary() - { - [NameValidators.Default] = new IdentifierOnlyValidator(DeclarationType.Variable, false), - [NameValidators.UserDefinedType] = new IdentifierOnlyValidator(DeclarationType.UserDefinedType, false), - [NameValidators.UserDefinedTypeMember] = new IdentifierOnlyValidator(DeclarationType.UserDefinedTypeMember, false), - [NameValidators.UserDefinedTypeMemberArray] = new IdentifierOnlyValidator(DeclarationType.UserDefinedTypeMember, true), - }; - - public EncapsulateFieldValidationsProvider() - {} - - public static IValidateVBAIdentifiers NameOnlyValidator(NameValidators validatorType) - => _nameOnlyValidators[validatorType]; - } -} diff --git a/Rubberduck.Refactorings/EncapsulateField/Validations/IdentifierOnlyValidator.cs b/Rubberduck.Refactorings/EncapsulateField/Validations/IdentifierOnlyValidator.cs deleted file mode 100644 index 781fd33cc2..0000000000 --- a/Rubberduck.Refactorings/EncapsulateField/Validations/IdentifierOnlyValidator.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Rubberduck.Parsing.Symbols; -using Rubberduck.Refactorings.Common; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Rubberduck.Refactorings.EncapsulateField -{ - public interface IValidateVBAIdentifiers - { - bool IsValidVBAIdentifier(string identifier, out string errorMessage); - } - - public class IdentifierOnlyValidator : IValidateVBAIdentifiers - { - private DeclarationType _declarationType; - private bool _isArray; - public IdentifierOnlyValidator(DeclarationType declarationType, bool isArray = false) - { - _declarationType = declarationType; - _isArray = isArray; - } - - public bool IsValidVBAIdentifier(string identifier, out string errorMessage) - => !VBAIdentifierValidator.TryMatchInvalidIdentifierCriteria(identifier, _declarationType, out errorMessage, _isArray); - } -} From 42fe1651e127b6c55f94f66659f21ecec414a61d Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Fri, 4 Sep 2020 07:09:38 -0700 Subject: [PATCH 27/67] Update tests to incorporate new factories Tests retain prior coverage and testing logic - changed only to compile with modifications to the refactored object model. Organized tests by refactoring action. --- .../EncapsulateFieldCommandTests.cs | 19 ++- ...ieldsAsUDTMembersRefactoringActionTests.cs | 41 +++++- .../EncapsulateFIeldTestSupport.cs | 31 +++-- .../EncapsulateFieldTestComponentResolver.cs | 52 ++++++-- .../EncapsulateArrayFieldTests.cs | 1 - .../EncapsulateFieldTests.cs | 15 +-- .../EncapsulateUDTFieldTests.cs} | 21 +-- .../EncapsulateUsingStateUDTTests.cs | 52 +------- .../EncapsulateFieldValidatorTests.cs | 3 - .../EncapsulationIdentifiersTests.cs | 3 +- .../EncapsulateField/PreviewerTests.cs | 126 ++++++++++++++++++ 11 files changed, 253 insertions(+), 111 deletions(-) rename RubberduckTests/Refactoring/EncapsulateField/{ => EncapsulateFieldUseBackingField}/EncapsulateArrayFieldTests.cs (99%) rename RubberduckTests/Refactoring/EncapsulateField/{ => EncapsulateFieldUseBackingField}/EncapsulateFieldTests.cs (98%) rename RubberduckTests/Refactoring/EncapsulateField/{EncapsulatedUDTFieldTests.cs => EncapsulateFieldUseBackingField/EncapsulateUDTFieldTests.cs} (98%) rename RubberduckTests/Refactoring/EncapsulateField/{ => EncapsulateFieldUseBackingUDTMember}/EncapsulateUsingStateUDTTests.cs (93%) create mode 100644 RubberduckTests/Refactoring/EncapsulateField/PreviewerTests.cs diff --git a/RubberduckTests/Commands/RefactorCommands/EncapsulateFieldCommandTests.cs b/RubberduckTests/Commands/RefactorCommands/EncapsulateFieldCommandTests.cs index 0c180b970b..800f9eaf34 100644 --- a/RubberduckTests/Commands/RefactorCommands/EncapsulateFieldCommandTests.cs +++ b/RubberduckTests/Commands/RefactorCommands/EncapsulateFieldCommandTests.cs @@ -7,7 +7,6 @@ using Rubberduck.Parsing.VBA; using Rubberduck.Refactorings; using Rubberduck.Refactorings.EncapsulateField; -using Rubberduck.SmartIndenter; using Rubberduck.UI.Command; using Rubberduck.UI.Command.Refactorings; using Rubberduck.UI.Command.Refactorings.Notifiers; @@ -20,8 +19,9 @@ namespace RubberduckTests.Commands.RefactorCommands { public class EncapsulateFieldCommandTests : RefactorCodePaneCommandTestBase { - [Category("Commands")] [Test] + [Category("Commands")] + [Category("Encapsulate Field")] public void EncapsulateField_CanExecute_LocalVariable() { const string input = @@ -32,8 +32,9 @@ public void EncapsulateField_CanExecute_LocalVariable() Assert.IsFalse(CanExecute(input, selection)); } - [Category("Commands")] [Test] + [Category("Commands")] + [Category("Encapsulate Field")] public void EncapsulateField_CanExecute_Proc() { const string input = @@ -44,8 +45,9 @@ Sub Foo() Assert.IsFalse(CanExecute(input, selection)); } - [Category("Commands")] [Test] + [Category("Commands")] + [Category("Encapsulate Field")] public void EncapsulateField_CanExecute_Field() { const string input = @@ -68,7 +70,14 @@ protected override CommandBase TestCommand(IVBE vbe, RubberduckParserState state .Setup(m => m.Invoke(It.IsAny())) .Callback((Action action) => action.Invoke()); var userInteraction = new RefactoringUserInteraction(factory, uiDispatcherMock.Object); - var refactoring = new EncapsulateFieldRefactoring(resolver.Resolve(), resolver.Resolve(), state, userInteraction, rewritingManager, selectionService, selectedDeclarationProvider); + var refactoring = new EncapsulateFieldRefactoring(resolver.Resolve(), + resolver.Resolve(), + resolver.Resolve(), + state, + userInteraction, + rewritingManager, + selectionService, + selectedDeclarationProvider); var notifier = new EncapsulateFieldFailedNotifier(msgBox); var selectedDeclarationService = new SelectedDeclarationProvider(selectionService, state); return new RefactorEncapsulateFieldCommand(refactoring, notifier, state, selectionService, selectedDeclarationService); diff --git a/RubberduckTests/Refactoring/DeclareFieldsAsUDTMembers/DeclareFieldsAsUDTMembersRefactoringActionTests.cs b/RubberduckTests/Refactoring/DeclareFieldsAsUDTMembers/DeclareFieldsAsUDTMembersRefactoringActionTests.cs index 94321c9810..280777ed22 100644 --- a/RubberduckTests/Refactoring/DeclareFieldsAsUDTMembers/DeclareFieldsAsUDTMembersRefactoringActionTests.cs +++ b/RubberduckTests/Refactoring/DeclareFieldsAsUDTMembers/DeclareFieldsAsUDTMembersRefactoringActionTests.cs @@ -15,6 +15,7 @@ public class DeclareFieldsAsUDTMembersRefactoringActionTests : RefactoringAction [TestCase(4)] [TestCase(2)] [Category("Refactorings")] + [Category("Encapsulate Field")] [Category(nameof(DeclareFieldsAsUDTMembersRefactoringAction))] public void FormatSingleExistingMember(int indentionLevel) { @@ -45,6 +46,7 @@ End Type [TestCase(4)] [TestCase(2)] [Category("Refactorings")] + [Category("Encapsulate Field")] [Category(nameof(DeclareFieldsAsUDTMembersRefactoringAction))] public void FormatMatchesLastMemberIndent(int indentionLevel) { @@ -77,8 +79,9 @@ End Type [Test] [Category("Refactorings")] + [Category("Encapsulate Field")] [Category(nameof(DeclareFieldsAsUDTMembersRefactoringAction))] - public void FormatPreservesComment() + public void FormatPreservesComments() { var indention = string.Concat(Enumerable.Repeat(" ", 2)); @@ -106,6 +109,42 @@ End Type StringAssert.Contains(expectedUDT, results); } + [Test] + [Category("Refactorings")] + [Category("Encapsulate Field")] + [Category(nameof(DeclareFieldsAsUDTMembersRefactoringAction))] + public void FormatMultipleInsertions() + { + var indention = string.Concat(Enumerable.Repeat(" ", 2)); + + string inputCode = +$@" +Option Explicit + +Private mTest As Long +Private mTest1 As Long +Private mTest2 As Long + +Private Type TestType + FirstValue As String + SecondValue As Double +End Type +"; + var expectedUDT = +$@" +Private Type TestType + FirstValue As String + SecondValue As Double + Test As Long + Test1 As Long + Test2 As Long +End Type +"; + + var results = ExecuteTest(inputCode, "TestType", ("mTest", "Test"), ("mTest1", "Test1"), ("mTest2", "Test2")); + StringAssert.Contains(expectedUDT, results); + } + private string ExecuteTest(string inputCode, string udtIdentifier, params (string, string)[] fieldConversions) { return RefactoredCode(inputCode, state => TestModel(state, udtIdentifier, fieldConversions)); diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFIeldTestSupport.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFIeldTestSupport.cs index c0b999002f..783949837b 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFIeldTestSupport.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFIeldTestSupport.cs @@ -1,12 +1,9 @@ -using Moq; -using Rubberduck.Common; +using Rubberduck.Common; using Rubberduck.Parsing.Rewriter; using Rubberduck.Parsing.Symbols; -using Rubberduck.Parsing.UIContext; using Rubberduck.Parsing.VBA; using Rubberduck.Refactorings; using Rubberduck.Refactorings.EncapsulateField; -using Rubberduck.SmartIndenter; using Rubberduck.VBEditor; using Rubberduck.VBEditor.SafeComWrappers.Abstract; using Rubberduck.VBEditor.Utility; @@ -21,7 +18,7 @@ public class EncapsulateFieldTestSupport : InteractiveRefactoringTestBase $"{property.ToLowerCaseFirstLetter()}Value"; - public string StateUDTDefaultType => $"T{MockVbeBuilder.TestModuleName}"; + public string StateUDTDefaultTypeName => $"T{MockVbeBuilder.TestModuleName}"; private TestEncapsulationAttributes UserModifiedEncapsulationAttributes(string field, string property = null, bool isReadonly = false, bool encapsulateFlag = true) { @@ -122,7 +119,14 @@ public string RefactoredCode(CodeString codeString, Func(), resolver.Resolve(), state, userInteraction, rewritingManager, selectionService, selectedDeclarationProvider); + return new EncapsulateFieldRefactoring(resolver.Resolve(), + resolver.Resolve(), + resolver.Resolve(), + state, + userInteraction, + rewritingManager, + selectionService, + selectedDeclarationProvider); } public IEncapsulateFieldCandidate RetrieveEncapsulateFieldCandidate(string inputCode, string fieldName) @@ -143,12 +147,12 @@ public IEncapsulateFieldCandidate RetrieveEncapsulateFieldCandidate(IVBE vbe, st using (state) { var match = state.DeclarationFinder.MatchName(fieldName).Where(m => m.DeclarationType.Equals(declarationType)).Single(); - var builder = new EncapsulateFieldElementsBuilder(state, match.QualifiedModuleName); - foreach (var candidate in builder.Candidates) - { - candidate.NameValidator = EncapsulateFieldValidationsProvider.NameOnlyValidator(NameValidators.Default); - } - return builder.Candidates.First(); + + var resolver = new EncapsulateFieldTestComponentResolver(state, null); + + var model = resolver.Resolve().Create(match); + + return model[match.IdentifierName]; } } @@ -178,8 +182,7 @@ public class TestEncapsulationAttributes { public TestEncapsulationAttributes(string fieldName, bool encapsulationFlag = true, bool isReadOnly = false) { - var validator = EncapsulateFieldValidationsProvider.NameOnlyValidator(NameValidators.Default); - _identifiers = new EncapsulationIdentifiers(fieldName, validator); + _identifiers = new EncapsulationIdentifiers(fieldName); EncapsulateFlag = encapsulationFlag; IsReadOnly = isReadOnly; } diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs index c692e47076..31efa5c7a9 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs @@ -3,12 +3,14 @@ using Rubberduck.Refactorings; using Rubberduck.Refactorings.DeclareFieldsAsUDTMembers; using Rubberduck.Refactorings.EncapsulateField; -using Rubberduck.SmartIndenter; -using Rubberduck.VBEditor.SafeComWrappers.Abstract; using Rubberduck.Refactorings.ReplaceReferences; using Rubberduck.Refactorings.ReplacePrivateUDTMemberReferences; using Rubberduck.Refactorings.ReplaceDeclarationIdentifier; using Rubberduck.Refactorings.CodeBlockInsert; +using Rubberduck.Refactorings.EncapsulateFieldUseBackingUDTMember; +using Rubberduck.Refactorings.EncapsulateFieldUseBackingField; +using Rubberduck.Refactorings.EncapsulateFieldInsertNewCode; +using System; namespace RubberduckTests.Refactoring.EncapsulateField { @@ -32,27 +34,32 @@ public EncapsulateFieldTestComponentResolver(IDeclarationFinderProvider declarat switch (typeof(T).Name) { case nameof(EncapsulateFieldRefactoringAction): - return new EncapsulateFieldRefactoringAction( + return new EncapsulateFieldRefactoringAction(_declarationFinderProvider, ResolveImpl(), ResolveImpl()) as T; + case nameof(ReplaceReferencesRefactoringAction): return new ReplaceReferencesRefactoringAction(_rewritingManager) as T; + case nameof(ReplaceDeclarationIdentifierRefactoringAction): return new ReplaceDeclarationIdentifierRefactoringAction(_rewritingManager) as T; + case nameof(CodeBlockInsertRefactoringAction): return new CodeBlockInsertRefactoringAction(_declarationFinderProvider, _rewritingManager, new CodeBuilder()) as T; + case nameof(EncapsulateFieldInsertNewCodeRefactoringAction): return new EncapsulateFieldInsertNewCodeRefactoringAction( ResolveImpl(), _declarationFinderProvider, _rewritingManager, new CodeBuilder()) as T; + case nameof(ReplacePrivateUDTMemberReferencesRefactoringAction): return new ReplacePrivateUDTMemberReferencesRefactoringAction(_rewritingManager) as T; + case nameof(IEncapsulateFieldRefactoringActionsProvider): - case nameof(EncapsulateFieldRefactoringActionsProvider): return new EncapsulateFieldRefactoringActionsProvider( _declarationFinderProvider, _rewritingManager, @@ -62,30 +69,36 @@ public EncapsulateFieldTestComponentResolver(IDeclarationFinderProvider declarat ResolveImpl(), ResolveImpl(), ResolveImpl()) as T; + case nameof(EncapsulateFieldUseBackingFieldRefactoringAction): return new EncapsulateFieldUseBackingFieldRefactoringAction( - ResolveImpl(), + ResolveImpl(), ResolveImpl(), _declarationFinderProvider, _rewritingManager) as T; + case nameof(EncapsulateFieldUseBackingUDTMemberRefactoringAction): return new EncapsulateFieldUseBackingUDTMemberRefactoringAction( - ResolveImpl(), + ResolveImpl(), ResolveImpl(), _declarationFinderProvider, _rewritingManager, new CodeBuilder()) as T; + case nameof(IReplacePrivateUDTMemberReferencesModelFactory): return new ReplacePrivateUDTMemberReferencesModelFactory(_declarationFinderProvider) as T; + case nameof(DeclareFieldsAsUDTMembersRefactoringAction): return new DeclareFieldsAsUDTMembersRefactoringAction( _declarationFinderProvider, _rewritingManager, new CodeBuilder()) as T; + case nameof(EncapsulateFieldPreviewProvider): - return new EncapsulateFieldPreviewProvider( + return new EncapsulateFieldPreviewProvider(_declarationFinderProvider, ResolveImpl(), ResolveImpl()) as T; + case nameof(EncapsulateFieldUseBackingFieldPreviewProvider): return new EncapsulateFieldUseBackingFieldPreviewProvider( ResolveImpl(), @@ -94,13 +107,26 @@ public EncapsulateFieldTestComponentResolver(IDeclarationFinderProvider declarat return new EncapsulateFieldUseBackingUDTMemberPreviewProvider( ResolveImpl(), _rewritingManager) as T; - } - return null; - } - private static IIndenter CreateIndenter(IVBE vbe = null) - { - return new Indenter(vbe, () => Settings.IndenterSettingsTests.GetMockIndenterSettings()); + case nameof(IEncapsulateFieldModelFactory): + return new EncapsulateFieldModelFactory(_declarationFinderProvider, + ResolveImpl(), + ResolveImpl(), + ResolveImpl()) as T; + + case nameof(EncapsulateFieldUseBackingUDTMemberModelFactory): + return new EncapsulateFieldUseBackingUDTMemberModelFactory(_declarationFinderProvider, + ResolveImpl()) as T; + + case nameof(EncapsulateFieldUseBackingFieldModelFactory): + return new EncapsulateFieldUseBackingFieldModelFactory(_declarationFinderProvider, + ResolveImpl()) as T; + + case nameof(IEncapsulateFieldCandidateFactory): + return new EncapsulateFieldCandidateFactory(_declarationFinderProvider, new CodeBuilder()) as T; + + } + throw new ArgumentException($"Unable to resolve {typeof(T).Name}") ; } } } diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateArrayFieldTests.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateArrayFieldTests.cs similarity index 99% rename from RubberduckTests/Refactoring/EncapsulateField/EncapsulateArrayFieldTests.cs rename to RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateArrayFieldTests.cs index 70ef69a41b..424dcf7d54 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateArrayFieldTests.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateArrayFieldTests.cs @@ -146,7 +146,6 @@ End Sub StringAssert.Contains("myArray_1(idx) = idx", actualCode); } - [Test] [Category("Refactorings")] [Category("Encapsulate Field")] diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTests.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldTests.cs similarity index 98% rename from RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTests.cs rename to RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldTests.cs index 3fdfda77b9..0e58afe7e9 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTests.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldTests.cs @@ -54,11 +54,9 @@ public class EncapsulateFieldTests : InteractiveRefactoringTestBase !flags[k]) - .Select(k => k); + var notEncapsulated = flags.Keys.Where(k => !flags[k]).Select(k => k); - var encapsulated = flags.Keys.Where(k => flags[k]) - .Select(k => k); + var encapsulated = flags.Keys.Where(k => flags[k]).Select(k => k); foreach ( var variable in notEncapsulated) { @@ -116,8 +114,7 @@ public class EncapsulateFieldTests : InteractiveRefactoringTestBase !flags[k]) - .Select(k => $"{k} As Integer"); + var remainInList = flags.Keys.Where(k => !flags[k]).Select(k => $"{k} As Integer"); if (remainInList.Any()) { @@ -173,7 +170,6 @@ public void EncapsulatePublicField_InvalidIdentifierSelected_Throws() Assert.AreEqual(codeString.Code, actualCode); } - [Test] [Category("Refactorings")] [Category("Encapsulate Field")] @@ -416,7 +412,7 @@ public void EncapsulatePrivateField_DefaultsAsUDT() var actualCode = Support.RefactoredCode(inputCode.ToCodeString(), presenterAction); StringAssert.Contains("Fizz As Integer", actualCode); - StringAssert.Contains($"this As {Support.StateUDTDefaultType}", actualCode); + StringAssert.Contains($"this As {Support.StateUDTDefaultTypeName}", actualCode); StringAssert.Contains($"this.Fizz = {rhsParameterName}", actualCode); } @@ -438,8 +434,7 @@ Sub Bar(ByVal name As Integer) var presenterAction = Support.SetParametersForSingleTarget("fizz", "Name"); - var validator = EncapsulateFieldValidationsProvider.NameOnlyValidator(NameValidators.Default); - var enapsulationIdentifiers = new EncapsulationIdentifiers("fizz", validator) { Property = "Name" }; + var enapsulationIdentifiers = new EncapsulationIdentifiers("fizz") { Property = "Name" }; var rhsParameterName = Support.RhsParameterNameBuilder("Name"); var actualCode = Support.RefactoredCode(inputCode.ToCodeString(), presenterAction); diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulatedUDTFieldTests.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateUDTFieldTests.cs similarity index 98% rename from RubberduckTests/Refactoring/EncapsulateField/EncapsulatedUDTFieldTests.cs rename to RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateUDTFieldTests.cs index 570168a7f5..c7e844b163 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/EncapsulatedUDTFieldTests.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateUDTFieldTests.cs @@ -10,7 +10,7 @@ namespace RubberduckTests.Refactoring.EncapsulateField { [TestFixture] - public class EncapsulatedUDTFieldTests : InteractiveRefactoringTestBase + public class EncapsulateUDTFieldTests : InteractiveRefactoringTestBase { private EncapsulateFieldTestSupport Support { get; } = new EncapsulateFieldTestSupport(); @@ -29,7 +29,6 @@ End Type {accessibility} th|is As TBar"; - var presenterAction = Support.UserAcceptsDefaults(); var rhsParameterNameFirst = Support.RhsParameterNameBuilder("First"); var rhsParameterNameSecond = Support.RhsParameterNameBuilder("Second"); @@ -64,13 +63,12 @@ End Type Public th|is As TBar Public that As TBar"; - var validator = EncapsulateFieldValidationsProvider.NameOnlyValidator(NameValidators.Default); - var expectedThis = new EncapsulationIdentifiers("this", validator); - var expectedThat = new EncapsulationIdentifiers("that", validator); + var expectedThis = new EncapsulationIdentifiers("this"); + var expectedThat = new EncapsulationIdentifiers("that"); var userInput = new UserInputDataObject() - .AddUserInputSet(expectedThis.TargetFieldName, encapsulationFlag: encapsulateThis) - .AddUserInputSet(expectedThat.TargetFieldName, encapsulationFlag: encapsulateThat); + .AddUserInputSet(expectedThis.TargetFieldName, encapsulationFlag: encapsulateThis) + .AddUserInputSet(expectedThat.TargetFieldName, encapsulationFlag: encapsulateThat); var presenterAction = Support.SetParameters(userInput); var rhsParameterNameFirst = Support.RhsParameterNameBuilder("First"); @@ -155,7 +153,6 @@ End Type End Sub "; - var presenterAction = Support.UserAcceptsDefaults(); var rhsParameterNameFirst = Support.RhsParameterNameBuilder("First"); var rhsParameterNameSecond = Support.RhsParameterNameBuilder("Second"); @@ -297,7 +294,6 @@ Private mFizz {accessibility} t|his As TBar"; - var userInput = new UserInputDataObject() .UserSelectsField("this", "MyType") .UserSelectsField("mFoo", "Foo") @@ -492,7 +488,6 @@ End Type Assert.AreEqual(typeDefinition, actualModuleCode["Module1"]); - var actualCode = actualModuleCode["Class1"]; StringAssert.Contains("Private this As TBar", actualCode); @@ -715,7 +710,6 @@ public void ClassModuleUDTFieldSelection_ExternalReferences_ClassModule() Public th|is As TBar"; - string classModuleReferencingCode = $@"Option Explicit @@ -765,7 +759,6 @@ End Sub StringAssert.Contains($" .MyType.Second = ", referencingClassCode); } - [Test] [Category("Refactorings")] [Category("Encapsulate Field")] @@ -983,8 +976,8 @@ End Type StringAssert.Contains("Public Property Let Bar(", actualCode); StringAssert.Contains("Public Property Let Foo_1(", actualCode); StringAssert.Contains("Public Property Let Bar_1(", actualCode); - StringAssert.Contains("myBar.FooBar.Foo = fooValue", actualCode); - StringAssert.Contains("myBar.ReBar.Foo = foo_1Value", actualCode); + StringAssert.Contains("myBar.FooBar.Foo = foo_1Value", actualCode); + StringAssert.Contains("myBar.ReBar.Foo = fooValue", actualCode); } [Test] diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateUsingStateUDTTests.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateUsingStateUDTTests.cs similarity index 93% rename from RubberduckTests/Refactoring/EncapsulateField/EncapsulateUsingStateUDTTests.cs rename to RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateUsingStateUDTTests.cs index 998c3c0757..f69f753aa7 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateUsingStateUDTTests.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateUsingStateUDTTests.cs @@ -50,11 +50,10 @@ End Type Private this As Long"; - var presenterAction = Support.UserAcceptsDefaults(convertFieldToUDTMember: true); var actualCode = Support.RefactoredCode(inputCode.ToCodeString(), presenterAction); StringAssert.Contains("Private this As Long", actualCode); - StringAssert.Contains($"Private this_1 As {Support.StateUDTDefaultType}", actualCode); + StringAssert.Contains($"Private this_1 As {Support.StateUDTDefaultTypeName}", actualCode); } [Test] @@ -71,7 +70,6 @@ End Type Private my|Bar As TBar"; - var userInput = new UserInputDataObject() .UserSelectsField("myBar"); @@ -108,7 +106,6 @@ End Type myBar.First = newValue End Sub"; - var userInput = new UserInputDataObject() .UserSelectsField("myBar"); @@ -148,7 +145,7 @@ End Type var presenterAction = Support.SetParameters(userInput); var actualCode = Support.RefactoredCode(inputCode.ToCodeString(), presenterAction); - StringAssert.DoesNotContain($"Private this As {Support.StateUDTDefaultType}", actualCode); + StringAssert.DoesNotContain($"Private this As {Support.StateUDTDefaultTypeName}", actualCode); StringAssert.Contains("Foo As Long", actualCode); StringAssert.DoesNotContain("Public foo As Long", actualCode); StringAssert.Contains("Bar As String", actualCode); @@ -219,8 +216,8 @@ public void MultipleFields() var presenterAction = Support.SetParameters(userInput); var actualCode = Support.RefactoredCode(inputCode.ToCodeString(), presenterAction); - StringAssert.Contains($"Private this As {Support.StateUDTDefaultType}", actualCode); - StringAssert.Contains($"Private Type {Support.StateUDTDefaultType}", actualCode); + StringAssert.Contains($"Private this As {Support.StateUDTDefaultTypeName}", actualCode); + StringAssert.Contains($"Private Type {Support.StateUDTDefaultTypeName}", actualCode); StringAssert.Contains("Foo As Long", actualCode); StringAssert.Contains("Bar As String", actualCode); StringAssert.Contains("Foobar As Byte", actualCode); @@ -306,7 +303,6 @@ End Enum Public numberT|ype As NumberTypes "; - var userInput = new UserInputDataObject() .UserSelectsField("numberType"); @@ -401,7 +397,6 @@ End Type .UserSelectsField("foo") .UserSelectsField("myBar"); - userInput.EncapsulateUsingUDTField(); var presenterAction = Support.SetParameters(userInput); @@ -507,45 +502,6 @@ End Type StringAssert.Contains($"this.MyBar.FooBar = {rhsParameterName}", actualCode); } - [Test] - [Category("Refactorings")] - [Category("Encapsulate Field")] - public void UDTMemberIsPrivateUDT_RepeatedType() - { - string inputCode = -$@" - -Private Type TFoo - Foo As Integer - Bar As Byte -End Type - -Private Type TBar - FooBar As TFoo - ReBar As TFoo -End Type - -Private my|Bar As TBar -"; - - var userInput = new UserInputDataObject(); - - userInput.EncapsulateUsingUDTField(); - - var presenterAction = Support.SetParameters(userInput); - - var actualCode = Support.RefactoredCode(inputCode.ToCodeString(), presenterAction); - var rhsParameterNameFoo = Support.RhsParameterNameBuilder("Foo"); - var rhsParameterNameFoo_1 = Support.RhsParameterNameBuilder("Foo_1"); - - StringAssert.Contains("Public Property Let Foo(", actualCode); - StringAssert.Contains("Public Property Let Bar(", actualCode); - StringAssert.Contains("Public Property Let Foo_1(", actualCode); - StringAssert.Contains("Public Property Let Bar_1(", actualCode); - StringAssert.Contains($"this.MyBar.FooBar.Foo = {rhsParameterNameFoo}", actualCode); - StringAssert.Contains($"this.MyBar.ReBar.Foo = {rhsParameterNameFoo_1}", actualCode); - } - [Test] [Category("Refactorings")] [Category("Encapsulate Field")] diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldValidatorTests.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldValidatorTests.cs index f2646bdb62..7566b7bc61 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldValidatorTests.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldValidatorTests.cs @@ -152,7 +152,6 @@ End Property Assert.IsFalse(model["fizz"].TryValidateEncapsulationAttributes(out _)); } - [TestCase("Number", "Bazzle", true, true)] [TestCase("Number", "Number", false, false)] [TestCase("Test", "Number", false, true)] @@ -176,7 +175,6 @@ public void UserModificationIsExistingPropertyNameConflicts(string fizz_modified .UserSelectsField(fieldUT, fizz_modifiedPropertyName, true) .UserSelectsField("bazz", bazz_modifiedPropertyName, true); - var presenterAction = Support.SetParameters(userInput); var model = Support.RetrieveUserModifiedModelPriorToRefactoring(inputCode, fieldUT, DeclarationType.Variable, presenterAction); @@ -463,7 +461,6 @@ End Type Public mF|oo As Long "; - var fieldUT = "mFoo"; var userInput = new UserInputDataObject() .UserSelectsField(fieldUT); diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulationIdentifiersTests.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulationIdentifiersTests.cs index 810368a3c7..16979e12c0 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/EncapsulationIdentifiersTests.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulationIdentifiersTests.cs @@ -61,8 +61,7 @@ public void FieldNameValuesPerSequenceOfPropertyNameChanges() [Category("Encapsulate Field")] public void AccountsForHungarianNamesAndMemberPrefix(string inputName, string expectedPropertyName, string expectedFieldName) { - var validator = EncapsulateFieldValidationsProvider.NameOnlyValidator(NameValidators.Default); - var sut = new EncapsulationIdentifiers(inputName, validator); + var sut = new EncapsulationIdentifiers(inputName); Assert.AreEqual(expectedPropertyName, sut.DefaultPropertyName); Assert.AreEqual(expectedFieldName, sut.DefaultNewFieldName); diff --git a/RubberduckTests/Refactoring/EncapsulateField/PreviewerTests.cs b/RubberduckTests/Refactoring/EncapsulateField/PreviewerTests.cs new file mode 100644 index 0000000000..0f7271df44 --- /dev/null +++ b/RubberduckTests/Refactoring/EncapsulateField/PreviewerTests.cs @@ -0,0 +1,126 @@ +using NUnit.Framework; +using Rubberduck.Parsing.Rewriter; +using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.VBA; +using Rubberduck.Refactorings; +using Rubberduck.Refactorings.EncapsulateField; +using Rubberduck.VBEditor.SafeComWrappers; +using Rubberduck.VBEditor.Utility; +using RubberduckTests.Mocks; +using System.Linq; + +namespace RubberduckTests.Refactoring.EncapsulateField +{ + [TestFixture] + public class EncapsulateFieldPreviewerTests : InteractiveRefactoringTestBase + { + private EncapsulateFieldTestSupport Support { get; } = new EncapsulateFieldTestSupport(); + + [TestCase(EncapsulateFieldStrategy.UseBackingFields)] + [TestCase(EncapsulateFieldStrategy.ConvertFieldsToUDTMembers)] + [Category("Refactorings")] + [Category("Encapsulate Field")] + [Category(nameof(EncapsulateFieldPreviewProvider))] + public void Preview_EditPropertyIdentifier(EncapsulateFieldStrategy strategy) + { + string inputCode = +$@"Option Explicit + +Public mTest As Long +"; + + var presenterAction = Support.SetParametersForSingleTarget("mTest", "ATest"); + var actualCode = RefactoredCode("mTest", DeclarationType.Variable, null, (MockVbeBuilder.TestModuleName, inputCode, ComponentType.StandardModule)); + + var vbe = MockVbeBuilder.BuildFromSingleModule(inputCode, ComponentType.StandardModule, out _); + (RubberduckParserState state, IRewritingManager rewritingManager) = MockParser.CreateAndParseWithRewritingManager(vbe.Object); + using (state) + { + var target = state.DeclarationFinder.MatchName("mTest").First(); + var resolver = new EncapsulateFieldTestComponentResolver(state, rewritingManager); + + var modelfactory = resolver.Resolve(); + var model = modelfactory.Create(target); + + var field = model["mTest"]; + field.PropertyIdentifier = "ATest"; + model.EncapsulateFieldStrategy = strategy; + + var previewProvider = resolver.Resolve(); + + var firstPreview = previewProvider.Preview(model); + StringAssert.Contains("Property Get ATest", firstPreview); + + field.PropertyIdentifier = "BTest"; + var secondPreview = previewProvider.Preview(model); + StringAssert.Contains("Property Get BTest", secondPreview); + StringAssert.DoesNotContain("Property Get ATest", secondPreview); + } + } + + [Test] + [Category("Refactorings")] + [Category("Encapsulate Field")] + [Category(nameof(EncapsulateFieldPreviewProvider))] + public void PreviewWrapMember_EditPropertyIdentifier() + { + string inputCode = +$@"Option Explicit + +Private Type T{MockVbeBuilder.TestModuleName} + FirstValue As Long +End Type + +Private Type B{MockVbeBuilder.TestModuleName} + FirstValue As Long +End Type + +Public mTest As Long + +Private tType As T{MockVbeBuilder.TestModuleName} + +Private bType As B{MockVbeBuilder.TestModuleName} +"; + + var presenterAction = Support.SetParametersForSingleTarget("mTest", "ATest"); + var actualCode = RefactoredCode("mTest", DeclarationType.Variable, null, (MockVbeBuilder.TestModuleName, inputCode, ComponentType.StandardModule)); + + var vbe = MockVbeBuilder.BuildFromSingleModule(inputCode, ComponentType.StandardModule, out _); + (RubberduckParserState state, IRewritingManager rewritingManager) = MockParser.CreateAndParseWithRewritingManager(vbe.Object); + using (state) + { + var target = state.DeclarationFinder.MatchName("mTest").First(); + var resolver = new EncapsulateFieldTestComponentResolver(state, rewritingManager); + + var modelfactory = resolver.Resolve(); + var model = modelfactory.Create(target); + + var field = model["mTest"]; + field.PropertyIdentifier = "ATest"; + model.EncapsulateFieldStrategy = EncapsulateFieldStrategy.ConvertFieldsToUDTMembers; + + var test = model.ObjectStateUDTCandidates; + Assert.AreEqual(3, test.Count()); + + var previewProvider = resolver.Resolve(); + + var firstPreview = previewProvider.Preview(model); + StringAssert.Contains("Property Get ATest", firstPreview); + + field.PropertyIdentifier = "BTest"; + var secondPreview = previewProvider.Preview(model); + StringAssert.Contains("Property Get BTest", secondPreview); + StringAssert.DoesNotContain("Property Get ATest", secondPreview); + } + } + + protected override IRefactoring TestRefactoring( + IRewritingManager rewritingManager, + RubberduckParserState state, + RefactoringUserInteraction userInteraction, + ISelectionService selectionService) + { + return Support.SupportTestRefactoring(rewritingManager, state, userInteraction, selectionService); + } + } +} From 05bae54f9f9e72b07b5ad3a8779d7d959723f01a Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Mon, 7 Sep 2020 16:31:28 -0700 Subject: [PATCH 28/67] Add new factories (7) Added EncapsulateFieldConflictFinderFactory, EncapsulateFieldModelFactory, EncapsulateFieldRequestFactory, EncapsulateFieldUseBackingFieldModelFactory EncapsulateFieldUseBackingUDTMemberModelFactory, EncapsulateFieldCandidateCollectionFactory, ObjectStateUserDefinedTypeFactory --- .../EncapsulateFieldConflictFinderFactory.cs | 31 ++++ .../EncapsulateFieldModelFactory.cs | 65 ++++---- .../EncapsulateFieldRequestFactory.cs | 17 ++ ...psulateFieldUseBackingFieldModelFactory.cs | 77 +++++---- ...ateFieldUseBackingUDTMemberModelFactory.cs | 153 ++++++++++-------- ...apsulateFieldCandidateCollectionFactory.cs | 36 +++++ .../ObjectStateUserDefinedTypeFactory.cs | 31 ++++ 7 files changed, 265 insertions(+), 145 deletions(-) create mode 100644 Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldConflictFinderFactory.cs create mode 100644 Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRequest/EncapsulateFieldRequestFactory.cs create mode 100644 Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidateCollectionFactory.cs create mode 100644 Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT/ObjectStateUserDefinedTypeFactory.cs diff --git a/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldConflictFinderFactory.cs b/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldConflictFinderFactory.cs new file mode 100644 index 0000000000..8ec8912702 --- /dev/null +++ b/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldConflictFinderFactory.cs @@ -0,0 +1,31 @@ +using Rubberduck.Parsing.VBA; +using Rubberduck.Refactorings.EncapsulateField; +using System.Collections.Generic; + +namespace Rubberduck.Refactorings +{ + public interface IEncapsulateFieldConflictFinderFactory + { + IEncapsulateFieldConflictFinder CreateEncapsulateFieldUseBackingFieldConflictFinder(IReadOnlyCollection candidates); + IEncapsulateFieldConflictFinder CreateEncapsulateFieldUseBackingUDTMemberConflictFinder(IReadOnlyCollection candidates, IReadOnlyCollection objectStateUDTs); + } + + public class EncapsulateFieldConflictFinderFactory : IEncapsulateFieldConflictFinderFactory + { + private readonly IDeclarationFinderProvider _declarationFinderProvider; + public EncapsulateFieldConflictFinderFactory(IDeclarationFinderProvider declarationFinderProvider) + { + _declarationFinderProvider = declarationFinderProvider; + } + + public IEncapsulateFieldConflictFinder CreateEncapsulateFieldUseBackingFieldConflictFinder(IReadOnlyCollection candidates) + { + return new EncapsulateFieldUseBackingFieldsConflictFinder(_declarationFinderProvider, candidates); + } + + public IEncapsulateFieldConflictFinder CreateEncapsulateFieldUseBackingUDTMemberConflictFinder(IReadOnlyCollection candidates, IReadOnlyCollection objectStateUDTs) + { + return new EncapsulateFieldUseBackingUDTMemberConflictFinder(_declarationFinderProvider, candidates, objectStateUDTs); + } + } +} diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModelFactory.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModelFactory.cs index 7ee4cd091b..73d7cdc0a9 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModelFactory.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModelFactory.cs @@ -1,70 +1,61 @@ using Rubberduck.Parsing.Symbols; using Rubberduck.Parsing.VBA; -using Rubberduck.Refactorings.Common; using Rubberduck.Refactorings.EncapsulateField; -using Rubberduck.VBEditor; +using System; using System.Collections.Generic; -using System.Linq; namespace Rubberduck.Refactorings { - public interface IEncapsulateFieldModelsFactory - { - T Create(QualifiedModuleName qmn); - T Create(IEnumerable candidates, IEnumerable objectStateUDTCandidates); - } - public interface IEncapsulateFieldModelFactory { + /// + /// Creates the supporting EncapsulateFieldRefactoringAction models for the EncapsulateFieldRefactoring. + /// EncapsulateFieldModel Create(Declaration target); } public class EncapsulateFieldModelFactory : IEncapsulateFieldModelFactory { - private readonly IDeclarationFinderProvider _declarationFinderProvider; - private readonly IEncapsulateFieldCandidateFactory _encapsulateFieldCandidateFactory; private readonly IEncapsulateFieldUseBackingUDTMemberModelFactory _useBackingUDTMemberModelFactory; private readonly IEncapsulateFieldUseBackingFieldModelFactory _useBackingFieldModelFactory; + private readonly IEncapsulateFieldCandidateCollectionFactory _fieldCandidateCollectionFactory; + private readonly IEncapsulateFieldRequestFactory _requestFactory; - public EncapsulateFieldModelFactory(IDeclarationFinderProvider declarationFinderProvider, - IEncapsulateFieldCandidateFactory encapsulateFieldCandidateFactory, + public EncapsulateFieldModelFactory( IEncapsulateFieldUseBackingUDTMemberModelFactory encapsulateFieldUseBackingUDTMemberModelFactory, - IEncapsulateFieldUseBackingFieldModelFactory encapsulateFieldUseBackingFieldModelFactory) + IEncapsulateFieldUseBackingFieldModelFactory encapsulateFieldUseBackingFieldModelFactory, + IEncapsulateFieldCandidateCollectionFactory encapsulateFieldCandidateCollectionFactory, + IEncapsulateFieldRequestFactory encapsulateFieldRequestFactory) { - _declarationFinderProvider = declarationFinderProvider; - _encapsulateFieldCandidateFactory = encapsulateFieldCandidateFactory; - _useBackingUDTMemberModelFactory = encapsulateFieldUseBackingUDTMemberModelFactory; + _useBackingUDTMemberModelFactory = encapsulateFieldUseBackingUDTMemberModelFactory as IEncapsulateFieldUseBackingUDTMemberModelFactory; _useBackingFieldModelFactory = encapsulateFieldUseBackingFieldModelFactory; + _fieldCandidateCollectionFactory = encapsulateFieldCandidateCollectionFactory; + _requestFactory = encapsulateFieldRequestFactory; } public EncapsulateFieldModel Create(Declaration target) { - var fields = _declarationFinderProvider.DeclarationFinder - .Members(target.QualifiedModuleName) - .Where(v => v.IsMemberVariable() && !v.IsWithEvents); - - var candidates = fields.Select(fd => _encapsulateFieldCandidateFactory.Create(fd)) - .ToList(); + if (!(target is VariableDeclaration targetField)) + { + throw new ArgumentException(); + } + + var fieldCandidates = _fieldCandidateCollectionFactory.Create(targetField.QualifiedModuleName); - var objectStateUDTCandidates = candidates.Where(c => c is IUserDefinedTypeCandidate udt - && udt.CanBeObjectStateUDT) - .Select(udtc => new ObjectStateUDT(udtc as IUserDefinedTypeCandidate)) - .ToList(); + var encapsulationRequest = _requestFactory.Create(targetField); + var requests = new List() { encapsulationRequest }; - var initialStrategy = objectStateUDTCandidates - .Any(os => os.AsTypeDeclaration.IdentifierName.StartsWith($"T{target.QualifiedModuleName.ComponentName}", System.StringComparison.InvariantCultureIgnoreCase)) - ? EncapsulateFieldStrategy.ConvertFieldsToUDTMembers - : EncapsulateFieldStrategy.UseBackingFields; + var useBackingFieldModel = _useBackingFieldModelFactory.Create(fieldCandidates, requests); - var selected = candidates.Single(c => c.Declaration == target); - selected.EncapsulateFlag = true; + var useBackingUDTMemberModel = _useBackingUDTMemberModelFactory.Create(fieldCandidates, requests); - var udtModel = _useBackingUDTMemberModelFactory.Create(candidates, objectStateUDTCandidates); - var backingModel = _useBackingFieldModelFactory.Create(candidates, objectStateUDTCandidates); + var initialStrategy = useBackingUDTMemberModel.ObjectStateUDTField.IsExistingDeclaration + ? EncapsulateFieldStrategy.ConvertFieldsToUDTMembers + : EncapsulateFieldStrategy.UseBackingFields; - return new EncapsulateFieldModel(backingModel, udtModel) + return new EncapsulateFieldModel(useBackingFieldModel, useBackingUDTMemberModel) { - EncapsulateFieldStrategy = initialStrategy + EncapsulateFieldStrategy = initialStrategy, }; } } diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRequest/EncapsulateFieldRequestFactory.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRequest/EncapsulateFieldRequestFactory.cs new file mode 100644 index 0000000000..82df5f3db8 --- /dev/null +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRequest/EncapsulateFieldRequestFactory.cs @@ -0,0 +1,17 @@ +using Rubberduck.Parsing.Symbols; + +namespace Rubberduck.Refactorings.EncapsulateField +{ + public interface IEncapsulateFieldRequestFactory + { + EncapsulateFieldRequest Create(VariableDeclaration target, bool isReadOnly = false, string propertyIdentifier = null); + } + + public class EncapsulateFieldRequestFactory : IEncapsulateFieldRequestFactory + { + public EncapsulateFieldRequest Create(VariableDeclaration target, bool isReadOnly = false, string propertyIdentifier = null) + { + return new EncapsulateFieldRequest(target, isReadOnly, propertyIdentifier); + } + } +} diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldModelFactory.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldModelFactory.cs index 274762aab4..a35181577e 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldModelFactory.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldModelFactory.cs @@ -1,74 +1,71 @@ using Rubberduck.Parsing.Symbols; using Rubberduck.Parsing.VBA; -using Rubberduck.Refactorings.Common; using Rubberduck.Refactorings.EncapsulateField; using Rubberduck.Refactorings.EncapsulateFieldUseBackingField; -using Rubberduck.VBEditor; using System.Collections.Generic; using System.Linq; namespace Rubberduck.Refactorings { - public interface IEncapsulateFieldUseBackingFieldModelFactory : IEncapsulateFieldModelsFactory - { } + public interface IEncapsulateFieldUseBackingFieldModelFactory + { + /// + /// Creates an EncapsulateFieldUseBackingFieldModel used by the EncapsulateFieldUseBackingFieldRefactoringAction. + /// + /// Optional: UserDefinedType Field to include the Encapsulated Field(s) + EncapsulateFieldUseBackingFieldModel Create(IEnumerable requests); + + /// + /// Creates an EncapsulateFieldUseBackingFieldModel based upon collection of + /// IEncapsulateFieldCandidate instances created by EncapsulateFieldCandidateCollectionFactory. + /// This function is intended for exclusive use by EncapsulateFieldModelFactory + /// + EncapsulateFieldUseBackingFieldModel Create(IReadOnlyCollection candidates, IEnumerable requests); + } public class EncapsulateFieldUseBackingFieldModelFactory : IEncapsulateFieldUseBackingFieldModelFactory { private readonly IDeclarationFinderProvider _declarationFinderProvider; - private readonly IEncapsulateFieldCandidateFactory _fieldCandidateFactory; + private readonly IEncapsulateFieldCandidateCollectionFactory _fieldCandidateCollectionFactory; + private readonly IEncapsulateFieldConflictFinderFactory _conflictFinderFactory; public EncapsulateFieldUseBackingFieldModelFactory(IDeclarationFinderProvider declarationFinderProvider, - IEncapsulateFieldCandidateFactory fieldCandidateFactory) + IEncapsulateFieldCandidateCollectionFactory encapsulateFieldCandidateCollectionFactory, + IEncapsulateFieldConflictFinderFactory encapsulateFieldConflictFinderFactory) { _declarationFinderProvider = declarationFinderProvider; - _fieldCandidateFactory = fieldCandidateFactory; + _fieldCandidateCollectionFactory = encapsulateFieldCandidateCollectionFactory; + _conflictFinderFactory = encapsulateFieldConflictFinderFactory; } - public EncapsulateFieldUseBackingFieldModel Create(QualifiedModuleName qmn) + public EncapsulateFieldUseBackingFieldModel Create(IEnumerable requests) { - var fields = _declarationFinderProvider.DeclarationFinder.UserDeclarations(DeclarationType.Variable) - .Where(v => v.ParentDeclaration is ModuleDeclaration - && !v.IsWithEvents); - - var candidates = fields.Select(f => _fieldCandidateFactory.Create(f)); - - var objectStateUDTCandidates = candidates.Where(c => c is IUserDefinedTypeCandidate udt && udt.CanBeObjectStateUDT) - .Select(udtc => new ObjectStateUDT(udtc as IUserDefinedTypeCandidate)); + if (!requests.Any()) + { + return new EncapsulateFieldUseBackingFieldModel(Enumerable.Empty(), _declarationFinderProvider); + } - return Create(candidates, objectStateUDTCandidates); + var fieldCandidates = _fieldCandidateCollectionFactory.Create(requests.First().Declaration.QualifiedModuleName); + return Create(fieldCandidates, requests); } - public EncapsulateFieldUseBackingFieldModel Create(IEnumerable candidates, IEnumerable objectStateUDTCandidates) + public EncapsulateFieldUseBackingFieldModel Create(IReadOnlyCollection candidates, IEnumerable requests) { - var fieldCandidates = new List(candidates); - var objectStateFieldCandidates = new List(objectStateUDTCandidates); - var udtMemberCandidates = new List(); + var fieldCandidates = candidates.ToList(); - fieldCandidates.ForEach(c => LoadUDTMembers(udtMemberCandidates, c)); + foreach (var request in requests) + { + var candidate = fieldCandidates.Single(c => c.Declaration.Equals(request.Declaration)); + request.ApplyRequest(candidate); + } - var conflictsFinder = new UseBackingFieldsStrategyConflictFinder(_declarationFinderProvider, candidates, udtMemberCandidates); + var conflictsFinder = _conflictFinderFactory.CreateEncapsulateFieldUseBackingFieldConflictFinder(fieldCandidates); fieldCandidates.ForEach(c => c.ConflictFinder = conflictsFinder); - return new EncapsulateFieldUseBackingFieldModel(candidates, _declarationFinderProvider) + return new EncapsulateFieldUseBackingFieldModel(fieldCandidates, _declarationFinderProvider) { ConflictFinder = conflictsFinder }; } - - private void LoadUDTMembers(List udtMembers, IEncapsulateFieldCandidate candidate) - { - if (candidate is IUserDefinedTypeCandidate udtCandidate) - { - foreach (var member in udtCandidate.Members) - { - udtMembers.Add(member); - if (member.WrappedCandidate is IUserDefinedTypeCandidate childUDT - && childUDT.Declaration.AsTypeDeclaration.HasPrivateAccessibility()) - { - LoadUDTMembers(udtMembers, childUDT); - } - } - } - } } } diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModelFactory.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModelFactory.cs index b173aea29d..b0982147d5 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModelFactory.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModelFactory.cs @@ -1,112 +1,129 @@ using Rubberduck.Parsing.Symbols; using Rubberduck.Parsing.VBA; -using Rubberduck.Refactorings.Common; using Rubberduck.Refactorings.EncapsulateField; using Rubberduck.Refactorings.EncapsulateFieldUseBackingUDTMember; -using Rubberduck.VBEditor; +using System; using System.Collections.Generic; using System.Linq; namespace Rubberduck.Refactorings { - public interface IEncapsulateFieldUseBackingUDTMemberModelFactory : IEncapsulateFieldModelsFactory - { } + public interface IEncapsulateFieldUseBackingUDTMemberModelFactory + { + /// + /// Creates an EncapsulateFieldUseBackingUDTMemberModel used by the EncapsulateFieldUseBackingUDTMemberRefactoringAction. + /// + /// Optional: UserDefinedType Field to include the Encapsulated Field(s) + EncapsulateFieldUseBackingUDTMemberModel Create(IEnumerable requests, Declaration userDefinedTypeTarget = null); + + /// + /// Creates an EncapsulateFieldUseBackingUDTMemberModel based upon collection of + /// IEncapsulateFieldCandidate instances created by EncapsulateFieldCandidateCollectionFactory. + /// This function is intended for exclusive use by EncapsulateFieldModelFactory + /// + EncapsulateFieldUseBackingUDTMemberModel Create(IReadOnlyCollection candidates, IEnumerable requests, Declaration userDefinedTypeTarget = null); + } public class EncapsulateFieldUseBackingUDTMemberModelFactory : IEncapsulateFieldUseBackingUDTMemberModelFactory { private readonly IDeclarationFinderProvider _declarationFinderProvider; - private readonly IEncapsulateFieldCandidateFactory _fieldCandidateFactory; - - public EncapsulateFieldUseBackingUDTMemberModelFactory(IDeclarationFinderProvider declarationFinderProvider, IEncapsulateFieldCandidateFactory fieldCandidateFactory) + private readonly IEncapsulateFieldCandidateCollectionFactory _fieldCandidateCollectionFactory; + private readonly IObjectStateUserDefinedTypeFactory _objectStateUDTFactory; + private readonly IEncapsulateFieldConflictFinderFactory _conflictFinderFactory; + + public EncapsulateFieldUseBackingUDTMemberModelFactory(IDeclarationFinderProvider declarationFinderProvider, + IEncapsulateFieldCandidateCollectionFactory encapsulateFieldCandidateCollectionFactory, + IObjectStateUserDefinedTypeFactory objectStateUserDefinedTypeFactory, + IEncapsulateFieldConflictFinderFactory encapsulateFieldConflictFinderFactory) { _declarationFinderProvider = declarationFinderProvider; - _fieldCandidateFactory = fieldCandidateFactory; + _fieldCandidateCollectionFactory = encapsulateFieldCandidateCollectionFactory; + _objectStateUDTFactory = objectStateUserDefinedTypeFactory; + _conflictFinderFactory = encapsulateFieldConflictFinderFactory; } - public EncapsulateFieldUseBackingUDTMemberModel Create(QualifiedModuleName qmn) { - var fields = _declarationFinderProvider.DeclarationFinder.UserDeclarations(DeclarationType.Variable) - .Where(v => v.ParentDeclaration is ModuleDeclaration - && !v.IsWithEvents); + public EncapsulateFieldUseBackingUDTMemberModel Create(IEnumerable requests, Declaration clientTarget) + { + if (!requests.Any()) + { + throw new ArgumentException(); + } + var fieldCandidates = _fieldCandidateCollectionFactory.Create(requests.First().Declaration.QualifiedModuleName); + return Create(fieldCandidates, requests, clientTarget); + } - var candidates = fields.Select(f => _fieldCandidateFactory.Create(f)); + public EncapsulateFieldUseBackingUDTMemberModel Create(IReadOnlyCollection candidates, IEnumerable requests, Declaration clientTarget = null) + { + if (clientTarget != null + && (clientTarget.Accessibility != Accessibility.Private + || !candidates.Any(c => c.Declaration == clientTarget && c is IUserDefinedTypeCandidate))) + { + throw new ArgumentException("The object state Field must be a Private UserDefinedType"); + } - var objectStateUDTCandidates = candidates.Where(c => c is IUserDefinedTypeCandidate udt - && udt.CanBeObjectStateUDT) - .Select(udtc => new ObjectStateUDT(udtc as IUserDefinedTypeCandidate)); + var fieldCandidates = candidates.ToList(); - return Create(candidates, objectStateUDTCandidates); - } + var objectStateUDTs = fieldCandidates + .Where(c => c is IUserDefinedTypeCandidate udt && udt.IsObjectStateUDTCandidate) + .Select(udtc => _objectStateUDTFactory.Create(udtc as IUserDefinedTypeCandidate)) + .ToList(); - public EncapsulateFieldUseBackingUDTMemberModel Create(IEnumerable candidates, IEnumerable objectStateUDTCandidates) - { - var fieldCandidates = new List(candidates); - var objectStateFieldCandidates = new List(objectStateUDTCandidates); + var defaultObjectStateUDT = _objectStateUDTFactory.Create(fieldCandidates.First().QualifiedModuleName); - var udtMemberCandidates = new List(); - fieldCandidates.ForEach(c => LoadUDTMembers(udtMemberCandidates, c)); + objectStateUDTs.Add(defaultObjectStateUDT); - var conflictsFinder = new ConvertFieldsToUDTMembersStrategyConflictFinder(_declarationFinderProvider, candidates, udtMemberCandidates, objectStateUDTCandidates); - fieldCandidates.ForEach(c => c.ConflictFinder = conflictsFinder); + var targetStateUDT = DetermineObjectStateUDTTarget(defaultObjectStateUDT, clientTarget, objectStateUDTs); - var defaultObjectStateUDT = CreateStateUDTField(candidates.First().QualifiedModuleName); - conflictsFinder.AssignNoConflictIdentifiers(defaultObjectStateUDT, _declarationFinderProvider); + foreach (var request in requests) + { + var candidate = fieldCandidates.Single(c => c.Declaration.Equals(request.Declaration)); + request.ApplyRequest(candidate); + } - var convertedToUDTMemberCandidates = new List(); - foreach (var field in candidates) + var conflictsFinder = _conflictFinderFactory.CreateEncapsulateFieldUseBackingUDTMemberConflictFinder(candidates, objectStateUDTs) + as IEncapsulateFieldUseBackingUDTMemberConflictFinder; + + fieldCandidates.ForEach(c => c.ConflictFinder = conflictsFinder); + + if (clientTarget == null && !targetStateUDT.IsExistingDeclaration) { - if (field is ConvertToUDTMember cm) - { - convertedToUDTMemberCandidates.Add(cm); - continue; - } - convertedToUDTMemberCandidates.Add(new ConvertToUDTMember(field, defaultObjectStateUDT)); + conflictsFinder.AssignNoConflictIdentifiers(targetStateUDT, _declarationFinderProvider); } - return new EncapsulateFieldUseBackingUDTMemberModel(convertedToUDTMemberCandidates, defaultObjectStateUDT, objectStateUDTCandidates, _declarationFinderProvider) + var udtMemberCandidates = + fieldCandidates.Select(c => new EncapsulateFieldAsUDTMemberCandidate(c, targetStateUDT)).ToList(); + + udtMemberCandidates.ForEach(c => conflictsFinder.AssignNoConflictIdentifiers(c)); + + return new EncapsulateFieldUseBackingUDTMemberModel(targetStateUDT, udtMemberCandidates, objectStateUDTs) { ConflictFinder = conflictsFinder }; } - private IObjectStateUDT CreateStateUDTField(QualifiedModuleName qualifiedModuleName) + IObjectStateUDT DetermineObjectStateUDTTarget(IObjectStateUDT defaultObjectStateUDT, Declaration clientTarget, List objectStateUDTs) { - var stateUDT = new ObjectStateUDT(qualifiedModuleName) as IObjectStateUDT; - stateUDT.IsSelected = true; - - return stateUDT; - } + var targetStateUDT = defaultObjectStateUDT; - private void ResolveConflict(IEncapsulateFieldConflictFinder conflictFinder, IEncapsulateFieldCandidate candidate) - { - conflictFinder.AssignNoConflictIdentifiers(candidate); - if (candidate is IUserDefinedTypeCandidate udtCandidate) + if (clientTarget != null) { - foreach (var member in udtCandidate.Members) - { - conflictFinder.AssignNoConflictIdentifiers(member); - if (member.WrappedCandidate is IUserDefinedTypeCandidate childUDT - && childUDT.Declaration.AsTypeDeclaration.HasPrivateAccessibility()) - { - ResolveConflict(conflictFinder, childUDT); - } - } + targetStateUDT = objectStateUDTs.Single(osc => clientTarget == osc.Declaration); } - } - - private void LoadUDTMembers(List udtMembers, IEncapsulateFieldCandidate candidate) - { - if (candidate is IUserDefinedTypeCandidate udtCandidate) + else { - foreach (var member in udtCandidate.Members) + var preExistingDefaultUDTField = + objectStateUDTs.Where(osc => osc.TypeIdentifier == defaultObjectStateUDT.TypeIdentifier + && osc.IsExistingDeclaration); + + if (preExistingDefaultUDTField.Any() && preExistingDefaultUDTField.Count() == 1) { - udtMembers.Add(member); - if (member.WrappedCandidate is IUserDefinedTypeCandidate childUDT - && childUDT.Declaration.AsTypeDeclaration.HasPrivateAccessibility()) - { - LoadUDTMembers(udtMembers, childUDT); - } + targetStateUDT = preExistingDefaultUDTField.First(); } } + + targetStateUDT.IsSelected = true; + + return targetStateUDT; } } } diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidateCollectionFactory.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidateCollectionFactory.cs new file mode 100644 index 0000000000..72b556ec1a --- /dev/null +++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidateCollectionFactory.cs @@ -0,0 +1,36 @@ +using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.VBA; +using Rubberduck.Refactorings.EncapsulateField; +using Rubberduck.VBEditor; +using System.Collections.Generic; +using System.Linq; + +namespace Rubberduck.Refactorings +{ + public interface IEncapsulateFieldCandidateCollectionFactory + { + IReadOnlyCollection Create(QualifiedModuleName qualifiedModuleName); + } + + public class EncapsulateFieldCandidateCollectionFactory : IEncapsulateFieldCandidateCollectionFactory + { + private readonly IDeclarationFinderProvider _declarationFinderProvider; + private readonly IEncapsulateFieldCandidateFactory _fieldCandidateFactory; + public EncapsulateFieldCandidateCollectionFactory( + IDeclarationFinderProvider declarationFinderProvider, + IEncapsulateFieldCandidateFactory encapsulateFieldCandidateFactory) + { + _declarationFinderProvider = declarationFinderProvider; + _fieldCandidateFactory = encapsulateFieldCandidateFactory; + } + + public IReadOnlyCollection Create(QualifiedModuleName qualifiedModuleName) + { + return _declarationFinderProvider.DeclarationFinder.Members(qualifiedModuleName, DeclarationType.Variable) + .Where(v => v.ParentDeclaration is ModuleDeclaration + && !v.IsWithEvents) + .Select(f => _fieldCandidateFactory.Create(f)) + .ToList(); + } + } +} diff --git a/Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT/ObjectStateUserDefinedTypeFactory.cs b/Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT/ObjectStateUserDefinedTypeFactory.cs new file mode 100644 index 0000000000..6595d21c68 --- /dev/null +++ b/Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT/ObjectStateUserDefinedTypeFactory.cs @@ -0,0 +1,31 @@ +using Rubberduck.Parsing.Symbols; +using Rubberduck.Refactorings.EncapsulateField; +using Rubberduck.VBEditor; +using System; + +namespace Rubberduck.Refactorings +{ + public interface IObjectStateUserDefinedTypeFactory + { + IObjectStateUDT Create(QualifiedModuleName qualifiedModuleName); + IObjectStateUDT Create(IUserDefinedTypeCandidate userDefinedTypeField); + } + + public class ObjectStateUserDefinedTypeFactory : IObjectStateUserDefinedTypeFactory + { + public IObjectStateUDT Create(QualifiedModuleName qualifiedModuleName) + { + return new ObjectStateUDT(qualifiedModuleName); + } + + public IObjectStateUDT Create(IUserDefinedTypeCandidate userDefinedTypeField) + { + if ((userDefinedTypeField.Declaration.AsTypeDeclaration?.Accessibility ?? Accessibility.Implicit) != Accessibility.Private) + { + throw new ArgumentException(); + } + + return new ObjectStateUDT(userDefinedTypeField); + } + } +} From 217f2d89ca4f393fe018de27f942a76f04c5f91c Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Mon, 7 Sep 2020 16:33:34 -0700 Subject: [PATCH 29/67] Add registration of more EncapsulateFieldRefactoring factories --- Rubberduck.Main/Root/RubberduckIoCInstaller.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Rubberduck.Main/Root/RubberduckIoCInstaller.cs b/Rubberduck.Main/Root/RubberduckIoCInstaller.cs index d86f04efcc..66897fb3ba 100644 --- a/Rubberduck.Main/Root/RubberduckIoCInstaller.cs +++ b/Rubberduck.Main/Root/RubberduckIoCInstaller.cs @@ -411,6 +411,15 @@ private void RegisterEncapsulateFieldRefactoringFactories(IWindsorContainer cont container.Register(Component.For() .ImplementedBy() .LifestyleSingleton()); + container.Register(Component.For() + .ImplementedBy() + .LifestyleSingleton()); + container.Register(Component.For() + .ImplementedBy() + .LifestyleSingleton()); + container.Register(Component.For() + .ImplementedBy() + .LifestyleSingleton()); } From a0e2e31ef1f084d25c3c38d2d978f7a36667687f Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Mon, 7 Sep 2020 16:37:53 -0700 Subject: [PATCH 30/67] Reorganize - add folders and move files --- ...ateFieldUseBackingFieldsConflictFinder.cs} | 7 +- ...FieldUseBackingUDTMemberConflictFinder.cs} | 22 ++++--- ...> EncapsulateFieldAsUDTMemberCandidate.cs} | 18 ++--- .../EncapsulateFieldCandidateFactory.cs | 24 ++----- .../{ => ObjectStateUDT}/ObjectStateUDT.cs | 65 +++++++++---------- 5 files changed, 63 insertions(+), 73 deletions(-) rename Rubberduck.Refactorings/EncapsulateField/ConflictDetection/{UseBackingFieldsStrategyConflictFinder.cs => EncapsulateFieldUseBackingFieldsConflictFinder.cs} (51%) rename Rubberduck.Refactorings/EncapsulateField/ConflictDetection/{ConvertFieldsToUDTMembersStrategyConflictFinder.cs => EncapsulateFieldUseBackingUDTMemberConflictFinder.cs} (84%) rename Rubberduck.Refactorings/EncapsulateField/FieldCandidates/{ConvertToUDTMemberCandidate.cs => EncapsulateFieldAsUDTMemberCandidate.cs} (91%) rename Rubberduck.Refactorings/EncapsulateField/{ => FieldCandidates}/EncapsulateFieldCandidateFactory.cs (67%) rename Rubberduck.Refactorings/EncapsulateField/{ => ObjectStateUDT}/ObjectStateUDT.cs (56%) diff --git a/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/UseBackingFieldsStrategyConflictFinder.cs b/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldUseBackingFieldsConflictFinder.cs similarity index 51% rename from Rubberduck.Refactorings/EncapsulateField/ConflictDetection/UseBackingFieldsStrategyConflictFinder.cs rename to Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldUseBackingFieldsConflictFinder.cs index 04224dd792..b17a5cb92e 100644 --- a/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/UseBackingFieldsStrategyConflictFinder.cs +++ b/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldUseBackingFieldsConflictFinder.cs @@ -5,10 +5,11 @@ namespace Rubberduck.Refactorings.EncapsulateField { - public class UseBackingFieldsStrategyConflictFinder : EncapsulateFieldConflictFinderBase, IEncapsulateFieldConflictFinder + public class EncapsulateFieldUseBackingFieldsConflictFinder : EncapsulateFieldConflictFinderBase, IEncapsulateFieldConflictFinder { - public UseBackingFieldsStrategyConflictFinder(IDeclarationFinderProvider declarationFinderProvider, IEnumerable candidates, IEnumerable udtCandidates) - : base(declarationFinderProvider, candidates, udtCandidates) { } + public EncapsulateFieldUseBackingFieldsConflictFinder(IDeclarationFinderProvider declarationFinderProvider, IEnumerable candidates) + : base(declarationFinderProvider, candidates) + { } protected override IEnumerable FindRelevantMembers(IEncapsulateFieldCandidate candidate) => _declarationFinderProvider.DeclarationFinder.Members(candidate.QualifiedModuleName) diff --git a/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/ConvertFieldsToUDTMembersStrategyConflictFinder.cs b/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldUseBackingUDTMemberConflictFinder.cs similarity index 84% rename from Rubberduck.Refactorings/EncapsulateField/ConflictDetection/ConvertFieldsToUDTMembersStrategyConflictFinder.cs rename to Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldUseBackingUDTMemberConflictFinder.cs index 41f06a0af1..6688218205 100644 --- a/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/ConvertFieldsToUDTMembersStrategyConflictFinder.cs +++ b/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldUseBackingUDTMemberConflictFinder.cs @@ -6,12 +6,12 @@ namespace Rubberduck.Refactorings.EncapsulateField { - public interface IConvertFieldsToUDTMembersStrategyConflictFinder : IEncapsulateFieldConflictFinder + public interface IEncapsulateFieldUseBackingUDTMemberConflictFinder : IEncapsulateFieldConflictFinder { IObjectStateUDT AssignNoConflictIdentifiers(IObjectStateUDT stateUDT, IDeclarationFinderProvider declarationFinderProvider); } - public class ConvertFieldsToUDTMembersStrategyConflictFinder : EncapsulateFieldConflictFinderBase, IConvertFieldsToUDTMembersStrategyConflictFinder + public class EncapsulateFieldUseBackingUDTMemberConflictFinder : EncapsulateFieldConflictFinderBase, IEncapsulateFieldUseBackingUDTMemberConflictFinder { private static DeclarationType[] _udtTypeIdentifierNonConflictTypes = new DeclarationType[] { @@ -27,17 +27,20 @@ public class ConvertFieldsToUDTMembersStrategyConflictFinder : EncapsulateFieldC DeclarationType.Parameter }; - private IEnumerable _objectStateUDTs; - public ConvertFieldsToUDTMembersStrategyConflictFinder(IDeclarationFinderProvider declarationFinderProvider, IEnumerable candidates, IEnumerable udtCandidates, IEnumerable objectStateUDTs) - : base(declarationFinderProvider, candidates, udtCandidates) + private List _objectStateUDTs; + public EncapsulateFieldUseBackingUDTMemberConflictFinder(IDeclarationFinderProvider declarationFinderProvider, IEnumerable candidates, IEnumerable objectStateUDTs) + : base(declarationFinderProvider, candidates) { - _objectStateUDTs = objectStateUDTs; + _objectStateUDTs = objectStateUDTs.ToList(); } public override bool TryValidateEncapsulationAttributes(IEncapsulateFieldCandidate field, out string errorMessage) { errorMessage = string.Empty; - if (!field.EncapsulateFlag) { return true; } + if (!field.EncapsulateFlag) + { + return true; + } if (!base.TryValidateEncapsulationAttributes(field, out errorMessage)) { @@ -97,7 +100,10 @@ protected override IEncapsulateFieldCandidate AssignNoConflictIdentifier(IEncaps private bool ConflictsWithExistingUDTMembers(IObjectStateUDT objectStateUDT, string identifier) { - if (objectStateUDT is null) { return false; } + if (objectStateUDT is null) + { + return false; + } return objectStateUDT.ExistingMembers.Any(nm => nm.IdentifierName.IsEquivalentVBAIdentifierTo(identifier)); } diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ConvertToUDTMemberCandidate.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldAsUDTMemberCandidate.cs similarity index 91% rename from Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ConvertToUDTMemberCandidate.cs rename to Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldAsUDTMemberCandidate.cs index 2010acc65e..395e4b664b 100644 --- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ConvertToUDTMemberCandidate.cs +++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldAsUDTMemberCandidate.cs @@ -7,19 +7,18 @@ namespace Rubberduck.Refactorings.EncapsulateField { - public interface IConvertToUDTMember : IEncapsulateFieldCandidate + public interface IEncapsulateFieldAsUDTMemberCandidate : IEncapsulateFieldCandidate { string UDTMemberDeclaration { get; } - IEncapsulateFieldCandidate WrappedCandidate { get; } IObjectStateUDT ObjectStateUDT { set; get; } } - public class ConvertToUDTMember : IConvertToUDTMember + public class EncapsulateFieldAsUDTMemberCandidate : IEncapsulateFieldAsUDTMemberCandidate { private int _hashCode; private readonly string _uniqueID; - private readonly IEncapsulateFieldCandidate _wrapped; - public ConvertToUDTMember(IEncapsulateFieldCandidate candidate, IObjectStateUDT objStateUDT) + private IEncapsulateFieldCandidate _wrapped; + public EncapsulateFieldAsUDTMemberCandidate(IEncapsulateFieldCandidate candidate, IObjectStateUDT objStateUDT) { _wrapped = candidate; PropertyIdentifier = _wrapped.PropertyIdentifier; @@ -40,8 +39,6 @@ public virtual string UDTMemberDeclaration } } - public IEncapsulateFieldCandidate WrappedCandidate => _wrapped; - public IObjectStateUDT ObjectStateUDT { set; get; } public string TargetID => _wrapped.TargetID; @@ -123,7 +120,10 @@ public string IdentifierForReference(IdentifierReference idRef) public bool TryValidateEncapsulationAttributes(out string errorMessage) { errorMessage = string.Empty; - if (!_wrapped.EncapsulateFlag) { return true; } + if (!_wrapped.EncapsulateFlag) + { + return true; + } if (_wrapped is IArrayCandidate ac) { @@ -159,7 +159,7 @@ public IEnumerable PropertyAttributeSets public override bool Equals(object obj) { return obj != null - && obj is ConvertToUDTMember convertWrapper + && obj is EncapsulateFieldAsUDTMemberCandidate convertWrapper && BuildUniqueID(convertWrapper, convertWrapper.ObjectStateUDT) == _uniqueID; } diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldCandidateFactory.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidateFactory.cs similarity index 67% rename from Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldCandidateFactory.cs rename to Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidateFactory.cs index 4d161fee2b..c3c7808dfa 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldCandidateFactory.cs +++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidateFactory.cs @@ -2,7 +2,6 @@ using Rubberduck.Parsing.VBA; using Rubberduck.Refactorings.Common; using Rubberduck.Refactorings.EncapsulateField; -using System.Collections.Generic; using System.Linq; namespace Rubberduck.Refactorings @@ -11,6 +10,7 @@ public interface IEncapsulateFieldCandidateFactory { IEncapsulateFieldCandidate Create(Declaration target); } + public class EncapsulateFieldCandidateFactory : IEncapsulateFieldCandidateFactory { private readonly IDeclarationFinderProvider _declarationFinderProvider; @@ -28,7 +28,9 @@ public IEncapsulateFieldCandidate Create(Declaration target) { var udtField = new UserDefinedTypeCandidate(target, _codeBuilder.BuildPropertyRhsParameterName) as IUserDefinedTypeCandidate; - (Declaration udtDeclaration, IEnumerable udtMembers) = GetUDTAndMembersForField(udtField); + var udtMembers = _declarationFinderProvider.DeclarationFinder + .UserDeclarations(DeclarationType.UserDefinedTypeMember) + .Where(utm => udtField.Declaration.AsTypeDeclaration == utm.ParentDeclaration); foreach (var udtMemberDeclaration in udtMembers) { @@ -38,9 +40,9 @@ public IEncapsulateFieldCandidate Create(Declaration target) } var udtVariablesOfSameType = _declarationFinderProvider.DeclarationFinder.UserDeclarations(DeclarationType.Variable) - .Where(v => v.AsTypeDeclaration == udtDeclaration); + .Where(v => v.AsTypeDeclaration == udtField.Declaration.AsTypeDeclaration); - udtField.CanBeObjectStateUDT = udtField.TypeDeclarationIsPrivate + udtField.IsObjectStateUDTCandidate = udtField.TypeDeclarationIsPrivate && udtField.Declaration.HasPrivateAccessibility() && udtVariablesOfSameType.Count() == 1; @@ -52,19 +54,7 @@ public IEncapsulateFieldCandidate Create(Declaration target) return arrayCandidate; } - var candidate = new EncapsulateFieldCandidate(target, _codeBuilder.BuildPropertyRhsParameterName); - return candidate; - } - - private (Declaration TypeDeclaration, IEnumerable Members) GetUDTAndMembersForField(IUserDefinedTypeCandidate udtField) - { - var userDefinedTypeDeclaration = udtField.Declaration.AsTypeDeclaration; - - var udtMembers = _declarationFinderProvider.DeclarationFinder - .UserDeclarations(DeclarationType.UserDefinedTypeMember) - .Where(utm => userDefinedTypeDeclaration == utm.ParentDeclaration); - - return (userDefinedTypeDeclaration, udtMembers); + return new EncapsulateFieldCandidate(target, _codeBuilder.BuildPropertyRhsParameterName); } } } diff --git a/Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT.cs b/Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT/ObjectStateUDT.cs similarity index 56% rename from Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT.cs rename to Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT/ObjectStateUDT.cs index c775bb70fa..fd260aaadb 100644 --- a/Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT.cs +++ b/Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT/ObjectStateUDT.cs @@ -11,13 +11,14 @@ namespace Rubberduck.Refactorings.EncapsulateField { public interface IObjectStateUDT : IEncapsulateFieldRefactoringElement { + Declaration Declaration { get; } string TypeIdentifier { set; get; } string FieldDeclarationBlock { get; } string FieldIdentifier { set; get; } bool IsExistingDeclaration { get; } Declaration AsTypeDeclaration { get; } bool IsSelected { set; get; } - IEnumerable ExistingMembers { get; } + IReadOnlyCollection ExistingMembers { get; } } /// @@ -31,46 +32,43 @@ public interface IObjectStateUDT : IEncapsulateFieldRefactoringElement public class ObjectStateUDT : IObjectStateUDT { private static string _defaultNewFieldName = RubberduckUI.EncapsulateField_DefaultObjectStateUDTFieldName; - private List _convertedMembers; + private readonly IUserDefinedTypeCandidate _wrappedUDTField; + private readonly int _hashCode; - private readonly IUserDefinedTypeCandidate _wrappedUDT; - private readonly ICodeBuilder _codeBuilder; - private int _hashCode; - - public ObjectStateUDT(IUserDefinedTypeCandidate udt) - : this(udt.Declaration.AsTypeName) + public ObjectStateUDT(IUserDefinedTypeCandidate udtField) + : this(udtField.Declaration.AsTypeName) { - if (!udt.TypeDeclarationIsPrivate) + if (!udtField.TypeDeclarationIsPrivate) { throw new ArgumentException(); } - FieldIdentifier = udt.IdentifierName; - _wrappedUDT = udt; - _hashCode = ($"{_qmn.Name}.{_wrappedUDT.IdentifierName}").GetHashCode(); + QualifiedModuleName = udtField.QualifiedModuleName; + FieldIdentifier = udtField.IdentifierName; + _wrappedUDTField = udtField; + _hashCode = ($"{QualifiedModuleName.Name}.{_wrappedUDTField.IdentifierName}").GetHashCode(); } - public ObjectStateUDT(QualifiedModuleName qmn) - :this($"T{qmn.ComponentName.CapitalizeFirstLetter()}") + public ObjectStateUDT(QualifiedModuleName qualifiedModuleName) + :this($"T{qualifiedModuleName.ComponentName.CapitalizeFirstLetter()}") { - QualifiedModuleName = qmn; + QualifiedModuleName = qualifiedModuleName; } private ObjectStateUDT(string typeIdentifier) { FieldIdentifier = _defaultNewFieldName; TypeIdentifier = typeIdentifier; - _convertedMembers = new List(); - _codeBuilder = new CodeBuilder(); - _convertedMembers = new List(); } public string FieldDeclarationBlock => $"{Accessibility.Private} {IdentifierName} {Tokens.As} {AsTypeName}"; - public string IdentifierName => _wrappedUDT?.IdentifierName ?? FieldIdentifier; + public string IdentifierName => _wrappedUDTField?.IdentifierName ?? FieldIdentifier; + + public Declaration Declaration => _wrappedUDTField?.Declaration; - public string AsTypeName => _wrappedUDT?.AsTypeName ?? TypeIdentifier; + public string AsTypeName => _wrappedUDTField?.AsTypeName ?? TypeIdentifier; private bool _isSelected; public bool IsSelected @@ -78,35 +76,28 @@ public bool IsSelected set { _isSelected = value; - if (_wrappedUDT != null) + if (_wrappedUDTField != null) { - _wrappedUDT.IsSelectedObjectStateUDT = value; + _wrappedUDTField.IsSelectedObjectStateUDT = value; if (_isSelected && IsExistingDeclaration) { - _wrappedUDT.EncapsulateFlag = false; + _wrappedUDTField.EncapsulateFlag = false; } } } get => _isSelected; } - public IEnumerable ExistingMembers - => IsExistingDeclaration - ? _wrappedUDT.Members - : Enumerable.Empty(); + public IReadOnlyCollection ExistingMembers + => _wrappedUDTField?.Members.ToList() ?? new List(); - private QualifiedModuleName _qmn; - public QualifiedModuleName QualifiedModuleName - { - set => _qmn = value; - get => _wrappedUDT?.QualifiedModuleName ?? _qmn; - } + public QualifiedModuleName QualifiedModuleName { get; } public string TypeIdentifier { set; get; } - public bool IsExistingDeclaration => _wrappedUDT != null; + public bool IsExistingDeclaration => _wrappedUDTField != null; - public Declaration AsTypeDeclaration => _wrappedUDT?.Declaration.AsTypeDeclaration; + public Declaration AsTypeDeclaration => _wrappedUDTField?.Declaration.AsTypeDeclaration; public string FieldIdentifier { set; get; } @@ -116,13 +107,15 @@ public override bool Equals(object obj) { return true; } + if (obj is IEncapsulateFieldRefactoringElement fd && fd.IdentifierName == IdentifierName) { return true; } + return false; } - public override int GetHashCode() => _hashCode; + public override int GetHashCode() => _hashCode; } } From f845cb78823697fe878d0d64f5994b8544d9e570 Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Mon, 7 Sep 2020 16:41:59 -0700 Subject: [PATCH 31/67] Refactor to support standalone operation of RefactoringActions Modified interfaces, objects and tests to support combined and independent execution of EncapsulateFieldUseBackingFieldRefactoringAction and EncapsulateFieldUseBackingUDTMemberRefactoringAction --- .../EncapsulateFieldConflictFinderBase.cs | 25 +- .../EncapsulateField/EncapsulateFieldModel.cs | 29 ++- .../EncapsulateFieldPreviewProvider.cs | 12 +- .../EncapsulateFieldRefactoring.cs | 3 - .../EncapsulateFieldRefactoringAction.cs | 4 +- ...apsulateFieldRefactoringActionsProvider.cs | 7 +- .../EncapsulateFieldRequest.cs | 40 +++ ...ncapsulateFieldUseBackingUDTMemberModel.cs | 82 ++---- ...FieldUseBackingUDTMemberPreviewProvider.cs | 1 + .../FieldCandidates/ArrayCandidate.cs | 5 +- .../EncapsulateFieldCandidate.cs | 12 +- .../UserDefinedTypeCandidate.cs | 4 +- .../EncapsulateFieldCommandTests.cs | 1 - .../EncapsulateFIeldTestSupport.cs | 1 - .../EncapsulateFieldTestComponentResolver.cs | 38 ++- ...ldUseBackingFieldRefactoringActionTests.cs | 90 +++++++ ...ncapsulateFieldUseBackingUDTMemberTests.cs | 239 ++++++++++++++++++ .../EncapsulateField/PreviewerTests.cs | 2 +- 18 files changed, 482 insertions(+), 113 deletions(-) create mode 100644 Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRequest/EncapsulateFieldRequest.cs create mode 100644 RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldRefactoringActionTests.cs create mode 100644 RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberTests.cs diff --git a/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldConflictFinderBase.cs b/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldConflictFinderBase.cs index 324e27adb6..befabb796e 100644 --- a/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldConflictFinderBase.cs +++ b/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldConflictFinderBase.cs @@ -24,11 +24,30 @@ public abstract class EncapsulateFieldConflictFinderBase protected List _fieldCandidates { set; get; } = new List(); protected List _udtMemberCandidates { set; get; } = new List(); - public EncapsulateFieldConflictFinderBase(IDeclarationFinderProvider declarationFinderProvider, IEnumerable candidates, IEnumerable udtMemberCandidates) + public EncapsulateFieldConflictFinderBase(IDeclarationFinderProvider declarationFinderProvider, IEnumerable candidates) { _declarationFinderProvider = declarationFinderProvider; _fieldCandidates.AddRange(candidates); - _udtMemberCandidates.AddRange(udtMemberCandidates); + _fieldCandidates.ForEach(c => LoadUDTMembers(_udtMemberCandidates, c)); + } + + private void LoadUDTMembers(List udtMembers, IEncapsulateFieldCandidate candidate) + { + if (!(candidate is IUserDefinedTypeCandidate udtCandidate)) + { + return; + } + + foreach (var member in udtCandidate.Members) + { + udtMembers.Add(member); + + if (member.WrappedCandidate is IUserDefinedTypeCandidate childUDT + && childUDT.Declaration.AsTypeDeclaration.HasPrivateAccessibility()) + { + LoadUDTMembers(udtMembers, childUDT); + } + } } public virtual bool TryValidateEncapsulationAttributes(IEncapsulateFieldCandidate field, out string errorMessage) @@ -39,7 +58,7 @@ public virtual bool TryValidateEncapsulationAttributes(IEncapsulateFieldCandidat return true; } - var declarationType = field is IConvertToUDTMember udtMember + var declarationType = field is IEncapsulateFieldAsUDTMemberCandidate udtMember ? DeclarationType.UserDefinedTypeMember : field.Declaration.DeclarationType; diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs index 7f885a1c29..df415c61f5 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs @@ -8,12 +8,16 @@ namespace Rubberduck.Refactorings.EncapsulateField { public class EncapsulateFieldModel : IRefactoringModel { + private readonly IObjectStateUDT _defaultObjectStateUDT; + public EncapsulateFieldModel(EncapsulateFieldUseBackingFieldModel backingFieldModel, - EncapsulateFieldUseBackingUDTMemberModel udtModel) + EncapsulateFieldUseBackingUDTMemberModel udtModel) { EncapsulateFieldUseBackingFieldModel = backingFieldModel; EncapsulateFieldUseBackingUDTMemberModel = udtModel; ResetConflictDetection(EncapsulateFieldStrategy.UseBackingFields); + ObjectStateUDTCandidates = udtModel.ObjectStateUDTCandidates; + _defaultObjectStateUDT = ObjectStateUDTCandidates.SingleOrDefault(os => !os.IsExistingDeclaration); } public EncapsulateFieldUseBackingUDTMemberModel EncapsulateFieldUseBackingUDTMemberModel { get; } @@ -22,7 +26,22 @@ public class EncapsulateFieldModel : IRefactoringModel public IRefactoringPreviewProvider PreviewProvider { set; get; } - public IEnumerable ObjectStateUDTCandidates => EncapsulateFieldUseBackingUDTMemberModel.ObjectStateUDTCandidates; + public IReadOnlyCollection ObjectStateUDTCandidates { private set; get; } + + public IObjectStateUDT ObjectStateUDTField + { + set + { + EncapsulateFieldUseBackingUDTMemberModel.ObjectStateUDTField = value; + foreach (var candidate in EncapsulateFieldUseBackingUDTMemberModel.SelectedFieldCandidates) + { + EncapsulateFieldUseBackingUDTMemberModel.ConflictFinder.AssignNoConflictIdentifiers(candidate); + } + } + get => EncapsulateFieldStrategy == EncapsulateFieldStrategy.ConvertFieldsToUDTMembers + ? EncapsulateFieldUseBackingUDTMemberModel.ObjectStateUDTField + : null; + } private EncapsulateFieldStrategy _strategy; public EncapsulateFieldStrategy EncapsulateFieldStrategy @@ -81,11 +100,5 @@ public IEnumerable SelectedFieldCandidates public IEncapsulateFieldCandidate this[string encapsulatedFieldTargetID] => EncapsulationCandidates.Where(c => c.TargetID.Equals(encapsulatedFieldTargetID)).Single(); - - public IObjectStateUDT ObjectStateUDTField - { - get => EncapsulateFieldUseBackingUDTMemberModel.ObjectStateUDTField; - set => EncapsulateFieldUseBackingUDTMemberModel.ObjectStateUDTField = value; - } } } diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldPreviewProvider.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldPreviewProvider.cs index 9e80fdab42..8c6765cfe2 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldPreviewProvider.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldPreviewProvider.cs @@ -7,21 +7,19 @@ namespace Rubberduck.Refactorings.EncapsulateField public class EncapsulateFieldPreviewProvider : IRefactoringPreviewProvider { private readonly EncapsulateFieldUseBackingFieldPreviewProvider _useBackingFieldPreviewer; - private readonly EncapsulateFieldUseBackingUDTMemberPreviewProvider _useUDTMembmerPreviewer; - private readonly IDeclarationFinderProvider _declarationFinderProvider; - public EncapsulateFieldPreviewProvider(IDeclarationFinderProvider declarationFinderProvider, + private readonly EncapsulateFieldUseBackingUDTMemberPreviewProvider _useBackingUDTMemberPreviewer; + public EncapsulateFieldPreviewProvider( EncapsulateFieldUseBackingFieldPreviewProvider useBackingFieldPreviewProvider, - EncapsulateFieldUseBackingUDTMemberPreviewProvider useUDTMemberPreviewProvide) + EncapsulateFieldUseBackingUDTMemberPreviewProvider useBackingUDTMemberPreviewProvide) { - _declarationFinderProvider = declarationFinderProvider; _useBackingFieldPreviewer = useBackingFieldPreviewProvider; - _useUDTMembmerPreviewer = useUDTMemberPreviewProvide; + _useBackingUDTMemberPreviewer = useBackingUDTMemberPreviewProvide; } public string Preview(EncapsulateFieldModel model) { var preview = model.EncapsulateFieldStrategy == EncapsulateFieldStrategy.ConvertFieldsToUDTMembers - ? _useUDTMembmerPreviewer.Preview(model.EncapsulateFieldUseBackingUDTMemberModel) + ? _useBackingUDTMemberPreviewer.Preview(model.EncapsulateFieldUseBackingUDTMemberModel) : _useBackingFieldPreviewer.Preview(model.EncapsulateFieldUseBackingFieldModel); return preview; diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoring.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoring.cs index 0927c1e171..f6f6420d3e 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoring.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoring.cs @@ -16,7 +16,6 @@ public enum EncapsulateFieldStrategy public class EncapsulateFieldRefactoring : InteractiveRefactoringBase { - private readonly IDeclarationFinderProvider _declarationFinderProvider; private readonly ISelectedDeclarationProvider _selectedDeclarationProvider; private readonly IRewritingManager _rewritingManager; private readonly EncapsulateFieldRefactoringAction _refactoringAction; @@ -27,7 +26,6 @@ public class EncapsulateFieldRefactoring : InteractiveRefactoringBase userInteraction, IRewritingManager rewritingManager, ISelectionProvider selectionProvider, @@ -36,7 +34,6 @@ public class EncapsulateFieldRefactoring : InteractiveRefactoringBase + /// EncapsulateFieldRequest aggregates attributes necessary for the EncapsulateFieldUseBackingFieldRefactoringAction. + /// + /// + /// EncapsulateFieldRequest provides the data needed to encapsulate a field. + /// There is no validation or conflict checking performed for non-UserDefinedTypes. + /// The caller can specify a readonly Property and select an IdentifierName for + /// the Property. If the target is a UserDefinedType Field and the UserDefinedType is Private, + /// then the propertyIdentifier parameter is ignored and PropertyIdentifiers for each UserDefinedTypeMember + /// are generated by the refactoring action. + /// + public class EncapsulateFieldRequest + { + public EncapsulateFieldRequest(VariableDeclaration target, bool isReadOnly = false, string propertyIdentifier = null) + { + Declaration = target; + IsReadOnly = isReadOnly; + PropertyIdentifier = propertyIdentifier; + } + + public VariableDeclaration Declaration { get; } + public string PropertyIdentifier { set; get; } + public bool IsReadOnly { set; get; } + + public IEncapsulateFieldCandidate ApplyRequest(IEncapsulateFieldCandidate candidate) + { + candidate.EncapsulateFlag = true; + candidate.IsReadOnly = IsReadOnly; + if (PropertyIdentifier != null) + { + candidate.PropertyIdentifier = PropertyIdentifier; + } + return candidate; + } + } +} diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModel.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModel.cs index 51e4ad3282..3199c0fcc8 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModel.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModel.cs @@ -1,61 +1,31 @@ using System.Collections.Generic; using System.Linq; -using Rubberduck.Parsing.VBA; using Rubberduck.VBEditor; using Rubberduck.Refactorings.CodeBlockInsert; using Rubberduck.Refactorings.EncapsulateField; -using System; namespace Rubberduck.Refactorings.EncapsulateFieldUseBackingUDTMember { public class EncapsulateFieldUseBackingUDTMemberModel : IRefactoringModel { - private readonly IObjectStateUDT _defaultObjectStateUDT; - private readonly IDeclarationFinderProvider _declarationFinderProvider; - private readonly string _defaultObjectStateUDTTypeName; - private readonly IObjectStateUDT _preExistingObjectStateUDT; - - private List _convertedFields; - private List _objStateCandidates; - - public EncapsulateFieldUseBackingUDTMemberModel(IEnumerable candidates, - IObjectStateUDT defaultObjectStateUserDefinedType, - IEnumerable objectStateUserDefinedTypeCandidates, - IDeclarationFinderProvider declarationFinderProvider) - { - _convertedFields = new List(candidates); - _declarationFinderProvider = declarationFinderProvider; - _defaultObjectStateUDT = defaultObjectStateUserDefinedType; - - QualifiedModuleName = candidates.First().QualifiedModuleName; - _defaultObjectStateUDTTypeName = $"T{QualifiedModuleName.ComponentName}"; + private List _encapsulateAsUDTMemberCandidates; - _objStateCandidates = new List(); - - if (objectStateUserDefinedTypeCandidates.Any()) - { - _objStateCandidates.AddRange(objectStateUserDefinedTypeCandidates.Distinct()); + public EncapsulateFieldUseBackingUDTMemberModel(IObjectStateUDT targetObjectStateUserDefinedTypeField, + IEnumerable encapsulateAsUDTMemberCandidates, + IEnumerable objectStateUserDefinedTypeCandidates) + { + _encapsulateAsUDTMemberCandidates = new List(encapsulateAsUDTMemberCandidates); - _preExistingObjectStateUDT = objectStateUserDefinedTypeCandidates - .FirstOrDefault(os => os.AsTypeDeclaration.IdentifierName.StartsWith(_defaultObjectStateUDTTypeName, StringComparison.InvariantCultureIgnoreCase)); + ObjectStateUDTField = targetObjectStateUserDefinedTypeField; - if (_preExistingObjectStateUDT != null) - { - HasPreExistingObjectStateUDT = true; - _defaultObjectStateUDT.IsSelected = false; - _preExistingObjectStateUDT.IsSelected = true; - _convertedFields.ForEach(c => c.ObjectStateUDT = _preExistingObjectStateUDT); - } - } + ObjectStateUDTCandidates = objectStateUserDefinedTypeCandidates.ToList(); - _objStateCandidates.Add(_defaultObjectStateUDT); + QualifiedModuleName = encapsulateAsUDTMemberCandidates.First().QualifiedModuleName; ResetNewContent(); - - _convertedFields.ForEach(c => c.ObjectStateUDT = ObjectStateUDTField); } - private void ResetNewContent() + public void ResetNewContent() { NewContent = new Dictionary> { @@ -66,17 +36,17 @@ private void ResetNewContent() }; } - public bool HasPreExistingObjectStateUDT { get; } + public IReadOnlyCollection ObjectStateUDTCandidates { get; } public IEncapsulateFieldConflictFinder ConflictFinder { set; get; } public bool IncludeNewContentMarker { set; get; } = false; public IReadOnlyCollection EncapsulationCandidates - => _convertedFields.Cast().ToList(); + => _encapsulateAsUDTMemberCandidates.Cast().ToList(); public IEnumerable SelectedFieldCandidates - => EncapsulationCandidates.Where(v => v.EncapsulateFlag); + => EncapsulationCandidates.Where(v => v.EncapsulateFlag && v.Declaration != ObjectStateUDTField?.Declaration); public void AddContentBlock(NewContentType contentType, string block) => NewContent[contentType].Add(block); @@ -85,29 +55,29 @@ public void AddContentBlock(NewContentType contentType, string block) public QualifiedModuleName QualifiedModuleName { get; } - public IEnumerable ObjectStateUDTCandidates => _objStateCandidates; - + private IObjectStateUDT _objectStateUDT; public IObjectStateUDT ObjectStateUDTField { - get => _objStateCandidates.SingleOrDefault(os => os.IsSelected) - ?? _defaultObjectStateUDT; - set { - if (value is null) + if (_objectStateUDT == value) { - _objStateCandidates.ForEach(osc => osc.IsSelected = (osc == _defaultObjectStateUDT)); return; } - var matchingCandidate = _objStateCandidates - .SingleOrDefault(os => os.FieldIdentifier.Equals(value.FieldIdentifier)) - ?? _defaultObjectStateUDT; - - _objStateCandidates.ForEach(osc => osc.IsSelected = (osc == matchingCandidate)); + if (_objectStateUDT != null) + { + _objectStateUDT.IsSelected = false; + } - _convertedFields.ForEach(cf => cf.ObjectStateUDT = matchingCandidate); + _objectStateUDT = value; + if (_objectStateUDT != null) + { + _objectStateUDT.IsSelected = true; + } + _encapsulateAsUDTMemberCandidates.ForEach(cf => cf.ObjectStateUDT = _objectStateUDT); } + get => _objectStateUDT; } } } diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberPreviewProvider.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberPreviewProvider.cs index 2cb236913e..e75085e6b7 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberPreviewProvider.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberPreviewProvider.cs @@ -18,6 +18,7 @@ public override string Preview(EncapsulateFieldUseBackingUDTMemberModel model) model.IncludeNewContentMarker = true; try { + model.ResetNewContent(); preview = base.Preview(model); } catch (Exception e) { } diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ArrayCandidate.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ArrayCandidate.cs index 985a89beb1..9a0fce6035 100644 --- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ArrayCandidate.cs +++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ArrayCandidate.cs @@ -35,7 +35,10 @@ public ArrayCandidate(Declaration declaration, Func parameterName public override bool TryValidateEncapsulationAttributes(out string errorMessage) { errorMessage = string.Empty; - if (!EncapsulateFlag) { return true; } + if (!EncapsulateFlag) + { + return true; + } if (HasExternalRedimOperation(out errorMessage)) { diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidate.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidate.cs index fae1741bb7..0a437de8fe 100644 --- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidate.cs +++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidate.cs @@ -113,11 +113,7 @@ public virtual string BackingIdentifier public virtual bool TryValidateEncapsulationAttributes(out string errorMessage) { errorMessage = string.Empty; - if (ConflictFinder is null) - { - return true; - } - return ConflictFinder.TryValidateEncapsulationAttributes(this, out errorMessage); + return ConflictFinder?.TryValidateEncapsulationAttributes(this, out errorMessage) ?? true; } public virtual string TargetID => _target?.IdentifierName ?? IdentifierName; @@ -189,7 +185,7 @@ public string PropertyIdentifier private void TryRestoreNewFieldNameAsOriginalFieldIdentifierName() { var canNowUseOriginalFieldName = !_fieldAndProperty.TargetFieldName.IsEquivalentVBAIdentifierTo(_fieldAndProperty.Property) - && !ConflictFinder.IsConflictingProposedIdentifier(_fieldAndProperty.TargetFieldName, this, DeclarationType.Variable); + && !(ConflictFinder?.IsConflictingProposedIdentifier(_fieldAndProperty.TargetFieldName, this, DeclarationType.Variable) ?? false); if (canNowUseOriginalFieldName) { @@ -200,11 +196,11 @@ private void TryRestoreNewFieldNameAsOriginalFieldIdentifierName() if (_fieldAndProperty.Field.IsEquivalentVBAIdentifierTo(_fieldAndProperty.TargetFieldName)) { _fieldAndProperty.Field = _fieldAndProperty.DefaultNewFieldName; - var isConflictingFieldIdentifier = ConflictFinder.HasConflictingIdentifier(this, DeclarationType.Variable, out _); + var isConflictingFieldIdentifier = ConflictFinder?.HasConflictingIdentifier(this, DeclarationType.Variable, out _) ?? false; for (var count = 1; count < 10 && isConflictingFieldIdentifier; count++) { BackingIdentifier = BackingIdentifier.IncrementEncapsulationIdentifier(); - isConflictingFieldIdentifier = ConflictFinder.HasConflictingIdentifier(this, DeclarationType.Variable, out _); + isConflictingFieldIdentifier = ConflictFinder?.HasConflictingIdentifier(this, DeclarationType.Variable, out _) ?? false; } } } diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeCandidate.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeCandidate.cs index edcda870df..4e984f19ac 100644 --- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeCandidate.cs +++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeCandidate.cs @@ -11,7 +11,7 @@ public interface IUserDefinedTypeCandidate : IEncapsulateFieldCandidate IEnumerable Members { get; } void AddMember(IUserDefinedTypeMemberCandidate member); bool TypeDeclarationIsPrivate { get; } - bool CanBeObjectStateUDT { set; get; } + bool IsObjectStateUDTCandidate { set; get; } bool IsSelectedObjectStateUDT { set; get; } } @@ -40,7 +40,7 @@ public bool TypeDeclarationIsPrivate public bool IsSelectedObjectStateUDT { set; get; } - public bool CanBeObjectStateUDT { set; get; } + public bool IsObjectStateUDTCandidate { set; get; } public override string BackingIdentifier { diff --git a/RubberduckTests/Commands/RefactorCommands/EncapsulateFieldCommandTests.cs b/RubberduckTests/Commands/RefactorCommands/EncapsulateFieldCommandTests.cs index 800f9eaf34..730e03defc 100644 --- a/RubberduckTests/Commands/RefactorCommands/EncapsulateFieldCommandTests.cs +++ b/RubberduckTests/Commands/RefactorCommands/EncapsulateFieldCommandTests.cs @@ -73,7 +73,6 @@ protected override CommandBase TestCommand(IVBE vbe, RubberduckParserState state var refactoring = new EncapsulateFieldRefactoring(resolver.Resolve(), resolver.Resolve(), resolver.Resolve(), - state, userInteraction, rewritingManager, selectionService, diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFIeldTestSupport.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFIeldTestSupport.cs index 783949837b..bb75a0193b 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFIeldTestSupport.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFIeldTestSupport.cs @@ -122,7 +122,6 @@ public string RefactoredCode(CodeString codeString, Func(), resolver.Resolve(), resolver.Resolve(), - state, userInteraction, rewritingManager, selectionService, diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs index 31efa5c7a9..6364842330 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs @@ -34,7 +34,7 @@ public EncapsulateFieldTestComponentResolver(IDeclarationFinderProvider declarat switch (typeof(T).Name) { case nameof(EncapsulateFieldRefactoringAction): - return new EncapsulateFieldRefactoringAction(_declarationFinderProvider, + return new EncapsulateFieldRefactoringAction( ResolveImpl(), ResolveImpl()) as T; @@ -61,8 +61,6 @@ public EncapsulateFieldTestComponentResolver(IDeclarationFinderProvider declarat case nameof(IEncapsulateFieldRefactoringActionsProvider): return new EncapsulateFieldRefactoringActionsProvider( - _declarationFinderProvider, - _rewritingManager, ResolveImpl(), ResolveImpl(), ResolveImpl(), @@ -95,7 +93,7 @@ public EncapsulateFieldTestComponentResolver(IDeclarationFinderProvider declarat new CodeBuilder()) as T; case nameof(EncapsulateFieldPreviewProvider): - return new EncapsulateFieldPreviewProvider(_declarationFinderProvider, + return new EncapsulateFieldPreviewProvider( ResolveImpl(), ResolveImpl()) as T; @@ -109,22 +107,36 @@ public EncapsulateFieldTestComponentResolver(IDeclarationFinderProvider declarat _rewritingManager) as T; case nameof(IEncapsulateFieldModelFactory): - return new EncapsulateFieldModelFactory(_declarationFinderProvider, - ResolveImpl(), - ResolveImpl(), - ResolveImpl()) as T; - - case nameof(EncapsulateFieldUseBackingUDTMemberModelFactory): + return new EncapsulateFieldModelFactory( + ResolveImpl(), + ResolveImpl(), + ResolveImpl(), + new EncapsulateFieldRequestFactory() as IEncapsulateFieldRequestFactory + ) as T; + + case nameof(IEncapsulateFieldUseBackingUDTMemberModelFactory): return new EncapsulateFieldUseBackingUDTMemberModelFactory(_declarationFinderProvider, - ResolveImpl()) as T; + ResolveImpl(), + ResolveImpl(), + ResolveImpl< IEncapsulateFieldConflictFinderFactory>()) as T; - case nameof(EncapsulateFieldUseBackingFieldModelFactory): + case nameof(IEncapsulateFieldUseBackingFieldModelFactory): return new EncapsulateFieldUseBackingFieldModelFactory(_declarationFinderProvider, - ResolveImpl()) as T; + ResolveImpl(), + ResolveImpl< IEncapsulateFieldConflictFinderFactory>()) as T; case nameof(IEncapsulateFieldCandidateFactory): return new EncapsulateFieldCandidateFactory(_declarationFinderProvider, new CodeBuilder()) as T; + case nameof(IObjectStateUserDefinedTypeFactory): + return new ObjectStateUserDefinedTypeFactory() as T; + + case nameof(IEncapsulateFieldCandidateCollectionFactory): + return new EncapsulateFieldCandidateCollectionFactory(_declarationFinderProvider, ResolveImpl()) as T; + + case nameof(IEncapsulateFieldConflictFinderFactory): + return new EncapsulateFieldConflictFinderFactory(_declarationFinderProvider) as T; + } throw new ArgumentException($"Unable to resolve {typeof(T).Name}") ; } diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldRefactoringActionTests.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldRefactoringActionTests.cs new file mode 100644 index 0000000000..747ab07519 --- /dev/null +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldRefactoringActionTests.cs @@ -0,0 +1,90 @@ +using NUnit.Framework; +using Rubberduck.Common; +using Rubberduck.Parsing.Rewriter; +using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.VBA; +using Rubberduck.Refactorings; +using Rubberduck.Refactorings.EncapsulateField; +using Rubberduck.Refactorings.EncapsulateFieldUseBackingField; +using System.Collections.Generic; +using System.Linq; + +namespace RubberduckTests.Refactoring.EncapsulateField.EncapsulateFieldUseBackingField +{ + [TestFixture] + public class EncapsulateFieldUseBackingFieldRefactoringActionTests : RefactoringActionTestBase + { + private EncapsulateFieldTestSupport Support { get; } = new EncapsulateFieldTestSupport(); + + [TestCase(false, "Name")] + [TestCase(true, "Name")] + [TestCase(false, null)] + [TestCase(true, null)] + [Category("Refactorings")] + [Category("Encapsulate Field")] + [Category(nameof(EncapsulateFieldUseBackingFieldRefactoringAction))] + public void EncapsulatePublicField(bool isReadOnly, string propertyIdentifier) + { + var target = "fizz"; + var inputCode = $"Public {target} As Integer"; + + EncapsulateFieldUseBackingFieldModel modelBuilder(RubberduckParserState state) + { + var resolver = new EncapsulateFieldTestComponentResolver(state, null); + var modelFactory = resolver.Resolve(); + + var field = state.DeclarationFinder.MatchName(target).Single(); + var encapsulateFieldRequest = new EncapsulateFieldRequest(field as VariableDeclaration, isReadOnly, propertyIdentifier); + return modelFactory.Create( new List() { encapsulateFieldRequest }); + } + + var refactoredCode = RefactoredCode(inputCode, modelBuilder); + + var resultPropertyIdentifier = propertyIdentifier ?? target.CapitalizeFirstLetter(); + + var backingField = propertyIdentifier != null + ? target + : $"{target}_1"; + + StringAssert.Contains($"Public Property Get {resultPropertyIdentifier}()", refactoredCode); + StringAssert.Contains($"{resultPropertyIdentifier} = {backingField}", refactoredCode); + + if (isReadOnly) + { + StringAssert.DoesNotContain($"Public Property Let {resultPropertyIdentifier}(", refactoredCode); + StringAssert.DoesNotContain($"{backingField} = ", refactoredCode); + } + else + { + StringAssert.Contains($"Public Property Let {resultPropertyIdentifier}(", refactoredCode); + StringAssert.Contains($"{backingField} = ", refactoredCode); + } + } + + [Test] + [Category("Refactorings")] + [Category("Encapsulate Field")] + [Category(nameof(EncapsulateFieldUseBackingFieldRefactoringAction))] + public void EmptyTargetSet() + { + var target = "fizz"; + var inputCode = $"Public {target} As Integer"; + + EncapsulateFieldUseBackingFieldModel modelBuilder(RubberduckParserState state) + { + var resolver = new EncapsulateFieldTestComponentResolver(state, null); + var modelFactory = resolver.Resolve(); + return modelFactory.Create(Enumerable.Empty()); + } + + var refactoredCode = RefactoredCode(inputCode, modelBuilder); + Assert.AreEqual(refactoredCode, inputCode); + } + + protected override IRefactoringAction TestBaseRefactoring(RubberduckParserState state, IRewritingManager rewritingManager) + { + var resolver = new EncapsulateFieldTestComponentResolver(state, rewritingManager); + return resolver.Resolve(); + } + } +} diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberTests.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberTests.cs new file mode 100644 index 0000000000..843794e7ea --- /dev/null +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberTests.cs @@ -0,0 +1,239 @@ +using NUnit.Framework; +using Rubberduck.Common; +using Rubberduck.Parsing.Rewriter; +using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.VBA; +using Rubberduck.Refactorings; +using Rubberduck.Refactorings.EncapsulateField; +using Rubberduck.Refactorings.EncapsulateFieldUseBackingField; +using Rubberduck.Refactorings.EncapsulateFieldUseBackingUDTMember; +using RubberduckTests.Mocks; +using System.Collections.Generic; +using System.Linq; + +namespace RubberduckTests.Refactoring.EncapsulateField.EncapsulateFieldUseBackingUDTMember +{ + [TestFixture] + public class EncapsulateFieldUseBackingUDTMemberTests : RefactoringActionTestBase + { + private EncapsulateFieldTestSupport Support { get; } = new EncapsulateFieldTestSupport(); + + [TestCase(false, "Name")] + [TestCase(true, "Name")] + [TestCase(false, null)] + [TestCase(true, null)] + [Category("Refactorings")] + [Category("Encapsulate Field")] + [Category(nameof(EncapsulateFieldUseBackingUDTMemberRefactoringAction))] + public void EncapsulatePublicField(bool isReadOnly, string propertyIdentifier) + { + var target = "fizz"; + var inputCode = $"Public {target} As Integer"; + + EncapsulateFieldUseBackingUDTMemberModel modelBuilder(RubberduckParserState state) + { + var resolver = new EncapsulateFieldTestComponentResolver(state, null); + var modelFactory = resolver.Resolve(); + + var field = state.DeclarationFinder.MatchName(target).Single(); + var encapsulateFieldRequest = new EncapsulateFieldRequest(field as VariableDeclaration, isReadOnly); + return modelFactory.Create(new List() { encapsulateFieldRequest }); + } + + var refactoredCode = RefactoredCode(inputCode, modelBuilder); + + var resultPropertyIdentifier = target.CapitalizeFirstLetter(); + + var backingFieldexpression = propertyIdentifier != null + ? $"this.{resultPropertyIdentifier}" + : $"this.{resultPropertyIdentifier}"; + + StringAssert.Contains($"T{MockVbeBuilder.TestModuleName}", refactoredCode); + StringAssert.Contains($"Public Property Get {resultPropertyIdentifier}()", refactoredCode); + StringAssert.Contains($"{resultPropertyIdentifier} = {backingFieldexpression}", refactoredCode); + + if (isReadOnly) + { + StringAssert.DoesNotContain($"Public Property Let {resultPropertyIdentifier}(", refactoredCode); + StringAssert.DoesNotContain($"{backingFieldexpression} = ", refactoredCode); + } + else + { + StringAssert.Contains($"Public Property Let {resultPropertyIdentifier}(", refactoredCode); + StringAssert.Contains($"{backingFieldexpression} = ", refactoredCode); + } + } + + [Test] + [Category("Refactorings")] + [Category("Encapsulate Field")] + [Category(nameof(EncapsulateFieldUseBackingUDTMemberRefactoringAction))] + public void EncapsulatePublicFields_ExistingObjectStateUDT() + { + var inputCode = +$@" +Option Explicit + +Private Type T{MockVbeBuilder.TestModuleName} + FirstValue As Long + SecondValue As String +End Type + +Private this As T{MockVbeBuilder.TestModuleName} + +Public thirdValue As Integer + +Public bazz As String"; + + EncapsulateFieldUseBackingUDTMemberModel modelBuilder(RubberduckParserState state) + { + var resolver = new EncapsulateFieldTestComponentResolver(state, null); + var modelFactory = resolver.Resolve(); + + var firstValueField = state.DeclarationFinder.MatchName("thirdValue").Single(d => d.DeclarationType.HasFlag(DeclarationType.Variable)); + var bazzField = state.DeclarationFinder.MatchName("bazz").Single(); + var encapsulateFieldRequestfirstValueField = new EncapsulateFieldRequest(firstValueField as VariableDeclaration); + var encapsulateFieldRequestfirstbazzField = new EncapsulateFieldRequest(bazzField as VariableDeclaration); + var inputList = new List() { encapsulateFieldRequestfirstValueField, encapsulateFieldRequestfirstbazzField }; + return modelFactory.Create(inputList); + } + + var refactoredCode = RefactoredCode(inputCode, modelBuilder); + + StringAssert.Contains($" ThirdValue As Integer", refactoredCode); + StringAssert.Contains($"Property Get ThirdValue", refactoredCode); + StringAssert.Contains($" ThirdValue = this.ThirdValue", refactoredCode); + + StringAssert.Contains($"Property Let ThirdValue", refactoredCode); + StringAssert.Contains($" this.ThirdValue =", refactoredCode); + + StringAssert.Contains($" Bazz As String", refactoredCode); + StringAssert.Contains($"Property Get Bazz", refactoredCode); + StringAssert.Contains($" Bazz = this.Bazz", refactoredCode); + + StringAssert.Contains($"Property Let Bazz", refactoredCode); + StringAssert.Contains($" this.Bazz =", refactoredCode); + } + + [Test] + [Category("Refactorings")] + [Category("Encapsulate Field")] + [Category(nameof(EncapsulateFieldUseBackingUDTMemberRefactoringAction))] + public void EncapsulatePublicFields_ExistingUDT() + { + var inputCode = +$@" +Option Explicit + +Private Type TestType + FirstValue As Long + SecondValue As String +End Type + +Private this As TestType + +Public thirdValue As Integer + +Public bazz As String"; + + EncapsulateFieldUseBackingUDTMemberModel modelBuilder(RubberduckParserState state) + { + var resolver = new EncapsulateFieldTestComponentResolver(state, null); + var modelFactory = resolver.Resolve(); + + var thirdValueField = state.DeclarationFinder.MatchName("thirdValue").Single(d => d.DeclarationType.HasFlag(DeclarationType.Variable)); + var bazzField = state.DeclarationFinder.MatchName("bazz").Single(); + var encapsulateFieldRequestThirdValueField = new EncapsulateFieldRequest(thirdValueField as VariableDeclaration); + var encapsulateFieldRequestBazzField = new EncapsulateFieldRequest(bazzField as VariableDeclaration); + + var inputList = new List() { encapsulateFieldRequestThirdValueField, encapsulateFieldRequestBazzField }; + + var targetUDT = state.DeclarationFinder.MatchName("this").Single(d => d.DeclarationType.HasFlag(DeclarationType.Variable)); + + return modelFactory.Create(inputList, targetUDT); + } + + var refactoredCode = RefactoredCode(inputCode, modelBuilder); + + StringAssert.DoesNotContain($"T{ MockVbeBuilder.TestModuleName}", refactoredCode); + + StringAssert.Contains($" ThirdValue As Integer", refactoredCode); + StringAssert.Contains($"Property Get ThirdValue", refactoredCode); + StringAssert.Contains($" ThirdValue = this.ThirdValue", refactoredCode); + + StringAssert.Contains($"Property Let ThirdValue", refactoredCode); + StringAssert.Contains($" this.ThirdValue =", refactoredCode); + + StringAssert.Contains($" Bazz As String", refactoredCode); + StringAssert.Contains($"Property Get Bazz", refactoredCode); + StringAssert.Contains($" Bazz = this.Bazz", refactoredCode); + + StringAssert.Contains($"Property Let Bazz", refactoredCode); + StringAssert.Contains($" this.Bazz =", refactoredCode); + } + + [Test] + [Category("Refactorings")] + [Category("Encapsulate Field")] + [Category(nameof(EncapsulateFieldUseBackingUDTMemberRefactoringAction))] + public void EmptyTargetSet_Throws() + { + var inputCode = $"Public fizz As Integer"; + + EncapsulateFieldUseBackingUDTMemberModel modelBuilder(RubberduckParserState state) + { + var resolver = new EncapsulateFieldTestComponentResolver(state, null); + var modelFactory = resolver.Resolve(); + return modelFactory.Create(Enumerable.Empty()); + } + + Assert.Throws(() => RefactoredCode(inputCode, modelBuilder)); + } + + [TestCase("notAUserDefinedTypeField")] + [TestCase("notAnOption")] + [Category("Refactorings")] + [Category("Encapsulate Field")] + [Category(nameof(EncapsulateFieldUseBackingUDTMemberRefactoringAction))] + public void InvalidObjectStateTarget_Throws(string objectStateTargetIdentifier) + { + var inputCode = +$@" +Option Explicit + +Public Type CannotUseThis + FirstValue As Long + SecondValue As String +End Type + +Private Type TestType + FirstValue As Long + SecondValue As String +End Type + +Private this As TestType + +Public notAnOption As CannotUseThis + +Public notAUserDefinedTypeField As String"; + + EncapsulateFieldUseBackingUDTMemberModel modelBuilder(RubberduckParserState state) + { + var invalidTarget = state.DeclarationFinder.MatchName(objectStateTargetIdentifier).Single(d => d.DeclarationType.HasFlag(DeclarationType.Variable)); + var resolver = new EncapsulateFieldTestComponentResolver(state, null); + var modelFactory = resolver.Resolve(); + var request = new EncapsulateFieldRequest(invalidTarget as VariableDeclaration); + + return modelFactory.Create(new List() { request }, invalidTarget); + } + + Assert.Throws(() => RefactoredCode(inputCode, modelBuilder)); + } + + protected override IRefactoringAction TestBaseRefactoring(RubberduckParserState state, IRewritingManager rewritingManager) + { + var resolver = new EncapsulateFieldTestComponentResolver(state, rewritingManager); + return resolver.Resolve(); + } + } +} diff --git a/RubberduckTests/Refactoring/EncapsulateField/PreviewerTests.cs b/RubberduckTests/Refactoring/EncapsulateField/PreviewerTests.cs index 0f7271df44..f984d0ffb5 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/PreviewerTests.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/PreviewerTests.cs @@ -42,9 +42,9 @@ public void Preview_EditPropertyIdentifier(EncapsulateFieldStrategy strategy) var modelfactory = resolver.Resolve(); var model = modelfactory.Create(target); + model.EncapsulateFieldStrategy = strategy; var field = model["mTest"]; field.PropertyIdentifier = "ATest"; - model.EncapsulateFieldStrategy = strategy; var previewProvider = resolver.Resolve(); From 7a89dac3403ede78368b46bfbc109719d65ff201 Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Wed, 9 Sep 2020 07:14:08 -0700 Subject: [PATCH 32/67] Add EncapsulateFieldCodeBuilder and factory --- .../EncapsulateFieldCodeBuilder.cs | 91 +++++++++++++++++++ .../EncapsulateFieldCodeBuilderFactory.cs | 22 +++++ 2 files changed, 113 insertions(+) create mode 100644 Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldCodeBuilder.cs create mode 100644 Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldCodeBuilderFactory.cs diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldCodeBuilder.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldCodeBuilder.cs new file mode 100644 index 0000000000..fec942ff48 --- /dev/null +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldCodeBuilder.cs @@ -0,0 +1,91 @@ +using Rubberduck.Parsing.Grammar; +using Rubberduck.Parsing.Symbols; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Rubberduck.Refactorings.EncapsulateField +{ + public interface IEncapsulateFieldCodeBuilder + { + (string Get, string Let, string Set) BuildPropertyBlocks(PropertyAttributeSet propertyAttributeSet); + string BuildUserDefinedTypeDeclaration(IObjectStateUDT objectStateUDT, IEnumerable candidates); + string BuildObjectStateFieldDeclaration(IObjectStateUDT objectStateUDT); + } + + public class EncapsulateFieldCodeBuilder : IEncapsulateFieldCodeBuilder + { + private const string FourSpaces = " "; + private static string _doubleSpace = $"{Environment.NewLine}{Environment.NewLine}"; + + private readonly ICodeBuilder _codeBuilder; + + public EncapsulateFieldCodeBuilder(ICodeBuilder codeBuilder) + { + _codeBuilder = codeBuilder; + } + + public (string Get, string Let, string Set) BuildPropertyBlocks(PropertyAttributeSet propertyAttributes) + { + string propertyLet = null; + string propertySet = null; + + if (propertyAttributes.GenerateLetter) + { + var letterContent = $"{FourSpaces}{propertyAttributes.BackingField} = {propertyAttributes.ParameterName}"; + if (!_codeBuilder.TryBuildPropertyLetCodeBlock(propertyAttributes.Declaration, propertyAttributes.PropertyName, out propertyLet, content: letterContent)) + { + throw new ArgumentException(); + } + } + + if (propertyAttributes.GenerateSetter) + { + var setterContent = $"{FourSpaces}{Tokens.Set} {propertyAttributes.BackingField} = {propertyAttributes.ParameterName}"; + if (!_codeBuilder.TryBuildPropertySetCodeBlock(propertyAttributes.Declaration, propertyAttributes.PropertyName, out propertySet, content: setterContent)) + { + throw new ArgumentException(); + } + } + + var getterContent = $"{propertyAttributes.PropertyName} = {propertyAttributes.BackingField}"; + if (propertyAttributes.UsesSetAssignment) + { + getterContent = $"{Tokens.Set} {getterContent}"; + } + + if (propertyAttributes.AsTypeName.Equals(Tokens.Variant) && !propertyAttributes.Declaration.IsArray) + { + getterContent = string.Join(Environment.NewLine, + $"{Tokens.If} IsObject({propertyAttributes.BackingField}) {Tokens.Then}", + $"{FourSpaces}{Tokens.Set} {propertyAttributes.PropertyName} = {propertyAttributes.BackingField}", + Tokens.Else, + $"{FourSpaces}{propertyAttributes.PropertyName} = {propertyAttributes.BackingField}", + $"{Tokens.End} {Tokens.If}", + Environment.NewLine); + } + + if (!_codeBuilder.TryBuildPropertyGetCodeBlock(propertyAttributes.Declaration, propertyAttributes.PropertyName, out var propertyGet, content: $"{FourSpaces}{getterContent}")) + { + throw new ArgumentException(); + } + + return (propertyGet, propertyLet, propertySet); + } + + public string BuildUserDefinedTypeDeclaration(IObjectStateUDT objectStateUDT, IEnumerable candidates) + { + var selected = candidates.Where(c => c.EncapsulateFlag); + + var newUDTMembers = selected + .Select(m => (m.Declaration as VariableDeclaration, m.BackingIdentifier)); + + return _codeBuilder.BuildUserDefinedTypeDeclaration(objectStateUDT.AsTypeName, newUDTMembers); + } + + public string BuildObjectStateFieldDeclaration(IObjectStateUDT objectStateUDT) + { + return $"{Accessibility.Private} {objectStateUDT.IdentifierName} {Tokens.As} {objectStateUDT.AsTypeName}"; + } + } +} diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldCodeBuilderFactory.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldCodeBuilderFactory.cs new file mode 100644 index 0000000000..e903e81127 --- /dev/null +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldCodeBuilderFactory.cs @@ -0,0 +1,22 @@ + +namespace Rubberduck.Refactorings.EncapsulateField +{ + public interface IEncapsulateFieldCodeBuilderFactory + { + IEncapsulateFieldCodeBuilder Create(); + } + + public class EncapsulateFieldCodeBuilderFactory : IEncapsulateFieldCodeBuilderFactory + { + private readonly ICodeBuilder _codeBuilder; + public EncapsulateFieldCodeBuilderFactory(ICodeBuilder codeBuilder) + { + _codeBuilder = codeBuilder; + } + + public IEncapsulateFieldCodeBuilder Create() + { + return new EncapsulateFieldCodeBuilder(_codeBuilder); + } + } +} From be6f32bef8f1f42dc38dff23daf05aa04bd80431 Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Wed, 9 Sep 2020 07:16:39 -0700 Subject: [PATCH 33/67] Add NewContentAggregator and factory Replaces CodeBlockInsertRefactoringAction --- .../CodeBlockInsert/CodeBlockInsertModel.cs | 53 -------- .../CodeBlockInsertRefactoringAction.cs | 116 ----------------- .../NewContentAggregator.cs | 122 ++++++++++++++++++ .../NewContentAggregatorFactory.cs | 16 +++ .../CodeBlockInsertRefactoringActionTests.cs | 122 ------------------ 5 files changed, 138 insertions(+), 291 deletions(-) delete mode 100644 Rubberduck.Refactorings/CodeBlockInsert/CodeBlockInsertModel.cs delete mode 100644 Rubberduck.Refactorings/CodeBlockInsert/CodeBlockInsertRefactoringAction.cs create mode 100644 Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/NewContentAggregator.cs create mode 100644 Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/NewContentAggregatorFactory.cs delete mode 100644 RubberduckTests/Refactoring/CodeBlockInsert/CodeBlockInsertRefactoringActionTests.cs diff --git a/Rubberduck.Refactorings/CodeBlockInsert/CodeBlockInsertModel.cs b/Rubberduck.Refactorings/CodeBlockInsert/CodeBlockInsertModel.cs deleted file mode 100644 index 9989d1484b..0000000000 --- a/Rubberduck.Refactorings/CodeBlockInsert/CodeBlockInsertModel.cs +++ /dev/null @@ -1,53 +0,0 @@ -using Rubberduck.Refactorings.EncapsulateField; -using Rubberduck.VBEditor; -using System.Collections.Generic; -using System.Linq; - -namespace Rubberduck.Refactorings.CodeBlockInsert -{ - public enum NewContentType - { - TypeDeclarationBlock, - DeclarationBlock, - CodeSectionBlock, - PostContentMessage - } - - public class CodeBlockInsertModel : IRefactoringModel - { - public CodeBlockInsertModel() - { - _selectedFields = new List(); - - NewContent = new Dictionary> - { - { NewContentType.PostContentMessage, new List() }, - { NewContentType.DeclarationBlock, new List() }, - { NewContentType.CodeSectionBlock, new List() }, - { NewContentType.TypeDeclarationBlock, new List() } - }; - } - - public QualifiedModuleName QualifiedModuleName { set; get; } - - public bool IncludeComments { set; get; } - - public Dictionary> NewContent { set; get; } - - public void AddContentBlock(NewContentType contentType, string block) - => NewContent[contentType].Add(block); - - public int NewLineLimit { set; get; } = 2; - - private List _selectedFields; - public IEnumerable SelectedFieldCandidates - { - set => _selectedFields = value.ToList(); - get => _selectedFields; - } - - public int? CodeSectionStartIndex { set; get; } = null; - - public int? NewContentInsertionIndex => CodeSectionStartIndex; - } -} diff --git a/Rubberduck.Refactorings/CodeBlockInsert/CodeBlockInsertRefactoringAction.cs b/Rubberduck.Refactorings/CodeBlockInsert/CodeBlockInsertRefactoringAction.cs deleted file mode 100644 index 79ff2fe008..0000000000 --- a/Rubberduck.Refactorings/CodeBlockInsert/CodeBlockInsertRefactoringAction.cs +++ /dev/null @@ -1,116 +0,0 @@ -using Rubberduck.Parsing.Rewriter; -using Rubberduck.Parsing.VBA; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Rubberduck.Refactorings.CodeBlockInsert -{ - /// - /// Inserts new content into a ModuleDeclarationSection of CodeSection - /// based on the presence or absence of existing CodeSection content. - /// - /// If there is an existing member Declaration, then the - /// new content will be inserted above the existing member. - /// - public class CodeBlockInsertRefactoringAction : CodeOnlyRefactoringActionBase - { - private static string _doubleSpace = $"{Environment.NewLine}{Environment.NewLine}"; - - private readonly IDeclarationFinderProvider _declarationFinderProvider; - private readonly ICodeBuilder _codeBuilder; - - public CodeBlockInsertRefactoringAction(IDeclarationFinderProvider declarationFinderProvider, IRewritingManager rewritingManager, ICodeBuilder codeBuilder) - : base(rewritingManager) - { - _declarationFinderProvider = declarationFinderProvider; - _codeBuilder = codeBuilder; - } - - public override void Refactor(CodeBlockInsertModel model, IRewriteSession rewriteSession) - { - var newDeclarationSectionBlock = BuildBlock(model, NewContentType.TypeDeclarationBlock, NewContentType.DeclarationBlock); - - var newCodeSectionBlock = BuildBlock(model, NewContentType.CodeSectionBlock); - - var aggregatedBlocks = new List(); - if (!string.IsNullOrEmpty(newDeclarationSectionBlock)) - { - aggregatedBlocks.Add(newDeclarationSectionBlock); - } - - if (!string.IsNullOrEmpty(newCodeSectionBlock)) - { - aggregatedBlocks.Add(newCodeSectionBlock); - } - - if (aggregatedBlocks.Count == 0) - { - return; - } - - var rewriter = rewriteSession.CheckOutModuleRewriter(model.QualifiedModuleName); - - var allNewContent = string.Join(_doubleSpace, aggregatedBlocks); - - var comments = string.Join(_doubleSpace, model.NewContent[NewContentType.PostContentMessage]); - if (!string.IsNullOrEmpty(comments) && model.IncludeComments) - { - allNewContent = $"{allNewContent}{Environment.NewLine}{comments}"; - } - - InsertBlock(allNewContent, model.NewContentInsertionIndex, rewriter); - } - - private static void InsertBlock(string content, int? insertionIndex, IModuleRewriter rewriter) - { - if (string.IsNullOrEmpty(content)) - { - return; - } - - if (insertionIndex.HasValue) - { - rewriter.InsertBefore(insertionIndex.Value, $"{content}{_doubleSpace}"); - return; - } - rewriter.InsertBefore(rewriter.TokenStream.Size - 1, $"{_doubleSpace}{content}"); - } - - private string BuildBlock(CodeBlockInsertModel model, params NewContentType[] newContentTypes) - { - var block = string.Empty; - foreach (var newContentType in newContentTypes) - { - var newContent = string.Join(_doubleSpace, - (model.NewContent[newContentType])); - if (!string.IsNullOrEmpty(newContent)) - { - block = string.IsNullOrEmpty(block) - ? newContent - : $"{block}{_doubleSpace}{newContent}"; - } - } - return LimitNewLines(block.Trim(), model.NewLineLimit); - } - - private static string LimitNewLines(string content, int maxConsecutiveNewlines = 2) - { - var target = string.Concat(Enumerable.Repeat(Environment.NewLine, maxConsecutiveNewlines + 1).ToList()); - var replacement = string.Concat(Enumerable.Repeat(Environment.NewLine, maxConsecutiveNewlines).ToList()); - var guard = 0; - var maxAttempts = 100; - while (++guard < maxAttempts && content.Contains(target)) - { - content = content.Replace(target, replacement); - } - - if (guard >= maxAttempts) - { - throw new FormatException($"Unable to limit consecutive '{Environment.NewLine}' strings to {maxConsecutiveNewlines}"); - } - return content; - } - - } -} diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/NewContentAggregator.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/NewContentAggregator.cs new file mode 100644 index 0000000000..75c1417f9f --- /dev/null +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/NewContentAggregator.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Rubberduck.Refactorings.EncapsulateField +{ + public enum NewContentType + { + ImplementsDeclaration, + WithEventsDeclaration, + EnumerationTypeDeclaration, + UserDefinedTypeDeclaration, + DeclarationBlock, + CodeSectionBlock, + } + + public interface INewContentAggregator + { + void AddNewContent(NewContentType contentType, string block); + void AddNewContent(string contentIdentifier, string block); + string RetrieveBlock(params NewContentType[] newContentTypes); + string RetrieveBlock(params string[] contentIdentifiers); + int NewLineLimit { set; get; } + } + + public class NewContentAggregator : INewContentAggregator + { + private readonly static string _doubleSpace = $"{Environment.NewLine}{Environment.NewLine}"; + private readonly Dictionary> _newContent; + private Dictionary> _unStructuredContent; + + public NewContentAggregator() + { + _newContent = new Dictionary> + { + { NewContentType.UserDefinedTypeDeclaration, new List() }, + { NewContentType.DeclarationBlock, new List() }, + { NewContentType.CodeSectionBlock, new List() }, + }; + + _unStructuredContent = new Dictionary>(); + } + + public void AddNewContent(NewContentType contentType, string newContent) + { + if (!string.IsNullOrEmpty(newContent)) + { + _newContent[contentType].Add(newContent); + } + } + + public void AddNewContent(string contentIdentifier, string newContent) + { + if (!string.IsNullOrEmpty(newContent)) + { + if (!_unStructuredContent.ContainsKey(contentIdentifier)) + { + _unStructuredContent.Add(contentIdentifier, new List()); + } + _unStructuredContent[contentIdentifier].Add(newContent); + } + } + + public string RetrieveBlock(params NewContentType[] newContentTypes) + { + var block = string.Empty; + foreach (var newContentType in newContentTypes) + { + var newContent = string.Join(_doubleSpace, _newContent[newContentType]); + if (!string.IsNullOrEmpty(newContent)) + { + block = string.IsNullOrEmpty(block) + ? newContent + : $"{block}{_doubleSpace}{newContent}"; + } + } + return LimitNewLines(block.Trim(), NewLineLimit); + } + + public string RetrieveBlock(params string[] contentIdentifiers) + { + var block = string.Empty; + foreach (var identifier in contentIdentifiers) + { + if (_unStructuredContent.TryGetValue(identifier, out var adHocContent)) + { + var newContent = string.Join(_doubleSpace, adHocContent); + if (!string.IsNullOrEmpty(newContent)) + { + block = string.IsNullOrEmpty(block) + ? newContent + : $"{block}{_doubleSpace}{newContent}"; + } + } + } + + return string.IsNullOrEmpty(block) + ? null + : LimitNewLines(block.Trim(), NewLineLimit); + } + + public int NewLineLimit { set; get; } = 2; + + private static string LimitNewLines(string content, int maxConsecutiveNewlines = 2) + { + var target = string.Concat(Enumerable.Repeat(Environment.NewLine, maxConsecutiveNewlines + 1).ToList()); + var replacement = string.Concat(Enumerable.Repeat(Environment.NewLine, maxConsecutiveNewlines).ToList()); + var guard = 0; + var maxAttempts = 100; + while (++guard < maxAttempts && content.Contains(target)) + { + content = content.Replace(target, replacement); + } + + if (guard >= maxAttempts) + { + throw new FormatException($"Unable to limit consecutive '{Environment.NewLine}' strings to {maxConsecutiveNewlines}"); + } + return content; + } + } +} diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/NewContentAggregatorFactory.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/NewContentAggregatorFactory.cs new file mode 100644 index 0000000000..05f07e972a --- /dev/null +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/NewContentAggregatorFactory.cs @@ -0,0 +1,16 @@ + +namespace Rubberduck.Refactorings.EncapsulateField +{ + public interface INewContentAggregatorFactory + { + INewContentAggregator Create(); + } + + public class NewContentAggregatorFactory : INewContentAggregatorFactory + { + public INewContentAggregator Create() + { + return new NewContentAggregator(); + } + } +} diff --git a/RubberduckTests/Refactoring/CodeBlockInsert/CodeBlockInsertRefactoringActionTests.cs b/RubberduckTests/Refactoring/CodeBlockInsert/CodeBlockInsertRefactoringActionTests.cs deleted file mode 100644 index dab8b604a3..0000000000 --- a/RubberduckTests/Refactoring/CodeBlockInsert/CodeBlockInsertRefactoringActionTests.cs +++ /dev/null @@ -1,122 +0,0 @@ -using NUnit.Framework; -using Rubberduck.Parsing.Rewriter; -using Rubberduck.Parsing.Symbols; -using Rubberduck.Parsing.VBA; -using Rubberduck.Refactorings; -using Rubberduck.Refactorings.CodeBlockInsert; -using RubberduckTests.Mocks; -using System.Linq; - -namespace RubberduckTests.Refactoring.CodeBlockInsert -{ - [TestFixture] - public class CodeBlockInsertRefactoringActionTests : RefactoringActionTestBase - { - private static string _declarationSectionMisplaced = "DeclarationSection content in wrong location"; - private static string _codeSectionMisplaced = "CodeSection content in wrong location"; - private static string _commentMisplaced = "PostContentMessage content in wrong location"; - - private static string _declarationContent = "'This is the DeclarationSection"; - private static string _codeContent = "'This is the CodeSection"; - private static string _commentContent = "'This is a comment"; - - [Test] - [Category("Refactorings")] - [Category(nameof(CodeBlockInsertRefactoringAction))] - public void CorrectOrder() - { - string inputCode = -$@" -Option Explicit - -"; - var results = ExecuteTest(inputCode, (_declarationContent, _codeContent, _commentContent)); - var declarationsIndex = results.IndexOf(_declarationContent); - var codeIndex = results.IndexOf(_codeContent); - var commentIndex = results.IndexOf(_commentContent); - Assert.Greater(codeIndex, declarationsIndex, _codeSectionMisplaced); - Assert.Greater(commentIndex, codeIndex, _commentMisplaced); - } - - [Test] - [Category("Refactorings")] - [Category(nameof(CodeBlockInsertRefactoringAction))] - public void CorrectOrder_NoMembers() - { - string inputCode = -$@" -Option Explicit - -Private mTest As Long - -"; - var results = ExecuteTest(inputCode, (_declarationContent, _codeContent, _commentContent)); - var existingDeclarationIndex = results.IndexOf("Private mTest As Long"); - var declarationsIndex = results.IndexOf(_declarationContent); - var codeIndex = results.IndexOf(_codeContent); - var commentIndex = results.IndexOf(_commentContent); - Assert.Greater(declarationsIndex, existingDeclarationIndex, _declarationSectionMisplaced); - Assert.Greater(codeIndex, declarationsIndex, _codeSectionMisplaced); - Assert.Greater(commentIndex, codeIndex, _commentMisplaced); - } - - - [Test] - [Category("Refactorings")] - [Category(nameof(CodeBlockInsertRefactoringAction))] - public void CorrectOrder_ExistingMember() - { - string inputCode = -$@" -Option Explicit - -Private mTest As Long - -Public Sub Test() -End Sub -"; - var results = ExecuteTest(inputCode, (_declarationContent, _codeContent, _commentContent)); - var existingDeclarationIndex = results.IndexOf("Private mTest As Long"); - var existingMemberIndex = results.IndexOf("Sub Test()"); - var declarationsIndex = results.IndexOf(_declarationContent); - var codeIndex = results.IndexOf(_codeContent); - var commentIndex = results.IndexOf(_commentContent); - Assert.Greater(declarationsIndex, existingDeclarationIndex, _declarationSectionMisplaced); - Assert.Greater(codeIndex, declarationsIndex, _codeSectionMisplaced); - Assert.Greater(existingMemberIndex, codeIndex, _codeSectionMisplaced); - Assert.Greater(existingMemberIndex, commentIndex, _commentMisplaced); - } - - private string ExecuteTest(string inputCode, params (string, string, string)[] content) - { - return RefactoredCode(inputCode, state => TestModel(state, content)); - } - - private CodeBlockInsertModel TestModel(RubberduckParserState state, params (string declarationSection, string codeSection, string comment)[] blocks) - { - var members = state.DeclarationFinder.UserDeclarations(DeclarationType.Member); - - var module = state.DeclarationFinder.MatchName(MockVbeBuilder.TestModuleName).Single(); - var model = new CodeBlockInsertModel() - { - QualifiedModuleName = module.QualifiedModuleName, - CodeSectionStartIndex = members - .OrderBy(m => m?.Context.Start.TokenIndex) - .FirstOrDefault()?.Context.Start.TokenIndex, - }; - - foreach ((string declaration, string code, string comment) in blocks) - { - model.AddContentBlock(NewContentType.DeclarationBlock, declaration); - model.AddContentBlock(NewContentType.CodeSectionBlock, code); - model.AddContentBlock(NewContentType.PostContentMessage, comment); - } - return model; - } - - protected override IRefactoringAction TestBaseRefactoring(RubberduckParserState state, IRewritingManager rewritingManager) - { - return new CodeBlockInsertRefactoringAction(state, rewritingManager, new CodeBuilder()); - } - } -} \ No newline at end of file From fe263ab2b00659ffe9947d4a7345b93f3aa737c4 Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Wed, 9 Sep 2020 07:21:20 -0700 Subject: [PATCH 34/67] Refactor code and objects related to new content insertion --- ...eFieldUseBackingUDTMemberConflictFinder.cs | 8 +- .../EncapsulateFieldInsertNewCodeModel.cs | 23 +--- ...lateFieldInsertNewCodeRefactoringAction.cs | 109 ++++++------------ .../EncapsulateFieldRefactoringAction.cs | 3 +- ...apsulateFieldRefactoringActionsProvider.cs | 14 +-- .../EncapsulateFieldUseBackingFieldModel.cs | 41 ++----- ...psulateFieldUseBackingFieldModelFactory.cs | 12 +- ...lateFieldUseBackingFieldPreviewProvider.cs | 29 ++--- ...teFieldUseBackingFieldRefactoringAction.cs | 34 +++--- ...ncapsulateFieldUseBackingUDTMemberModel.cs | 51 +++----- ...ateFieldUseBackingUDTMemberModelFactory.cs | 7 +- ...FieldUseBackingUDTMemberPreviewProvider.cs | 29 ++--- ...eldUseBackingUDTMemberRefactoringAction.cs | 39 +++---- .../EncapsulateFieldAsUDTMemberCandidate.cs | 14 ++- .../EncapsulateFieldTestComponentResolver.cs | 42 +++---- .../EncapsulateField/PreviewerTests.cs | 34 ++++++ 16 files changed, 207 insertions(+), 282 deletions(-) diff --git a/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldUseBackingUDTMemberConflictFinder.cs b/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldUseBackingUDTMemberConflictFinder.cs index 6688218205..4885b7d329 100644 --- a/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldUseBackingUDTMemberConflictFinder.cs +++ b/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldUseBackingUDTMemberConflictFinder.cs @@ -8,7 +8,7 @@ namespace Rubberduck.Refactorings.EncapsulateField { public interface IEncapsulateFieldUseBackingUDTMemberConflictFinder : IEncapsulateFieldConflictFinder { - IObjectStateUDT AssignNoConflictIdentifiers(IObjectStateUDT stateUDT, IDeclarationFinderProvider declarationFinderProvider); + IObjectStateUDT AssignNoConflictIdentifiers(IObjectStateUDT stateUDT); } public class EncapsulateFieldUseBackingUDTMemberConflictFinder : EncapsulateFieldConflictFinderBase, IEncapsulateFieldUseBackingUDTMemberConflictFinder @@ -52,16 +52,16 @@ public override bool TryValidateEncapsulationAttributes(IEncapsulateFieldCandida return !ConflictsWithExistingUDTMembers(objectStateUDT, field.BackingIdentifier); } - public IObjectStateUDT AssignNoConflictIdentifiers(IObjectStateUDT stateUDT, IDeclarationFinderProvider declarationFinderProvider) + public IObjectStateUDT AssignNoConflictIdentifiers(IObjectStateUDT stateUDT) { - var members = declarationFinderProvider.DeclarationFinder.Members(stateUDT.QualifiedModuleName); + var members = _declarationFinderProvider.DeclarationFinder.Members(stateUDT.QualifiedModuleName); var guard = 0; while (guard++ < 10 && members.Any(m => m.IdentifierName.IsEquivalentVBAIdentifierTo(stateUDT.FieldIdentifier))) { stateUDT.FieldIdentifier = stateUDT.FieldIdentifier.IncrementEncapsulationIdentifier(); } - members = declarationFinderProvider.DeclarationFinder.Members(stateUDT.QualifiedModuleName) + members = _declarationFinderProvider.DeclarationFinder.Members(stateUDT.QualifiedModuleName) .Where(m => !_udtTypeIdentifierNonConflictTypes.Any(nct => m.DeclarationType.HasFlag(nct))); guard = 0; diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeModel.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeModel.cs index 76b3cf7d87..bbd42cf6b1 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeModel.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeModel.cs @@ -1,5 +1,4 @@ using Rubberduck.VBEditor; -using Rubberduck.Refactorings.CodeBlockInsert; using System.Collections.Generic; using System.Linq; using Rubberduck.Refactorings.EncapsulateField; @@ -10,29 +9,17 @@ public class EncapsulateFieldInsertNewCodeModel : IRefactoringModel { public EncapsulateFieldInsertNewCodeModel(IEnumerable selectedFieldCandidates) { - _selectedCandidates = selectedFieldCandidates.ToList(); - if (_selectedCandidates.Any()) + SelectedFieldCandidates = selectedFieldCandidates.ToList(); + if (SelectedFieldCandidates.Any()) { - QualifiedModuleName = _selectedCandidates.Select(f => f.QualifiedModuleName).First(); + QualifiedModuleName = SelectedFieldCandidates.First().QualifiedModuleName; } - - NewContent = new Dictionary> - { - { NewContentType.PostContentMessage, new List() }, - { NewContentType.DeclarationBlock, new List() }, - { NewContentType.CodeSectionBlock, new List() }, - { NewContentType.TypeDeclarationBlock, new List() } - }; } - public bool IncludeNewContentMarker { set; get; } = false; + public INewContentAggregator NewContentAggregator { set; get; } public QualifiedModuleName QualifiedModuleName { get; } = new QualifiedModuleName(); - public Dictionary> NewContent { set; get; } - - private List _selectedCandidates; - public IEnumerable SelectedFieldCandidates - => _selectedCandidates; + public IReadOnlyCollection SelectedFieldCandidates { get; } } } diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeRefactoringAction.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeRefactoringAction.cs index 214b4a4365..dd8119f4b9 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeRefactoringAction.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeRefactoringAction.cs @@ -1,129 +1,94 @@ -using Rubberduck.Parsing.Grammar; -using Rubberduck.Parsing.Rewriter; +using Rubberduck.Parsing.Rewriter; using Rubberduck.Parsing.Symbols; using Rubberduck.Parsing.VBA; using Rubberduck.Refactorings.Common; using Rubberduck.Resources; -using Rubberduck.Refactorings.CodeBlockInsert; using System; using System.Diagnostics; using System.Linq; using Rubberduck.Refactorings.EncapsulateField; +using System.Collections.Generic; namespace Rubberduck.Refactorings.EncapsulateFieldInsertNewCode { public class EncapsulateFieldInsertNewCodeRefactoringAction : CodeOnlyRefactoringActionBase { - private const string FourSpaces = " "; - + private readonly static string _doubleSpace = $"{Environment.NewLine}{Environment.NewLine}"; + private int? _codeSectionStartIndex; private readonly IDeclarationFinderProvider _declarationFinderProvider; - private readonly IRewritingManager _rewritingManager; - private readonly ICodeBuilder _codeBuilder; - private readonly ICodeOnlyRefactoringAction _codeBlockInsertRefactoringAction; + private readonly IEncapsulateFieldCodeBuilderFactory _encapsulateFieldCodeBuilderFactory; public EncapsulateFieldInsertNewCodeRefactoringAction( - CodeBlockInsertRefactoringAction codeBlockInsertRefactoringAction, IDeclarationFinderProvider declarationFinderProvider, - IRewritingManager rewritingManager, - ICodeBuilder codeBuilder) + IRewritingManager rewritingManager, + IEncapsulateFieldCodeBuilderFactory encapsulateFieldCodeBuilderFactory) : base(rewritingManager) { _declarationFinderProvider = declarationFinderProvider; - _rewritingManager = rewritingManager; - _codeBuilder = codeBuilder; - _codeBlockInsertRefactoringAction = codeBlockInsertRefactoringAction; + _encapsulateFieldCodeBuilderFactory = encapsulateFieldCodeBuilderFactory; } public override void Refactor(EncapsulateFieldInsertNewCodeModel model, IRewriteSession rewriteSession) { - var codeSectionStartIndex = _declarationFinderProvider.DeclarationFinder + _codeSectionStartIndex = _declarationFinderProvider.DeclarationFinder .Members(model.QualifiedModuleName).Where(m => m.IsMember()) .OrderBy(c => c.Selection) .FirstOrDefault()?.Context.Start.TokenIndex; - var codeBlockInsertModel = new CodeBlockInsertModel() - { - QualifiedModuleName = model.QualifiedModuleName, - SelectedFieldCandidates = model.SelectedFieldCandidates, - NewContent = model.NewContent, - CodeSectionStartIndex = codeSectionStartIndex, - IncludeComments = model.IncludeNewContentMarker - }; + LoadNewPropertyBlocks(model, rewriteSession); - LoadNewPropertyBlocks(codeBlockInsertModel, _codeBuilder, rewriteSession); + InsertBlocks(model, rewriteSession); - _codeBlockInsertRefactoringAction.Refactor(codeBlockInsertModel, rewriteSession); + model.NewContentAggregator = null; } - public void LoadNewPropertyBlocks(CodeBlockInsertModel model, ICodeBuilder codeBuilder, IRewriteSession rewriteSession) + public void LoadNewPropertyBlocks(EncapsulateFieldInsertNewCodeModel model, IRewriteSession rewriteSession) { - if (model.IncludeComments) - { - model.AddContentBlock(NewContentType.PostContentMessage, RubberduckUI.EncapsulateField_PreviewMarker); - } - + var builder = _encapsulateFieldCodeBuilderFactory.Create(); foreach (var propertyAttributes in model.SelectedFieldCandidates.SelectMany(f => f.PropertyAttributeSets)) { Debug.Assert(propertyAttributes.Declaration.DeclarationType.HasFlag(DeclarationType.Variable) || propertyAttributes.Declaration.DeclarationType.HasFlag(DeclarationType.UserDefinedTypeMember)); - LoadPropertyGetCodeBlock(model, propertyAttributes, codeBuilder); - - if (propertyAttributes.GenerateLetter) - { - LoadPropertyLetCodeBlock(model, propertyAttributes, codeBuilder); - } + var (Get, Let, Set) = builder.BuildPropertyBlocks(propertyAttributes); - if (propertyAttributes.GenerateSetter) - { - LoadPropertySetCodeBlock(model, propertyAttributes, codeBuilder); - } + var blocks = new List() { Get, Let, Set }; + blocks.ForEach(s => model.NewContentAggregator.AddNewContent(NewContentType.CodeSectionBlock, s)); } } - private static void LoadPropertyLetCodeBlock(CodeBlockInsertModel model, PropertyAttributeSet propertyAttributes, ICodeBuilder codeBuilder) + private void InsertBlocks(EncapsulateFieldInsertNewCodeModel model, IRewriteSession rewriteSession) { - var letterContent = $"{FourSpaces}{propertyAttributes.BackingField} = {propertyAttributes.ParameterName}"; - if (!codeBuilder.TryBuildPropertyLetCodeBlock(propertyAttributes.Declaration, propertyAttributes.PropertyName, out var propertyLet, content: letterContent)) + var newDeclarationSectionBlock = model.NewContentAggregator.RetrieveBlock(NewContentType.UserDefinedTypeDeclaration, NewContentType.DeclarationBlock, NewContentType.CodeSectionBlock); + if (string.IsNullOrEmpty(newDeclarationSectionBlock)) { - throw new ArgumentException(); + return; } - model.AddContentBlock(NewContentType.CodeSectionBlock, propertyLet); - } - private static void LoadPropertySetCodeBlock(CodeBlockInsertModel model, PropertyAttributeSet propertyAttributes, ICodeBuilder codeBuilder) - { - var setterContent = $"{FourSpaces}{Tokens.Set} {propertyAttributes.BackingField} = {propertyAttributes.ParameterName}"; - if (!codeBuilder.TryBuildPropertySetCodeBlock(propertyAttributes.Declaration, propertyAttributes.PropertyName, out var propertySet, content: setterContent)) + var allNewContent = string.Join(_doubleSpace, new string[] { newDeclarationSectionBlock }); + + var previewMarker = model.NewContentAggregator.RetrieveBlock(RubberduckUI.EncapsulateField_PreviewMarker); + if (!string.IsNullOrEmpty(previewMarker)) { - throw new ArgumentException(); + allNewContent = $"{allNewContent}{Environment.NewLine}{previewMarker}"; } - model.AddContentBlock(NewContentType.CodeSectionBlock, propertySet); + + var rewriter = rewriteSession.CheckOutModuleRewriter(model.QualifiedModuleName); + + InsertBlock(allNewContent, _codeSectionStartIndex, rewriter); } - private static void LoadPropertyGetCodeBlock(CodeBlockInsertModel model, PropertyAttributeSet propertyAttributes, ICodeBuilder codeBuilder) + private static void InsertBlock(string content, int? insertionIndex, IModuleRewriter rewriter) { - var getterContent = $"{propertyAttributes.PropertyName} = {propertyAttributes.BackingField}"; - if (propertyAttributes.UsesSetAssignment) + if (string.IsNullOrEmpty(content)) { - getterContent = $"{Tokens.Set} {getterContent}"; + return; } - if (propertyAttributes.AsTypeName.Equals(Tokens.Variant) && !propertyAttributes.Declaration.IsArray) + if (insertionIndex.HasValue) { - getterContent = string.Join(Environment.NewLine, - $"{Tokens.If} IsObject({propertyAttributes.BackingField}) {Tokens.Then}", - $"{FourSpaces}{Tokens.Set} {propertyAttributes.PropertyName} = {propertyAttributes.BackingField}", - Tokens.Else, - $"{FourSpaces}{propertyAttributes.PropertyName} = {propertyAttributes.BackingField}", - $"{Tokens.End} {Tokens.If}", - Environment.NewLine); + rewriter.InsertBefore(insertionIndex.Value, $"{content}{_doubleSpace}"); + return; } - - if (!codeBuilder.TryBuildPropertyGetCodeBlock(propertyAttributes.Declaration, propertyAttributes.PropertyName, out var propertyGet, content: $"{FourSpaces}{getterContent}")) - { - throw new ArgumentException(); - } - - model.AddContentBlock(NewContentType.CodeSectionBlock, propertyGet); + rewriter.InsertBefore(rewriter.TokenStream.Size - 1, $"{_doubleSpace}{content}"); } } } diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringAction.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringAction.cs index e519066f3d..b33b298064 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringAction.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringAction.cs @@ -1,5 +1,4 @@ -using Rubberduck.Parsing.VBA; -using Rubberduck.Refactorings.EncapsulateFieldUseBackingField; +using Rubberduck.Refactorings.EncapsulateFieldUseBackingField; using Rubberduck.Refactorings.EncapsulateFieldUseBackingUDTMember; using System.Linq; diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringActionsProvider.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringActionsProvider.cs index 18d04f9832..344abd8b2f 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringActionsProvider.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringActionsProvider.cs @@ -1,10 +1,7 @@ -using Rubberduck.Parsing.Rewriter; -using Rubberduck.Parsing.VBA; -using Rubberduck.Refactorings.DeclareFieldsAsUDTMembers; +using Rubberduck.Refactorings.DeclareFieldsAsUDTMembers; using Rubberduck.Refactorings.ReplaceDeclarationIdentifier; using Rubberduck.Refactorings.ReplaceReferences; using Rubberduck.Refactorings.ReplacePrivateUDTMemberReferences; -using Rubberduck.Refactorings.CodeBlockInsert; using Rubberduck.Refactorings.EncapsulateFieldInsertNewCode; namespace Rubberduck.Refactorings.EncapsulateField @@ -14,7 +11,6 @@ public interface IEncapsulateFieldRefactoringActionsProvider ICodeOnlyRefactoringAction ReplaceReferences { get; } ICodeOnlyRefactoringAction ReplaceUDTMemberReferences { get; } ICodeOnlyRefactoringAction ReplaceDeclarationIdentifiers { get; } - ICodeOnlyRefactoringAction CodeBlockInsert { get; } ICodeOnlyRefactoringAction DeclareFieldsAsUDTMembers { get; } ICodeOnlyRefactoringAction EncapsulateFieldInsertNewCode { get; } } @@ -23,7 +19,6 @@ public class EncapsulateFieldRefactoringActionsProvider : IEncapsulateFieldRefac { private readonly ReplaceReferencesRefactoringAction _replaceReferences; private readonly ReplaceDeclarationIdentifierRefactoringAction _replaceDeclarationIdentifiers; - private readonly CodeBlockInsertRefactoringAction _codeBlockInsertRefactoringAction; private readonly ReplacePrivateUDTMemberReferencesRefactoringAction _replaceUDTMemberReferencesRefactoringAction; private readonly DeclareFieldsAsUDTMembersRefactoringAction _declareFieldsAsUDTMembersRefactoringAction; private readonly EncapsulateFieldInsertNewCodeRefactoringAction _encapsulateFieldInsertNewCodeRefactoringAction; @@ -33,14 +28,12 @@ public class EncapsulateFieldRefactoringActionsProvider : IEncapsulateFieldRefac ReplacePrivateUDTMemberReferencesRefactoringAction replaceUDTMemberReferencesRefactoringAction, ReplaceDeclarationIdentifierRefactoringAction replaceDeclarationIdentifierRefactoringAction, DeclareFieldsAsUDTMembersRefactoringAction declareFieldsAsUDTMembersRefactoringAction, - EncapsulateFieldInsertNewCodeRefactoringAction encapsulateFieldInsertNewCodeRefactoringAction, - CodeBlockInsertRefactoringAction codeBlockInsertRefactoringAction) + EncapsulateFieldInsertNewCodeRefactoringAction encapsulateFieldInsertNewCodeRefactoringAction) { _replaceReferences = replaceReferencesRefactoringAction; _replaceUDTMemberReferencesRefactoringAction = replaceUDTMemberReferencesRefactoringAction; _replaceDeclarationIdentifiers = replaceDeclarationIdentifierRefactoringAction; _declareFieldsAsUDTMembersRefactoringAction = declareFieldsAsUDTMembersRefactoringAction; - _codeBlockInsertRefactoringAction = codeBlockInsertRefactoringAction; _encapsulateFieldInsertNewCodeRefactoringAction = encapsulateFieldInsertNewCodeRefactoringAction; } @@ -50,9 +43,6 @@ public ICodeOnlyRefactoringAction ReplaceReferences public ICodeOnlyRefactoringAction ReplaceDeclarationIdentifiers => _replaceDeclarationIdentifiers; - public ICodeOnlyRefactoringAction CodeBlockInsert - => _codeBlockInsertRefactoringAction; - public ICodeOnlyRefactoringAction ReplaceUDTMemberReferences => _replaceUDTMemberReferencesRefactoringAction; diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldModel.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldModel.cs index a859b3ee4f..647ff2e2a2 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldModel.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldModel.cs @@ -1,51 +1,30 @@ using System.Collections.Generic; using System.Linq; -using Rubberduck.Parsing.VBA; using Rubberduck.VBEditor; -using Rubberduck.Refactorings.CodeBlockInsert; using Rubberduck.Refactorings.EncapsulateField; namespace Rubberduck.Refactorings.EncapsulateFieldUseBackingField { public class EncapsulateFieldUseBackingFieldModel : IRefactoringModel { - private readonly IDeclarationFinderProvider _declarationFinderProvider; - - public EncapsulateFieldUseBackingFieldModel(IEnumerable candidates, - IDeclarationFinderProvider declarationFinderProvider) + public EncapsulateFieldUseBackingFieldModel(IEnumerable candidates) { - _declarationFinderProvider = declarationFinderProvider; - EncapsulationCandidates = candidates.ToList(); - - ResetNewContent(); - } - - public void ResetNewContent() - { - NewContent = new Dictionary> + if (EncapsulationCandidates.Any()) { - { NewContentType.PostContentMessage, new List() }, - { NewContentType.DeclarationBlock, new List() }, - { NewContentType.CodeSectionBlock, new List() }, - { NewContentType.TypeDeclarationBlock, new List() } - }; + QualifiedModuleName = EncapsulationCandidates.First().QualifiedModuleName; + } } - public IEncapsulateFieldConflictFinder ConflictFinder { set; get; } - - public bool IncludeNewContentMarker { set; get; } = false; + public INewContentAggregator NewContentAggregator { set; get; } - public List EncapsulationCandidates { get; } - - public IEnumerable SelectedFieldCandidates - => EncapsulationCandidates.Where(v => v.EncapsulateFlag); + public IEncapsulateFieldConflictFinder ConflictFinder { set; get; } - public void AddContentBlock(NewContentType contentType, string block) - => NewContent[contentType].Add(block); + public IReadOnlyCollection EncapsulationCandidates { get; } - public Dictionary> NewContent { set; get; } + public IReadOnlyCollection SelectedFieldCandidates + => EncapsulationCandidates.Where(c => c.EncapsulateFlag).ToList(); - public QualifiedModuleName QualifiedModuleName => EncapsulationCandidates.First().QualifiedModuleName; + public QualifiedModuleName QualifiedModuleName { get; } = new QualifiedModuleName(); } } diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldModelFactory.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldModelFactory.cs index a35181577e..913a9c6d33 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldModelFactory.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldModelFactory.cs @@ -1,6 +1,4 @@ -using Rubberduck.Parsing.Symbols; -using Rubberduck.Parsing.VBA; -using Rubberduck.Refactorings.EncapsulateField; +using Rubberduck.Refactorings.EncapsulateField; using Rubberduck.Refactorings.EncapsulateFieldUseBackingField; using System.Collections.Generic; using System.Linq; @@ -25,15 +23,13 @@ public interface IEncapsulateFieldUseBackingFieldModelFactory public class EncapsulateFieldUseBackingFieldModelFactory : IEncapsulateFieldUseBackingFieldModelFactory { - private readonly IDeclarationFinderProvider _declarationFinderProvider; private readonly IEncapsulateFieldCandidateCollectionFactory _fieldCandidateCollectionFactory; private readonly IEncapsulateFieldConflictFinderFactory _conflictFinderFactory; - public EncapsulateFieldUseBackingFieldModelFactory(IDeclarationFinderProvider declarationFinderProvider, + public EncapsulateFieldUseBackingFieldModelFactory( IEncapsulateFieldCandidateCollectionFactory encapsulateFieldCandidateCollectionFactory, IEncapsulateFieldConflictFinderFactory encapsulateFieldConflictFinderFactory) { - _declarationFinderProvider = declarationFinderProvider; _fieldCandidateCollectionFactory = encapsulateFieldCandidateCollectionFactory; _conflictFinderFactory = encapsulateFieldConflictFinderFactory; } @@ -42,7 +38,7 @@ public EncapsulateFieldUseBackingFieldModel Create(IEnumerable(), _declarationFinderProvider); + return new EncapsulateFieldUseBackingFieldModel(Enumerable.Empty()); } var fieldCandidates = _fieldCandidateCollectionFactory.Create(requests.First().Declaration.QualifiedModuleName); @@ -62,7 +58,7 @@ public EncapsulateFieldUseBackingFieldModel Create(IReadOnlyCollection c.ConflictFinder = conflictsFinder); - return new EncapsulateFieldUseBackingFieldModel(fieldCandidates, _declarationFinderProvider) + return new EncapsulateFieldUseBackingFieldModel(fieldCandidates) { ConflictFinder = conflictsFinder }; diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldPreviewProvider.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldPreviewProvider.cs index a68c440bbe..26ee695e82 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldPreviewProvider.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldPreviewProvider.cs @@ -1,32 +1,27 @@ using Rubberduck.Parsing.Rewriter; +using Rubberduck.Refactorings.EncapsulateField; +using Rubberduck.Resources; using Rubberduck.VBEditor; -using System; namespace Rubberduck.Refactorings.EncapsulateFieldUseBackingField { public class EncapsulateFieldUseBackingFieldPreviewProvider : RefactoringPreviewProviderWrapperBase { + private readonly INewContentAggregatorFactory _aggregatorFactory; + public EncapsulateFieldUseBackingFieldPreviewProvider(EncapsulateFieldUseBackingFieldRefactoringAction refactoringAction, - IRewritingManager rewritingManager) + IRewritingManager rewritingManager, + INewContentAggregatorFactory aggregatorFactory) : base(refactoringAction, rewritingManager) - { } + { + _aggregatorFactory = aggregatorFactory; + } public override string Preview(EncapsulateFieldUseBackingFieldModel model) { - var preview = string.Empty; - var initialFlagValue = model.IncludeNewContentMarker; - model.IncludeNewContentMarker = true; - try - { - model.ResetNewContent(); - preview = base.Preview(model); - } - catch (Exception) { } - finally - { - model.IncludeNewContentMarker = initialFlagValue; - } - return preview; + model.NewContentAggregator = _aggregatorFactory.Create(); + model.NewContentAggregator.AddNewContent(RubberduckUI.EncapsulateField_PreviewMarker, RubberduckUI.EncapsulateField_PreviewMarker); + return base.Preview(model); } protected override QualifiedModuleName ComponentToShow(EncapsulateFieldUseBackingFieldModel model) diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldRefactoringAction.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldRefactoringAction.cs index 3797933245..1393fd21f5 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldRefactoringAction.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldRefactoringAction.cs @@ -3,12 +3,10 @@ using Rubberduck.Parsing.Grammar; using Rubberduck.Parsing.Rewriter; using Rubberduck.Parsing.Symbols; -using Rubberduck.Parsing.VBA; using Rubberduck.Refactorings.Common; using Rubberduck.Refactorings.ReplaceDeclarationIdentifier; using Rubberduck.Refactorings.ReplaceReferences; using Rubberduck.Refactorings.ReplacePrivateUDTMemberReferences; -using Rubberduck.Refactorings.CodeBlockInsert; using System; using System.Collections.Generic; using System.Linq; @@ -19,26 +17,26 @@ namespace Rubberduck.Refactorings.EncapsulateFieldUseBackingField { public class EncapsulateFieldUseBackingFieldRefactoringAction : CodeOnlyRefactoringActionBase { - private readonly IDeclarationFinderProvider _declarationFinderProvider; private readonly ICodeOnlyRefactoringAction _replaceUDTMemberReferencesRefactoringAction; private readonly ICodeOnlyRefactoringAction _replaceReferencesRefactoringAction; private readonly ICodeOnlyRefactoringAction _replaceDeclarationIdentifiers; private readonly ICodeOnlyRefactoringAction _encapsulateFieldInsertNewCodeRefactoringAction; private readonly IReplacePrivateUDTMemberReferencesModelFactory _replaceUDTMemberReferencesModelFactory; + private readonly INewContentAggregatorFactory _newContentAggregatorFactory; public EncapsulateFieldUseBackingFieldRefactoringAction( IEncapsulateFieldRefactoringActionsProvider refactoringActionsProvider, IReplacePrivateUDTMemberReferencesModelFactory replaceUDTMemberReferencesModelFactory, - IDeclarationFinderProvider declarationFinderProvider, - IRewritingManager rewritingManager) + IRewritingManager rewritingManager, + INewContentAggregatorFactory newContentAggregatorFactory) :base(rewritingManager) { - _declarationFinderProvider = declarationFinderProvider; _replaceUDTMemberReferencesRefactoringAction = refactoringActionsProvider.ReplaceUDTMemberReferences; _replaceReferencesRefactoringAction = refactoringActionsProvider.ReplaceReferences; _replaceDeclarationIdentifiers = refactoringActionsProvider.ReplaceDeclarationIdentifiers; _encapsulateFieldInsertNewCodeRefactoringAction = refactoringActionsProvider.EncapsulateFieldInsertNewCode; _replaceUDTMemberReferencesModelFactory = replaceUDTMemberReferencesModelFactory; + _newContentAggregatorFactory = newContentAggregatorFactory; } public override void Refactor(EncapsulateFieldUseBackingFieldModel model, IRewriteSession rewriteSession) @@ -48,6 +46,11 @@ public override void Refactor(EncapsulateFieldUseBackingFieldModel model, IRewri return; } + if (model.NewContentAggregator is null) + { + model.NewContentAggregator = _newContentAggregatorFactory.Create(); + } + ModifyFields(model, rewriteSession); ModifyReferences(model, rewriteSession); @@ -73,10 +76,12 @@ private void ModifyFields(EncapsulateFieldUseBackingFieldModel model, IRewriteSe ? $"{Tokens.Private} {targetIdentifier}" : $"{Tokens.Private} {targetIdentifier} {Tokens.As} {field.Declaration.AsTypeName}"; - model.AddContentBlock(NewContentType.DeclarationBlock, newField); + model.NewContentAggregator.AddNewContent(NewContentType.DeclarationBlock, newField); } - var retainedFieldDeclarations = model.SelectedFieldCandidates.Except(fieldDeclarationsToDeleteAndReplace).ToList(); + var retainedFieldDeclarations = model.SelectedFieldCandidates + .Except(fieldDeclarationsToDeleteAndReplace) + .ToList(); if (retainedFieldDeclarations.Any()) { @@ -103,9 +108,9 @@ private void InsertNewContent(EncapsulateFieldUseBackingFieldModel model, IRewri { var encapsulateFieldInsertNewCodeModel = new EncapsulateFieldInsertNewCodeModel(model.SelectedFieldCandidates) { - NewContent = model.NewContent, - IncludeNewContentMarker = model.IncludeNewContentMarker + NewContentAggregator = model.NewContentAggregator }; + _encapsulateFieldInsertNewCodeRefactoringAction.Refactor(encapsulateFieldInsertNewCodeModel, rewriteSession); } @@ -154,7 +159,7 @@ private void ReplaceEncapsulatedPrivateUserDefinedTypeMemberReferences(IEnumerab } } - private static void MakeImplicitDeclarationTypeExplicit(IEnumerable fields, IModuleRewriter rewriter) + private static void MakeImplicitDeclarationTypeExplicit(IReadOnlyCollection fields, IModuleRewriter rewriter) { var fieldsToChange = fields.Where(f => !f.Declaration.Context.TryGetChildContext(out _)) .Select(f => f.Declaration); @@ -165,7 +170,7 @@ private static void MakeImplicitDeclarationTypeExplicit(IEnumerable fields, IModuleRewriter rewriter) + private static void SetPrivateVariableVisiblity(IReadOnlyCollection fields, IModuleRewriter rewriter) { var visibility = Accessibility.Private.TokenString(); foreach (var element in fields.Where(f => !f.Declaration.HasPrivateAccessibility()).Select(f => f.Declaration)) @@ -187,12 +192,13 @@ private static void SetPrivateVariableVisiblity(IEnumerable fields, IRewriteSession rewriteSession) + private void Rename(IReadOnlyCollection fields, IRewriteSession rewriteSession) { var fieldToNewNamePairs = fields.Where(f => !f.BackingIdentifier.Equals(f.Declaration.IdentifierName, StringComparison.InvariantCultureIgnoreCase)) .Select(f => (f.Declaration, f.BackingIdentifier)); - _replaceDeclarationIdentifiers.Refactor(new ReplaceDeclarationIdentifierModel(fieldToNewNamePairs), rewriteSession); + var model = new ReplaceDeclarationIdentifierModel(fieldToNewNamePairs); + _replaceDeclarationIdentifiers.Refactor(model, rewriteSession); } } } diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModel.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModel.cs index 3199c0fcc8..6b2de55248 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModel.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModel.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using System.Linq; using Rubberduck.VBEditor; -using Rubberduck.Refactorings.CodeBlockInsert; using Rubberduck.Refactorings.EncapsulateField; namespace Rubberduck.Refactorings.EncapsulateFieldUseBackingUDTMember @@ -14,70 +13,48 @@ public class EncapsulateFieldUseBackingUDTMemberModel : IRefactoringModel IEnumerable encapsulateAsUDTMemberCandidates, IEnumerable objectStateUserDefinedTypeCandidates) { - _encapsulateAsUDTMemberCandidates = new List(encapsulateAsUDTMemberCandidates); + _encapsulateAsUDTMemberCandidates = encapsulateAsUDTMemberCandidates.ToList(); + EncapsulationCandidates = _encapsulateAsUDTMemberCandidates.Cast().ToList(); ObjectStateUDTField = targetObjectStateUserDefinedTypeField; ObjectStateUDTCandidates = objectStateUserDefinedTypeCandidates.ToList(); QualifiedModuleName = encapsulateAsUDTMemberCandidates.First().QualifiedModuleName; - - ResetNewContent(); } - public void ResetNewContent() - { - NewContent = new Dictionary> - { - { NewContentType.PostContentMessage, new List() }, - { NewContentType.DeclarationBlock, new List() }, - { NewContentType.CodeSectionBlock, new List() }, - { NewContentType.TypeDeclarationBlock, new List() } - }; - } + public INewContentAggregator NewContentAggregator { set; get; } public IReadOnlyCollection ObjectStateUDTCandidates { get; } public IEncapsulateFieldConflictFinder ConflictFinder { set; get; } - public bool IncludeNewContentMarker { set; get; } = false; - - public IReadOnlyCollection EncapsulationCandidates - => _encapsulateAsUDTMemberCandidates.Cast().ToList(); + public IReadOnlyCollection EncapsulationCandidates { get; } - public IEnumerable SelectedFieldCandidates - => EncapsulationCandidates.Where(v => v.EncapsulateFlag && v.Declaration != ObjectStateUDTField?.Declaration); - - public void AddContentBlock(NewContentType contentType, string block) - => NewContent[contentType].Add(block); - - public Dictionary> NewContent { set; get; } + public IReadOnlyCollection SelectedFieldCandidates + => EncapsulationCandidates + .Where(v => v.EncapsulateFlag) + .ToList(); public QualifiedModuleName QualifiedModuleName { get; } - private IObjectStateUDT _objectStateUDT; public IObjectStateUDT ObjectStateUDTField { set { - if (_objectStateUDT == value) + if (ObjectStateUDTField != null) { - return; + ObjectStateUDTField.IsSelected = false; } - if (_objectStateUDT != null) + if (value != null) { - _objectStateUDT.IsSelected = false; + value.IsSelected = true; } - _objectStateUDT = value; - if (_objectStateUDT != null) - { - _objectStateUDT.IsSelected = true; - } - _encapsulateAsUDTMemberCandidates.ForEach(cf => cf.ObjectStateUDT = _objectStateUDT); + _encapsulateAsUDTMemberCandidates.ForEach(cf => cf.ObjectStateUDT = value); } - get => _objectStateUDT; + get => _encapsulateAsUDTMemberCandidates.FirstOrDefault()?.ObjectStateUDT; } } } diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModelFactory.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModelFactory.cs index b0982147d5..4abf01485b 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModelFactory.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModelFactory.cs @@ -1,5 +1,4 @@ using Rubberduck.Parsing.Symbols; -using Rubberduck.Parsing.VBA; using Rubberduck.Refactorings.EncapsulateField; using Rubberduck.Refactorings.EncapsulateFieldUseBackingUDTMember; using System; @@ -26,17 +25,15 @@ public interface IEncapsulateFieldUseBackingUDTMemberModelFactory public class EncapsulateFieldUseBackingUDTMemberModelFactory : IEncapsulateFieldUseBackingUDTMemberModelFactory { - private readonly IDeclarationFinderProvider _declarationFinderProvider; private readonly IEncapsulateFieldCandidateCollectionFactory _fieldCandidateCollectionFactory; private readonly IObjectStateUserDefinedTypeFactory _objectStateUDTFactory; private readonly IEncapsulateFieldConflictFinderFactory _conflictFinderFactory; - public EncapsulateFieldUseBackingUDTMemberModelFactory(IDeclarationFinderProvider declarationFinderProvider, + public EncapsulateFieldUseBackingUDTMemberModelFactory( IEncapsulateFieldCandidateCollectionFactory encapsulateFieldCandidateCollectionFactory, IObjectStateUserDefinedTypeFactory objectStateUserDefinedTypeFactory, IEncapsulateFieldConflictFinderFactory encapsulateFieldConflictFinderFactory) { - _declarationFinderProvider = declarationFinderProvider; _fieldCandidateCollectionFactory = encapsulateFieldCandidateCollectionFactory; _objectStateUDTFactory = objectStateUserDefinedTypeFactory; _conflictFinderFactory = encapsulateFieldConflictFinderFactory; @@ -87,7 +84,7 @@ public EncapsulateFieldUseBackingUDTMemberModel Create(IReadOnlyCollection { + private readonly INewContentAggregatorFactory _aggregatorFactory; + public EncapsulateFieldUseBackingUDTMemberPreviewProvider(EncapsulateFieldUseBackingUDTMemberRefactoringAction refactoringAction, - IRewritingManager rewritingManager) + IRewritingManager rewritingManager, + INewContentAggregatorFactory aggregatorFactory) : base(refactoringAction, rewritingManager) - { } + { + _aggregatorFactory = aggregatorFactory; + } public override string Preview(EncapsulateFieldUseBackingUDTMemberModel model) { - var preview = string.Empty; - var initialFlagValue = model.IncludeNewContentMarker; - model.IncludeNewContentMarker = true; - try - { - model.ResetNewContent(); - preview = base.Preview(model); - } - catch (Exception e) { } - finally - { - model.IncludeNewContentMarker = initialFlagValue; - } - return preview; + model.NewContentAggregator = _aggregatorFactory.Create(); + model.NewContentAggregator.AddNewContent(RubberduckUI.EncapsulateField_PreviewMarker, RubberduckUI.EncapsulateField_PreviewMarker); + return base.Preview(model); } protected override QualifiedModuleName ComponentToShow(EncapsulateFieldUseBackingUDTMemberModel model) diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs index f41cc7032e..c5118f8f6e 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs @@ -1,14 +1,10 @@ using Rubberduck.Common; -using Rubberduck.Parsing.Grammar; using Rubberduck.Parsing.Rewriter; using Rubberduck.Parsing.Symbols; -using Rubberduck.Parsing.VBA; using Rubberduck.Refactorings.Common; using Rubberduck.Refactorings.DeclareFieldsAsUDTMembers; using Rubberduck.Refactorings.ReplaceReferences; using Rubberduck.Refactorings.ReplacePrivateUDTMemberReferences; -using Rubberduck.Refactorings.CodeBlockInsert; -using System.Collections.Generic; using System.Linq; using Rubberduck.Refactorings.EncapsulateField; using Rubberduck.Refactorings.EncapsulateFieldInsertNewCode; @@ -17,29 +13,29 @@ namespace Rubberduck.Refactorings.EncapsulateFieldUseBackingUDTMember { public class EncapsulateFieldUseBackingUDTMemberRefactoringAction : CodeOnlyRefactoringActionBase { - private readonly IDeclarationFinderProvider _declarationFinderProvider; private readonly ICodeOnlyRefactoringAction _declareFieldAsUDTMemberRefactoringAction; private readonly ICodeOnlyRefactoringAction _replaceUDTMemberReferencesRefactoringAction; private readonly ICodeOnlyRefactoringAction _replaceFieldReferencesRefactoringAction; private readonly ICodeOnlyRefactoringAction _encapsulateFieldInsertNewCodeRefactoringAction; - private readonly ICodeBuilder _codeBuilder; + private readonly IEncapsulateFieldCodeBuilder _encapsulateFieldCodeBuilder; + private readonly INewContentAggregatorFactory _newContentAggregatorFactory; private readonly IReplacePrivateUDTMemberReferencesModelFactory _replaceUDTMemberReferencesModelFactory; public EncapsulateFieldUseBackingUDTMemberRefactoringAction( IEncapsulateFieldRefactoringActionsProvider refactoringActionsProvider, IReplacePrivateUDTMemberReferencesModelFactory replaceUDTMemberReferencesModelFactory, - IDeclarationFinderProvider declarationFinderProvider, IRewritingManager rewritingManager, - ICodeBuilder codeBuilder) + INewContentAggregatorFactory newContentAggregatorFactory, + IEncapsulateFieldCodeBuilderFactory encapsulateFieldCodeBuilderFactory) : base(rewritingManager) { - _declarationFinderProvider = declarationFinderProvider; _declareFieldAsUDTMemberRefactoringAction = refactoringActionsProvider.DeclareFieldsAsUDTMembers; _replaceUDTMemberReferencesRefactoringAction = refactoringActionsProvider.ReplaceUDTMemberReferences; _replaceFieldReferencesRefactoringAction = refactoringActionsProvider.ReplaceReferences; _encapsulateFieldInsertNewCodeRefactoringAction = refactoringActionsProvider.EncapsulateFieldInsertNewCode; - _codeBuilder = codeBuilder; + _encapsulateFieldCodeBuilder = encapsulateFieldCodeBuilderFactory.Create(); _replaceUDTMemberReferencesModelFactory = replaceUDTMemberReferencesModelFactory; + _newContentAggregatorFactory = newContentAggregatorFactory; } public override void Refactor(EncapsulateFieldUseBackingUDTMemberModel model, IRewriteSession rewriteSession) @@ -49,13 +45,10 @@ public override void Refactor(EncapsulateFieldUseBackingUDTMemberModel model, IR return; } - model.NewContent = new Dictionary> + if (model.NewContentAggregator is null) { - { NewContentType.PostContentMessage, new List() }, - { NewContentType.DeclarationBlock, new List() }, - { NewContentType.CodeSectionBlock, new List() }, - { NewContentType.TypeDeclarationBlock, new List() } - }; + model.NewContentAggregator = _newContentAggregatorFactory.Create(); + } ModifyFields(model, rewriteSession); @@ -83,14 +76,12 @@ private void ModifyFields(EncapsulateFieldUseBackingUDTMemberModel encapsulateFi } else { - var newUDTMembers = encapsulateFieldModel.SelectedFieldCandidates - .Select(m => (m.Declaration as VariableDeclaration, m.BackingIdentifier)); + var objectStateTypeDeclarationBlock = _encapsulateFieldCodeBuilder.BuildUserDefinedTypeDeclaration(encapsulateFieldModel.ObjectStateUDTField, encapsulateFieldModel.EncapsulationCandidates); - var typeDeclarationBlock = _codeBuilder.BuildUserDefinedTypeDeclaration(encapsulateFieldModel.ObjectStateUDTField.AsTypeName, newUDTMembers); + encapsulateFieldModel.NewContentAggregator.AddNewContent(NewContentType.UserDefinedTypeDeclaration, objectStateTypeDeclarationBlock); - encapsulateFieldModel.AddContentBlock(NewContentType.TypeDeclarationBlock, typeDeclarationBlock); - - encapsulateFieldModel.AddContentBlock(NewContentType.DeclarationBlock, $"{Accessibility.Private} {encapsulateFieldModel.ObjectStateUDTField.IdentifierName} {Tokens.As} {encapsulateFieldModel.ObjectStateUDTField.AsTypeName}"); + var objectStateFieldDeclaration = _encapsulateFieldCodeBuilder.BuildObjectStateFieldDeclaration(encapsulateFieldModel.ObjectStateUDTField); + encapsulateFieldModel.NewContentAggregator.AddNewContent(NewContentType.DeclarationBlock, objectStateFieldDeclaration); } } @@ -146,9 +137,9 @@ private void InsertNewContent(EncapsulateFieldUseBackingUDTMemberModel model, IR { var encapsulateFieldInsertNewCodeModel = new EncapsulateFieldInsertNewCodeModel(model.SelectedFieldCandidates) { - NewContent = model.NewContent, - IncludeNewContentMarker = model.IncludeNewContentMarker + NewContentAggregator = model.NewContentAggregator, }; + _encapsulateFieldInsertNewCodeRefactoringAction.Refactor(encapsulateFieldInsertNewCodeModel, rewriteSession); } } diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldAsUDTMemberCandidate.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldAsUDTMemberCandidate.cs index 395e4b664b..3237eba8ec 100644 --- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldAsUDTMemberCandidate.cs +++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldAsUDTMemberCandidate.cs @@ -39,7 +39,19 @@ public virtual string UDTMemberDeclaration } } - public IObjectStateUDT ObjectStateUDT { set; get; } + private IObjectStateUDT _objectStateUDT; + public IObjectStateUDT ObjectStateUDT + { + set + { + _objectStateUDT = value; + if (_objectStateUDT?.Declaration == Declaration) + { + EncapsulateFlag = false; + } + } + get => _objectStateUDT; + } public string TargetID => _wrapped.TargetID; diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs index 6364842330..6ddd0642fc 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs @@ -6,7 +6,6 @@ using Rubberduck.Refactorings.ReplaceReferences; using Rubberduck.Refactorings.ReplacePrivateUDTMemberReferences; using Rubberduck.Refactorings.ReplaceDeclarationIdentifier; -using Rubberduck.Refactorings.CodeBlockInsert; using Rubberduck.Refactorings.EncapsulateFieldUseBackingUDTMember; using Rubberduck.Refactorings.EncapsulateFieldUseBackingField; using Rubberduck.Refactorings.EncapsulateFieldInsertNewCode; @@ -44,17 +43,11 @@ public EncapsulateFieldTestComponentResolver(IDeclarationFinderProvider declarat case nameof(ReplaceDeclarationIdentifierRefactoringAction): return new ReplaceDeclarationIdentifierRefactoringAction(_rewritingManager) as T; - case nameof(CodeBlockInsertRefactoringAction): - return new CodeBlockInsertRefactoringAction(_declarationFinderProvider, - _rewritingManager, - new CodeBuilder()) as T; - case nameof(EncapsulateFieldInsertNewCodeRefactoringAction): return new EncapsulateFieldInsertNewCodeRefactoringAction( - ResolveImpl(), _declarationFinderProvider, - _rewritingManager, - new CodeBuilder()) as T; + _rewritingManager, + ResolveImpl()) as T; case nameof(ReplacePrivateUDTMemberReferencesRefactoringAction): return new ReplacePrivateUDTMemberReferencesRefactoringAction(_rewritingManager) as T; @@ -65,23 +58,23 @@ public EncapsulateFieldTestComponentResolver(IDeclarationFinderProvider declarat ResolveImpl(), ResolveImpl(), ResolveImpl(), - ResolveImpl(), - ResolveImpl()) as T; + ResolveImpl() + ) as T; case nameof(EncapsulateFieldUseBackingFieldRefactoringAction): return new EncapsulateFieldUseBackingFieldRefactoringAction( ResolveImpl(), ResolveImpl(), - _declarationFinderProvider, - _rewritingManager) as T; + _rewritingManager, + ResolveImpl()) as T; case nameof(EncapsulateFieldUseBackingUDTMemberRefactoringAction): return new EncapsulateFieldUseBackingUDTMemberRefactoringAction( ResolveImpl(), ResolveImpl(), - _declarationFinderProvider, _rewritingManager, - new CodeBuilder()) as T; + ResolveImpl(), + ResolveImpl()) as T; case nameof(IReplacePrivateUDTMemberReferencesModelFactory): return new ReplacePrivateUDTMemberReferencesModelFactory(_declarationFinderProvider) as T; @@ -100,11 +93,14 @@ public EncapsulateFieldTestComponentResolver(IDeclarationFinderProvider declarat case nameof(EncapsulateFieldUseBackingFieldPreviewProvider): return new EncapsulateFieldUseBackingFieldPreviewProvider( ResolveImpl(), - _rewritingManager) as T; + _rewritingManager, + ResolveImpl()) as T; + case nameof(EncapsulateFieldUseBackingUDTMemberPreviewProvider): return new EncapsulateFieldUseBackingUDTMemberPreviewProvider( - ResolveImpl(), - _rewritingManager) as T; + ResolveImpl(), + _rewritingManager, + ResolveImpl()) as T; case nameof(IEncapsulateFieldModelFactory): return new EncapsulateFieldModelFactory( @@ -115,13 +111,13 @@ public EncapsulateFieldTestComponentResolver(IDeclarationFinderProvider declarat ) as T; case nameof(IEncapsulateFieldUseBackingUDTMemberModelFactory): - return new EncapsulateFieldUseBackingUDTMemberModelFactory(_declarationFinderProvider, + return new EncapsulateFieldUseBackingUDTMemberModelFactory( ResolveImpl(), ResolveImpl(), ResolveImpl< IEncapsulateFieldConflictFinderFactory>()) as T; case nameof(IEncapsulateFieldUseBackingFieldModelFactory): - return new EncapsulateFieldUseBackingFieldModelFactory(_declarationFinderProvider, + return new EncapsulateFieldUseBackingFieldModelFactory( ResolveImpl(), ResolveImpl< IEncapsulateFieldConflictFinderFactory>()) as T; @@ -137,6 +133,12 @@ public EncapsulateFieldTestComponentResolver(IDeclarationFinderProvider declarat case nameof(IEncapsulateFieldConflictFinderFactory): return new EncapsulateFieldConflictFinderFactory(_declarationFinderProvider) as T; + case nameof(INewContentAggregatorFactory): + return new NewContentAggregatorFactory() as T; + + case nameof(IEncapsulateFieldCodeBuilderFactory): + return new EncapsulateFieldCodeBuilderFactory(new CodeBuilder()) as T; + } throw new ArgumentException($"Unable to resolve {typeof(T).Name}") ; } diff --git a/RubberduckTests/Refactoring/EncapsulateField/PreviewerTests.cs b/RubberduckTests/Refactoring/EncapsulateField/PreviewerTests.cs index f984d0ffb5..d8f7f5a3d3 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/PreviewerTests.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/PreviewerTests.cs @@ -4,6 +4,7 @@ using Rubberduck.Parsing.VBA; using Rubberduck.Refactorings; using Rubberduck.Refactorings.EncapsulateField; +using Rubberduck.Resources; using Rubberduck.VBEditor.SafeComWrappers; using Rubberduck.VBEditor.Utility; using RubberduckTests.Mocks; @@ -114,6 +115,39 @@ End Type } } + [TestCase(EncapsulateFieldStrategy.UseBackingFields)] + [TestCase(EncapsulateFieldStrategy.ConvertFieldsToUDTMembers)] + [Category("Refactorings")] + [Category("Encapsulate Field")] + [Category(nameof(EncapsulateFieldPreviewProvider))] + public void Preview_IncludeEndOfChangesMarker(EncapsulateFieldStrategy strategy) + { + string inputCode = +$@"Option Explicit + +Public mTest As Long +"; + + var vbe = MockVbeBuilder.BuildFromSingleModule(inputCode, ComponentType.StandardModule, out _); + (RubberduckParserState state, IRewritingManager rewritingManager) = MockParser.CreateAndParseWithRewritingManager(vbe.Object); + using (state) + { + var target = state.DeclarationFinder.MatchName("mTest").First(); + var resolver = new EncapsulateFieldTestComponentResolver(state, rewritingManager); + + var modelfactory = resolver.Resolve(); + var previewProvider = resolver.Resolve(); + + var model = modelfactory.Create(target); + + model.EncapsulateFieldStrategy = strategy; + + var previewResult = previewProvider.Preview(model); + + StringAssert.Contains(RubberduckUI.EncapsulateField_PreviewMarker, previewResult); + } + } + protected override IRefactoring TestRefactoring( IRewritingManager rewritingManager, RubberduckParserState state, From 0c413402e93002f48dd8f50b0ad7949d473bc445 Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Fri, 11 Sep 2020 10:42:47 -0700 Subject: [PATCH 35/67] Resolve build errors and cleanup following merge --- Rubberduck.Refactorings/Common/CodeBuilder.cs | 6 - .../EncapsulateFieldElementsBuilder.cs | 162 ----------------- .../NewContentAggregator.cs | 3 - .../FieldCandidates/ArrayCandidate.cs | 4 +- .../EncapsulateFieldCandidate.cs | 14 +- .../EncapsulateFieldCandidateFactory.cs | 8 +- .../UserDefinedTypeCandidate.cs | 5 +- .../EncapsulateField/ObjectStateUDT.cs | 170 ------------------ .../ObjectStateUDT/ObjectStateUDT.cs | 2 +- RubberduckTests/CodeBuilderTests.cs | 3 +- .../EncapsulateFieldTests.cs | 5 +- ...ncapsulateFieldUseBackingUDTMemberTests.cs | 1 - ...TMemberReferencesRefactoringActionTests.cs | 13 +- 13 files changed, 22 insertions(+), 374 deletions(-) delete mode 100644 Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldElementsBuilder.cs delete mode 100644 Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT.cs diff --git a/Rubberduck.Refactorings/Common/CodeBuilder.cs b/Rubberduck.Refactorings/Common/CodeBuilder.cs index 5dcff343d3..150904b666 100644 --- a/Rubberduck.Refactorings/Common/CodeBuilder.cs +++ b/Rubberduck.Refactorings/Common/CodeBuilder.cs @@ -70,12 +70,6 @@ public interface ICodeBuilder string content = null, string parameterIdentifier = null); - /// - /// Generates a default RHS property parameter IdentifierName - /// - /// Let/Set Property IdentifierName - string BuildPropertyRhsParameterName(string propertyIdentifier); - /// /// Generates a UserDefinedType (UDT) declaration using a VariableDeclaration as the prototype for /// creating the UserDefinedTypeMember. diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldElementsBuilder.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldElementsBuilder.cs deleted file mode 100644 index 6b36b7bb02..0000000000 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldElementsBuilder.cs +++ /dev/null @@ -1,162 +0,0 @@ -using Rubberduck.Parsing.Symbols; -using Rubberduck.Parsing.VBA; -using Rubberduck.Refactorings.Common; -using Rubberduck.VBEditor; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; - -namespace Rubberduck.Refactorings.EncapsulateField -{ - public class EncapsulateFieldElementsBuilder - { - private readonly IDeclarationFinderProvider _declarationFinderProvider; - private QualifiedModuleName _targetQMN; - private string _defaultObjectStateUDTTypeName; - private ICodeBuilder _codeBuilder; - - public EncapsulateFieldElementsBuilder(IDeclarationFinderProvider declarationFinderProvider, QualifiedModuleName targetQMN) - { - _declarationFinderProvider = declarationFinderProvider; - _targetQMN = targetQMN; - _defaultObjectStateUDTTypeName = $"T{_targetQMN.ComponentName}"; - _codeBuilder = new CodeBuilder(); - CreateRefactoringElements(); - } - - public IObjectStateUDT DefaultObjectStateUDT { private set; get; } - - public IObjectStateUDT ObjectStateUDT { private set; get; } - - public IEncapsulateFieldValidationsProvider ValidationsProvider { private set; get; } - - public IEnumerable Candidates { private set; get; } - - public IEnumerable ObjectStateUDTCandidates { private set; get; } = new List(); - - private void CreateRefactoringElements() - { - var fieldDeclarations = _declarationFinderProvider.DeclarationFinder - .Members(_targetQMN) - .Where(v => v.IsMemberVariable() && !v.IsWithEvents); - - var defaultNamesValidator = EncapsulateFieldValidationsProvider.NameOnlyValidator(NameValidators.Default); - - var candidates = new List(); - foreach (var fieldDeclaration in fieldDeclarations) - { - Debug.Assert(!fieldDeclaration.DeclarationType.Equals(DeclarationType.UserDefinedTypeMember)); - - var fieldEncapsulationCandidate = CreateCandidate(fieldDeclaration, defaultNamesValidator); - - candidates.Add(fieldEncapsulationCandidate); - } - - Candidates = candidates; - - ObjectStateUDTCandidates = BuildObjectStateUDTCandidates(candidates).ToList(); - - ObjectStateUDT = ObjectStateUDTCandidates.FirstOrDefault(os => os.AsTypeDeclaration.IdentifierName.StartsWith(_defaultObjectStateUDTTypeName, StringComparison.InvariantCultureIgnoreCase)); - - DefaultObjectStateUDT = CreateStateUDTField(); - DefaultObjectStateUDT.IsSelected = true; - if (ObjectStateUDT != null) - { - ObjectStateUDT.IsSelected = true; - DefaultObjectStateUDT.IsSelected = false; - } - - ObjectStateUDTCandidates = ObjectStateUDTCandidates.Concat(new IObjectStateUDT[] { DefaultObjectStateUDT }); - - ValidationsProvider = new EncapsulateFieldValidationsProvider(Candidates, ObjectStateUDTCandidates); - - var conflictsFinder = ValidationsProvider.ConflictDetector(EncapsulateFieldStrategy.UseBackingFields, _declarationFinderProvider); - foreach (var candidate in candidates) - { - candidate.ConflictFinder = conflictsFinder; - } - } - - private IEnumerable BuildObjectStateUDTCandidates(IEnumerable candidates) - { - var udtCandidates = candidates.Where(c => c is IUserDefinedTypeCandidate udt - && udt.CanBeObjectStateUDT); - - var objectStateUDTs = new List(); - foreach (var udt in udtCandidates) - { - objectStateUDTs.Add(new ObjectStateUDT(udt as IUserDefinedTypeCandidate)); - } - - var objectStateUDT = objectStateUDTs.FirstOrDefault(os => os.AsTypeDeclaration.IdentifierName.StartsWith(_defaultObjectStateUDTTypeName, StringComparison.InvariantCultureIgnoreCase)); - - return objectStateUDTs; - } - - private IObjectStateUDT CreateStateUDTField() - { - var stateUDT = new ObjectStateUDT(_targetQMN) as IObjectStateUDT; - - EncapsulateFieldValidationsProvider.AssignNoConflictIdentifiers(stateUDT, _declarationFinderProvider); - - stateUDT.IsSelected = true; - - return stateUDT; - } - - private IEncapsulateFieldCandidate CreateCandidate(Declaration target, IValidateVBAIdentifiers validator) - { - if (target.IsUserDefinedType()) - { - var udtValidator = EncapsulateFieldValidationsProvider.NameOnlyValidator(NameValidators.UserDefinedType); - var udtField = new UserDefinedTypeCandidate(target, udtValidator) as IUserDefinedTypeCandidate; - - (Declaration udtDeclaration, IEnumerable udtMembers) = GetUDTAndMembersForField(udtField); - - udtField.TypeDeclarationIsPrivate = udtDeclaration.HasPrivateAccessibility(); - - udtField.NameValidator = udtValidator; - - foreach (var udtMemberDeclaration in udtMembers) - { - var udtMemberValidator = EncapsulateFieldValidationsProvider.NameOnlyValidator(NameValidators.UserDefinedTypeMember); - if (udtMemberDeclaration.IsArray) - { - udtMemberValidator = EncapsulateFieldValidationsProvider.NameOnlyValidator(NameValidators.UserDefinedTypeMemberArray); - } - var candidateUDTMember = new UserDefinedTypeMemberCandidate(CreateCandidate(udtMemberDeclaration, udtMemberValidator), udtField) as IUserDefinedTypeMemberCandidate; - - udtField.AddMember(candidateUDTMember); - } - - var udtVariablesOfSameType = _declarationFinderProvider.DeclarationFinder.UserDeclarations(DeclarationType.Variable) - .Where(v => v.AsTypeDeclaration == udtDeclaration); - - udtField.CanBeObjectStateUDT = udtField.TypeDeclarationIsPrivate - && udtField.Declaration.HasPrivateAccessibility() - && udtVariablesOfSameType.Count() == 1; - - return udtField; - } - else if (target.IsArray) - { - return new ArrayCandidate(target, validator); - } - - var candidate = new EncapsulateFieldCandidate(target, validator); - return candidate; - } - - private (Declaration TypeDeclaration, IEnumerable Members) GetUDTAndMembersForField(IUserDefinedTypeCandidate udtField) - { - var userDefinedTypeDeclaration = udtField.Declaration.AsTypeDeclaration; - - var udtMembers = _declarationFinderProvider.DeclarationFinder - .UserDeclarations(DeclarationType.UserDefinedTypeMember) - .Where(utm => userDefinedTypeDeclaration == utm.ParentDeclaration); - - return (userDefinedTypeDeclaration, udtMembers); - } - } -} diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/NewContentAggregator.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/NewContentAggregator.cs index 75c1417f9f..6bc74c875e 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/NewContentAggregator.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/NewContentAggregator.cs @@ -6,9 +6,6 @@ namespace Rubberduck.Refactorings.EncapsulateField { public enum NewContentType { - ImplementsDeclaration, - WithEventsDeclaration, - EnumerationTypeDeclaration, UserDefinedTypeDeclaration, DeclarationBlock, CodeSectionBlock, diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ArrayCandidate.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ArrayCandidate.cs index b7c0b592e4..807232efed 100644 --- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ArrayCandidate.cs +++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ArrayCandidate.cs @@ -15,8 +15,8 @@ public interface IArrayCandidate : IEncapsulateFieldCandidate public class ArrayCandidate : EncapsulateFieldCandidate, IArrayCandidate { private string _subscripts; - public ArrayCandidate(Declaration declaration, Func parameterNameBuilder) - :base(declaration, parameterNameBuilder) + public ArrayCandidate(Declaration declaration) + :base(declaration) { ImplementLet = false; ImplementSet = false; diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidate.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidate.cs index 7a7d3cf74c..d6ef2bb681 100644 --- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidate.cs +++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidate.cs @@ -4,7 +4,6 @@ using Rubberduck.Refactorings.Common; using Rubberduck.Refactorings.EncapsulateField.Extensions; using Rubberduck.VBEditor; -using System; using System.Collections.Generic; namespace Rubberduck.Refactorings.EncapsulateField @@ -59,18 +58,10 @@ public class EncapsulateFieldCandidate : IEncapsulateFieldCandidate protected EncapsulationIdentifiers _fieldAndProperty; private string _rhsParameterIdentifierName; -//<<<<<<< HEAD - public EncapsulateFieldCandidate(Declaration declaration, Func parameterNameBuilder) - //{ - //_target = declaration; - //_parameterNameBuilder = parameterNameBuilder; -//======= - //public EncapsulateFieldCandidate(Declaration declaration, IValidateVBAIdentifiers identifierValidator) + public EncapsulateFieldCandidate(Declaration declaration) { _target = declaration; - //NameValidator = identifierValidator; _rhsParameterIdentifierName = Resources.Refactorings.Refactorings.CodeBuilder_DefaultPropertyRHSParam; -//>>>>>>> rubberduck-vba/next _fieldAndProperty = new EncapsulationIdentifiers(declaration.IdentifierName); IdentifierName = declaration.IdentifierName; @@ -86,8 +77,7 @@ public EncapsulateFieldCandidate(Declaration declaration, Func p ImplementSet = false; if (_target.IsEnumField() && _target.AsTypeDeclaration.HasPrivateAccessibility()) { - //5.3.1 The declared type of a function declaration - //may not be a private enum name. + //5.3.1 The declared type of a function declaration may not be a private enum. PropertyAsTypeName = Tokens.Long; } else if (_target.AsTypeName.Equals(Tokens.Variant) diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidateFactory.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidateFactory.cs index c3c7808dfa..3a41906e91 100644 --- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidateFactory.cs +++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidateFactory.cs @@ -26,7 +26,7 @@ public IEncapsulateFieldCandidate Create(Declaration target) { if (target.IsUserDefinedType()) { - var udtField = new UserDefinedTypeCandidate(target, _codeBuilder.BuildPropertyRhsParameterName) as IUserDefinedTypeCandidate; + var udtField = new UserDefinedTypeCandidate(target) as IUserDefinedTypeCandidate; var udtMembers = _declarationFinderProvider.DeclarationFinder .UserDeclarations(DeclarationType.UserDefinedTypeMember) @@ -34,7 +34,7 @@ public IEncapsulateFieldCandidate Create(Declaration target) foreach (var udtMemberDeclaration in udtMembers) { - var candidateUDTMember = new UserDefinedTypeMemberCandidate(Create(udtMemberDeclaration), udtField, _codeBuilder.BuildPropertyRhsParameterName) as IUserDefinedTypeMemberCandidate; + var candidateUDTMember = new UserDefinedTypeMemberCandidate(Create(udtMemberDeclaration), udtField) as IUserDefinedTypeMemberCandidate; udtField.AddMember(candidateUDTMember); } @@ -50,11 +50,11 @@ public IEncapsulateFieldCandidate Create(Declaration target) } else if (target.IsArray) { - var arrayCandidate = new ArrayCandidate(target, _codeBuilder.BuildPropertyRhsParameterName); + var arrayCandidate = new ArrayCandidate(target); return arrayCandidate; } - return new EncapsulateFieldCandidate(target, _codeBuilder.BuildPropertyRhsParameterName); + return new EncapsulateFieldCandidate(target); } } } diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeCandidate.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeCandidate.cs index 4e984f19ac..c2a65a2c30 100644 --- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeCandidate.cs +++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeCandidate.cs @@ -1,7 +1,6 @@ using Rubberduck.Parsing.Grammar; using Rubberduck.Parsing.Symbols; using Rubberduck.Refactorings.Common; -using System; using System.Collections.Generic; namespace Rubberduck.Refactorings.EncapsulateField @@ -17,8 +16,8 @@ public interface IUserDefinedTypeCandidate : IEncapsulateFieldCandidate public class UserDefinedTypeCandidate : EncapsulateFieldCandidate, IUserDefinedTypeCandidate { - public UserDefinedTypeCandidate(Declaration declaration, Func parameterNameBuilder) - : base(declaration, parameterNameBuilder) + public UserDefinedTypeCandidate(Declaration declaration) + : base(declaration) { TypeDeclarationIsPrivate = declaration.HasPrivateAccessibility(); } diff --git a/Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT.cs b/Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT.cs deleted file mode 100644 index f64202ad68..0000000000 --- a/Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT.cs +++ /dev/null @@ -1,170 +0,0 @@ -using Rubberduck.Parsing.Grammar; -using Rubberduck.Parsing.Symbols; -using Rubberduck.Common; -using Rubberduck.SmartIndenter; -using Rubberduck.VBEditor; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Rubberduck.Refactorings.EncapsulateField.Extensions; -using Rubberduck.Resources; - -namespace Rubberduck.Refactorings.EncapsulateField -{ - public interface IObjectStateUDT : IEncapsulateFieldRefactoringElement - { - string TypeIdentifier { set; get; } - string FieldIdentifier { set; get; } - string TypeDeclarationBlock(IIndenter indenter = null); - string FieldDeclarationBlock { get; } - void AddMembers(IEnumerable fields); - bool IsExistingDeclaration { get; } - Declaration AsTypeDeclaration { get; } - bool IsSelected { set; get; } - IEnumerable ExistingMembers { get; } - } - - //ObjectStateUDT can be an existing UDT (Private only) selected by the user, or a - //newly inserted declaration - public class ObjectStateUDT : IObjectStateUDT - { - private static string _defaultNewFieldName = "this"; - private List _convertedMembers; - - private readonly IUserDefinedTypeCandidate _wrappedUDT; - private int _hashCode; - - public ObjectStateUDT(IUserDefinedTypeCandidate udt) - : this(udt.Declaration.AsTypeName) - { - if (!udt.TypeDeclarationIsPrivate) - { - throw new ArgumentException(); - } - - FieldIdentifier = udt.IdentifierName; - _wrappedUDT = udt; - _hashCode = ($"{_qmn.Name}.{_wrappedUDT.IdentifierName}").GetHashCode(); - } - - public ObjectStateUDT(QualifiedModuleName qmn) - :this($"T{qmn.ComponentName.CapitalizeFirstLetter()}") - { - QualifiedModuleName = qmn; - } - - private ObjectStateUDT(string typeIdentifier) - { - FieldIdentifier = _defaultNewFieldName; - TypeIdentifier = typeIdentifier; - _convertedMembers = new List(); - } - - public string IdentifierName => _wrappedUDT?.IdentifierName ?? FieldIdentifier; - - public string AsTypeName => _wrappedUDT?.AsTypeName ?? TypeIdentifier; - - private bool _isSelected; - public bool IsSelected - { - set - { - _isSelected = value; - if (_wrappedUDT != null) - { - _wrappedUDT.IsSelectedObjectStateUDT = value; - } - - if (_isSelected && IsExistingDeclaration) - { - _wrappedUDT.EncapsulateFlag = false; - } - } - get => _isSelected; - } - - public IEnumerable ExistingMembers - { - get - { - if (IsExistingDeclaration) - { - return _wrappedUDT.Members; - } - return Enumerable.Empty(); - } - } - - - private QualifiedModuleName _qmn; - public QualifiedModuleName QualifiedModuleName - { - set => _qmn = value; - get => _wrappedUDT?.QualifiedModuleName ?? _qmn; - } - - public string TypeIdentifier { set; get; } - - public bool IsExistingDeclaration => _wrappedUDT != null; - - public Declaration AsTypeDeclaration => _wrappedUDT?.Declaration.AsTypeDeclaration; - - public string FieldIdentifier { set; get; } - - public void AddMembers(IEnumerable fields) - { - _convertedMembers = new List(); - if (IsExistingDeclaration) - { - foreach (var member in _wrappedUDT.Members) - { - var convertedMember = new ConvertToUDTMember(member, this) { EncapsulateFlag = false }; - _convertedMembers.Add(convertedMember); - } - } - _convertedMembers.AddRange(fields); - } - - public string FieldDeclarationBlock - => $"{Accessibility.Private} {IdentifierName} {Tokens.As} {AsTypeName}"; - - public string TypeDeclarationBlock(IIndenter indenter = null) - { - if (indenter != null) - { - return string.Join(Environment.NewLine, indenter?.Indent(BlockLines(Accessibility.Private) ?? BlockLines(Accessibility.Private), true)); - } - return string.Join(Environment.NewLine, BlockLines(Accessibility.Private)); - } - - public override bool Equals(object obj) - { - if (obj is IObjectStateUDT stateUDT && stateUDT.FieldIdentifier == FieldIdentifier) - { - return true; - } - if (obj is IEncapsulateFieldRefactoringElement fd && fd.IdentifierName == IdentifierName) - { - return true; - } - return false; - } - - public override int GetHashCode() => _hashCode; - - private IEnumerable BlockLines(Accessibility accessibility) - { - var blockLines = new List(); - - blockLines.Add($"{accessibility.TokenString()} {Tokens.Type} {TypeIdentifier}"); - - _convertedMembers.ForEach(m => blockLines.Add($"{m.UDTMemberDeclaration}")); - - blockLines.Add($"{Tokens.End} {Tokens.Type}"); - - return blockLines; - } - } -} diff --git a/Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT/ObjectStateUDT.cs b/Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT/ObjectStateUDT.cs index fd260aaadb..e081c733a6 100644 --- a/Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT/ObjectStateUDT.cs +++ b/Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT/ObjectStateUDT.cs @@ -31,7 +31,7 @@ public interface IObjectStateUDT : IEncapsulateFieldRefactoringElement /// public class ObjectStateUDT : IObjectStateUDT { - private static string _defaultNewFieldName = RubberduckUI.EncapsulateField_DefaultObjectStateUDTFieldName; + private static string _defaultNewFieldName = "this"; private readonly IUserDefinedTypeCandidate _wrappedUDTField; private readonly int _hashCode; diff --git a/RubberduckTests/CodeBuilderTests.cs b/RubberduckTests/CodeBuilderTests.cs index 9e0c415c6b..12efac7b60 100644 --- a/RubberduckTests/CodeBuilderTests.cs +++ b/RubberduckTests/CodeBuilderTests.cs @@ -1,4 +1,5 @@ using NUnit.Framework; +using Rubberduck.Common; using Rubberduck.Parsing.Symbols; using Rubberduck.Refactorings; using RubberduckTests.Mocks; @@ -193,7 +194,7 @@ End Enum declarationType, testParams, PropertyLetBlockFromPrototypeTest); - StringAssert.Contains($"Property Let {testParams.Identifier}(ByVal {Param(testParams.Identifier)} As {typeName})", result); + StringAssert.Contains($"Property Let {testParams.Identifier}(ByVal RHS As {typeName})", result); } [TestCase("fizz", DeclarationType.Variable, "Variant")] diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldTests.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldTests.cs index 44b4fa8822..18df036206 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldTests.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldTests.cs @@ -403,7 +403,7 @@ public void EncapsulatePrivateField_DefaultsAsUDT() var actualCode = Support.RefactoredCode(inputCode.ToCodeString(), presenterAction); StringAssert.Contains("Fizz As Integer", actualCode); - StringAssert.Contains($"this As {Support.StateUDTDefaultType}", actualCode); + StringAssert.Contains($"this As {Support.StateUDTDefaultTypeName}", actualCode); StringAssert.Contains($"this.Fizz = {Support.RHSIdentifier}", actualCode); } @@ -425,8 +425,7 @@ Sub Bar(ByVal name As Integer) var presenterAction = Support.SetParametersForSingleTarget("fizz", "Name"); - var validator = EncapsulateFieldValidationsProvider.NameOnlyValidator(NameValidators.Default); - var enapsulationIdentifiers = new EncapsulationIdentifiers("fizz", validator) { Property = "Name" }; + var enapsulationIdentifiers = new EncapsulationIdentifiers("fizz") { Property = "Name" }; var actualCode = Support.RefactoredCode(inputCode.ToCodeString(), presenterAction); StringAssert.AreEqualIgnoringCase(enapsulationIdentifiers.TargetFieldName, "fizz"); diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberTests.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberTests.cs index 843794e7ea..782050ef4f 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberTests.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberTests.cs @@ -5,7 +5,6 @@ using Rubberduck.Parsing.VBA; using Rubberduck.Refactorings; using Rubberduck.Refactorings.EncapsulateField; -using Rubberduck.Refactorings.EncapsulateFieldUseBackingField; using Rubberduck.Refactorings.EncapsulateFieldUseBackingUDTMember; using RubberduckTests.Mocks; using System.Collections.Generic; diff --git a/RubberduckTests/Refactoring/ReplacePrivateUDTMemberReferences/ReplacePrivateUDTMemberReferencesRefactoringActionTests.cs b/RubberduckTests/Refactoring/ReplacePrivateUDTMemberReferences/ReplacePrivateUDTMemberReferencesRefactoringActionTests.cs index 07203596d0..79ba15b1e2 100644 --- a/RubberduckTests/Refactoring/ReplacePrivateUDTMemberReferences/ReplacePrivateUDTMemberReferencesRefactoringActionTests.cs +++ b/RubberduckTests/Refactoring/ReplacePrivateUDTMemberReferences/ReplacePrivateUDTMemberReferencesRefactoringActionTests.cs @@ -15,11 +15,12 @@ namespace RubberduckTests.ReplacePrivateUDTMemberReferences [TestFixture] public class ReplacePrivateUDTMemberReferencesRefactoringActionTests : RefactoringActionTestBase { - [Test] + [TestCase("TheFirst", "TheSecond")] + [TestCase("afirst", "asecond")] //respects casing [Category("Refactorings")] [Category("Encapsulate Field")] [Category(nameof(ReplacePrivateUDTMemberReferencesRefactoringAction))] - public void RenameFieldReferences() + public void RenameFieldReferences(string firstValueRefReplacement, string secondValueRefReplacement) { string inputCode = $@" @@ -43,16 +44,16 @@ End Sub var testParam1 = new PrivateUDTExpressions("myBazz", "FirstValue") { - InternalName = "TheFirst", + InternalName = firstValueRefReplacement, }; var testParam2 = new PrivateUDTExpressions("myBazz", "SecondValue") { - InternalName = "TheSecond", + InternalName = secondValueRefReplacement, }; var results = RefactoredCode(vbe.Object, state => TestModel(state, false, testParam1, testParam2)); - StringAssert.Contains(" TheFirst = newValue", results[MockVbeBuilder.TestModuleName]); - StringAssert.Contains(" TheSecond = newValue", results[MockVbeBuilder.TestModuleName]); + StringAssert.Contains($" {firstValueRefReplacement} = newValue", results[MockVbeBuilder.TestModuleName]); + StringAssert.Contains($" {secondValueRefReplacement} = newValue", results[MockVbeBuilder.TestModuleName]); } [Test] From 8b59aa52cd2f7671c073f8f26c50b11a977232e4 Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Sun, 13 Sep 2020 16:17:12 -0700 Subject: [PATCH 36/67] Add PropertyAttributeSetsGenerator Extracts the PropertyAttributeSet generation responsibility from the various IEncapuslateFieldCandidate classes. Some removal of unused functions/properties and refactoring of IEncapsulateFielCandidate classes. --- .../EncapsulateFieldCodeBuilder.cs | 8 +- .../EncapsulateFieldInsertNewCodeModel.cs | 4 + ...lateFieldInsertNewCodeRefactoringAction.cs | 58 +++--- .../FieldCandidates/ArrayCandidate.cs | 3 - .../EncapsulateFieldAsUDTMemberCandidate.cs | 82 +-------- .../EncapsulateFieldCandidate.cs | 96 +++------- .../UserDefinedTypeCandidate.cs | 75 +------- .../UserDefinedTypeMemberCandidate.cs | 169 ++++-------------- .../PropertyAttributeSetsGenerator.cs | 142 +++++++++++++++ .../PropertyAttributeSetsGeneratorTests.cs | 129 +++++++++++++ 10 files changed, 371 insertions(+), 395 deletions(-) create mode 100644 Rubberduck.Refactorings/EncapsulateField/PropertyAttributeSetsGenerator.cs create mode 100644 RubberduckTests/Refactoring/EncapsulateField/PropertyAttributeSetsGeneratorTests.cs diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldCodeBuilder.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldCodeBuilder.cs index fec942ff48..ec4237b70e 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldCodeBuilder.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldCodeBuilder.cs @@ -30,18 +30,18 @@ public EncapsulateFieldCodeBuilder(ICodeBuilder codeBuilder) string propertyLet = null; string propertySet = null; - if (propertyAttributes.GenerateLetter) + if (propertyAttributes.GeneratePropertyLet) { - var letterContent = $"{FourSpaces}{propertyAttributes.BackingField} = {propertyAttributes.ParameterName}"; + var letterContent = $"{FourSpaces}{propertyAttributes.BackingField} = {propertyAttributes.RHSParameterIdentifier}"; if (!_codeBuilder.TryBuildPropertyLetCodeBlock(propertyAttributes.Declaration, propertyAttributes.PropertyName, out propertyLet, content: letterContent)) { throw new ArgumentException(); } } - if (propertyAttributes.GenerateSetter) + if (propertyAttributes.GeneratePropertySet) { - var setterContent = $"{FourSpaces}{Tokens.Set} {propertyAttributes.BackingField} = {propertyAttributes.ParameterName}"; + var setterContent = $"{FourSpaces}{Tokens.Set} {propertyAttributes.BackingField} = {propertyAttributes.RHSParameterIdentifier}"; if (!_codeBuilder.TryBuildPropertySetCodeBlock(propertyAttributes.Declaration, propertyAttributes.PropertyName, out propertySet, content: setterContent)) { throw new ArgumentException(); diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeModel.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeModel.cs index bbd42cf6b1..fc77ba75dc 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeModel.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeModel.cs @@ -20,6 +20,10 @@ public EncapsulateFieldInsertNewCodeModel(IEnumerable !ObjectStateUDTField?.IsExistingDeclaration ?? false; + + public IObjectStateUDT ObjectStateUDTField { set; get; } + public IReadOnlyCollection SelectedFieldCandidates { get; } } } diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeRefactoringAction.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeRefactoringAction.cs index dd8119f4b9..dce344ba99 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeRefactoringAction.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeRefactoringAction.cs @@ -13,26 +13,32 @@ namespace Rubberduck.Refactorings.EncapsulateFieldInsertNewCode { public class EncapsulateFieldInsertNewCodeRefactoringAction : CodeOnlyRefactoringActionBase { - private readonly static string _doubleSpace = $"{Environment.NewLine}{Environment.NewLine}"; - private int? _codeSectionStartIndex; private readonly IDeclarationFinderProvider _declarationFinderProvider; - private readonly IEncapsulateFieldCodeBuilderFactory _encapsulateFieldCodeBuilderFactory; + private readonly IPropertyAttributeSetsGenerator _propertyAttributeSetsGenerator; + private readonly IEncapsulateFieldCodeBuilder _encapsulateFieldCodeBuilder; + public EncapsulateFieldInsertNewCodeRefactoringAction( IDeclarationFinderProvider declarationFinderProvider, IRewritingManager rewritingManager, + IPropertyAttributeSetsGenerator propertyAttributeSetsGenerator, IEncapsulateFieldCodeBuilderFactory encapsulateFieldCodeBuilderFactory) : base(rewritingManager) { _declarationFinderProvider = declarationFinderProvider; - _encapsulateFieldCodeBuilderFactory = encapsulateFieldCodeBuilderFactory; + _propertyAttributeSetsGenerator = propertyAttributeSetsGenerator; + _encapsulateFieldCodeBuilder = encapsulateFieldCodeBuilderFactory.Create(); } public override void Refactor(EncapsulateFieldInsertNewCodeModel model, IRewriteSession rewriteSession) { - _codeSectionStartIndex = _declarationFinderProvider.DeclarationFinder - .Members(model.QualifiedModuleName).Where(m => m.IsMember()) - .OrderBy(c => c.Selection) - .FirstOrDefault()?.Context.Start.TokenIndex; + if (model.CreateNewObjectStateUDT) + { + var objectStateFieldDeclaration = _encapsulateFieldCodeBuilder.BuildObjectStateFieldDeclaration(model.ObjectStateUDTField); + model.NewContentAggregator.AddNewContent(NewContentType.DeclarationBlock, objectStateFieldDeclaration); + + var objectStateTypeDeclarationBlock = _encapsulateFieldCodeBuilder.BuildUserDefinedTypeDeclaration(model.ObjectStateUDTField, model.SelectedFieldCandidates); + model.NewContentAggregator.AddNewContent(NewContentType.UserDefinedTypeDeclaration, objectStateTypeDeclarationBlock); + } LoadNewPropertyBlocks(model, rewriteSession); @@ -41,14 +47,16 @@ public override void Refactor(EncapsulateFieldInsertNewCodeModel model, IRewrite model.NewContentAggregator = null; } - public void LoadNewPropertyBlocks(EncapsulateFieldInsertNewCodeModel model, IRewriteSession rewriteSession) + private void LoadNewPropertyBlocks(EncapsulateFieldInsertNewCodeModel model, IRewriteSession rewriteSession) { - var builder = _encapsulateFieldCodeBuilderFactory.Create(); - foreach (var propertyAttributes in model.SelectedFieldCandidates.SelectMany(f => f.PropertyAttributeSets)) + var propAttributeSets = model.SelectedFieldCandidates + .SelectMany(f => _propertyAttributeSetsGenerator.GeneratePropertyAttributeSets(f)).ToList(); + + foreach (var propertyAttributeSet in propAttributeSets) { - Debug.Assert(propertyAttributes.Declaration.DeclarationType.HasFlag(DeclarationType.Variable) || propertyAttributes.Declaration.DeclarationType.HasFlag(DeclarationType.UserDefinedTypeMember)); + Debug.Assert(propertyAttributeSet.Declaration.DeclarationType.HasFlag(DeclarationType.Variable) || propertyAttributeSet.Declaration.DeclarationType.HasFlag(DeclarationType.UserDefinedTypeMember)); - var (Get, Let, Set) = builder.BuildPropertyBlocks(propertyAttributes); + var (Get, Let, Set) = _encapsulateFieldCodeBuilder.BuildPropertyBlocks(propertyAttributeSet); var blocks = new List() { Get, Let, Set }; blocks.ForEach(s => model.NewContentAggregator.AddNewContent(NewContentType.CodeSectionBlock, s)); @@ -57,13 +65,16 @@ public void LoadNewPropertyBlocks(EncapsulateFieldInsertNewCodeModel model, IRew private void InsertBlocks(EncapsulateFieldInsertNewCodeModel model, IRewriteSession rewriteSession) { + var newDeclarationSectionBlock = model.NewContentAggregator.RetrieveBlock(NewContentType.UserDefinedTypeDeclaration, NewContentType.DeclarationBlock, NewContentType.CodeSectionBlock); if (string.IsNullOrEmpty(newDeclarationSectionBlock)) { return; } - var allNewContent = string.Join(_doubleSpace, new string[] { newDeclarationSectionBlock }); + var doubleSpace = $"{Environment.NewLine}{Environment.NewLine}"; + + var allNewContent = string.Join(doubleSpace, new string[] { newDeclarationSectionBlock }); var previewMarker = model.NewContentAggregator.RetrieveBlock(RubberduckUI.EncapsulateField_PreviewMarker); if (!string.IsNullOrEmpty(previewMarker)) @@ -73,22 +84,17 @@ private void InsertBlocks(EncapsulateFieldInsertNewCodeModel model, IRewriteSess var rewriter = rewriteSession.CheckOutModuleRewriter(model.QualifiedModuleName); - InsertBlock(allNewContent, _codeSectionStartIndex, rewriter); - } - - private static void InsertBlock(string content, int? insertionIndex, IModuleRewriter rewriter) - { - if (string.IsNullOrEmpty(content)) - { - return; - } + var codeSectionStartIndex = _declarationFinderProvider.DeclarationFinder + .Members(model.QualifiedModuleName).Where(m => m.IsMember()) + .OrderBy(c => c.Selection) + .FirstOrDefault()?.Context.Start.TokenIndex; - if (insertionIndex.HasValue) + if (codeSectionStartIndex.HasValue) { - rewriter.InsertBefore(insertionIndex.Value, $"{content}{_doubleSpace}"); + rewriter.InsertBefore(codeSectionStartIndex.Value, $"{allNewContent}{doubleSpace}"); return; } - rewriter.InsertBefore(rewriter.TokenStream.Size - 1, $"{_doubleSpace}{content}"); + rewriter.InsertBefore(rewriter.TokenStream.Size - 1, $"{doubleSpace}{allNewContent}"); } } } diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ArrayCandidate.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ArrayCandidate.cs index 807232efed..6ca449396f 100644 --- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ArrayCandidate.cs +++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ArrayCandidate.cs @@ -49,9 +49,6 @@ public override bool TryValidateEncapsulationAttributes(out string errorMessage) public string UDTMemberDeclaration => $"{PropertyIdentifier}({_subscripts}) {Tokens.As} {Declaration.AsTypeName}"; - protected override string IdentifierForLocalReferences(IdentifierReference idRef) - => BackingIdentifier; - public bool HasExternalRedimOperation(out string errorMessage) { errorMessage = string.Empty; diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldAsUDTMemberCandidate.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldAsUDTMemberCandidate.cs index 3237eba8ec..db8459345f 100644 --- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldAsUDTMemberCandidate.cs +++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldAsUDTMemberCandidate.cs @@ -1,16 +1,12 @@ -using System.Collections.Generic; -using System.Linq; -using Rubberduck.Common; -using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.Symbols; using Rubberduck.VBEditor; namespace Rubberduck.Refactorings.EncapsulateField { - public interface IEncapsulateFieldAsUDTMemberCandidate : IEncapsulateFieldCandidate { - string UDTMemberDeclaration { get; } IObjectStateUDT ObjectStateUDT { set; get; } + IEncapsulateFieldCandidate WrappedCandidate { get; } } public class EncapsulateFieldAsUDTMemberCandidate : IEncapsulateFieldAsUDTMemberCandidate @@ -27,17 +23,7 @@ public EncapsulateFieldAsUDTMemberCandidate(IEncapsulateFieldCandidate candidate _hashCode = _uniqueID.GetHashCode(); } - public virtual string UDTMemberDeclaration - { - get - { - if (_wrapped is IArrayCandidate array) - { - return array.UDTMemberDeclaration; - } - return $"{BackingIdentifier} As {_wrapped.AsTypeName}"; - } - } + public IEncapsulateFieldCandidate WrappedCandidate => _wrapped; private IObjectStateUDT _objectStateUDT; public IObjectStateUDT ObjectStateUDT @@ -94,35 +80,12 @@ public bool IsReadOnly get => _wrapped.IsReadOnly; } - public string ParameterName => _wrapped.ParameterName; - public IEncapsulateFieldConflictFinder ConflictFinder { set => _wrapped.ConflictFinder = value; get => _wrapped.ConflictFinder; } - private string AccessorInProperty - { - get - { - if (_wrapped is IUserDefinedTypeMemberCandidate udtm) - { - return $"{ObjectStateUDT.FieldIdentifier}.{udtm.UDTField.PropertyIdentifier}.{BackingIdentifier}"; - } - return $"{ObjectStateUDT.FieldIdentifier}.{BackingIdentifier}"; - } - } - - public string IdentifierForReference(IdentifierReference idRef) - { - if (idRef.QualifiedModuleName != QualifiedModuleName) - { - return PropertyIdentifier; - } - return BackingIdentifier; - } - public string IdentifierName => _wrapped.IdentifierName; public QualifiedModuleName QualifiedModuleName => _wrapped.QualifiedModuleName; @@ -147,27 +110,6 @@ public bool TryValidateEncapsulationAttributes(out string errorMessage) return ConflictFinder.TryValidateEncapsulationAttributes(this, out errorMessage); } - public IEnumerable PropertyAttributeSets - { - get - { - var modifiedSets = new List(); - var sets = _wrapped.PropertyAttributeSets; - for (var idx = 0; idx < sets.Count(); idx++) - { - var attributeSet = sets.ElementAt(idx); - var fields = attributeSet.BackingField.Split(new char[] { '.' }); - - attributeSet.BackingField = fields.Count() > 1 - ? $"{ObjectStateUDT.FieldIdentifier}.{attributeSet.BackingField.CapitalizeFirstLetter()}" - : $"{ObjectStateUDT.FieldIdentifier}.{attributeSet.PropertyName.CapitalizeFirstLetter()}"; - - modifiedSets.Add(attributeSet); - } - return modifiedSets; - } - } - public override bool Equals(object obj) { return obj != null @@ -179,23 +121,5 @@ public override bool Equals(object obj) private static string BuildUniqueID(IEncapsulateFieldCandidate candidate, IObjectStateUDT field) => $"{candidate.QualifiedModuleName.Name}.{field.IdentifierName}.{candidate.IdentifierName}"; - - private PropertyAttributeSet AsPropertyAttributeSet - { - get - { - return new PropertyAttributeSet() - { - PropertyName = PropertyIdentifier, - BackingField = AccessorInProperty, - AsTypeName = PropertyAsTypeName, - ParameterName = ParameterName, - GenerateLetter = ImplementLet, - GenerateSetter = ImplementSet, - UsesSetAssignment = Declaration.IsObject, - IsUDTProperty = true - }; - } - } } } diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidate.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidate.cs index d6ef2bb681..b5294fc02a 100644 --- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidate.cs +++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidate.cs @@ -1,26 +1,11 @@ -using Antlr4.Runtime; -using Rubberduck.Parsing.Grammar; +using Rubberduck.Parsing.Grammar; using Rubberduck.Parsing.Symbols; using Rubberduck.Refactorings.Common; using Rubberduck.Refactorings.EncapsulateField.Extensions; using Rubberduck.VBEditor; -using System.Collections.Generic; namespace Rubberduck.Refactorings.EncapsulateField { - public struct PropertyAttributeSet - { - public string PropertyName { get; set; } - public string BackingField { get; set; } - public string AsTypeName { get; set; } - public string ParameterName { get; set; } - public bool GenerateLetter { get; set; } - public bool GenerateSetter { get; set; } - public bool UsesSetAssignment { get; set; } - public bool IsUDTProperty { get; set; } - public Declaration Declaration { get; set; } - } - public interface IEncapsulateFieldRefactoringElement { string IdentifierName { get; } @@ -41,11 +26,8 @@ public interface IEncapsulateFieldCandidate : IEncapsulateFieldRefactoringElemen bool ImplementLet { get; } bool ImplementSet { get; } bool IsReadOnly { set; get; } - string ParameterName { get; } IEncapsulateFieldConflictFinder ConflictFinder { set; get; } bool TryValidateEncapsulationAttributes(out string errorMessage); - string IdentifierForReference(IdentifierReference idRef); - IEnumerable PropertyAttributeSets { get; } } public class EncapsulateFieldCandidate : IEncapsulateFieldCandidate @@ -92,8 +74,6 @@ public EncapsulateFieldCandidate(Declaration declaration) } } - protected Dictionary IdentifierReplacements { get; } = new Dictionary(); - public Declaration Declaration => _target; public string AsTypeName => _target.AsTypeName; @@ -140,34 +120,8 @@ public virtual bool EncapsulateFlag } public virtual bool IsReadOnly { set; get; } - public bool CanBeReadWrite { set; get; } - - public override bool Equals(object obj) - { - return obj != null - && obj is IEncapsulateFieldCandidate efc - && $"{efc.QualifiedModuleName.Name}.{efc.IdentifierName}" == _uniqueID; - } - - public override int GetHashCode() => _hashCode; - - public override string ToString() - =>$"({TargetID}){Declaration.ToString()}"; - - protected string IdentifierInNewProperties - => BackingIdentifier; - - public string IdentifierForReference(IdentifierReference idRef) - { - if (idRef.QualifiedModuleName != QualifiedModuleName) - { - return PropertyIdentifier; - } - return IdentifierForLocalReferences(idRef); - } - protected virtual string IdentifierForLocalReferences(IdentifierReference idRef) - => PropertyIdentifier; + public bool CanBeReadWrite { set; get; } public string PropertyIdentifier { @@ -213,38 +167,30 @@ public string IdentifierName set => _identifierName = value; } - public virtual string ReferenceQualifier { set; get; } - - public string ParameterName => _rhsParameterIdentifierName; - private bool _implLet; - public bool ImplementLet { get => !IsReadOnly && _implLet; set => _implLet = value; } + public bool ImplementLet + { + get => !IsReadOnly && _implLet; + set => _implLet = value; + } private bool _implSet; - public bool ImplementSet { get => !IsReadOnly && _implSet; set => _implSet = value; } - - public EncapsulateFieldStrategy EncapsulateFieldStrategy { set; get; } = EncapsulateFieldStrategy.UseBackingFields; - - public virtual IEnumerable PropertyAttributeSets - => new List() { AsPropertyAttributeSet }; + public bool ImplementSet + { + get => !IsReadOnly && _implSet; + set => _implSet = value; + } - protected virtual PropertyAttributeSet AsPropertyAttributeSet + public override bool Equals(object obj) { - get - { - return new PropertyAttributeSet() - { - PropertyName = PropertyIdentifier, - BackingField = IdentifierInNewProperties, - AsTypeName = PropertyAsTypeName, - ParameterName = ParameterName, - GenerateLetter = ImplementLet, - GenerateSetter = ImplementSet, - UsesSetAssignment = Declaration.IsObject, - IsUDTProperty = false, - Declaration = Declaration - }; - } + return obj != null + && obj is IEncapsulateFieldCandidate efc + && $"{efc.QualifiedModuleName.Name}.{efc.IdentifierName}" == _uniqueID; } + + public override int GetHashCode() => _hashCode; + + public override string ToString() + => $"({TargetID}){Declaration.ToString()}"; } } diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeCandidate.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeCandidate.cs index c2a65a2c30..9e9b442731 100644 --- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeCandidate.cs +++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeCandidate.cs @@ -1,5 +1,4 @@ -using Rubberduck.Parsing.Grammar; -using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.Symbols; using Rubberduck.Refactorings.Common; using System.Collections.Generic; @@ -18,9 +17,7 @@ public class UserDefinedTypeCandidate : EncapsulateFieldCandidate, IUserDefinedT { public UserDefinedTypeCandidate(Declaration declaration) : base(declaration) - { - TypeDeclarationIsPrivate = declaration.HasPrivateAccessibility(); - } + {} public void AddMember(IUserDefinedTypeMemberCandidate member) { @@ -30,12 +27,8 @@ public void AddMember(IUserDefinedTypeMemberCandidate member) private List _udtMembers = new List(); public IEnumerable Members => _udtMembers; - private bool _isPrivate; public bool TypeDeclarationIsPrivate - { - set => _isPrivate = value; - get => Declaration.AsTypeDeclaration?.HasPrivateAccessibility() ?? false; - } + => Declaration.AsTypeDeclaration?.HasPrivateAccessibility() ?? false; public bool IsSelectedObjectStateUDT { set; get; } @@ -47,18 +40,18 @@ public override string BackingIdentifier set => _fieldAndProperty.Field = value; } - private IEncapsulateFieldConflictFinder _conflictsValidator; + private IEncapsulateFieldConflictFinder _conflictsFinder; public override IEncapsulateFieldConflictFinder ConflictFinder { set { - _conflictsValidator = value; + _conflictsFinder = value; foreach (var member in Members) { member.ConflictFinder = value; } } - get => _conflictsValidator; + get => _conflictsFinder; } private bool _isReadOnly; @@ -91,16 +84,6 @@ public override bool EncapsulateFlag get => base.EncapsulateFlag; } - protected override string IdentifierForLocalReferences(IdentifierReference idRef) - { - if (idRef.Context.Parent.Parent is VBAParser.WithStmtContext wsc) - { - return BackingIdentifier; - } - - return TypeDeclarationIsPrivate ? BackingIdentifier : PropertyIdentifier; - } - public override bool Equals(object obj) { if (obj is IUserDefinedTypeCandidate udt) @@ -114,51 +97,5 @@ public override int GetHashCode() { return base.GetHashCode(); } - - public override IEnumerable PropertyAttributeSets - { - get - { - if (TypeDeclarationIsPrivate) - { - var specs = new List(); - foreach (var member in Members) - { - var sets = member.PropertyAttributeSets; - var modifiedSets = new List(); - PropertyAttributeSet newSet; - foreach (var set in sets) - { - newSet = set; - newSet.BackingField = $"{BackingIdentifier}.{set.BackingField}"; - modifiedSets.Add(newSet); - } - specs.AddRange(modifiedSets); - } - return specs; - } - return new List() { AsPropertyAttributeSet }; - } - } - - protected override PropertyAttributeSet AsPropertyAttributeSet - { - get - { - return new PropertyAttributeSet() - { - PropertyName = PropertyIdentifier, - BackingField = IdentifierInNewProperties, - AsTypeName = PropertyAsTypeName, - ParameterName = ParameterName, - GenerateLetter = ImplementLet, - GenerateSetter = ImplementSet, - UsesSetAssignment = Declaration.IsObject, - IsUDTProperty = true, - Declaration = Declaration - }; - } - } - } } diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeMemberCandidate.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeMemberCandidate.cs index 40835b7d1f..3dafc2200a 100644 --- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeMemberCandidate.cs +++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeMemberCandidate.cs @@ -1,48 +1,32 @@ -using Rubberduck.Parsing; -using Rubberduck.Parsing.Grammar; -using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.Symbols; using Rubberduck.VBEditor; -using System; -using System.Collections.Generic; -using System.Linq; namespace Rubberduck.Refactorings.EncapsulateField { public interface IUserDefinedTypeMemberCandidate : IEncapsulateFieldCandidate { IUserDefinedTypeCandidate UDTField { get; } - PropertyAttributeSet AsPropertyGeneratorSpec { get; } - IEnumerable FieldContextReferences { get; } IEncapsulateFieldCandidate WrappedCandidate { get; } } public class UserDefinedTypeMemberCandidate : IUserDefinedTypeMemberCandidate { - private int _hashCode; - private readonly string _uniqueID; - private string _rhsParameterIdentifierName; + private readonly int _hashCode; + public UserDefinedTypeMemberCandidate(IEncapsulateFieldCandidate candidate, IUserDefinedTypeCandidate udtField) { - _wrappedCandidate = candidate; - _rhsParameterIdentifierName = Resources.Refactorings.Refactorings.CodeBuilder_DefaultPropertyRHSParam; + WrappedCandidate = candidate; UDTField = udtField; PropertyIdentifier = IdentifierName; BackingIdentifier = IdentifierName; - _uniqueID = BuildUniqueID(candidate, UDTField); - _hashCode = _uniqueID.GetHashCode(); + _hashCode = TargetID.GetHashCode(); } - private IEncapsulateFieldCandidate _wrappedCandidate; - - public IEncapsulateFieldCandidate WrappedCandidate => _wrappedCandidate; + public IEncapsulateFieldCandidate WrappedCandidate { private set; get; } - public string AsTypeName => _wrappedCandidate.AsTypeName; + public string AsTypeName => WrappedCandidate.AsTypeName; - public string BackingIdentifier - { - get => _wrappedCandidate.IdentifierName; - set { } - } + public string BackingIdentifier { set; get; } public string BackingAsTypeName => Declaration.AsTypeName; @@ -50,96 +34,20 @@ public string BackingIdentifier public IEncapsulateFieldConflictFinder ConflictFinder { - set => _wrappedCandidate.ConflictFinder = value; - get => _wrappedCandidate.ConflictFinder; + set => WrappedCandidate.ConflictFinder = value; + get => WrappedCandidate.ConflictFinder; } public string TargetID => $"{UDTField.IdentifierName}.{IdentifierName}"; - public IEnumerable FieldContextReferences - => GetUDTMemberReferencesForField(this, UDTField); - public string IdentifierForReference(IdentifierReference idRef) => PropertyIdentifier; - public PropertyAttributeSet AsPropertyGeneratorSpec - { - get - { - return new PropertyAttributeSet() - { - PropertyName = PropertyIdentifier, - BackingField = BackingIdentifier, - AsTypeName = PropertyAsTypeName, - ParameterName = ParameterName, - GenerateLetter = ImplementLet, - GenerateSetter = ImplementSet, - UsesSetAssignment = Declaration.IsObject, - IsUDTProperty = Declaration.DeclarationType == DeclarationType.UserDefinedType, - Declaration = Declaration - }; - } - } - - public override bool Equals(object obj) - { - return obj != null - && obj is IUserDefinedTypeMemberCandidate udtMember - && BuildUniqueID(udtMember, udtMember.UDTField) == _uniqueID; - } - - public override int GetHashCode() => _hashCode; - public string PropertyIdentifier { set; get; } - private static string BuildUniqueID(IEncapsulateFieldCandidate candidate, IEncapsulateFieldCandidate field) => $"{candidate.QualifiedModuleName.Name}.{field.IdentifierName}.{candidate.IdentifierName}"; - - private static IEnumerable GetUDTMemberReferencesForField(IEncapsulateFieldCandidate udtMember, IUserDefinedTypeCandidate field) - { - var refs = new List(); - foreach (var idRef in udtMember.Declaration.References) - { - if (idRef.Context.TryGetAncestor(out var mac)) - { - var LHS = mac.children.First(); - switch (LHS) - { - case VBAParser.SimpleNameExprContext snec: - if (snec.GetText().Equals(field.IdentifierName)) - { - refs.Add(idRef); - } - break; - case VBAParser.MemberAccessExprContext submac: - if (submac.children.Last() is VBAParser.UnrestrictedIdentifierContext ur && ur.GetText().Equals(field.IdentifierName)) - { - refs.Add(idRef); - } - break; - case VBAParser.WithMemberAccessExprContext wmac: - if (wmac.children.Last().GetText().Equals(field.IdentifierName)) - { - refs.Add(idRef); - } - break; - } - } - else if (idRef.Context.TryGetAncestor(out var wmac)) - { - var wm = wmac.GetAncestor(); - var Lexpr = wm.GetChild(); - if (Lexpr.GetText().Equals(field.IdentifierName)) - { - refs.Add(idRef); - } - } - } - return refs; - } - - public Declaration Declaration => _wrappedCandidate.Declaration; + public Declaration Declaration => WrappedCandidate.Declaration; - public string IdentifierName => _wrappedCandidate.IdentifierName; + public string IdentifierName => WrappedCandidate.IdentifierName; public bool TryValidateEncapsulationAttributes(out string errorMessage) { @@ -149,8 +57,8 @@ public bool TryValidateEncapsulationAttributes(out string errorMessage) public bool IsReadOnly { - set => _wrappedCandidate.IsReadOnly = value; - get => _wrappedCandidate.IsReadOnly; + set => WrappedCandidate.IsReadOnly = value; + get => WrappedCandidate.IsReadOnly; } private bool _encapsulateFlag; @@ -158,7 +66,7 @@ public bool EncapsulateFlag { set { - if (_wrappedCandidate is IUserDefinedTypeCandidate udt && udt.TypeDeclarationIsPrivate) + if (WrappedCandidate is IUserDefinedTypeCandidate udt && udt.TypeDeclarationIsPrivate) { foreach (var member in udt.Members) { @@ -169,7 +77,7 @@ public bool EncapsulateFlag var valueChanged = _encapsulateFlag != value; _encapsulateFlag = value; - PropertyIdentifier = _wrappedCandidate.PropertyIdentifier; + PropertyIdentifier = WrappedCandidate.PropertyIdentifier; if (_encapsulateFlag && valueChanged && ConflictFinder != null) { ConflictFinder.AssignNoConflictIdentifiers(this); @@ -177,7 +85,7 @@ public bool EncapsulateFlag if (!_encapsulateFlag) { - _wrappedCandidate.EncapsulateFlag = value; + WrappedCandidate.EncapsulateFlag = value; } } @@ -186,45 +94,28 @@ public bool EncapsulateFlag public bool CanBeReadWrite { - set => _wrappedCandidate.CanBeReadWrite = value; - get => _wrappedCandidate.CanBeReadWrite; + set => WrappedCandidate.CanBeReadWrite = value; + get => WrappedCandidate.CanBeReadWrite; } public bool HasValidEncapsulationAttributes => true; public QualifiedModuleName QualifiedModuleName - => _wrappedCandidate.QualifiedModuleName; + => WrappedCandidate.QualifiedModuleName; - public string PropertyAsTypeName => _wrappedCandidate.PropertyAsTypeName; + public string PropertyAsTypeName => WrappedCandidate.PropertyAsTypeName; - public string ParameterName => _rhsParameterIdentifierName; + public bool ImplementLet => WrappedCandidate.ImplementLet; - public bool ImplementLet => _wrappedCandidate.ImplementLet; + public bool ImplementSet => WrappedCandidate.ImplementSet; - public bool ImplementSet => _wrappedCandidate.ImplementSet; - - public IEnumerable PropertyAttributeSets + public override bool Equals(object obj) { - get - { - if (!(_wrappedCandidate is IUserDefinedTypeCandidate udt)) - { - return new List() { AsPropertyGeneratorSpec }; - } - - var sets = _wrappedCandidate.PropertyAttributeSets; - if (udt.TypeDeclarationIsPrivate) - { - return sets; - } - var modifiedSets = new List(); - for(var idx = 0; idx < sets.Count(); idx++) - { - var attr = sets.ElementAt(idx); - attr.BackingField = attr.PropertyName; - modifiedSets.Add(attr); - } - return modifiedSets; - } + return obj != null + && obj is IUserDefinedTypeMemberCandidate udtMember + && udtMember.QualifiedModuleName == QualifiedModuleName + && udtMember.TargetID == TargetID; } + + public override int GetHashCode() => _hashCode; } } diff --git a/Rubberduck.Refactorings/EncapsulateField/PropertyAttributeSetsGenerator.cs b/Rubberduck.Refactorings/EncapsulateField/PropertyAttributeSetsGenerator.cs new file mode 100644 index 0000000000..60d7b67668 --- /dev/null +++ b/Rubberduck.Refactorings/EncapsulateField/PropertyAttributeSetsGenerator.cs @@ -0,0 +1,142 @@ +using Rubberduck.Parsing.Symbols; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Rubberduck.Refactorings.EncapsulateField +{ + public struct PropertyAttributeSet + { + public string PropertyName { get; set; } + public string BackingField { get; set; } + public string AsTypeName { get; set; } + public string RHSParameterIdentifier { get; set; } + public bool GeneratePropertyLet { get; set; } + public bool GeneratePropertySet { get; set; } + public bool UsesSetAssignment { get; set; } + public bool IsUDTProperty { get; set; } + public Declaration Declaration { get; set; } + } + + public interface IPropertyAttributeSetsGenerator + { + IReadOnlyCollection GeneratePropertyAttributeSets(IEncapsulateFieldCandidate candidate); + } + + public class PropertyAttributeSetsGenerator : IPropertyAttributeSetsGenerator + { + private Func _backingFieldQualifierFunc; + + public PropertyAttributeSetsGenerator() + { + _backingFieldQualifierFunc = BackingField_BackingFieldQualifier; + } + + private static string BackingUDTMember_BackingFieldQualifier(IEncapsulateFieldCandidate candidate, string backingField) + => $"{candidate.PropertyIdentifier}.{backingField}"; + + private static string BackingField_BackingFieldQualifier(IEncapsulateFieldCandidate candidate, string backingField) + => $"{candidate.BackingIdentifier}.{backingField}"; + + public IReadOnlyCollection GeneratePropertyAttributeSets(IEncapsulateFieldCandidate candidate) + { + if (!(candidate is IEncapsulateFieldAsUDTMemberCandidate asUDTCandidate)) + { + _backingFieldQualifierFunc = BackingField_BackingFieldQualifier; + return CreatePropertyAttributeSets(candidate).ToList(); + } + + return GeneratePropertyAttributeSets(asUDTCandidate); + } + + private IReadOnlyCollection GeneratePropertyAttributeSets(IEncapsulateFieldAsUDTMemberCandidate asUDTCandidate) + { + _backingFieldQualifierFunc = BackingUDTMember_BackingFieldQualifier; + + Func QualifyPrivateUDTWrappedBackingField = attributeSet => + { + var fields = attributeSet.BackingField.Split(new char[] { '.' }); + + return fields.Count() > 1 + ? $"{asUDTCandidate.ObjectStateUDT.FieldIdentifier}.{attributeSet.BackingField}" + : $"{asUDTCandidate.ObjectStateUDT.FieldIdentifier}.{attributeSet.PropertyName}"; + }; + + var propertyAttributeSet = CreatePropertyAttributeSets(asUDTCandidate.WrappedCandidate); + + return QualifyBackingField(propertyAttributeSet, set => QualifyPrivateUDTWrappedBackingField(set)).ToList(); + } + + private IEnumerable CreatePropertyAttributeSets(IUserDefinedTypeCandidate candidate) + { + + if (candidate.TypeDeclarationIsPrivate) + { + var allPropertyAttributeSets = new List(); + foreach (var member in candidate.Members) + { + var propertyAttributeSets = CreatePropertyAttributeSets(member); + var modifiedSets = QualifyBackingField(propertyAttributeSets, propertyAttributeSet => _backingFieldQualifierFunc(candidate, propertyAttributeSet.BackingField)); + allPropertyAttributeSets.AddRange(modifiedSets); + } + return allPropertyAttributeSets; + } + + return new List() { CreatePropertyAttributeSet(candidate) }; + } + + private IEnumerable CreatePropertyAttributeSets(IUserDefinedTypeMemberCandidate udtMemberCandidate) + { + if (udtMemberCandidate.WrappedCandidate is IUserDefinedTypeCandidate udtCandidate) + { + var propertyAttributeSets = CreatePropertyAttributeSets(udtMemberCandidate.WrappedCandidate); + + return udtCandidate.TypeDeclarationIsPrivate + ? propertyAttributeSets + : QualifyBackingField(propertyAttributeSets, attr => attr.PropertyName); + } + + return new List() { CreatePropertyAttributeSet(udtMemberCandidate) }; + } + + private IEnumerable CreatePropertyAttributeSets(IEncapsulateFieldCandidate candidate) + { + switch (candidate) + { + case IUserDefinedTypeCandidate udtCandidate: + return CreatePropertyAttributeSets(udtCandidate); + case IUserDefinedTypeMemberCandidate udtMemberCandidate: + return CreatePropertyAttributeSets(udtMemberCandidate); + default: + return new List() { CreatePropertyAttributeSet(candidate) }; + } + } + + private IEnumerable QualifyBackingField(IEnumerable propertyAttributeSets, Func backingFieldQualifier) + { + var modifiedSets = new List(); + for (var idx = 0; idx < propertyAttributeSets.Count(); idx++) + { + var propertyAttributeSet = propertyAttributeSets.ElementAt(idx); + propertyAttributeSet.BackingField = backingFieldQualifier(propertyAttributeSet); + modifiedSets.Add(propertyAttributeSet); + } + return modifiedSets; + } + + private PropertyAttributeSet CreatePropertyAttributeSet(IEncapsulateFieldCandidate candidate) + { + return new PropertyAttributeSet() + { + PropertyName = candidate.PropertyIdentifier, + BackingField = candidate.BackingIdentifier, + AsTypeName = candidate.PropertyAsTypeName, + RHSParameterIdentifier = Resources.Refactorings.Refactorings.CodeBuilder_DefaultPropertyRHSParam, + GeneratePropertyLet = candidate.ImplementLet, + GeneratePropertySet = candidate.ImplementSet, + UsesSetAssignment = candidate.Declaration.IsObject, + Declaration = candidate.Declaration + }; + } + } +} diff --git a/RubberduckTests/Refactoring/EncapsulateField/PropertyAttributeSetsGeneratorTests.cs b/RubberduckTests/Refactoring/EncapsulateField/PropertyAttributeSetsGeneratorTests.cs new file mode 100644 index 0000000000..8e33774674 --- /dev/null +++ b/RubberduckTests/Refactoring/EncapsulateField/PropertyAttributeSetsGeneratorTests.cs @@ -0,0 +1,129 @@ +using NUnit.Framework; +using Rubberduck.Refactorings.EncapsulateField; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using RubberduckTests.Mocks; +using Rubberduck.Refactorings; + +namespace RubberduckTests.Refactoring.EncapsulateField +{ + [TestFixture] + public class PropertyAttributeSetsGeneratorTests + { + [Test] + [Category("Refactorings")] + [Category("Encapsulate Field")] + [Category(nameof(PropertyAttributeSetsGenerator))] + public void EncapsulateFieldCandidate_PrivateUDTField() + { + var inputCode = +$@" +Option Explicit + +Private Type TVehicle + Wheels As Integer +End Type + +Private Type TObjState + FirstValue As String +End Type + +Private this As TObjState + +Private mVehicle As TVehicle +"; + + + var vbe = MockVbeBuilder.BuildFromSingleStandardModule(inputCode, out _).Object; + var (state, rewritingManager) = MockParser.CreateAndParseWithRewritingManager(vbe); + using (state) + { + var encapsulateTarget = state.AllUserDeclarations.Single(d => d.IdentifierName.Equals("mVehicle")); + var objectStateUDTTarget = state.AllUserDeclarations.Single(d => d.IdentifierName.Equals("this")); + + var resolver = new EncapsulateFieldTestComponentResolver(state, null); + + var encapsulateFieldCandidateFactory = resolver.Resolve(); + var objStateFactory = resolver.Resolve(); + + var objStateCandidate = encapsulateFieldCandidateFactory.Create(objectStateUDTTarget); + var objStateUDT = objStateFactory.Create(objStateCandidate as IUserDefinedTypeCandidate); + + var candidate = new EncapsulateFieldAsUDTMemberCandidate(encapsulateFieldCandidateFactory.Create(encapsulateTarget), objStateUDT) + { + PropertyIdentifier = "MyType" + }; + + var generator = new PropertyAttributeSetsGenerator(); + var propAttributeSets = generator.GeneratePropertyAttributeSets(candidate); + StringAssert.Contains("this.MyType.Wheels", propAttributeSets.First().BackingField); + } + } + + [Test] + [Category("Refactorings")] + [Category("Encapsulate Field")] + [Category(nameof(PropertyAttributeSetsGenerator))] + public void EncapsulateFieldCandidate_DeeplyNestedUDTs() + { + var inputCode = +$@" +Option Explicit + +Private Type FirstType + DeeplyNested As Long +End Type + +Private Type SecondType + Number1Type As FirstType +End Type + +Private Type ThirdType + Number2Type As SecondType +End Type + +Private Type FourthType + Number3Type As ThirdType +End Type + +Private Type FifthType + Number4Type As FourthType +End Type + +Private Type ExistingType + ExistingValue As String +End Type + +Public mTest As FifthType + +Private this As ExistingType + +"; + + var vbe = MockVbeBuilder.BuildFromSingleStandardModule(inputCode, out _).Object; + var (state, rewritingManager) = MockParser.CreateAndParseWithRewritingManager(vbe); + using (state) + { + var encapsulateTarget = state.AllUserDeclarations.Single(d => d.IdentifierName.Equals("mTest")); + var objectStateUDTTarget = state.AllUserDeclarations.Single(d => d.IdentifierName.Equals("this")); + + var resolver = new EncapsulateFieldTestComponentResolver(state, null); + + var encapsulateFieldCandidateFactory = resolver.Resolve(); + var objStateFactory = resolver.Resolve(); + + var objStateCandidate = encapsulateFieldCandidateFactory.Create(objectStateUDTTarget); + var objStateUDT = objStateFactory.Create(objStateCandidate as IUserDefinedTypeCandidate); + + var candidate = new EncapsulateFieldAsUDTMemberCandidate(encapsulateFieldCandidateFactory.Create(encapsulateTarget), objStateUDT); + + var generator = new PropertyAttributeSetsGenerator(); + var propAttributeSets = generator.GeneratePropertyAttributeSets(candidate); + StringAssert.Contains("this.Test.Number4Type.Number3Type.Number2Type.Number1Type.DeeplyNested", propAttributeSets.First().BackingField); + } + } + } +} From 9185e6799f5bbca1888ead7b186c252b51f3fa2f Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Sun, 13 Sep 2020 16:24:22 -0700 Subject: [PATCH 37/67] Refactor split of existing code vs new code ops Moved residual new content generation/insertion out of refactoring logic operating on existing content. --- .../CreateUDTMember/CreateUDTMemberModel.cs | 54 ++++++++ .../CreateUDTMemberRefactoringAction.cs} | 8 +- .../DeclareFieldsAsUDTMembersModel.cs | 46 ------- .../EncapsulateFieldModelFactory.cs | 12 +- .../EncapsulateFieldRefactoringAction.cs | 7 +- ...apsulateFieldRefactoringActionsProvider.cs | 14 +-- .../EncapsulateFieldRequest.cs | 40 ------ .../EncapsulateFieldRequestFactory.cs | 17 --- ...psulateFieldUseBackingFieldModelFactory.cs | 15 ++- ...teFieldUseBackingFieldRefactoringAction.cs | 2 +- ...ncapsulateFieldUseBackingUDTMemberModel.cs | 2 +- ...ateFieldUseBackingUDTMemberModelFactory.cs | 15 ++- ...eldUseBackingUDTMemberRefactoringAction.cs | 33 ++--- .../FieldEncapsulationModel.cs | 28 +++++ .../ReplaceReferencesModel.cs | 6 +- .../ReplaceReferencesRefactoringAction.cs | 2 +- .../CreateUDTMemberRefactoringActionTests.cs} | 34 +++-- .../EncapsulateFieldTestComponentResolver.cs | 16 +-- ...ldUseBackingFieldRefactoringActionTests.cs | 6 +- ...ncapsulateFieldUseBackingUDTMemberTests.cs | 116 ++++++++++++++++-- ...ReplaceReferencesRefactoringActionTests.cs | 5 +- 21 files changed, 274 insertions(+), 204 deletions(-) create mode 100644 Rubberduck.Refactorings/CreateUDTMember/CreateUDTMemberModel.cs rename Rubberduck.Refactorings/{DeclareFieldsAsUDTMembers/DeclareFieldsAsUDTMembersRefactoringAction.cs => CreateUDTMember/CreateUDTMemberRefactoringAction.cs} (85%) delete mode 100644 Rubberduck.Refactorings/DeclareFieldsAsUDTMembers/DeclareFieldsAsUDTMembersModel.cs delete mode 100644 Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRequest/EncapsulateFieldRequest.cs delete mode 100644 Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRequest/EncapsulateFieldRequestFactory.cs create mode 100644 Rubberduck.Refactorings/EncapsulateField/FieldEncapsulationModel.cs rename RubberduckTests/Refactoring/{DeclareFieldsAsUDTMembers/DeclareFieldsAsUDTMembersRefactoringActionTests.cs => CreateUDTMember/CreateUDTMemberRefactoringActionTests.cs} (74%) diff --git a/Rubberduck.Refactorings/CreateUDTMember/CreateUDTMemberModel.cs b/Rubberduck.Refactorings/CreateUDTMember/CreateUDTMemberModel.cs new file mode 100644 index 0000000000..3eaf6cc94c --- /dev/null +++ b/Rubberduck.Refactorings/CreateUDTMember/CreateUDTMemberModel.cs @@ -0,0 +1,54 @@ +using Rubberduck.Parsing.Symbols; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Rubberduck.Refactorings.CreateUDTMember +{ + public class CreateUDTMemberModel : IRefactoringModel + { + private Dictionary> _targets { get; } = new Dictionary>(); + + public CreateUDTMemberModel() + { } + + public CreateUDTMemberModel(Declaration userDefinedType, IEnumerable<(VariableDeclaration prototype, string UserDefinedTypeMemberIdentifier)> conversionModels) + { + foreach ((VariableDeclaration prototype, string UDTMemberIdentifier) in conversionModels) + { + AssignPrototypeToUserDefinedType(userDefinedType, prototype, UDTMemberIdentifier); + } + } + + public IReadOnlyCollection UserDefinedTypeTargets => _targets.Keys; + + public IEnumerable<(VariableDeclaration prototype, string userDefinedTypeMemberIdentifier)> this[Declaration udt] + => _targets[udt].Select(pr => (pr.prototype, pr.UDTMemberIdentifier)); + + private void AssignPrototypeToUserDefinedType(Declaration udt, VariableDeclaration prototype, string udtMemberIdentifierName = null) + { + if (!udt.DeclarationType.HasFlag(DeclarationType.UserDefinedType)) + { + throw new ArgumentException(); + } + + if (!(_targets.TryGetValue(udt, out var memberPrototypes))) + { + _targets.Add(udt, new List<(VariableDeclaration, string)>()); + } + else + { + var hasDuplicateMemberNames = memberPrototypes + .Select(pr => pr.UDTMemberIdentifier?.ToUpperInvariant() ?? pr.prototype.IdentifierName) + .GroupBy(uc => uc).Any(g => g.Count() > 1); + + if (hasDuplicateMemberNames) + { + throw new ArgumentException(); + } + } + + _targets[udt].Add((prototype, udtMemberIdentifierName ?? prototype.IdentifierName)); + } + } +} diff --git a/Rubberduck.Refactorings/DeclareFieldsAsUDTMembers/DeclareFieldsAsUDTMembersRefactoringAction.cs b/Rubberduck.Refactorings/CreateUDTMember/CreateUDTMemberRefactoringAction.cs similarity index 85% rename from Rubberduck.Refactorings/DeclareFieldsAsUDTMembers/DeclareFieldsAsUDTMembersRefactoringAction.cs rename to Rubberduck.Refactorings/CreateUDTMember/CreateUDTMemberRefactoringAction.cs index d704bf0684..debe4ac665 100644 --- a/Rubberduck.Refactorings/DeclareFieldsAsUDTMembers/DeclareFieldsAsUDTMembersRefactoringAction.cs +++ b/Rubberduck.Refactorings/CreateUDTMember/CreateUDTMemberRefactoringAction.cs @@ -6,21 +6,21 @@ using System; using System.Collections.Generic; using System.Linq; -namespace Rubberduck.Refactorings.DeclareFieldsAsUDTMembers +namespace Rubberduck.Refactorings.CreateUDTMember { - public class DeclareFieldsAsUDTMembersRefactoringAction : CodeOnlyRefactoringActionBase + public class CreateUDTMemberRefactoringAction : CodeOnlyRefactoringActionBase { private readonly IDeclarationFinderProvider _declarationFinderProvider; private readonly ICodeBuilder _codeBuilder; - public DeclareFieldsAsUDTMembersRefactoringAction(IDeclarationFinderProvider declarationFinderProvider,IRewritingManager rewritingManager, ICodeBuilder codeBuilder) + public CreateUDTMemberRefactoringAction(IDeclarationFinderProvider declarationFinderProvider,IRewritingManager rewritingManager, ICodeBuilder codeBuilder) : base(rewritingManager) { _declarationFinderProvider = declarationFinderProvider; _codeBuilder = codeBuilder; } - public override void Refactor(DeclareFieldsAsUDTMembersModel model, IRewriteSession rewriteSession) + public override void Refactor(CreateUDTMemberModel model, IRewriteSession rewriteSession) { if (model.UserDefinedTypeTargets.Any( udt => !(udt.Context is VBAParser.UdtDeclarationContext))) { diff --git a/Rubberduck.Refactorings/DeclareFieldsAsUDTMembers/DeclareFieldsAsUDTMembersModel.cs b/Rubberduck.Refactorings/DeclareFieldsAsUDTMembers/DeclareFieldsAsUDTMembersModel.cs deleted file mode 100644 index 23f75fd2f2..0000000000 --- a/Rubberduck.Refactorings/DeclareFieldsAsUDTMembers/DeclareFieldsAsUDTMembersModel.cs +++ /dev/null @@ -1,46 +0,0 @@ -using Rubberduck.Parsing.Symbols; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Rubberduck.Refactorings.DeclareFieldsAsUDTMembers -{ - public class DeclareFieldsAsUDTMembersModel : IRefactoringModel - { - private Dictionary> _targets { get; } = new Dictionary>(); - - public DeclareFieldsAsUDTMembersModel() - {} - - public IReadOnlyCollection UserDefinedTypeTargets => _targets.Keys; - - public IEnumerable<(VariableDeclaration Field, string userDefinedTypeMemberIdentifier)> this[Declaration udt] - => _targets[udt].Select(pr => (pr.Field, pr.UDTMemberIdentifier)); - - public void AssignFieldToUserDefinedType(Declaration udt, VariableDeclaration field, string udtMemberIdentifierName = null) - { - if (!udt.DeclarationType.HasFlag(DeclarationType.UserDefinedType)) - { - throw new ArgumentException(); - } - - if (!(_targets.TryGetValue(udt, out var memberPrototypes))) - { - _targets.Add(udt, new List<(VariableDeclaration, string)>()); - } - else - { - var hasDuplicateMemberNames = memberPrototypes - .Select(pr => pr.UDTMemberIdentifier?.ToUpperInvariant() ?? pr.Field.IdentifierName) - .GroupBy(uc => uc).Any(g => g.Count() > 1); - - if (hasDuplicateMemberNames) - { - throw new ArgumentException(); - } - } - - _targets[udt].Add((field, udtMemberIdentifierName ?? field.IdentifierName)); - } - } -} diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModelFactory.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModelFactory.cs index 73d7cdc0a9..bec50cebb5 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModelFactory.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModelFactory.cs @@ -19,18 +19,15 @@ public class EncapsulateFieldModelFactory : IEncapsulateFieldModelFactory private readonly IEncapsulateFieldUseBackingUDTMemberModelFactory _useBackingUDTMemberModelFactory; private readonly IEncapsulateFieldUseBackingFieldModelFactory _useBackingFieldModelFactory; private readonly IEncapsulateFieldCandidateCollectionFactory _fieldCandidateCollectionFactory; - private readonly IEncapsulateFieldRequestFactory _requestFactory; public EncapsulateFieldModelFactory( IEncapsulateFieldUseBackingUDTMemberModelFactory encapsulateFieldUseBackingUDTMemberModelFactory, IEncapsulateFieldUseBackingFieldModelFactory encapsulateFieldUseBackingFieldModelFactory, - IEncapsulateFieldCandidateCollectionFactory encapsulateFieldCandidateCollectionFactory, - IEncapsulateFieldRequestFactory encapsulateFieldRequestFactory) + IEncapsulateFieldCandidateCollectionFactory encapsulateFieldCandidateCollectionFactory) { _useBackingUDTMemberModelFactory = encapsulateFieldUseBackingUDTMemberModelFactory as IEncapsulateFieldUseBackingUDTMemberModelFactory; _useBackingFieldModelFactory = encapsulateFieldUseBackingFieldModelFactory; _fieldCandidateCollectionFactory = encapsulateFieldCandidateCollectionFactory; - _requestFactory = encapsulateFieldRequestFactory; } public EncapsulateFieldModel Create(Declaration target) @@ -42,12 +39,11 @@ public EncapsulateFieldModel Create(Declaration target) var fieldCandidates = _fieldCandidateCollectionFactory.Create(targetField.QualifiedModuleName); - var encapsulationRequest = _requestFactory.Create(targetField); - var requests = new List() { encapsulationRequest }; + var fieldEncapsulationModels = new List() { new FieldEncapsulationModel(targetField) }; - var useBackingFieldModel = _useBackingFieldModelFactory.Create(fieldCandidates, requests); + var useBackingFieldModel = _useBackingFieldModelFactory.Create(fieldCandidates, fieldEncapsulationModels); - var useBackingUDTMemberModel = _useBackingUDTMemberModelFactory.Create(fieldCandidates, requests); + var useBackingUDTMemberModel = _useBackingUDTMemberModelFactory.Create(fieldCandidates, fieldEncapsulationModels); var initialStrategy = useBackingUDTMemberModel.ObjectStateUDTField.IsExistingDeclaration ? EncapsulateFieldStrategy.ConvertFieldsToUDTMembers diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringAction.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringAction.cs index b33b298064..8bcccad25f 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringAction.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringAction.cs @@ -8,13 +8,16 @@ public class EncapsulateFieldRefactoringAction : IRefactoringAction ReplaceReferences { get; } ICodeOnlyRefactoringAction ReplaceUDTMemberReferences { get; } ICodeOnlyRefactoringAction ReplaceDeclarationIdentifiers { get; } - ICodeOnlyRefactoringAction DeclareFieldsAsUDTMembers { get; } + ICodeOnlyRefactoringAction CreateUDTMember { get; } ICodeOnlyRefactoringAction EncapsulateFieldInsertNewCode { get; } } @@ -20,20 +20,20 @@ public class EncapsulateFieldRefactoringActionsProvider : IEncapsulateFieldRefac private readonly ReplaceReferencesRefactoringAction _replaceReferences; private readonly ReplaceDeclarationIdentifierRefactoringAction _replaceDeclarationIdentifiers; private readonly ReplacePrivateUDTMemberReferencesRefactoringAction _replaceUDTMemberReferencesRefactoringAction; - private readonly DeclareFieldsAsUDTMembersRefactoringAction _declareFieldsAsUDTMembersRefactoringAction; + private readonly CreateUDTMemberRefactoringAction _createUDTMemberRefactoringAction; private readonly EncapsulateFieldInsertNewCodeRefactoringAction _encapsulateFieldInsertNewCodeRefactoringAction; public EncapsulateFieldRefactoringActionsProvider( ReplaceReferencesRefactoringAction replaceReferencesRefactoringAction, ReplacePrivateUDTMemberReferencesRefactoringAction replaceUDTMemberReferencesRefactoringAction, ReplaceDeclarationIdentifierRefactoringAction replaceDeclarationIdentifierRefactoringAction, - DeclareFieldsAsUDTMembersRefactoringAction declareFieldsAsUDTMembersRefactoringAction, + CreateUDTMemberRefactoringAction createUDTMemberRefactoringActionRefactoringAction, EncapsulateFieldInsertNewCodeRefactoringAction encapsulateFieldInsertNewCodeRefactoringAction) { _replaceReferences = replaceReferencesRefactoringAction; _replaceUDTMemberReferencesRefactoringAction = replaceUDTMemberReferencesRefactoringAction; _replaceDeclarationIdentifiers = replaceDeclarationIdentifierRefactoringAction; - _declareFieldsAsUDTMembersRefactoringAction = declareFieldsAsUDTMembersRefactoringAction; + _createUDTMemberRefactoringAction = createUDTMemberRefactoringActionRefactoringAction; _encapsulateFieldInsertNewCodeRefactoringAction = encapsulateFieldInsertNewCodeRefactoringAction; } @@ -46,8 +46,8 @@ public ICodeOnlyRefactoringAction ReplaceDecl public ICodeOnlyRefactoringAction ReplaceUDTMemberReferences => _replaceUDTMemberReferencesRefactoringAction; - public ICodeOnlyRefactoringAction DeclareFieldsAsUDTMembers - => _declareFieldsAsUDTMembersRefactoringAction; + public ICodeOnlyRefactoringAction CreateUDTMember + => _createUDTMemberRefactoringAction; public ICodeOnlyRefactoringAction EncapsulateFieldInsertNewCode => _encapsulateFieldInsertNewCodeRefactoringAction; diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRequest/EncapsulateFieldRequest.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRequest/EncapsulateFieldRequest.cs deleted file mode 100644 index 8ecec8b3f8..0000000000 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRequest/EncapsulateFieldRequest.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Rubberduck.Parsing.Symbols; - -namespace Rubberduck.Refactorings.EncapsulateField -{ - /// - /// EncapsulateFieldRequest aggregates attributes necessary for the EncapsulateFieldUseBackingFieldRefactoringAction. - /// - /// - /// EncapsulateFieldRequest provides the data needed to encapsulate a field. - /// There is no validation or conflict checking performed for non-UserDefinedTypes. - /// The caller can specify a readonly Property and select an IdentifierName for - /// the Property. If the target is a UserDefinedType Field and the UserDefinedType is Private, - /// then the propertyIdentifier parameter is ignored and PropertyIdentifiers for each UserDefinedTypeMember - /// are generated by the refactoring action. - /// - public class EncapsulateFieldRequest - { - public EncapsulateFieldRequest(VariableDeclaration target, bool isReadOnly = false, string propertyIdentifier = null) - { - Declaration = target; - IsReadOnly = isReadOnly; - PropertyIdentifier = propertyIdentifier; - } - - public VariableDeclaration Declaration { get; } - public string PropertyIdentifier { set; get; } - public bool IsReadOnly { set; get; } - - public IEncapsulateFieldCandidate ApplyRequest(IEncapsulateFieldCandidate candidate) - { - candidate.EncapsulateFlag = true; - candidate.IsReadOnly = IsReadOnly; - if (PropertyIdentifier != null) - { - candidate.PropertyIdentifier = PropertyIdentifier; - } - return candidate; - } - } -} diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRequest/EncapsulateFieldRequestFactory.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRequest/EncapsulateFieldRequestFactory.cs deleted file mode 100644 index 82df5f3db8..0000000000 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRequest/EncapsulateFieldRequestFactory.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Rubberduck.Parsing.Symbols; - -namespace Rubberduck.Refactorings.EncapsulateField -{ - public interface IEncapsulateFieldRequestFactory - { - EncapsulateFieldRequest Create(VariableDeclaration target, bool isReadOnly = false, string propertyIdentifier = null); - } - - public class EncapsulateFieldRequestFactory : IEncapsulateFieldRequestFactory - { - public EncapsulateFieldRequest Create(VariableDeclaration target, bool isReadOnly = false, string propertyIdentifier = null) - { - return new EncapsulateFieldRequest(target, isReadOnly, propertyIdentifier); - } - } -} diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldModelFactory.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldModelFactory.cs index 913a9c6d33..d289debec3 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldModelFactory.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldModelFactory.cs @@ -11,14 +11,14 @@ public interface IEncapsulateFieldUseBackingFieldModelFactory /// Creates an EncapsulateFieldUseBackingFieldModel used by the EncapsulateFieldUseBackingFieldRefactoringAction. /// /// Optional: UserDefinedType Field to include the Encapsulated Field(s) - EncapsulateFieldUseBackingFieldModel Create(IEnumerable requests); + EncapsulateFieldUseBackingFieldModel Create(IEnumerable requests); /// /// Creates an EncapsulateFieldUseBackingFieldModel based upon collection of /// IEncapsulateFieldCandidate instances created by EncapsulateFieldCandidateCollectionFactory. /// This function is intended for exclusive use by EncapsulateFieldModelFactory /// - EncapsulateFieldUseBackingFieldModel Create(IReadOnlyCollection candidates, IEnumerable requests); + EncapsulateFieldUseBackingFieldModel Create(IReadOnlyCollection candidates, IEnumerable requests); } public class EncapsulateFieldUseBackingFieldModelFactory : IEncapsulateFieldUseBackingFieldModelFactory @@ -34,7 +34,7 @@ public class EncapsulateFieldUseBackingFieldModelFactory : IEncapsulateFieldUseB _conflictFinderFactory = encapsulateFieldConflictFinderFactory; } - public EncapsulateFieldUseBackingFieldModel Create(IEnumerable requests) + public EncapsulateFieldUseBackingFieldModel Create(IEnumerable requests) { if (!requests.Any()) { @@ -45,14 +45,19 @@ public EncapsulateFieldUseBackingFieldModel Create(IEnumerable candidates, IEnumerable requests) + public EncapsulateFieldUseBackingFieldModel Create(IReadOnlyCollection candidates, IEnumerable requests) { var fieldCandidates = candidates.ToList(); foreach (var request in requests) { var candidate = fieldCandidates.Single(c => c.Declaration.Equals(request.Declaration)); - request.ApplyRequest(candidate); + candidate.EncapsulateFlag = true; + candidate.IsReadOnly = request.IsReadOnly; + if (request.PropertyIdentifier != null) + { + candidate.PropertyIdentifier = request.PropertyIdentifier; + } } var conflictsFinder = _conflictFinderFactory.CreateEncapsulateFieldUseBackingFieldConflictFinder(fieldCandidates); diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldRefactoringAction.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldRefactoringAction.cs index 1393fd21f5..b73c3d1871 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldRefactoringAction.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldRefactoringAction.cs @@ -128,7 +128,7 @@ private void ReplaceEncapsulatedFieldReferences(IEnumerable EncapsulationCandidates { get; } public IReadOnlyCollection SelectedFieldCandidates - => EncapsulationCandidates + => _encapsulateAsUDTMemberCandidates .Where(v => v.EncapsulateFlag) .ToList(); diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModelFactory.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModelFactory.cs index 4abf01485b..7f5dcd357b 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModelFactory.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModelFactory.cs @@ -13,14 +13,14 @@ public interface IEncapsulateFieldUseBackingUDTMemberModelFactory /// Creates an EncapsulateFieldUseBackingUDTMemberModel used by the EncapsulateFieldUseBackingUDTMemberRefactoringAction. /// /// Optional: UserDefinedType Field to include the Encapsulated Field(s) - EncapsulateFieldUseBackingUDTMemberModel Create(IEnumerable requests, Declaration userDefinedTypeTarget = null); + EncapsulateFieldUseBackingUDTMemberModel Create(IEnumerable requests, Declaration userDefinedTypeTarget = null); /// /// Creates an EncapsulateFieldUseBackingUDTMemberModel based upon collection of /// IEncapsulateFieldCandidate instances created by EncapsulateFieldCandidateCollectionFactory. /// This function is intended for exclusive use by EncapsulateFieldModelFactory /// - EncapsulateFieldUseBackingUDTMemberModel Create(IReadOnlyCollection candidates, IEnumerable requests, Declaration userDefinedTypeTarget = null); + EncapsulateFieldUseBackingUDTMemberModel Create(IReadOnlyCollection candidates, IEnumerable requests, Declaration userDefinedTypeTarget = null); } public class EncapsulateFieldUseBackingUDTMemberModelFactory : IEncapsulateFieldUseBackingUDTMemberModelFactory @@ -39,7 +39,7 @@ public class EncapsulateFieldUseBackingUDTMemberModelFactory : IEncapsulateField _conflictFinderFactory = encapsulateFieldConflictFinderFactory; } - public EncapsulateFieldUseBackingUDTMemberModel Create(IEnumerable requests, Declaration clientTarget) + public EncapsulateFieldUseBackingUDTMemberModel Create(IEnumerable requests, Declaration clientTarget) { if (!requests.Any()) { @@ -49,7 +49,7 @@ public EncapsulateFieldUseBackingUDTMemberModel Create(IEnumerable candidates, IEnumerable requests, Declaration clientTarget = null) + public EncapsulateFieldUseBackingUDTMemberModel Create(IReadOnlyCollection candidates, IEnumerable requests, Declaration clientTarget = null) { if (clientTarget != null && (clientTarget.Accessibility != Accessibility.Private @@ -74,7 +74,12 @@ public EncapsulateFieldUseBackingUDTMemberModel Create(IReadOnlyCollection c.Declaration.Equals(request.Declaration)); - request.ApplyRequest(candidate); + candidate.EncapsulateFlag = true; + candidate.IsReadOnly = request.IsReadOnly; + if (request.PropertyIdentifier != null) + { + candidate.PropertyIdentifier = request.PropertyIdentifier; + } } var conflictsFinder = _conflictFinderFactory.CreateEncapsulateFieldUseBackingUDTMemberConflictFinder(candidates, objectStateUDTs) diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs index c5118f8f6e..5fc0773a2c 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs @@ -2,7 +2,7 @@ using Rubberduck.Parsing.Rewriter; using Rubberduck.Parsing.Symbols; using Rubberduck.Refactorings.Common; -using Rubberduck.Refactorings.DeclareFieldsAsUDTMembers; +using Rubberduck.Refactorings.CreateUDTMember; using Rubberduck.Refactorings.ReplaceReferences; using Rubberduck.Refactorings.ReplacePrivateUDTMemberReferences; using System.Linq; @@ -11,9 +11,10 @@ namespace Rubberduck.Refactorings.EncapsulateFieldUseBackingUDTMember { + public class EncapsulateFieldUseBackingUDTMemberRefactoringAction : CodeOnlyRefactoringActionBase { - private readonly ICodeOnlyRefactoringAction _declareFieldAsUDTMemberRefactoringAction; + private readonly ICodeOnlyRefactoringAction _createUDTMemberRefactoringAction; private readonly ICodeOnlyRefactoringAction _replaceUDTMemberReferencesRefactoringAction; private readonly ICodeOnlyRefactoringAction _replaceFieldReferencesRefactoringAction; private readonly ICodeOnlyRefactoringAction _encapsulateFieldInsertNewCodeRefactoringAction; @@ -29,7 +30,7 @@ public class EncapsulateFieldUseBackingUDTMemberRefactoringAction : CodeOnlyRefa IEncapsulateFieldCodeBuilderFactory encapsulateFieldCodeBuilderFactory) : base(rewritingManager) { - _declareFieldAsUDTMemberRefactoringAction = refactoringActionsProvider.DeclareFieldsAsUDTMembers; + _createUDTMemberRefactoringAction = refactoringActionsProvider.CreateUDTMember; _replaceUDTMemberReferencesRefactoringAction = refactoringActionsProvider.ReplaceUDTMemberReferences; _replaceFieldReferencesRefactoringAction = refactoringActionsProvider.ReplaceReferences; _encapsulateFieldInsertNewCodeRefactoringAction = refactoringActionsProvider.EncapsulateFieldInsertNewCode; @@ -61,28 +62,17 @@ private void ModifyFields(EncapsulateFieldUseBackingUDTMemberModel encapsulateFi { var rewriter = rewriteSession.CheckOutModuleRewriter(encapsulateFieldModel.QualifiedModuleName); - rewriter.RemoveVariables(encapsulateFieldModel.SelectedFieldCandidates.Select(f => f.Declaration) - .Cast()); - if (encapsulateFieldModel.ObjectStateUDTField.IsExistingDeclaration) { - var model = new DeclareFieldsAsUDTMembersModel(); + var conversionPairs = encapsulateFieldModel.SelectedFieldCandidates + .Select(c => (c.Declaration as VariableDeclaration, c.BackingIdentifier)); - foreach (var field in encapsulateFieldModel.SelectedFieldCandidates) - { - model.AssignFieldToUserDefinedType(encapsulateFieldModel.ObjectStateUDTField.AsTypeDeclaration, field.Declaration as VariableDeclaration, field.PropertyIdentifier); - } - _declareFieldAsUDTMemberRefactoringAction.Refactor(model, rewriteSession); + var model = new CreateUDTMemberModel(encapsulateFieldModel.ObjectStateUDTField.AsTypeDeclaration, conversionPairs); + _createUDTMemberRefactoringAction.Refactor(model, rewriteSession); } - else - { - var objectStateTypeDeclarationBlock = _encapsulateFieldCodeBuilder.BuildUserDefinedTypeDeclaration(encapsulateFieldModel.ObjectStateUDTField, encapsulateFieldModel.EncapsulationCandidates); - - encapsulateFieldModel.NewContentAggregator.AddNewContent(NewContentType.UserDefinedTypeDeclaration, objectStateTypeDeclarationBlock); - var objectStateFieldDeclaration = _encapsulateFieldCodeBuilder.BuildObjectStateFieldDeclaration(encapsulateFieldModel.ObjectStateUDTField); - encapsulateFieldModel.NewContentAggregator.AddNewContent(NewContentType.DeclarationBlock, objectStateFieldDeclaration); - } + rewriter.RemoveVariables(encapsulateFieldModel.SelectedFieldCandidates.Select(f => f.Declaration) + .Cast()); } private void ModifyReferences(EncapsulateFieldUseBackingUDTMemberModel model, IRewriteSession rewriteSession) @@ -126,7 +116,7 @@ private void ModifyReferences(EncapsulateFieldUseBackingUDTMemberModel model, IR var replacementExpression = idRef.QualifiedModuleName == field.QualifiedModuleName ? field.Declaration.IsArray ? $"{model.ObjectStateUDTField.FieldIdentifier}.{field.BackingIdentifier}" : field.PropertyIdentifier : field.PropertyIdentifier; - modelReplaceField.AssignFieldReferenceReplacementExpression(idRef, replacementExpression); + modelReplaceField.AssignReferenceReplacementExpression(idRef, replacementExpression); } } @@ -138,6 +128,7 @@ private void InsertNewContent(EncapsulateFieldUseBackingUDTMemberModel model, IR var encapsulateFieldInsertNewCodeModel = new EncapsulateFieldInsertNewCodeModel(model.SelectedFieldCandidates) { NewContentAggregator = model.NewContentAggregator, + ObjectStateUDTField = model.ObjectStateUDTField }; _encapsulateFieldInsertNewCodeRefactoringAction.Refactor(encapsulateFieldInsertNewCodeModel, rewriteSession); diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldEncapsulationModel.cs b/Rubberduck.Refactorings/EncapsulateField/FieldEncapsulationModel.cs new file mode 100644 index 0000000000..0e8336fe31 --- /dev/null +++ b/Rubberduck.Refactorings/EncapsulateField/FieldEncapsulationModel.cs @@ -0,0 +1,28 @@ +using Rubberduck.Parsing.Symbols; + +namespace Rubberduck.Refactorings.EncapsulateField +{ + /// + /// FieldEncapsulationModel contains attributes necessary for the EncapsulateFieldUseBackingFieldRefactoringAction + /// and the EncapsulateFieldUseBackingUDTMemberRefactoringAction. + /// + /// + /// There is no validation or conflict checking performed for non-UserDefinedTypes. + /// If the target is a UserDefinedType Field and the UserDefinedType is Private, + /// then the propertyIdentifier parameter is ignored and PropertyIdentifiers for each UserDefinedTypeMember + /// are generated by the refactoring action. + /// + public class FieldEncapsulationModel + { + public FieldEncapsulationModel(VariableDeclaration target, bool isReadOnly = false, string propertyIdentifier = null) + { + Declaration = target; + IsReadOnly = isReadOnly; + PropertyIdentifier = propertyIdentifier; + } + + public VariableDeclaration Declaration { get; } + public string PropertyIdentifier { set; get; } + public bool IsReadOnly { set; get; } + } +} diff --git a/Rubberduck.Refactorings/ReplaceReferences/ReplaceReferencesModel.cs b/Rubberduck.Refactorings/ReplaceReferences/ReplaceReferencesModel.cs index 205c0d9ef0..999cfb2bc1 100644 --- a/Rubberduck.Refactorings/ReplaceReferences/ReplaceReferencesModel.cs +++ b/Rubberduck.Refactorings/ReplaceReferences/ReplaceReferencesModel.cs @@ -6,11 +6,13 @@ namespace Rubberduck.Refactorings.ReplaceReferences { public class ReplaceReferencesModel :IRefactoringModel { + public ReplaceReferencesModel() + {} private Dictionary _fieldTargets = new Dictionary(); public bool ModuleQualifyExternalReferences { set; get; } = false; - public void AssignFieldReferenceReplacementExpression(IdentifierReference fieldReference, string replacementIdentifier) + public void AssignReferenceReplacementExpression(IdentifierReference fieldReference, string replacementIdentifier) { if (_fieldTargets.ContainsKey(fieldReference)) { @@ -19,7 +21,7 @@ public void AssignFieldReferenceReplacementExpression(IdentifierReference fieldR } _fieldTargets.Add(fieldReference, replacementIdentifier); } - public IReadOnlyList<(IdentifierReference IdentifierReference, string NewName)> FieldReferenceReplacementPairs + public IReadOnlyCollection<(IdentifierReference IdentifierReference, string NewName)> ReferenceReplacementPairs => _fieldTargets.Select(t => (t.Key, t.Value)).ToList(); } } diff --git a/Rubberduck.Refactorings/ReplaceReferences/ReplaceReferencesRefactoringAction.cs b/Rubberduck.Refactorings/ReplaceReferences/ReplaceReferencesRefactoringAction.cs index 8bc14a9984..74b3f4cb18 100644 --- a/Rubberduck.Refactorings/ReplaceReferences/ReplaceReferencesRefactoringAction.cs +++ b/Rubberduck.Refactorings/ReplaceReferences/ReplaceReferencesRefactoringAction.cs @@ -18,7 +18,7 @@ public ReplaceReferencesRefactoringAction(IRewritingManager rewritingManager) public override void Refactor(ReplaceReferencesModel model, IRewriteSession rewriteSession) { - var replacementPairByQualifiedModuleName = model.FieldReferenceReplacementPairs + var replacementPairByQualifiedModuleName = model.ReferenceReplacementPairs .Where(pr => pr.IdentifierReference.Context.GetText() != Tokens.Me && !pr.IdentifierReference.IsArrayAccess diff --git a/RubberduckTests/Refactoring/DeclareFieldsAsUDTMembers/DeclareFieldsAsUDTMembersRefactoringActionTests.cs b/RubberduckTests/Refactoring/CreateUDTMember/CreateUDTMemberRefactoringActionTests.cs similarity index 74% rename from RubberduckTests/Refactoring/DeclareFieldsAsUDTMembers/DeclareFieldsAsUDTMembersRefactoringActionTests.cs rename to RubberduckTests/Refactoring/CreateUDTMember/CreateUDTMemberRefactoringActionTests.cs index 280777ed22..bd3c8fdc87 100644 --- a/RubberduckTests/Refactoring/DeclareFieldsAsUDTMembers/DeclareFieldsAsUDTMembersRefactoringActionTests.cs +++ b/RubberduckTests/Refactoring/CreateUDTMember/CreateUDTMemberRefactoringActionTests.cs @@ -3,20 +3,20 @@ using Rubberduck.Parsing.Symbols; using Rubberduck.Parsing.VBA; using Rubberduck.Refactorings; -using Rubberduck.Refactorings.DeclareFieldsAsUDTMembers; +using Rubberduck.Refactorings.CreateUDTMember; using System.Collections.Generic; using System.Linq; -namespace RubberduckTests.Refactoring.MoveFieldsToUDT +namespace RubberduckTests.Refactoring.CreateUDTMember { [TestFixture] - public class DeclareFieldsAsUDTMembersRefactoringActionTests : RefactoringActionTestBase + public class CreateUDTMemberRefactoringActionTests : RefactoringActionTestBase { [TestCase(4)] [TestCase(2)] [Category("Refactorings")] [Category("Encapsulate Field")] - [Category(nameof(DeclareFieldsAsUDTMembersRefactoringAction))] + [Category(nameof(CreateUDTMemberRefactoringAction))] public void FormatSingleExistingMember(int indentionLevel) { var indention = string.Concat(Enumerable.Repeat(" ", indentionLevel)); @@ -47,7 +47,7 @@ End Type [TestCase(2)] [Category("Refactorings")] [Category("Encapsulate Field")] - [Category(nameof(DeclareFieldsAsUDTMembersRefactoringAction))] + [Category(nameof(CreateUDTMemberRefactoringAction))] public void FormatMatchesLastMemberIndent(int indentionLevel) { var indention = string.Concat(Enumerable.Repeat(" ", indentionLevel)); @@ -80,7 +80,7 @@ End Type [Test] [Category("Refactorings")] [Category("Encapsulate Field")] - [Category(nameof(DeclareFieldsAsUDTMembersRefactoringAction))] + [Category(nameof(CreateUDTMemberRefactoringAction))] public void FormatPreservesComments() { var indention = string.Concat(Enumerable.Repeat(" ", 2)); @@ -112,11 +112,9 @@ End Type [Test] [Category("Refactorings")] [Category("Encapsulate Field")] - [Category(nameof(DeclareFieldsAsUDTMembersRefactoringAction))] + [Category(nameof(CreateUDTMemberRefactoringAction))] public void FormatMultipleInsertions() { - var indention = string.Concat(Enumerable.Repeat(" ", 2)); - string inputCode = $@" Option Explicit @@ -150,21 +148,19 @@ private string ExecuteTest(string inputCode, string udtIdentifier, params (strin return RefactoredCode(inputCode, state => TestModel(state, udtIdentifier, fieldConversions)); } - private DeclareFieldsAsUDTMembersModel TestModel(RubberduckParserState state, string udtIdentifier, params (string fieldID, string udtMemberID)[] fieldConversions) + private CreateUDTMemberModel TestModel(RubberduckParserState state, string udtIdentifier, params (string fieldID, string udtMemberID)[] fieldConversions) { var udtDeclaration = GetUniquelyNamedDeclaration(state, DeclarationType.UserDefinedType, udtIdentifier); - var conversions = new List<(VariableDeclaration field, string udtMemberID)>(); + + var conversionPairs = new List<(VariableDeclaration, string)>(); foreach (var (fieldID, udtMemberID) in fieldConversions) { var fieldDeclaration = GetUniquelyNamedDeclaration(state, DeclarationType.Variable, fieldID) as VariableDeclaration; - conversions.Add((fieldDeclaration, udtMemberID)); + conversionPairs.Add((fieldDeclaration, udtMemberID)); } - var model = new DeclareFieldsAsUDTMembersModel(); - foreach ((VariableDeclaration field, string udtMemberID) in conversions) - { - model.AssignFieldToUserDefinedType(udtDeclaration, field, udtMemberID); - } + var model = new CreateUDTMemberModel(udtDeclaration, conversionPairs.ToArray()); + return model; } @@ -173,9 +169,9 @@ private static Declaration GetUniquelyNamedDeclaration(IDeclarationFinderProvide return declarationFinderProvider.DeclarationFinder.UserDeclarations(declarationType).Single(d => d.IdentifierName.Equals(identifier)); } - protected override IRefactoringAction TestBaseRefactoring(RubberduckParserState state, IRewritingManager rewritingManager) + protected override IRefactoringAction TestBaseRefactoring(RubberduckParserState state, IRewritingManager rewritingManager) { - return new DeclareFieldsAsUDTMembersRefactoringAction(state, rewritingManager, new CodeBuilder()); + return new CreateUDTMemberRefactoringAction(state, rewritingManager, new CodeBuilder()); } } } diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs index 6ddd0642fc..30493e4ddd 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs @@ -1,7 +1,7 @@ using Rubberduck.Parsing.Rewriter; using Rubberduck.Parsing.VBA; using Rubberduck.Refactorings; -using Rubberduck.Refactorings.DeclareFieldsAsUDTMembers; +using Rubberduck.Refactorings.CreateUDTMember; using Rubberduck.Refactorings.EncapsulateField; using Rubberduck.Refactorings.ReplaceReferences; using Rubberduck.Refactorings.ReplacePrivateUDTMemberReferences; @@ -35,7 +35,8 @@ public EncapsulateFieldTestComponentResolver(IDeclarationFinderProvider declarat case nameof(EncapsulateFieldRefactoringAction): return new EncapsulateFieldRefactoringAction( ResolveImpl(), - ResolveImpl()) as T; + ResolveImpl(), + ResolveImpl()) as T; case nameof(ReplaceReferencesRefactoringAction): return new ReplaceReferencesRefactoringAction(_rewritingManager) as T; @@ -47,6 +48,7 @@ public EncapsulateFieldTestComponentResolver(IDeclarationFinderProvider declarat return new EncapsulateFieldInsertNewCodeRefactoringAction( _declarationFinderProvider, _rewritingManager, + new PropertyAttributeSetsGenerator(), ResolveImpl()) as T; case nameof(ReplacePrivateUDTMemberReferencesRefactoringAction): @@ -57,7 +59,7 @@ public EncapsulateFieldTestComponentResolver(IDeclarationFinderProvider declarat ResolveImpl(), ResolveImpl(), ResolveImpl(), - ResolveImpl(), + ResolveImpl(), ResolveImpl() ) as T; @@ -79,8 +81,8 @@ ResolveImpl() case nameof(IReplacePrivateUDTMemberReferencesModelFactory): return new ReplacePrivateUDTMemberReferencesModelFactory(_declarationFinderProvider) as T; - case nameof(DeclareFieldsAsUDTMembersRefactoringAction): - return new DeclareFieldsAsUDTMembersRefactoringAction( + case nameof(CreateUDTMemberRefactoringAction): + return new CreateUDTMemberRefactoringAction( _declarationFinderProvider, _rewritingManager, new CodeBuilder()) as T; @@ -106,8 +108,7 @@ ResolveImpl() return new EncapsulateFieldModelFactory( ResolveImpl(), ResolveImpl(), - ResolveImpl(), - new EncapsulateFieldRequestFactory() as IEncapsulateFieldRequestFactory + ResolveImpl() ) as T; case nameof(IEncapsulateFieldUseBackingUDTMemberModelFactory): @@ -138,7 +139,6 @@ ResolveImpl() case nameof(IEncapsulateFieldCodeBuilderFactory): return new EncapsulateFieldCodeBuilderFactory(new CodeBuilder()) as T; - } throw new ArgumentException($"Unable to resolve {typeof(T).Name}") ; } diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldRefactoringActionTests.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldRefactoringActionTests.cs index 747ab07519..4f09055f0c 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldRefactoringActionTests.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldRefactoringActionTests.cs @@ -34,8 +34,8 @@ EncapsulateFieldUseBackingFieldModel modelBuilder(RubberduckParserState state) var modelFactory = resolver.Resolve(); var field = state.DeclarationFinder.MatchName(target).Single(); - var encapsulateFieldRequest = new EncapsulateFieldRequest(field as VariableDeclaration, isReadOnly, propertyIdentifier); - return modelFactory.Create( new List() { encapsulateFieldRequest }); + var encapsulateFieldRequest = new FieldEncapsulationModel(field as VariableDeclaration, isReadOnly, propertyIdentifier); + return modelFactory.Create( new List() { encapsulateFieldRequest }); } var refactoredCode = RefactoredCode(inputCode, modelBuilder); @@ -74,7 +74,7 @@ EncapsulateFieldUseBackingFieldModel modelBuilder(RubberduckParserState state) { var resolver = new EncapsulateFieldTestComponentResolver(state, null); var modelFactory = resolver.Resolve(); - return modelFactory.Create(Enumerable.Empty()); + return modelFactory.Create(Enumerable.Empty()); } var refactoredCode = RefactoredCode(inputCode, modelBuilder); diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberTests.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberTests.cs index 782050ef4f..983115b876 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberTests.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberTests.cs @@ -35,8 +35,8 @@ EncapsulateFieldUseBackingUDTMemberModel modelBuilder(RubberduckParserState stat var modelFactory = resolver.Resolve(); var field = state.DeclarationFinder.MatchName(target).Single(); - var encapsulateFieldRequest = new EncapsulateFieldRequest(field as VariableDeclaration, isReadOnly); - return modelFactory.Create(new List() { encapsulateFieldRequest }); + var encapsulateFieldRequest = new FieldEncapsulationModel(field as VariableDeclaration, isReadOnly); + return modelFactory.Create(new List() { encapsulateFieldRequest }); } var refactoredCode = RefactoredCode(inputCode, modelBuilder); @@ -91,9 +91,9 @@ EncapsulateFieldUseBackingUDTMemberModel modelBuilder(RubberduckParserState stat var firstValueField = state.DeclarationFinder.MatchName("thirdValue").Single(d => d.DeclarationType.HasFlag(DeclarationType.Variable)); var bazzField = state.DeclarationFinder.MatchName("bazz").Single(); - var encapsulateFieldRequestfirstValueField = new EncapsulateFieldRequest(firstValueField as VariableDeclaration); - var encapsulateFieldRequestfirstbazzField = new EncapsulateFieldRequest(bazzField as VariableDeclaration); - var inputList = new List() { encapsulateFieldRequestfirstValueField, encapsulateFieldRequestfirstbazzField }; + var encapsulateFieldRequestfirstValueField = new FieldEncapsulationModel(firstValueField as VariableDeclaration); + var encapsulateFieldRequestfirstbazzField = new FieldEncapsulationModel(bazzField as VariableDeclaration); + var inputList = new List() { encapsulateFieldRequestfirstValueField, encapsulateFieldRequestfirstbazzField }; return modelFactory.Create(inputList); } @@ -142,10 +142,10 @@ EncapsulateFieldUseBackingUDTMemberModel modelBuilder(RubberduckParserState stat var thirdValueField = state.DeclarationFinder.MatchName("thirdValue").Single(d => d.DeclarationType.HasFlag(DeclarationType.Variable)); var bazzField = state.DeclarationFinder.MatchName("bazz").Single(); - var encapsulateFieldRequestThirdValueField = new EncapsulateFieldRequest(thirdValueField as VariableDeclaration); - var encapsulateFieldRequestBazzField = new EncapsulateFieldRequest(bazzField as VariableDeclaration); + var encapsulateFieldRequestThirdValueField = new FieldEncapsulationModel(thirdValueField as VariableDeclaration); + var encapsulateFieldRequestBazzField = new FieldEncapsulationModel(bazzField as VariableDeclaration); - var inputList = new List() { encapsulateFieldRequestThirdValueField, encapsulateFieldRequestBazzField }; + var inputList = new List() { encapsulateFieldRequestThirdValueField, encapsulateFieldRequestBazzField }; var targetUDT = state.DeclarationFinder.MatchName("this").Single(d => d.DeclarationType.HasFlag(DeclarationType.Variable)); @@ -171,6 +171,100 @@ EncapsulateFieldUseBackingUDTMemberModel modelBuilder(RubberduckParserState stat StringAssert.Contains($" this.Bazz =", refactoredCode); } + [Test] + [Category("Refactorings")] + [Category("Encapsulate Field")] + [Category(nameof(EncapsulateFieldUseBackingUDTMemberRefactoringAction))] + public void EncapsulatePublicFields_NestedPathForPrivateUDTField() + { + var inputCode = +$@" +Option Explicit + +Private Type TVehicle + Wheels As Integer +End Type + +Private mVehicle As TVehicle +"; + + EncapsulateFieldUseBackingUDTMemberModel modelBuilder(RubberduckParserState state) + { + var resolver = new EncapsulateFieldTestComponentResolver(state, null); + var modelFactory = resolver.Resolve(); + + var mVehicleField = state.DeclarationFinder.UserDeclarations(DeclarationType.Variable).Single(d => d.IdentifierName.Equals("mVehicle")); + var encapsulateFieldRequestMVehicleField = new FieldEncapsulationModel(mVehicleField as VariableDeclaration, false, "Vehicle"); + + var inputList = new List() { encapsulateFieldRequestMVehicleField }; + + return modelFactory.Create(inputList); + } + + var refactoredCode = RefactoredCode(inputCode, modelBuilder); + + StringAssert.Contains($"T{ MockVbeBuilder.TestModuleName}", refactoredCode); + + StringAssert.Contains($" Vehicle As TVehicle", refactoredCode); + StringAssert.Contains($"Property Get Wheels", refactoredCode); + StringAssert.Contains($" Wheels = this.Vehicle.Wheels", refactoredCode); + + StringAssert.Contains($"Property Let Wheels", refactoredCode); + StringAssert.Contains($" this.Vehicle.Wheels =", refactoredCode); + } + + + [Test] + [Category("Refactorings")] + [Category("Encapsulate Field")] + [Category(nameof(EncapsulateFieldUseBackingUDTMemberRefactoringAction))] + public void EncapsulatePublicFields_DifferentLevelForNestedProperties() + { + var inputCode = +$@" +Option Explicit + +Private Type FirstType + FirstValue As Integer +End Type + +Private Type SecondType + SecondValue As Integer + FirstTypeValue As FirstType +End Type + +Private Type ThirdType + ThirdValue As SecondType +End Type + +Private mTest As ThirdType +"; + + EncapsulateFieldUseBackingUDTMemberModel modelBuilder(RubberduckParserState state) + { + var resolver = new EncapsulateFieldTestComponentResolver(state, null); + var modelFactory = resolver.Resolve(); + + var mTestField = state.DeclarationFinder.UserDeclarations(DeclarationType.Variable).Single(d => d.IdentifierName.Equals("mTest")); + var fieldEncapsulationModelMTest = new FieldEncapsulationModel(mTestField as VariableDeclaration, false); + + var inputList = new List() { fieldEncapsulationModelMTest }; + + return modelFactory.Create(inputList); + } + + var refactoredCode = RefactoredCode(inputCode, modelBuilder); + + StringAssert.Contains($"T{ MockVbeBuilder.TestModuleName}", refactoredCode); + + StringAssert.Contains($" Test As ThirdType", refactoredCode); + StringAssert.Contains($"Property Get FirstValue", refactoredCode); + StringAssert.Contains($"Property Get SecondValue", refactoredCode); + + StringAssert.Contains($" this.Test.ThirdValue.FirstTypeValue.FirstValue =", refactoredCode); + StringAssert.Contains($" this.Test.ThirdValue.SecondValue =", refactoredCode); + } + [Test] [Category("Refactorings")] [Category("Encapsulate Field")] @@ -183,7 +277,7 @@ EncapsulateFieldUseBackingUDTMemberModel modelBuilder(RubberduckParserState stat { var resolver = new EncapsulateFieldTestComponentResolver(state, null); var modelFactory = resolver.Resolve(); - return modelFactory.Create(Enumerable.Empty()); + return modelFactory.Create(Enumerable.Empty()); } Assert.Throws(() => RefactoredCode(inputCode, modelBuilder)); @@ -221,9 +315,9 @@ EncapsulateFieldUseBackingUDTMemberModel modelBuilder(RubberduckParserState stat var invalidTarget = state.DeclarationFinder.MatchName(objectStateTargetIdentifier).Single(d => d.DeclarationType.HasFlag(DeclarationType.Variable)); var resolver = new EncapsulateFieldTestComponentResolver(state, null); var modelFactory = resolver.Resolve(); - var request = new EncapsulateFieldRequest(invalidTarget as VariableDeclaration); + var request = new FieldEncapsulationModel(invalidTarget as VariableDeclaration); - return modelFactory.Create(new List() { request }, invalidTarget); + return modelFactory.Create(new List() { request }, invalidTarget); } Assert.Throws(() => RefactoredCode(inputCode, modelBuilder)); diff --git a/RubberduckTests/Refactoring/ReplaceReferences/ReplaceReferencesRefactoringActionTests.cs b/RubberduckTests/Refactoring/ReplaceReferences/ReplaceReferencesRefactoringActionTests.cs index 93f1c7e6a9..c4dda9ea66 100644 --- a/RubberduckTests/Refactoring/ReplaceReferences/ReplaceReferencesRefactoringActionTests.cs +++ b/RubberduckTests/Refactoring/ReplaceReferences/ReplaceReferencesRefactoringActionTests.cs @@ -467,9 +467,6 @@ private ReplaceReferencesModel TestModel(RubberduckParserState state, params (st ModuleQualifyExternalReferences = true, }; - var fields = state.DeclarationFinder.UserDeclarations(DeclarationType.Variable) - .Select(v => v as VariableDeclaration); - foreach (var (fieldID, internalName, externalName) in fieldConversions) { var fieldDeclaration = GetUniquelyNamedDeclaration(state, DeclarationType.Variable, fieldID); @@ -479,7 +476,7 @@ private ReplaceReferencesModel TestModel(RubberduckParserState state, params (st ? externalName : internalName; - model.AssignFieldReferenceReplacementExpression(reference, replacementExpression); + model.AssignReferenceReplacementExpression(reference, replacementExpression); } } return model; From 604e6921ecc325b264af811032171ca9b53a1c49 Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Tue, 15 Sep 2020 07:51:53 -0700 Subject: [PATCH 38/67] Expand prototype DeclarationType options Expanded the acceptable prototypes to include DeclarationTypes having flags Function and Constant in addition to Variable and UserDefinedTypeMember. --- Rubberduck.Refactorings/Common/CodeBuilder.cs | 90 +++--- .../CreateUDTMember/CreateUDTMemberModel.cs | 25 +- .../CreateUDTMemberRefactoringAction.cs | 35 ++- .../EncapsulateFieldCodeBuilder.cs | 6 +- ...eldUseBackingUDTMemberRefactoringAction.cs | 2 +- RubberduckTests/CodeBuilderTests.cs | 288 +++++++++++++++--- .../CreateUDTMemberRefactoringActionTests.cs | 2 +- 7 files changed, 342 insertions(+), 106 deletions(-) diff --git a/Rubberduck.Refactorings/Common/CodeBuilder.cs b/Rubberduck.Refactorings/Common/CodeBuilder.cs index 150904b666..cc155e9fc0 100644 --- a/Rubberduck.Refactorings/Common/CodeBuilder.cs +++ b/Rubberduck.Refactorings/Common/CodeBuilder.cs @@ -1,5 +1,4 @@ -using Rubberduck.Common; -using Rubberduck.Parsing; +using Rubberduck.Parsing; using Rubberduck.Parsing.Grammar; using Rubberduck.Parsing.Symbols; using System; @@ -35,7 +34,7 @@ public interface ICodeBuilder /// /// Generates a Property Get codeblock based on the prototype declaration /// - /// VariableDeclaration or UserDefinedTypeMember + /// DeclarationType with flags: Variable, Constant, UserDefinedTypeMember, or Function /// Member body content. Formatting is the responsibility of the caller /// Defaults to 'Value' unless otherwise specified bool TryBuildPropertyGetCodeBlock(Declaration prototype, @@ -47,7 +46,7 @@ public interface ICodeBuilder /// /// Generates a Property Let codeblock based on the prototype declaration /// - /// VariableDeclaration or UserDefinedTypeMember + /// DeclarationType with flags: Variable, Constant, UserDefinedTypeMember, or Function /// Member body content. Formatting is the responsibility of the caller /// Defaults to 'Value' unless otherwise specified bool TryBuildPropertyLetCodeBlock(Declaration prototype, @@ -60,7 +59,7 @@ public interface ICodeBuilder /// /// Generates a Property Set codeblock based on the prototype declaration /// - /// VariableDeclaration or UserDefinedTypeMember + /// DeclarationType with flags: Variable, Constant, UserDefinedTypeMember, or Function /// Member body content. Formatting is the responsibility of the caller /// Defaults to 'Value' unless otherwise specified bool TryBuildPropertySetCodeBlock(Declaration prototype, @@ -71,16 +70,20 @@ public interface ICodeBuilder string parameterIdentifier = null); /// - /// Generates a UserDefinedType (UDT) declaration using a VariableDeclaration as the prototype for - /// creating the UserDefinedTypeMember. + /// Generates a UserDefinedType (UDT) declaration using the prototype declarations for + /// creating the UserDefinedTypeMember declarations. /// - /// At least one VariableDeclaration must be provided and - /// all UDTMemberIdentifiers must be unique + /// No validation or conflict analysis is applied to the identifiers. /// - /// Collection of prototypes and their required identifier. Must have 1 or more elements - string BuildUserDefinedTypeDeclaration(string udtIdentifier, IEnumerable<(VariableDeclaration Field, string UDTMemberIdentifier)> memberPrototypes, Accessibility accessibility = Accessibility.Private); + /// DeclarationTypes with flags: Variable, Constant, UserDefinedTypeMember, or Function + bool TryBuildUserDefinedTypeDeclaration(string udtIdentifier, IEnumerable<(Declaration Prototype, string UDTMemberIdentifier)> memberPrototypes, out string declaration, Accessibility accessibility = Accessibility.Private); - string UDTMemberDeclaration(string identifier, string typeName, string indention = null); + /// + /// Generates a UserDefinedTypeMember declaration expression based on the prototype declaration + /// + /// DeclarationType with flags: Variable, Constant, UserDefinedTypeMember, or Function + /// Defaults is 4 spaces + bool TryBuildUDTMemberDeclaration(string identifier, Declaration prototype, out string declaration, string indentation = null); } public class CodeBuilder : ICodeBuilder @@ -119,7 +122,7 @@ public bool TryBuildPropertySetCodeBlock(Declaration prototype, string propertyI throw new ArgumentException(); } - if (!(prototype is VariableDeclaration || prototype.DeclarationType.HasFlag(DeclarationType.UserDefinedTypeMember))) + if (!IsValidPrototypeDeclarationType(prototype.DeclarationType)) { return false; } @@ -243,47 +246,64 @@ private static string EndStatement(DeclarationType declarationType) private static string TypeToken(DeclarationType declarationType) => _declarationTypeTokens[declarationType].TypeToken; - public string BuildUserDefinedTypeDeclaration(string udtIdentifier, IEnumerable<(VariableDeclaration Field, string UDTMemberIdentifier)> memberPrototypes, Accessibility accessibility = Accessibility.Private) + public bool TryBuildUserDefinedTypeDeclaration(string udtIdentifier, IEnumerable<(Declaration Prototype, string UDTMemberIdentifier)> memberPrototypes, out string declaration, Accessibility accessibility = Accessibility.Private) { - if (!memberPrototypes.Any()) + if (udtIdentifier is null + ||!memberPrototypes.Any() + || memberPrototypes.Any(p => p.Prototype is null || p.UDTMemberIdentifier is null) + || memberPrototypes.Any(mp => !IsValidPrototypeDeclarationType(mp.Prototype.DeclarationType))) { - throw new ArgumentOutOfRangeException(); + declaration = string.Empty; + return false; } - var hasDuplicateMemberNames = memberPrototypes.Select(pr => pr.UDTMemberIdentifier.ToUpperInvariant()) - .GroupBy(uc => uc).Any(g => g.Count() > 1); - if (hasDuplicateMemberNames) - { - throw new ArgumentException(); - } + var blockLines = memberPrototypes + .Select(m => BuildUDTMemberDeclaration(m.UDTMemberIdentifier, m.Prototype)) + .ToList(); - var newMemberTokenPairs = memberPrototypes.Select(m => (GetDeclarationIdentifier(m.Field, m.UDTMemberIdentifier), m.Field.AsTypeName)) - .Cast<(string Identifier, string AsTypeName)>(); + blockLines.Insert(0, $"{accessibility.TokenString()} {Tokens.Type} {udtIdentifier}"); - var blockLines = new List(); + blockLines.Add($"{Tokens.End} {Tokens.Type}"); - blockLines.Add($"{accessibility.TokenString()} {Tokens.Type} {udtIdentifier}"); + declaration = string.Join(Environment.NewLine, blockLines); + return true; + } - blockLines.AddRange(newMemberTokenPairs.Select(m => UDTMemberDeclaration(m.Identifier, m.AsTypeName))); + public bool TryBuildUDTMemberDeclaration(string udtMemberIdentifier, Declaration prototype, out string declaration, string indentation = null) + { + declaration = string.Empty; - blockLines.Add($"{Tokens.End} {Tokens.Type}"); + if (udtMemberIdentifier is null + || prototype is null + || !IsValidPrototypeDeclarationType(prototype.DeclarationType)) + { + return false; + } - return string.Join(Environment.NewLine, blockLines); + declaration = BuildUDTMemberDeclaration(udtMemberIdentifier, prototype, indentation); + return true; } - private static string GetDeclarationIdentifier(Declaration field, string udtMemberIdentifier) + private static string BuildUDTMemberDeclaration(string udtMemberIdentifier, Declaration prototype, string indentation = null) { - if (field.IsArray) + var identifierExpression = udtMemberIdentifier; + if (prototype.IsArray) { - return field.Context.TryGetChildContext(out var ctxt) + identifierExpression = prototype.Context.TryGetChildContext(out var ctxt) ? $"{udtMemberIdentifier}({ctxt.GetText()})" : $"{udtMemberIdentifier}()"; } - return udtMemberIdentifier; + + return $"{indentation ?? " "}{identifierExpression} {Tokens.As} {prototype.AsTypeName}"; } - public string UDTMemberDeclaration(string identifier, string typeName, string indention = null) - => $"{indention ?? " "}{identifier} {Tokens.As} {typeName}"; + private static bool IsValidPrototypeDeclarationType(DeclarationType declarationType) + { + return declarationType.HasFlag(DeclarationType.Variable) + || declarationType.HasFlag(DeclarationType.UserDefinedTypeMember) + || declarationType.HasFlag(DeclarationType.Constant) + || declarationType.HasFlag(DeclarationType.Function); + } private static bool IsEnumField(VariableDeclaration declaration) => IsMemberVariable(declaration) diff --git a/Rubberduck.Refactorings/CreateUDTMember/CreateUDTMemberModel.cs b/Rubberduck.Refactorings/CreateUDTMember/CreateUDTMemberModel.cs index 3eaf6cc94c..5bdfdae598 100644 --- a/Rubberduck.Refactorings/CreateUDTMember/CreateUDTMemberModel.cs +++ b/Rubberduck.Refactorings/CreateUDTMember/CreateUDTMemberModel.cs @@ -7,14 +7,19 @@ namespace Rubberduck.Refactorings.CreateUDTMember { public class CreateUDTMemberModel : IRefactoringModel { - private Dictionary> _targets { get; } = new Dictionary>(); + private Dictionary> _targets { get; } = new Dictionary>(); public CreateUDTMemberModel() { } - public CreateUDTMemberModel(Declaration userDefinedType, IEnumerable<(VariableDeclaration prototype, string UserDefinedTypeMemberIdentifier)> conversionModels) + public CreateUDTMemberModel(Declaration userDefinedType, IEnumerable<(Declaration prototype, string UserDefinedTypeMemberIdentifier)> conversionModels) { - foreach ((VariableDeclaration prototype, string UDTMemberIdentifier) in conversionModels) + if (conversionModels.Any(cm => !IsValidPrototypeDeclarationType(cm.prototype.DeclarationType))) + { + throw new ArgumentException(); + } + + foreach ((Declaration prototype, string UDTMemberIdentifier) in conversionModels) { AssignPrototypeToUserDefinedType(userDefinedType, prototype, UDTMemberIdentifier); } @@ -22,10 +27,10 @@ public CreateUDTMemberModel(Declaration userDefinedType, IEnumerable<(VariableDe public IReadOnlyCollection UserDefinedTypeTargets => _targets.Keys; - public IEnumerable<(VariableDeclaration prototype, string userDefinedTypeMemberIdentifier)> this[Declaration udt] + public IEnumerable<(Declaration prototype, string userDefinedTypeMemberIdentifier)> this[Declaration udt] => _targets[udt].Select(pr => (pr.prototype, pr.UDTMemberIdentifier)); - private void AssignPrototypeToUserDefinedType(Declaration udt, VariableDeclaration prototype, string udtMemberIdentifierName = null) + private void AssignPrototypeToUserDefinedType(Declaration udt, Declaration prototype, string udtMemberIdentifierName = null) { if (!udt.DeclarationType.HasFlag(DeclarationType.UserDefinedType)) { @@ -34,7 +39,7 @@ private void AssignPrototypeToUserDefinedType(Declaration udt, VariableDeclarati if (!(_targets.TryGetValue(udt, out var memberPrototypes))) { - _targets.Add(udt, new List<(VariableDeclaration, string)>()); + _targets.Add(udt, new List<(Declaration, string)>()); } else { @@ -50,5 +55,13 @@ private void AssignPrototypeToUserDefinedType(Declaration udt, VariableDeclarati _targets[udt].Add((prototype, udtMemberIdentifierName ?? prototype.IdentifierName)); } + + private static bool IsValidPrototypeDeclarationType(DeclarationType declarationType) + { + return declarationType.HasFlag(DeclarationType.Variable) + || declarationType.HasFlag(DeclarationType.UserDefinedTypeMember) + || declarationType.HasFlag(DeclarationType.Constant) + || declarationType.HasFlag(DeclarationType.Function); + } } } diff --git a/Rubberduck.Refactorings/CreateUDTMember/CreateUDTMemberRefactoringAction.cs b/Rubberduck.Refactorings/CreateUDTMember/CreateUDTMemberRefactoringAction.cs index debe4ac665..6e8e1822e5 100644 --- a/Rubberduck.Refactorings/CreateUDTMember/CreateUDTMemberRefactoringAction.cs +++ b/Rubberduck.Refactorings/CreateUDTMember/CreateUDTMemberRefactoringAction.cs @@ -27,19 +27,24 @@ public override void Refactor(CreateUDTMemberModel model, IRewriteSession rewrit throw new ArgumentException(); } + var rewriter = rewriteSession.CheckOutModuleRewriter(model.UserDefinedTypeTargets.First().QualifiedModuleName); + foreach (var udt in model.UserDefinedTypeTargets) { - InsertNewMembersBlock(BuildNewMembersBlock(udt, model[udt]), - GetInsertionIndex(udt.Context as VBAParser.UdtDeclarationContext), - rewriteSession.CheckOutModuleRewriter(udt.QualifiedModuleName)); + var newMembersBlock = BuildNewMembersBlock(udt, model); + + var insertionIndex = (udt.Context as VBAParser.UdtDeclarationContext) + .END_TYPE().Symbol.TokenIndex - 1; + + rewriter.InsertBefore(insertionIndex, $"{newMembersBlock}"); } } - private string BuildNewMembersBlock(Declaration udt, IEnumerable<(VariableDeclaration Field, string UDTMemberIdentifier)> newMemberPairs) + private string BuildNewMembersBlock(Declaration udt, CreateUDTMemberModel model) { var indentation = DetermineIndentationFromLastMember(udt); - var newMemberStatements = GenerateUserDefinedMemberDeclarations(newMemberPairs, indentation); + var newMemberStatements = GenerateUserDefinedMemberDeclarations(model[udt], indentation); return string.Concat(newMemberStatements); } @@ -55,13 +60,17 @@ private string DetermineIndentationFromLastMember(Declaration udt) return endOfStatementContextPrototype.GetText(); } - private IEnumerable GenerateUserDefinedMemberDeclarations(IEnumerable<(VariableDeclaration Field, string UDTMemberIdentifier)> newMemberPairs, string indentation) - => newMemberPairs.Select(pr => _codeBuilder.UDTMemberDeclaration(pr.UDTMemberIdentifier, pr.Field.AsTypeName, indentation)); - - private static void InsertNewMembersBlock(string newMembersBlock, int insertionIndex, IModuleRewriter rewriter) - => rewriter.InsertBefore(insertionIndex, $"{newMembersBlock}"); - - private int GetInsertionIndex(VBAParser.UdtDeclarationContext udtContext) - => udtContext.END_TYPE().Symbol.TokenIndex - 1; + private IEnumerable GenerateUserDefinedMemberDeclarations(IEnumerable<(Declaration Prototype, string UDTMemberIdentifier)> newMemberPairs, string indentation) + { + var declarations = new List(); + foreach (var (Prototype, UDTMemberIdentifier) in newMemberPairs) + { + if (_codeBuilder.TryBuildUDTMemberDeclaration(UDTMemberIdentifier, Prototype, out var declaration, indentation)) + { + declarations.Add(declaration); + } + } + return declarations; + } } } diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldCodeBuilder.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldCodeBuilder.cs index ec4237b70e..cb95bbdf9b 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldCodeBuilder.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldCodeBuilder.cs @@ -78,9 +78,11 @@ public string BuildUserDefinedTypeDeclaration(IObjectStateUDT objectStateUDT, IE var selected = candidates.Where(c => c.EncapsulateFlag); var newUDTMembers = selected - .Select(m => (m.Declaration as VariableDeclaration, m.BackingIdentifier)); + .Select(m => (m.Declaration, m.BackingIdentifier)); - return _codeBuilder.BuildUserDefinedTypeDeclaration(objectStateUDT.AsTypeName, newUDTMembers); + _codeBuilder.TryBuildUserDefinedTypeDeclaration(objectStateUDT.AsTypeName, newUDTMembers, out var declaration); + + return declaration ?? string.Empty; } public string BuildObjectStateFieldDeclaration(IObjectStateUDT objectStateUDT) diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs index 5fc0773a2c..3053f756fd 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs @@ -65,7 +65,7 @@ private void ModifyFields(EncapsulateFieldUseBackingUDTMemberModel encapsulateFi if (encapsulateFieldModel.ObjectStateUDTField.IsExistingDeclaration) { var conversionPairs = encapsulateFieldModel.SelectedFieldCandidates - .Select(c => (c.Declaration as VariableDeclaration, c.BackingIdentifier)); + .Select(c => (c.Declaration, c.BackingIdentifier)); var model = new CreateUDTMemberModel(encapsulateFieldModel.ObjectStateUDTField.AsTypeDeclaration, conversionPairs); _createUDTMemberRefactoringAction.Refactor(model, rewriteSession); diff --git a/RubberduckTests/CodeBuilderTests.cs b/RubberduckTests/CodeBuilderTests.cs index 12efac7b60..581b1d5810 100644 --- a/RubberduckTests/CodeBuilderTests.cs +++ b/RubberduckTests/CodeBuilderTests.cs @@ -13,6 +13,7 @@ namespace RubberduckTests public class CodeBuilderTests { private static string _rhsIdentifier = Rubberduck.Resources.Refactorings.Refactorings.CodeBuilder_DefaultPropertyRHSParam; + private static string _defaultUDTIdentifier = "TestUDT"; [TestCase("fizz", DeclarationType.Variable, "Integer")] [TestCase("FirstValue", DeclarationType.UserDefinedTypeMember, "Long")] @@ -96,12 +97,13 @@ End Enum StringAssert.Contains($"{accessibility} Property Get {testParams.Identifier}() As {typeName}", result); } - [TestCase("fizz", DeclarationType.Variable, "Integer", "Bazz = fizz")] - [TestCase("FirstValue", DeclarationType.UserDefinedTypeMember, "Long", "Bazz = fozz.FirstValue")] - [TestCase("fazz", DeclarationType.Variable, "Long", "Bazz = fazz")] - [TestCase("fuzz", DeclarationType.Variable, "TTestType2", "Bazz = fuzz")] + [TestCase("fizz", DeclarationType.Variable, "Bazz = fizz")] + [TestCase("FirstValue", DeclarationType.UserDefinedTypeMember, "Bazz = fozz.FirstValue")] + [TestCase("fazz", DeclarationType.Variable, "Bazz = fazz")] + [TestCase("fezz", DeclarationType.Variable, "Bazz = fezz")] + [TestCase("fuzz", DeclarationType.Variable, "Bazz = fuzz")] [Category(nameof(CodeBuilder))] - public void PropertyBlockFromPrototype_PropertyGetContent(string targetIdentifier, DeclarationType declarationType, string typeName, string content) + public void PropertyBlockFromPrototype_PropertyGetContent(string targetIdentifier, DeclarationType declarationType, string content) { var testParams = new PropertyBlockFromPrototypeParams("Bazz", DeclarationType.PropertyGet, content: content); string inputCode = @@ -128,6 +130,8 @@ End Enum Private fazz As ETestType +Private fezz As ETestType2 + Private fuzz As TTestType2 "; var result = ParseAndTest(inputCode, @@ -139,9 +143,9 @@ End Enum StringAssert.Contains(content, result); } - [TestCase("fizz", DeclarationType.Variable, "Integer", "Bazz = fizz")] + [Test] [Category(nameof(CodeBuilder))] - public void PropertyBlockFromPrototype_PropertyGetChangeParamName(string targetIdentifier, DeclarationType declarationType, string typeName, string content) + public void PropertyBlockFromPrototype_PropertyGetChangeParamName() { var testParams = new PropertyBlockFromPrototypeParams("Bazz", DeclarationType.PropertyGet, paramIdentifier: "testParam"); string inputCode = @@ -149,14 +153,56 @@ public void PropertyBlockFromPrototype_PropertyGetChangeParamName(string targetI Private fizz As Integer "; var result = ParseAndTest(inputCode, - targetIdentifier, - declarationType, + "fizz", + DeclarationType.Variable, testParams, PropertyGetBlockFromPrototypeTest); StringAssert.Contains("Property Get Bazz() As Integer", result); } + [TestCase("Private Const fizz As Integer = 5", DeclarationType.Constant, "Integer")] + [TestCase("Private Type TTestType\r\nfizz As String\r\nEnd Type", DeclarationType.UserDefinedTypeMember, "String")] + [Category(nameof(CodeBuilder))] + public void PropertyBlockFromVariousPrototypeTypes_PropertyGet(string inputCode, DeclarationType declarationType, string expectedTypeName) + { + var testParams = new PropertyBlockFromPrototypeParams("Bazz", DeclarationType.PropertyGet); + + var result = ParseAndTest(inputCode, + "fizz", + declarationType, + testParams, + PropertyGetBlockFromPrototypeTest); + + StringAssert.Contains($"Property Get Bazz() As {expectedTypeName}", result); + } + + [TestCase("Property Get", "Property", DeclarationType.PropertyGet, "Variant")] + [TestCase("Function", "Function", DeclarationType.Function, "Variant")] + [Category(nameof(CodeBuilder))] + public void PropertyBlockFromFromFunctionPrototypes(string memberType, string memberEndStatement, DeclarationType declarationType, string typeName) + { + var targetIdentifier = "TestValue"; + var testParams = new PropertyBlockFromPrototypeParams("Bazz", DeclarationType.PropertyLet); + var inputCode = +$@" + +Private mTestValue As {typeName} + +Public {memberType} TestValue() As {typeName} + TestValue = mTestValue +End {memberEndStatement} +"; + + var result = ParseAndTest(inputCode, + targetIdentifier, + declarationType, + testParams, + PropertyLetBlockFromPrototypeTest); + + StringAssert.Contains($"Property Let {testParams.Identifier}(ByVal RHS As {typeName})", result); + } + [TestCase("fizz", DeclarationType.Variable, "Integer")] [TestCase("FirstValue", DeclarationType.UserDefinedTypeMember, "Long")] [TestCase("fazz", DeclarationType.Variable, "Long")] @@ -307,50 +353,20 @@ public void UDT_CreateFromFields() Public field2 As String"; var expected = -@"Private Type TestUDT +$@"Private Type {_defaultUDTIdentifier} Field1 As Long Field2 As String End Type"; - var actual = CodeBuilderUDTResult(inputCode, "field1", "field2"); + var actual = CodeBuilderUDTResult(inputCode, DeclarationType.Variable, "field1", "field2"); StringAssert.AreEqualIgnoringCase(expected, actual); } - [TestCase("Duplicate", "Duplicate")] - [TestCase("Duplicate", "DuplicATE")] - [Category(nameof(CodeBuilder))] - public void UDT_DuplicateMemberIdentifiers_Throws(string field1Identifier, string field2Identifier) - { - var inputCode = "Public field1 : Public field2"; - var vbe = MockVbeBuilder.BuildFromSingleStandardModule(inputCode, out _).Object; - var state = MockParser.CreateAndParse(vbe); - using (state) - { - var targets = state.DeclarationFinder.UserDeclarations(DeclarationType.Variable) - .OfType() - .Where(d => new string[] { "field1", "field2" }.Contains(d.IdentifierName)); - - var inputPairs = new List<(VariableDeclaration, string)>(); - inputPairs.Add((targets.First(), field1Identifier)); - inputPairs.Add((targets.Last(), field2Identifier)); - ICodeBuilder codeBuilder = new CodeBuilder(); - Assert.Throws(() => { codeBuilder.BuildUserDefinedTypeDeclaration("TestUDT", inputPairs); }); - } - - } - - [Test] - [Category(nameof(CodeBuilder))] - public void UDT_EmptyMembersList_Throws() - { - Assert.Throws(() => { new CodeBuilder().BuildUserDefinedTypeDeclaration("TestUDT", Enumerable.Empty<(VariableDeclaration, string)>()); }); - } - [Test] [Category(nameof(CodeBuilder))] public void UDT_ImplicitTypeMadeExplicit() { var inputCode = "Public field1"; - var actual = CodeBuilderUDTResult(inputCode, "field1"); + var actual = CodeBuilderUDTResult(inputCode, DeclarationType.Variable, "field1"); StringAssert.Contains("Field1 As Variant", actual); } @@ -373,20 +389,196 @@ public void UDT_FromArrayField(string dimensions, string type) ? "Variant" : type; - var actual = CodeBuilderUDTResult(inputCode, field); + var actual = CodeBuilderUDTResult(inputCode, DeclarationType.Variable, field); StringAssert.Contains($"Field1{dimensions} As {expectedType}", actual); } - private string CodeBuilderUDTResult(string inputCode, params string[] fields) + [Test] + [Category(nameof(CodeBuilder))] + public void UDT_CreateFromConstants() { - var vbe = MockVbeBuilder.BuildFromSingleStandardModule(inputCode, out _).Object; + var inputCode = +@" + Public Const field1 As Long = 5 + Public Const field2 As String = ""Yo"" +"; + + var expected = +$@"Private Type {_defaultUDTIdentifier} + Field1 As Long + Field2 As String +End Type"; + var actual = CodeBuilderUDTResult(inputCode, DeclarationType.Constant, "field1", "field2"); + StringAssert.AreEqualIgnoringCase(expected, actual); + } + + [TestCase("Property Get", "Property", DeclarationType.PropertyGet)] + [TestCase("Function", "Function", DeclarationType.Function)] + [Category(nameof(CodeBuilder))] + public void UDT_CreateFromFunctionPrototypes(string memberType, string memberEndStatement, DeclarationType declarationType) + { + var inputCode = +$@" + +Private mTestValue As Long +Private mTestValue2 As Variant + +Public {memberType} TestValue() As Long + TestValue = mTestValue +End {memberEndStatement} + + +Public {memberType} TestValue2() As Variant + TestValue2 = mTestValue2 +End {memberEndStatement} +"; + + var expected = +$@"Private Type {_defaultUDTIdentifier} + TestValue As Long + TestValue2 As Variant +End Type"; + + var actual = CodeBuilderUDTResult(inputCode, declarationType, "TestValue", "TestValue2"); + StringAssert.AreEqualIgnoringCase(expected, actual); + } + + [Test] + [Category(nameof(CodeBuilder))] + public void UDT_CreateFromUDTMemberPrototypes() + { + var inputCode = +$@" +Private Type ExistingType + FirstValue As Long + SecondValue As Byte + ThirdValue As String +End Type +"; + + var expected = +$@"Private Type {_defaultUDTIdentifier} + FirstValue As Long + ThirdValue As String +End Type"; + + var actual = CodeBuilderUDTResult(inputCode, DeclarationType.UserDefinedTypeMember, "FirstValue", "ThirdValue"); + StringAssert.AreEqualIgnoringCase(expected, actual); + } + + [TestCase("Property Let", "Property", DeclarationType.PropertyLet)] + [TestCase("Property Set", "Property", DeclarationType.PropertySet)] + [TestCase("Sub", "Sub", DeclarationType.Procedure)] + [Category(nameof(CodeBuilder))] + public void UDT_InvalidPrototypes_NoResult(string memberType, string memberEndStatement, DeclarationType declarationType) + { + var inputCode = +$@" +Public {memberType} TestValue(arg As Long) +End {memberEndStatement} +"; + var actual = CodeBuilderUDTResult(inputCode, declarationType, "TestValue", "TestValue2"); + Assert.IsTrue(string.IsNullOrEmpty(actual)); + } + + [Test] + [Category(nameof(CodeBuilder))] + public void UDT_NullUDTIdentifierBuildUDT_NoResult() + { + var vbe = MockVbeBuilder.BuildFromSingleStandardModule("Private test As Long", out _).Object; + var state = MockParser.CreateAndParse(vbe); + using (state) + { + var targets = state.DeclarationFinder.DeclarationsWithType(DeclarationType.Variable) + .Where(d => d.IdentifierName == "test") + .Select(d => (d, d.IdentifierName)); + + var result = new CodeBuilder().TryBuildUserDefinedTypeDeclaration(null, targets, out var declaration); + + Assert.IsFalse(result); + Assert.IsTrue(string.IsNullOrEmpty(declaration)); + } + } + + [Test] + [Category(nameof(CodeBuilder))] + public void UDT_EmptyPrototypeList_NoResult() + { + var result = new CodeBuilder().TryBuildUserDefinedTypeDeclaration(_defaultUDTIdentifier, Enumerable.Empty<(Declaration, string)>(), out var declaration); + Assert.IsFalse(result); + Assert.IsTrue(string.IsNullOrEmpty(declaration)); + } + + [Test] + [Category(nameof(CodeBuilder))] + public void UDT_NullDeclarationInPrototypeList_NoResult() + { + var nullInList = new List<(Declaration, string)>() { (null, "Fizz") }; + var result = new CodeBuilder().TryBuildUserDefinedTypeDeclaration(_defaultUDTIdentifier, nullInList, out var declaration); + Assert.IsFalse(result); + Assert.IsTrue(string.IsNullOrEmpty(declaration)); + } + + [Test] + [Category(nameof(CodeBuilder))] + public void UDT_NullIdentifierInPrototypeList_NoResult() + { + var vbe = MockVbeBuilder.BuildFromSingleStandardModule("Private test As Long", out _).Object; var state = MockParser.CreateAndParse(vbe); using (state) { + string nullIdentifier = null; var targets = state.DeclarationFinder.DeclarationsWithType(DeclarationType.Variable) - .Where(d => fields.Contains(d.IdentifierName)) - .Select(field => (field as VariableDeclaration, field.IdentifierName.CapitalizeFirstLetter())); - return new CodeBuilder().BuildUserDefinedTypeDeclaration("TestUDT", targets); + .Where(d => d.IdentifierName == "test") + .Select(d => (d, nullIdentifier)); + + var result = new CodeBuilder().TryBuildUserDefinedTypeDeclaration("TestType", targets, out var declaration); + + Assert.IsFalse(result); + Assert.IsTrue(string.IsNullOrEmpty(declaration)); + } + } + + [Test] + [Category(nameof(CodeBuilder))] + public void UDT_NullPrototype_NoResult() + { + var result = new CodeBuilder().TryBuildUDTMemberDeclaration(_defaultUDTIdentifier, null, out var declaration); + Assert.IsFalse(result); + Assert.IsTrue(string.IsNullOrEmpty(declaration)); + } + + [Test] + [Category(nameof(CodeBuilder))] + public void UDT_NullUDTIdentifierBuildUDTMember_NoResult() + { + var vbe = MockVbeBuilder.BuildFromSingleStandardModule("Private test As Long", out _).Object; + var state = MockParser.CreateAndParse(vbe); + using (state) + { + var target = state.DeclarationFinder.DeclarationsWithType(DeclarationType.Variable) + .Single(d => d.IdentifierName == "test"); + + var result = new CodeBuilder().TryBuildUDTMemberDeclaration(null, target, out var declaration); + + Assert.IsFalse(result); + Assert.IsTrue(string.IsNullOrEmpty(declaration)); + } + } + + private string CodeBuilderUDTResult(string inputCode, DeclarationType declarationType, params string[] prototypes) + { + var vbe = MockVbeBuilder.BuildFromSingleStandardModule(inputCode, out _).Object; + var state = MockParser.CreateAndParse(vbe); + using (state) + { + var targets = state.DeclarationFinder.DeclarationsWithType(declarationType) + .Where(d => prototypes.Contains(d.IdentifierName)) + .Select(prototype => (prototype, prototype.IdentifierName.CapitalizeFirstLetter())); + + return new CodeBuilder().TryBuildUserDefinedTypeDeclaration(_defaultUDTIdentifier, targets, out string declaration) + ? declaration + : string.Empty; } } diff --git a/RubberduckTests/Refactoring/CreateUDTMember/CreateUDTMemberRefactoringActionTests.cs b/RubberduckTests/Refactoring/CreateUDTMember/CreateUDTMemberRefactoringActionTests.cs index bd3c8fdc87..1e0e326fd0 100644 --- a/RubberduckTests/Refactoring/CreateUDTMember/CreateUDTMemberRefactoringActionTests.cs +++ b/RubberduckTests/Refactoring/CreateUDTMember/CreateUDTMemberRefactoringActionTests.cs @@ -152,7 +152,7 @@ private CreateUDTMemberModel TestModel(RubberduckParserState state, string udtId { var udtDeclaration = GetUniquelyNamedDeclaration(state, DeclarationType.UserDefinedType, udtIdentifier); - var conversionPairs = new List<(VariableDeclaration, string)>(); + var conversionPairs = new List<(Declaration, string)>(); foreach (var (fieldID, udtMemberID) in fieldConversions) { var fieldDeclaration = GetUniquelyNamedDeclaration(state, DeclarationType.Variable, fieldID) as VariableDeclaration; From 05e5304b2a523e5c8d54729e664746ae7a76db93 Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Tue, 15 Sep 2020 15:56:16 -0700 Subject: [PATCH 39/67] Remove aggregator dependency from top-level action --- .../EncapsulateFieldCodeBuilder.cs | 9 ++ .../EncapsulateFieldInsertNewCodeModel.cs | 2 + ...lateFieldInsertNewCodeRefactoringAction.cs | 30 +++-- .../EncapsulateFieldRefactoringAction.cs | 7 +- ...teFieldUseBackingFieldRefactoringAction.cs | 98 ++++++++-------- ...eldUseBackingUDTMemberRefactoringAction.cs | 108 +++++++++++------- .../EncapsulateFieldTestComponentResolver.cs | 3 +- 7 files changed, 153 insertions(+), 104 deletions(-) diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldCodeBuilder.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldCodeBuilder.cs index cb95bbdf9b..17f77cd3fc 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldCodeBuilder.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldCodeBuilder.cs @@ -11,6 +11,7 @@ public interface IEncapsulateFieldCodeBuilder (string Get, string Let, string Set) BuildPropertyBlocks(PropertyAttributeSet propertyAttributeSet); string BuildUserDefinedTypeDeclaration(IObjectStateUDT objectStateUDT, IEnumerable candidates); string BuildObjectStateFieldDeclaration(IObjectStateUDT objectStateUDT); + string BuildFieldDeclaration(Declaration target, string identifier); } public class EncapsulateFieldCodeBuilder : IEncapsulateFieldCodeBuilder @@ -89,5 +90,13 @@ public string BuildObjectStateFieldDeclaration(IObjectStateUDT objectStateUDT) { return $"{Accessibility.Private} {objectStateUDT.IdentifierName} {Tokens.As} {objectStateUDT.AsTypeName}"; } + + public string BuildFieldDeclaration(Declaration target, string identifier) + { + var identifierExpressionSansVisibility = target.Context.GetText().Replace(target.IdentifierName, identifier); + return target.IsTypeSpecified + ? $"{Tokens.Private} {identifierExpressionSansVisibility}" + : $"{Tokens.Private} {identifierExpressionSansVisibility} {Tokens.As} {target.AsTypeName}"; + } } } diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeModel.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeModel.cs index fc77ba75dc..04a6d76d73 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeModel.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeModel.cs @@ -25,5 +25,7 @@ public EncapsulateFieldInsertNewCodeModel(IEnumerable SelectedFieldCandidates { get; } + + public IEnumerable CandidatesRequiringNewBackingFields { set; get; } = Enumerable.Empty(); } } diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeRefactoringAction.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeRefactoringAction.cs index dce344ba99..dfea910b89 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeRefactoringAction.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeRefactoringAction.cs @@ -33,21 +33,35 @@ public override void Refactor(EncapsulateFieldInsertNewCodeModel model, IRewrite { if (model.CreateNewObjectStateUDT) { - var objectStateFieldDeclaration = _encapsulateFieldCodeBuilder.BuildObjectStateFieldDeclaration(model.ObjectStateUDTField); - model.NewContentAggregator.AddNewContent(NewContentType.DeclarationBlock, objectStateFieldDeclaration); - - var objectStateTypeDeclarationBlock = _encapsulateFieldCodeBuilder.BuildUserDefinedTypeDeclaration(model.ObjectStateUDTField, model.SelectedFieldCandidates); - model.NewContentAggregator.AddNewContent(NewContentType.UserDefinedTypeDeclaration, objectStateTypeDeclarationBlock); + CreateObjectStateUDTElements(model, rewriteSession); } - LoadNewPropertyBlocks(model, rewriteSession); + CreateBackingFields(model, rewriteSession); + + CreatePropertyBlocks(model, rewriteSession); InsertBlocks(model, rewriteSession); + } + + private void CreateObjectStateUDTElements(EncapsulateFieldInsertNewCodeModel model, IRewriteSession rewriteSession) + { + var objectStateFieldDeclaration = _encapsulateFieldCodeBuilder.BuildObjectStateFieldDeclaration(model.ObjectStateUDTField); + model.NewContentAggregator.AddNewContent(NewContentType.DeclarationBlock, objectStateFieldDeclaration); - model.NewContentAggregator = null; + var objectStateTypeDeclarationBlock = _encapsulateFieldCodeBuilder.BuildUserDefinedTypeDeclaration(model.ObjectStateUDTField, model.SelectedFieldCandidates); + model.NewContentAggregator.AddNewContent(NewContentType.UserDefinedTypeDeclaration, objectStateTypeDeclarationBlock); + } + + private void CreateBackingFields(EncapsulateFieldInsertNewCodeModel model, IRewriteSession rewriteSession) + { + foreach (var field in model.CandidatesRequiringNewBackingFields) + { + var newFieldDeclaration = _encapsulateFieldCodeBuilder.BuildFieldDeclaration(field.Declaration, field.BackingIdentifier); + model.NewContentAggregator.AddNewContent(NewContentType.DeclarationBlock, newFieldDeclaration); + } } - private void LoadNewPropertyBlocks(EncapsulateFieldInsertNewCodeModel model, IRewriteSession rewriteSession) + private void CreatePropertyBlocks(EncapsulateFieldInsertNewCodeModel model, IRewriteSession rewriteSession) { var propAttributeSets = model.SelectedFieldCandidates .SelectMany(f => _propertyAttributeSetsGenerator.GeneratePropertyAttributeSets(f)).ToList(); diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringAction.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringAction.cs index 8bcccad25f..b33b298064 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringAction.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringAction.cs @@ -8,16 +8,13 @@ public class EncapsulateFieldRefactoringAction : IRefactoringAction f.Declaration.IsDeclaredInList() + && !f.Declaration.HasPrivateAccessibility()) + .ToList(); - ModifyFields(model, rewriteSession); + ModifyFields(model, publicFieldsDeclaredInListsToReDeclareAsPrivateBackingFields, rewriteSession); ModifyReferences(model, rewriteSession); - InsertNewContent(model, rewriteSession); + InsertNewContent(model, publicFieldsDeclaredInListsToReDeclareAsPrivateBackingFields, rewriteSession); } - private void ModifyFields(EncapsulateFieldUseBackingFieldModel model, IRewriteSession rewriteSession) + private void ModifyFields(EncapsulateFieldUseBackingFieldModel model, List publicFieldsToRemove, IRewriteSession rewriteSession) { - var fieldDeclarationsToDeleteAndReplace = model.SelectedFieldCandidates - .Where(f => f.Declaration.IsDeclaredInList() - && !f.Declaration.HasPrivateAccessibility()) - .ToList(); - var rewriter = rewriteSession.CheckOutModuleRewriter(model.QualifiedModuleName); - rewriter.RemoveVariables(fieldDeclarationsToDeleteAndReplace.Select(f => f.Declaration) + rewriter.RemoveVariables(publicFieldsToRemove.Select(f => f.Declaration) .Cast()); - foreach (var field in fieldDeclarationsToDeleteAndReplace) - { - var targetIdentifier = field.Declaration.Context.GetText().Replace(field.IdentifierName, field.BackingIdentifier); - var newField = field.Declaration.IsTypeSpecified - ? $"{Tokens.Private} {targetIdentifier}" - : $"{Tokens.Private} {targetIdentifier} {Tokens.As} {field.Declaration.AsTypeName}"; - - model.NewContentAggregator.AddNewContent(NewContentType.DeclarationBlock, newField); - } - var retainedFieldDeclarations = model.SelectedFieldCandidates - .Except(fieldDeclarationsToDeleteAndReplace) + .Except(publicFieldsToRemove) .ToList(); if (retainedFieldDeclarations.Any()) @@ -99,16 +84,20 @@ private void ModifyReferences(EncapsulateFieldUseBackingFieldModel model, IRewri .Where(f => (f.Declaration.AsTypeDeclaration?.DeclarationType.HasFlag(DeclarationType.UserDefinedType) ?? false) && f.Declaration.AsTypeDeclaration.Accessibility == Accessibility.Private); - ReplaceEncapsulatedPrivateUserDefinedTypeMemberReferences(privateUdtInstances, rewriteSession); + ReplaceUDTMemberReferencesOfPrivateUDTFields(privateUdtInstances, rewriteSession); ReplaceEncapsulatedFieldReferences(model.SelectedFieldCandidates.Except(privateUdtInstances), rewriteSession); } - private void InsertNewContent(EncapsulateFieldUseBackingFieldModel model, IRewriteSession rewriteSession) + private void InsertNewContent(EncapsulateFieldUseBackingFieldModel model, List candidatesRequiringNewBackingFields, IRewriteSession rewriteSession) { + var aggregator = model.NewContentAggregator ?? _newContentAggregatorFactory.Create(); + model.NewContentAggregator = null; + var encapsulateFieldInsertNewCodeModel = new EncapsulateFieldInsertNewCodeModel(model.SelectedFieldCandidates) { - NewContentAggregator = model.NewContentAggregator + CandidatesRequiringNewBackingFields = candidatesRequiringNewBackingFields, + NewContentAggregator = aggregator }; _encapsulateFieldInsertNewCodeRefactoringAction.Refactor(encapsulateFieldInsertNewCodeModel, rewriteSession); @@ -120,42 +109,57 @@ private void ReplaceEncapsulatedFieldReferences(IEnumerable udtFieldCandidates, IRewriteSession rewriteSession) + private void ReplaceUDTMemberReferencesOfPrivateUDTFields(IEnumerable udtFieldCandidates, IRewriteSession rewriteSession) { if (!udtFieldCandidates.Any()) { return; } - var replacePrivateUDTMemberReferencesModel = _replaceUDTMemberReferencesModelFactory.Create(udtFieldCandidates.Select(f => f.Declaration).Cast()); + var replacePrivateUDTMemberReferencesModel + = _replaceUDTMemberReferencesModelFactory.Create(udtFieldCandidates.Select(f => f.Declaration).Cast()); foreach (var udtfield in udtFieldCandidates) { - foreach (var udtMember in replacePrivateUDTMemberReferencesModel.UDTMembers) - { - var udtExpressions = new PrivateUDTMemberReferenceReplacementExpressions($"{udtfield.IdentifierName}.{udtMember.IdentifierName}") - { - LocalReferenceExpression = udtMember.IdentifierName.CapitalizeFirstLetter(), - }; + InitializeModel(replacePrivateUDTMemberReferencesModel, udtfield); + } + _replaceUDTMemberReferencesRefactoringAction.Refactor(replacePrivateUDTMemberReferencesModel, rewriteSession); + } - replacePrivateUDTMemberReferencesModel.AssignUDTMemberReferenceExpressions(udtfield.Declaration as VariableDeclaration, udtMember, udtExpressions); + private void InitializeModel(ReplaceReferencesModel model, IEncapsulateFieldCandidate field) + { + foreach (var idRef in field.Declaration.References) + { + var replacementExpression = field.PropertyIdentifier; + + if (idRef.QualifiedModuleName == field.QualifiedModuleName && field.Declaration.IsArray) + { + replacementExpression = field.BackingIdentifier; } - _replaceUDTMemberReferencesRefactoringAction.Refactor(replacePrivateUDTMemberReferencesModel, rewriteSession); + + model.AssignReferenceReplacementExpression(idRef, replacementExpression); + } + } + + private void InitializeModel(ReplacePrivateUDTMemberReferencesModel model, IEncapsulateFieldCandidate udtfield) + { + foreach (var udtMember in model.UDTMembers) + { + var udtExpressions = new PrivateUDTMemberReferenceReplacementExpressions($"{udtfield.IdentifierName}.{udtMember.IdentifierName}") + { + LocalReferenceExpression = udtMember.IdentifierName, + }; + + model.AssignUDTMemberReferenceExpressions(udtfield.Declaration as VariableDeclaration, udtMember, udtExpressions); } } diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs index 3053f756fd..05797b2d1d 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs @@ -1,5 +1,4 @@ -using Rubberduck.Common; -using Rubberduck.Parsing.Rewriter; +using Rubberduck.Parsing.Rewriter; using Rubberduck.Parsing.Symbols; using Rubberduck.Refactorings.Common; using Rubberduck.Refactorings.CreateUDTMember; @@ -8,15 +7,15 @@ using System.Linq; using Rubberduck.Refactorings.EncapsulateField; using Rubberduck.Refactorings.EncapsulateFieldInsertNewCode; +using System.Collections.Generic; namespace Rubberduck.Refactorings.EncapsulateFieldUseBackingUDTMember { - public class EncapsulateFieldUseBackingUDTMemberRefactoringAction : CodeOnlyRefactoringActionBase { private readonly ICodeOnlyRefactoringAction _createUDTMemberRefactoringAction; - private readonly ICodeOnlyRefactoringAction _replaceUDTMemberReferencesRefactoringAction; - private readonly ICodeOnlyRefactoringAction _replaceFieldReferencesRefactoringAction; + private readonly ICodeOnlyRefactoringAction _replacePrivateUDTMemberReferencesRefactoringAction; + private readonly ICodeOnlyRefactoringAction _replaceReferencesRefactoringAction; private readonly ICodeOnlyRefactoringAction _encapsulateFieldInsertNewCodeRefactoringAction; private readonly IEncapsulateFieldCodeBuilder _encapsulateFieldCodeBuilder; private readonly INewContentAggregatorFactory _newContentAggregatorFactory; @@ -31,8 +30,8 @@ public class EncapsulateFieldUseBackingUDTMemberRefactoringAction : CodeOnlyRefa : base(rewritingManager) { _createUDTMemberRefactoringAction = refactoringActionsProvider.CreateUDTMember; - _replaceUDTMemberReferencesRefactoringAction = refactoringActionsProvider.ReplaceUDTMemberReferences; - _replaceFieldReferencesRefactoringAction = refactoringActionsProvider.ReplaceReferences; + _replacePrivateUDTMemberReferencesRefactoringAction = refactoringActionsProvider.ReplaceUDTMemberReferences; + _replaceReferencesRefactoringAction = refactoringActionsProvider.ReplaceReferences; _encapsulateFieldInsertNewCodeRefactoringAction = refactoringActionsProvider.EncapsulateFieldInsertNewCode; _encapsulateFieldCodeBuilder = encapsulateFieldCodeBuilderFactory.Create(); _replaceUDTMemberReferencesModelFactory = replaceUDTMemberReferencesModelFactory; @@ -46,11 +45,6 @@ public override void Refactor(EncapsulateFieldUseBackingUDTMemberModel model, IR return; } - if (model.NewContentAggregator is null) - { - model.NewContentAggregator = _newContentAggregatorFactory.Create(); - } - ModifyFields(model, rewriteSession); ModifyReferences(model, rewriteSession); @@ -77,57 +71,89 @@ private void ModifyFields(EncapsulateFieldUseBackingUDTMemberModel encapsulateFi private void ModifyReferences(EncapsulateFieldUseBackingUDTMemberModel model, IRewriteSession rewriteSession) { - var udtFields = model.SelectedFieldCandidates + var privateUDTFields = model.SelectedFieldCandidates .Where(f => (f.Declaration.AsTypeDeclaration?.DeclarationType.HasFlag(DeclarationType.UserDefinedType) ?? false) && f.Declaration.AsTypeDeclaration.Accessibility == Accessibility.Private); - if (udtFields.Any()) + ReplaceUDTMemberReferencesOfPrivateUDTFields(privateUDTFields, rewriteSession); + + ReplaceEncapsulatedFieldReferences(model.SelectedFieldCandidates.Except(privateUDTFields), model.ObjectStateUDTField, rewriteSession); + } + + private void ReplaceUDTMemberReferencesOfPrivateUDTFields(IEnumerable udtFields, IRewriteSession rewriteSession) + { + if (!udtFields.Any()) + { + return; + } + + var replacePrivateUDTMemberReferencesModel + = _replaceUDTMemberReferencesModelFactory.Create(udtFields.Select(f => f.Declaration).Cast()); + + foreach (var udtfield in udtFields) { - var replaceUDTMemberReferencesModel = _replaceUDTMemberReferencesModelFactory.Create(udtFields.Select(f => f.Declaration).Cast()); + InitializeModel(replacePrivateUDTMemberReferencesModel, udtfield); + } - foreach (var udtfield in udtFields) - { - foreach (var udtMember in replaceUDTMemberReferencesModel.UDTMembers) - { - var localReplacement = udtfield.Declaration.IsArray - ? $"{udtfield.IdentifierName}.{udtMember.IdentifierName.CapitalizeFirstLetter()}" - : udtMember.IdentifierName.CapitalizeFirstLetter(); - - var udtExpressions = new PrivateUDTMemberReferenceReplacementExpressions($"{udtfield.IdentifierName}.{udtMember.IdentifierName}") - { - LocalReferenceExpression = udtMember.IdentifierName.CapitalizeFirstLetter(), - }; - - replaceUDTMemberReferencesModel.AssignUDTMemberReferenceExpressions(udtfield.Declaration as VariableDeclaration, udtMember, udtExpressions); - } - _replaceUDTMemberReferencesRefactoringAction.Refactor(replaceUDTMemberReferencesModel, rewriteSession); - } + _replacePrivateUDTMemberReferencesRefactoringAction.Refactor(replacePrivateUDTMemberReferencesModel, rewriteSession); + } + + private void ReplaceEncapsulatedFieldReferences(IEnumerable nonPrivateUDTFields, IObjectStateUDT objectStateUDTField, IRewriteSession rewriteSession) + { + if (!nonPrivateUDTFields.Any()) + { + return; } - var modelReplaceField = new ReplaceReferencesModel() + var replaceReferencesModel = new ReplaceReferencesModel() { ModuleQualifyExternalReferences = true, }; - foreach (var field in model.SelectedFieldCandidates.Except(udtFields)) + foreach (var field in nonPrivateUDTFields) + { + InitializeModel(replaceReferencesModel, field, objectStateUDTField); + } + + _replaceReferencesRefactoringAction.Refactor(replaceReferencesModel, rewriteSession); + } + + private void InitializeModel(ReplacePrivateUDTMemberReferencesModel model, IEncapsulateFieldCandidate udtfield) + { + foreach (var udtMember in model.UDTMembers) + { + var udtExpressions = new PrivateUDTMemberReferenceReplacementExpressions($"{udtfield.IdentifierName}.{udtMember.IdentifierName}") + { + LocalReferenceExpression = udtMember.IdentifierName, + }; + + model.AssignUDTMemberReferenceExpressions(udtfield.Declaration as VariableDeclaration, udtMember, udtExpressions); + } + } + + private void InitializeModel(ReplaceReferencesModel model, IEncapsulateFieldCandidate field, IObjectStateUDT objectStateUDTField) + { + foreach (var idRef in field.Declaration.References) { - foreach (var idRef in field.Declaration.References) + var replacementExpression = field.PropertyIdentifier; + + if (idRef.QualifiedModuleName == field.QualifiedModuleName && field.Declaration.IsArray) { - var replacementExpression = idRef.QualifiedModuleName == field.QualifiedModuleName - ? field.Declaration.IsArray ? $"{model.ObjectStateUDTField.FieldIdentifier}.{field.BackingIdentifier}" : field.PropertyIdentifier - : field.PropertyIdentifier; - modelReplaceField.AssignReferenceReplacementExpression(idRef, replacementExpression); + replacementExpression = $"{objectStateUDTField.FieldIdentifier}.{field.BackingIdentifier}"; } + model.AssignReferenceReplacementExpression(idRef, replacementExpression); } - _replaceFieldReferencesRefactoringAction.Refactor(modelReplaceField, rewriteSession); } private void InsertNewContent(EncapsulateFieldUseBackingUDTMemberModel model, IRewriteSession rewriteSession) { + var aggregator = model.NewContentAggregator ?? _newContentAggregatorFactory.Create(); + model.NewContentAggregator = null; + var encapsulateFieldInsertNewCodeModel = new EncapsulateFieldInsertNewCodeModel(model.SelectedFieldCandidates) { - NewContentAggregator = model.NewContentAggregator, + NewContentAggregator = aggregator, ObjectStateUDTField = model.ObjectStateUDTField }; diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs index 30493e4ddd..7361a74e46 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs @@ -35,8 +35,7 @@ public EncapsulateFieldTestComponentResolver(IDeclarationFinderProvider declarat case nameof(EncapsulateFieldRefactoringAction): return new EncapsulateFieldRefactoringAction( ResolveImpl(), - ResolveImpl(), - ResolveImpl()) as T; + ResolveImpl()) as T; case nameof(ReplaceReferencesRefactoringAction): return new ReplaceReferencesRefactoringAction(_rewritingManager) as T; From 84b7a683dbb85b2b43ab56773aa2acc4dfbcd448 Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Sat, 19 Sep 2020 07:47:46 -0700 Subject: [PATCH 40/67] Add EncapsulateFieldCandidateCollectionProvider Adds and registers the EncapsulateFieldCandidateCollectionProviderFactory as well. --- .../Root/RubberduckIoCInstaller.cs | 4 +- .../EncapsulateFieldCollectionsProvider.cs | 97 +++++++++++++++++++ ...apsulateFieldCollectionsProviderFactory.cs | 43 ++++++++ 3 files changed, 142 insertions(+), 2 deletions(-) create mode 100644 Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldCollectionsProvider.cs create mode 100644 Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldCollectionsProviderFactory.cs diff --git a/Rubberduck.Main/Root/RubberduckIoCInstaller.cs b/Rubberduck.Main/Root/RubberduckIoCInstaller.cs index 66897fb3ba..2235cabd8b 100644 --- a/Rubberduck.Main/Root/RubberduckIoCInstaller.cs +++ b/Rubberduck.Main/Root/RubberduckIoCInstaller.cs @@ -411,8 +411,8 @@ private void RegisterEncapsulateFieldRefactoringFactories(IWindsorContainer cont container.Register(Component.For() .ImplementedBy() .LifestyleSingleton()); - container.Register(Component.For() - .ImplementedBy() + container.Register(Component.For() + .ImplementedBy() .LifestyleSingleton()); container.Register(Component.For() .ImplementedBy() diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldCollectionsProvider.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldCollectionsProvider.cs new file mode 100644 index 0000000000..5de7b494b0 --- /dev/null +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldCollectionsProvider.cs @@ -0,0 +1,97 @@ +using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.VBA; +using Rubberduck.VBEditor; +using System.Collections.Generic; +using System.Linq; + +namespace Rubberduck.Refactorings.EncapsulateField +{ + public interface IEncapsulateFieldCollectionsProvider + { + IReadOnlyCollection EncapsulateFieldCandidates { get; } + IReadOnlyCollection EncapsulateAsUserDefinedTypeMemberCandidates { get; } + IReadOnlyCollection ObjectStateUDTCandidates { get; } + } + + /// + /// EncapsulateFieldCollectionsProvider generates collections IEncapsulateFieldCandidate + /// instances, IEncapsulateFieldAsUDTMemberCandidate instances, and IObjectStateUDT instances. + /// It provides these collections to the various objects of the EncapsulateFieldRefactoring + /// so that they all operate in the same object sets. + /// + /// + /// The EncapsulateFieldCollectionsProvider is the source of these collections for the various objects of + /// the EncapsulateFieldRefactoring so that they all operate in the same set of object instances. + /// + public class EncapsulateFieldCollectionsProvider : IEncapsulateFieldCollectionsProvider + { + private readonly IDeclarationFinderProvider _declarationFinderProvider; + private readonly IEncapsulateFieldCandidateFactory _encapsulateFieldCandidateFactory; + private readonly IObjectStateUserDefinedTypeFactory _objectStateUserDefinedTypeFactory; + + public EncapsulateFieldCollectionsProvider( + IDeclarationFinderProvider declarationFinderProvider, + IEncapsulateFieldCandidateFactory encapsulateFieldCandidateFactory, + IObjectStateUserDefinedTypeFactory objectStateUserDefinedTypeFactory, + QualifiedModuleName qualifiedModuleName) + { + _declarationFinderProvider = declarationFinderProvider; + _encapsulateFieldCandidateFactory = encapsulateFieldCandidateFactory; + _objectStateUserDefinedTypeFactory = objectStateUserDefinedTypeFactory; + + EncapsulateFieldCandidates = _declarationFinderProvider.DeclarationFinder.Members(qualifiedModuleName, DeclarationType.Variable) + .Where(v => v.ParentDeclaration is ModuleDeclaration + && !v.IsWithEvents) + .Select(f => _encapsulateFieldCandidateFactory.Create(f)) + .ToList(); + + ObjectStateUDTCandidates = LoadObjectStateUDTCandidates(EncapsulateFieldCandidates, _objectStateUserDefinedTypeFactory, qualifiedModuleName); + + EncapsulateAsUserDefinedTypeMemberCandidates = LoadAsUDTMemberCandidates(EncapsulateFieldCandidates, ObjectStateUDTCandidates); + } + + public IReadOnlyCollection EncapsulateFieldCandidates { get; } + + public IReadOnlyCollection EncapsulateAsUserDefinedTypeMemberCandidates { get; } + + public IReadOnlyCollection ObjectStateUDTCandidates { get; } + + private static List LoadObjectStateUDTCandidates(IReadOnlyCollection fieldCandidates, IObjectStateUserDefinedTypeFactory factory, QualifiedModuleName qmn) + { + var objectStateUDTs = new List(); + objectStateUDTs = fieldCandidates + .OfType() + .Where(fc => fc.Declaration.Accessibility == Accessibility.Private + && fc.Declaration.AsTypeDeclaration.Accessibility == Accessibility.Private) + .Select(udtc => factory.Create(udtc)) + .ToList(); + + //If more than one instance of a UserDefinedType is available, it is disqualified as + //a field to host the module's state. + var multipleFieldsOfTheSameUDT = objectStateUDTs.ToLookup(os => os.Declaration.AsTypeDeclaration.IdentifierName); + foreach (var duplicate in multipleFieldsOfTheSameUDT.Where(d => d.Count() > 1)) + { + objectStateUDTs.RemoveAll(os => duplicate.Contains(os)); + } + + var defaultObjectStateUDT = factory.Create(qmn); + objectStateUDTs.Add(defaultObjectStateUDT); + + return objectStateUDTs; + } + + private static List LoadAsUDTMemberCandidates(IReadOnlyCollection fieldCandidates, IReadOnlyCollection objectStateUDTCandidates) + { + var encapsulateAsUDTMembers = new List(); + var defaultObjectStateUDT = objectStateUDTCandidates.FirstOrDefault(os => !os.IsExistingDeclaration); + + foreach (var field in fieldCandidates) + { + var asUDT = new EncapsulateFieldAsUDTMemberCandidate(field, defaultObjectStateUDT); + encapsulateAsUDTMembers.Add(asUDT); + } + + return encapsulateAsUDTMembers; + } + } +} diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldCollectionsProviderFactory.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldCollectionsProviderFactory.cs new file mode 100644 index 0000000000..6049f830bf --- /dev/null +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldCollectionsProviderFactory.cs @@ -0,0 +1,43 @@ +using Rubberduck.Parsing.VBA; +using Rubberduck.Refactorings.EncapsulateField; +using Rubberduck.VBEditor; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Rubberduck.Refactorings +{ + public interface IEncapsulateFieldCollectionsProviderFactory + { + IEncapsulateFieldCollectionsProvider Create(QualifiedModuleName qualifiedModuleName); + } + + public class EncapsulateFieldCollectionsProviderFactory : IEncapsulateFieldCollectionsProviderFactory + { + private readonly IDeclarationFinderProvider _declarationFinderProvider; + private readonly IEncapsulateFieldCandidateFactory _encapsulateFieldCandidateFactory; + private readonly IObjectStateUserDefinedTypeFactory _objectStateUserDefinedTypeFactory; + + public EncapsulateFieldCollectionsProviderFactory( + IDeclarationFinderProvider declarationFinderProvider, + IEncapsulateFieldCandidateFactory encapsulateFieldCandidateFactory, + IObjectStateUserDefinedTypeFactory objectStateUserDefinedTypeFactory) + { + _declarationFinderProvider = declarationFinderProvider; + _encapsulateFieldCandidateFactory = encapsulateFieldCandidateFactory; + _objectStateUserDefinedTypeFactory = objectStateUserDefinedTypeFactory; + } + + public IEncapsulateFieldCollectionsProvider Create(QualifiedModuleName qualifiedModuleName) + { + return new EncapsulateFieldCollectionsProvider( + _declarationFinderProvider, + _encapsulateFieldCandidateFactory, + _objectStateUserDefinedTypeFactory, + qualifiedModuleName); + } + } + +} From f3226ef852adca3628f4efe98684096e6f9eb5a6 Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Wed, 16 Sep 2020 11:31:47 -0700 Subject: [PATCH 41/67] Simplify ConflictDetection Refactored ConflictFinders to a single class and interface. Restructured EncapsulateFieldRefactoring object instantiations and initialization given that all now share a single IEncapsulateFieldConflictFinder instance. --- .../EncapsulateFieldConflictFinder.cs | 282 ++++++++++++++++++ .../EncapsulateFieldConflictFinderBase.cs | 248 --------------- .../EncapsulateFieldConflictFinderFactory.cs | 13 +- ...lateFieldUseBackingFieldsConflictFinder.cs | 18 -- ...eFieldUseBackingUDTMemberConflictFinder.cs | 122 -------- .../EncapsulateField/EncapsulateFieldModel.cs | 66 ++-- .../EncapsulateFieldModelFactory.cs | 28 +- .../EncapsulateFieldRefactoring.cs | 34 ++- ...psulateFieldUseBackingFieldModelFactory.cs | 22 +- ...ateFieldUseBackingUDTMemberModelFactory.cs | 50 ++-- .../FieldCandidates/ArrayCandidate.cs | 5 +- .../EncapsulateFieldAsUDTMemberCandidate.cs | 15 +- .../EncapsulateFieldCandidate.cs | 22 +- ...apsulateFieldCandidateCollectionFactory.cs | 36 --- .../EncapsulateFieldCandidateFactory.cs | 4 +- .../UserDefinedTypeMemberCandidate.cs | 1 + .../EncapsulateFieldCommandTests.cs | 1 - .../EncapsulateFieldTestComponentResolver.cs | 18 +- ...port.cs => EncapsulateFieldTestSupport.cs} | 19 +- .../EncapsulateFieldValidatorTests.cs | 99 +++++- 20 files changed, 520 insertions(+), 583 deletions(-) create mode 100644 Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldConflictFinder.cs delete mode 100644 Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldConflictFinderBase.cs delete mode 100644 Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldUseBackingFieldsConflictFinder.cs delete mode 100644 Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldUseBackingUDTMemberConflictFinder.cs delete mode 100644 Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidateCollectionFactory.cs rename RubberduckTests/Refactoring/EncapsulateField/{EncapsulateFIeldTestSupport.cs => EncapsulateFieldTestSupport.cs} (97%) diff --git a/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldConflictFinder.cs b/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldConflictFinder.cs new file mode 100644 index 0000000000..d92f976977 --- /dev/null +++ b/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldConflictFinder.cs @@ -0,0 +1,282 @@ +using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.VBA; +using Rubberduck.Refactorings.Common; +using Rubberduck.Refactorings.EncapsulateField.Extensions; +using Rubberduck.Resources; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Rubberduck.Refactorings.EncapsulateField +{ + public interface IEncapsulateFieldConflictFinder + { + bool IsConflictingIdentifier(IEncapsulateFieldCandidate field, string identifierToCompare, out string errorMessage); + (bool IsValid, string ValidationError) ValidateEncapsulationAttributes(IEncapsulateFieldCandidate field); + void AssignNoConflictIdentifiers(IEncapsulateFieldCandidate candidate); + void AssignNoConflictIdentifiers(IObjectStateUDT stateUDT); + void AssignNoConflictIdentifiers(IEnumerable candidates); + } + + public class EncapsulateFieldConflictFinder : IEncapsulateFieldConflictFinder + { + private static List _declarationTypesThatNeverConflictWithFieldAndPropertyIdentifiers = new List() + { + DeclarationType.Project, + DeclarationType.ProceduralModule, + DeclarationType.ClassModule, + DeclarationType.Parameter, + DeclarationType.Enumeration, + DeclarationType.UserDefinedType, + DeclarationType.UserDefinedTypeMember + }; + + private readonly IDeclarationFinderProvider _declarationFinderProvider; + private readonly List _fieldCandidates; + private readonly List _udtMemberCandidates; + private readonly List _allCandidates; + private readonly List _objectStateUDTs; + private readonly List _members; + private readonly List _membersThatCanConflictWithFieldAndPropertyIdentifiers; + private readonly List _existingUserUDTsAndEnums; + + public EncapsulateFieldConflictFinder(IDeclarationFinderProvider declarationFinderProvider, IEnumerable candidates, IEnumerable objectStateUDTs) + { + _declarationFinderProvider = declarationFinderProvider; + + _fieldCandidates = candidates.ToList(); + + _udtMemberCandidates = new List(); + _fieldCandidates.ForEach(c => LoadUDTMembers(c)); + + _allCandidates = _fieldCandidates.Concat(_udtMemberCandidates).ToList(); + + _objectStateUDTs = objectStateUDTs.ToList(); + + _members = _declarationFinderProvider.DeclarationFinder.Members(_allCandidates.First().QualifiedModuleName).ToList(); + + _membersThatCanConflictWithFieldAndPropertyIdentifiers = + _members.Where(m => !_declarationTypesThatNeverConflictWithFieldAndPropertyIdentifiers.Contains(m.DeclarationType)).ToList(); + + _existingUserUDTsAndEnums = _members.Where(m => m.DeclarationType.HasFlag(DeclarationType.UserDefinedType) + || m.DeclarationType.HasFlag(DeclarationType.Enumeration)).ToList(); + } + + public (bool IsValid, string ValidationError) ValidateEncapsulationAttributes(IEncapsulateFieldCandidate field) + { + if (!field.EncapsulateFlag) + { + return (true, string.Empty); + } + + var declarationType = field is IEncapsulateFieldAsUDTMemberCandidate + ? DeclarationType.UserDefinedTypeMember + : field.Declaration.DeclarationType; + + var errorMessage = string.Empty; + + var hasInvalidIdentifierOrHasConflicts = + VBAIdentifierValidator.TryMatchInvalidIdentifierCriteria(field.PropertyIdentifier, declarationType, out errorMessage, field.Declaration.IsArray) + || IsConflictingIdentifier(field, field.PropertyIdentifier, out errorMessage) + || IsConflictingIdentifier(field, field.BackingIdentifier, out errorMessage) + || field is IEncapsulateFieldAsUDTMemberCandidate && ConflictsWithExistingUDTMembers(SelectedObjectStateUDT(), field.BackingIdentifier, out errorMessage); + + return (string.IsNullOrEmpty(errorMessage), errorMessage); + } + + public bool IsConflictingIdentifier(IEncapsulateFieldCandidate field, string identifierToCompare, out string errorMessage) + { + errorMessage = string.Empty; + if (HasConflictIdentifiers(field, identifierToCompare)) + { + errorMessage = RubberduckUI.EncapsulateField_NameConflictDetected; + } + return !string.IsNullOrEmpty(errorMessage); + } + + public void AssignNoConflictIdentifiers(IEnumerable candidates) + { + foreach (var candidate in candidates.Where(c => c.EncapsulateFlag)) + { + ResolveFieldConflicts(candidate); + } + } + + private void ResolveFieldConflicts(IEncapsulateFieldCandidate candidate) + { + AssignNoConflictIdentifiers(candidate); + if (candidate is IUserDefinedTypeCandidate udtCandidate) + { + ResolveUDTMemberConflicts(udtCandidate.Members); + } + } + + private void ResolveUDTMemberConflicts(IEnumerable members) + { + foreach (var member in members) + { + AssignNoConflictIdentifiers(member); + if (member.WrappedCandidate is IUserDefinedTypeCandidate childUDT + && childUDT.Declaration.AsTypeDeclaration.HasPrivateAccessibility()) + { + ResolveFieldConflicts(childUDT); + } + } + } + + public void AssignNoConflictIdentifiers(IEncapsulateFieldCandidate candidate) + { + if (candidate is IEncapsulateFieldAsUDTMemberCandidate) + { + AssignIdentifier( + () => ConflictsWithExistingUDTMembers(SelectedObjectStateUDT(), candidate.PropertyIdentifier, out _), + () => IncrementPropertyIdentifier(candidate)); + return; + } + + AssignNoConflictPropertyIdentifier(candidate); + AssignNoConflictBackingFieldIdentifier(candidate); + } + + public void AssignNoConflictIdentifiers(IObjectStateUDT stateUDT) + { + AssignIdentifier( + () => HasConflictingFieldIdentifier(stateUDT, stateUDT.FieldIdentifier), + () => stateUDT.FieldIdentifier = stateUDT.FieldIdentifier.IncrementEncapsulationIdentifier()); + + AssignIdentifier( + () => _existingUserUDTsAndEnums.Any(m => m.IdentifierName.IsEquivalentVBAIdentifierTo(stateUDT.TypeIdentifier)), + () => stateUDT.TypeIdentifier = stateUDT.TypeIdentifier.IncrementEncapsulationIdentifier()); + } + + private IObjectStateUDT SelectedObjectStateUDT() => _objectStateUDTs.SingleOrDefault(os => os.IsSelected); + + private static bool ConflictsWithExistingUDTMembers(IObjectStateUDT objectStateUDT, string identifier, out string errorMessage) + { + errorMessage = string.Empty; + if (objectStateUDT?.ExistingMembers.Any(nm => nm.IdentifierName.IsEquivalentVBAIdentifierTo(identifier)) ?? false) + { + errorMessage = RubberduckUI.EncapsulateField_NameConflictDetected; + } + return !string.IsNullOrEmpty(errorMessage); + } + + private void IncrementPropertyIdentifier(IEncapsulateFieldCandidate candidate) + => candidate.PropertyIdentifier = candidate.PropertyIdentifier.IncrementEncapsulationIdentifier(); + + private void AssignNoConflictPropertyIdentifier(IEncapsulateFieldCandidate candidate) + { + AssignIdentifier( + () => IsConflictingIdentifier(candidate, candidate.PropertyIdentifier, out _), + () => IncrementPropertyIdentifier(candidate)); + } + + private void AssignNoConflictBackingFieldIdentifier(IEncapsulateFieldCandidate candidate) + { + //Private UserDefinedTypes are never used directly as a backing field - so never change their identifier. + //The backing fields for an encapsulated Private UDT are its members. + if (!(candidate is UserDefinedTypeMemberCandidate + || candidate is IUserDefinedTypeCandidate udtCandidate && udtCandidate.TypeDeclarationIsPrivate)) + { + AssignIdentifier( + () => IsConflictingIdentifier(candidate, candidate.BackingIdentifier, out _), + () => candidate.BackingIdentifier = candidate.BackingIdentifier.IncrementEncapsulationIdentifier()); + } + } + + private static void AssignIdentifier(Func hasConflict, Action incrementIdentifier, int maxAttempts = 20) + { + var guard = 0; + while (guard++ < maxAttempts && hasConflict()) + { + incrementIdentifier(); + } + + if (guard >= maxAttempts) + { + throw new OverflowException("Unable to assign a non conflicting identifier"); + } + } + + private bool HasConflictIdentifiers(IEncapsulateFieldCandidate candidate, string identifierToCompare) + { + if (_allCandidates.Where(c => c.TargetID != candidate.TargetID + && c.EncapsulateFlag + && c.PropertyIdentifier.IsEquivalentVBAIdentifierTo(identifierToCompare)).Any()) + { + return true; + } + + var membersToEvaluate = _members.Where(d => d != candidate.Declaration); + + if (candidate is IEncapsulateFieldAsUDTMemberCandidate) + { + membersToEvaluate = membersToEvaluate.Except( + _fieldCandidates.Where(fc => fc.EncapsulateFlag && fc.Declaration.DeclarationType.HasFlag(DeclarationType.Variable)) + .Select(f => f.Declaration)); + } + + var nameConflictCandidates = membersToEvaluate.Where(member => !(member.IsLocalVariable() || member.IsLocalConstant() + || _declarationTypesThatNeverConflictWithFieldAndPropertyIdentifiers.Contains(member.DeclarationType))); + + if (nameConflictCandidates.Any(m => m.IdentifierName.IsEquivalentVBAIdentifierTo(identifierToCompare))) + { + return true; + } + + //Only check IdentifierReferences in the declaring module because IdentifierReferences in + //other modules will be module-qualified. + var candidateLocalReferences = candidate.Declaration.References.Where(rf => rf.QualifiedModuleName == candidate.QualifiedModuleName); + + var localDeclarationConflictCandidates = membersToEvaluate.Where(localDec => candidateLocalReferences + .Any(cr => cr.ParentScoping == localDec.ParentScopeDeclaration)); + + return localDeclarationConflictCandidates.Any(m => m.IdentifierName.IsEquivalentVBAIdentifierTo(identifierToCompare)); + } + + private bool HasConflictingFieldIdentifier(IObjectStateUDT candidate, string identifierToCompare) + { + if (candidate.IsExistingDeclaration) + { + return false; + } + + if (_allCandidates.Where(c => c.EncapsulateFlag + && c.PropertyIdentifier.IsEquivalentVBAIdentifierTo(identifierToCompare)).Any()) + { + return true; + } + + var fieldsToRemoveFromConflictCandidates = _fieldCandidates + .Where(fc => fc.EncapsulateFlag && fc.Declaration.DeclarationType.HasFlag(DeclarationType.Variable)) + .Select(fc => fc.Declaration); + + var nameConflictCandidates = + _members.Except(fieldsToRemoveFromConflictCandidates) + .Where(member => !(member.IsLocalVariable() || member.IsLocalConstant() + || _declarationTypesThatNeverConflictWithFieldAndPropertyIdentifiers.Contains(member.DeclarationType))); + + return nameConflictCandidates.Any(m => m.IdentifierName.IsEquivalentVBAIdentifierTo(identifierToCompare)); + } + + private void LoadUDTMembers(IEncapsulateFieldCandidate candidate) + { + if (!(candidate is IUserDefinedTypeCandidate udtCandidate)) + { + return; + } + + foreach (var member in udtCandidate.Members) + { + _udtMemberCandidates.Add(member); + + if (member.WrappedCandidate is IUserDefinedTypeCandidate childUDT + && childUDT.Declaration.AsTypeDeclaration.HasPrivateAccessibility()) + { + //recursive till a non-UserDefinedType member is found + LoadUDTMembers(childUDT); + } + } + } + } +} diff --git a/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldConflictFinderBase.cs b/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldConflictFinderBase.cs deleted file mode 100644 index befabb796e..0000000000 --- a/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldConflictFinderBase.cs +++ /dev/null @@ -1,248 +0,0 @@ -using Rubberduck.Parsing.Symbols; -using Rubberduck.Parsing.VBA; -using Rubberduck.Refactorings.Common; -using Rubberduck.Refactorings.EncapsulateField.Extensions; -using Rubberduck.Resources; -using Rubberduck.VBEditor; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; - -namespace Rubberduck.Refactorings.EncapsulateField -{ - public interface IEncapsulateFieldConflictFinder - { - bool HasConflictingIdentifier(IEncapsulateFieldCandidate field, DeclarationType declarationType, out string errorMessage); - IEncapsulateFieldCandidate AssignNoConflictIdentifiers(IEncapsulateFieldCandidate candidate); - bool IsConflictingProposedIdentifier(string fieldName, IEncapsulateFieldCandidate candidate, DeclarationType declarationType); - bool TryValidateEncapsulationAttributes(IEncapsulateFieldCandidate field, out string errorMessage); - } - - public abstract class EncapsulateFieldConflictFinderBase - { - protected readonly IDeclarationFinderProvider _declarationFinderProvider; - protected List _fieldCandidates { set; get; } = new List(); - protected List _udtMemberCandidates { set; get; } = new List(); - - public EncapsulateFieldConflictFinderBase(IDeclarationFinderProvider declarationFinderProvider, IEnumerable candidates) - { - _declarationFinderProvider = declarationFinderProvider; - _fieldCandidates.AddRange(candidates); - _fieldCandidates.ForEach(c => LoadUDTMembers(_udtMemberCandidates, c)); - } - - private void LoadUDTMembers(List udtMembers, IEncapsulateFieldCandidate candidate) - { - if (!(candidate is IUserDefinedTypeCandidate udtCandidate)) - { - return; - } - - foreach (var member in udtCandidate.Members) - { - udtMembers.Add(member); - - if (member.WrappedCandidate is IUserDefinedTypeCandidate childUDT - && childUDT.Declaration.AsTypeDeclaration.HasPrivateAccessibility()) - { - LoadUDTMembers(udtMembers, childUDT); - } - } - } - - public virtual bool TryValidateEncapsulationAttributes(IEncapsulateFieldCandidate field, out string errorMessage) - { - errorMessage = string.Empty; - if (!field.EncapsulateFlag) - { - return true; - } - - var declarationType = field is IEncapsulateFieldAsUDTMemberCandidate udtMember - ? DeclarationType.UserDefinedTypeMember - : field.Declaration.DeclarationType; - - if (VBAIdentifierValidator.TryMatchInvalidIdentifierCriteria(field.PropertyIdentifier, declarationType, out errorMessage, field.Declaration.IsArray)) - { - return false; - } - - if (HasConflictingIdentifier(field, DeclarationType.Property, out errorMessage)) - { - return false; - } - - return true; - } - - public bool HasConflictingIdentifier(IEncapsulateFieldCandidate field, DeclarationType declarationType, out string errorMessage) - => InternalHasConflictingIdentifier(field, declarationType, false, out errorMessage); - - public virtual IEncapsulateFieldCandidate AssignNoConflictIdentifiers(IEncapsulateFieldCandidate candidate) - { - candidate = AssignNoConflictIdentifier(candidate, DeclarationType.Property); - if (!(candidate is UserDefinedTypeMemberCandidate)) - { - candidate = AssignNoConflictIdentifier(candidate, DeclarationType.Variable); - } - return candidate; - } - - protected virtual IEncapsulateFieldCandidate AssignNoConflictIdentifier(IEncapsulateFieldCandidate candidate, DeclarationType declarationType) - { - Debug.Assert(declarationType.HasFlag(DeclarationType.Property) - || declarationType.HasFlag(DeclarationType.Variable)); - - var isConflictingIdentifier = HasConflictingIdentifierIgnoreEncapsulationFlag(candidate, declarationType, out _); - var guard = 0; - while (guard++ < 10 && isConflictingIdentifier) - { - var identifier = IdentifierToCompare(candidate, declarationType); - - if (declarationType.HasFlag(DeclarationType.Property)) - { - candidate.PropertyIdentifier = identifier.IncrementEncapsulationIdentifier(); - } - else - { - candidate.BackingIdentifier = identifier.IncrementEncapsulationIdentifier(); - } - isConflictingIdentifier = HasConflictingIdentifierIgnoreEncapsulationFlag(candidate, declarationType, out _); - } - - return candidate; - } - - public bool IsConflictingProposedIdentifier(string fieldName, IEncapsulateFieldCandidate candidate, DeclarationType declarationType) - => PotentialConflictIdentifiers(candidate, declarationType) - .Any(m => m.IsEquivalentVBAIdentifierTo(fieldName)); - - protected abstract IEnumerable FindRelevantMembers(IEncapsulateFieldCandidate candidate); - - protected virtual bool InternalHasConflictingIdentifier(IEncapsulateFieldCandidate field, DeclarationType declarationType, bool ignoreEncapsulationFlags, out string errorMessage) - { - errorMessage = string.Empty; - - var potentialDeclarationIdentifierConflicts = new List(); - var membersAndLocalReferencesMatches = PotentialConflictIdentifiers(field, declarationType); - potentialDeclarationIdentifierConflicts.AddRange(membersAndLocalReferencesMatches); - - var propertiesOfOtherFieldCandidates = _fieldCandidates.Where(fc => fc.TargetID != field.TargetID).Select(fc => fc.PropertyIdentifier); - if (ignoreEncapsulationFlags) - { - potentialDeclarationIdentifierConflicts.AddRange(propertiesOfOtherFieldCandidates); - } - else - { - potentialDeclarationIdentifierConflicts.AddRange(FlaggedCandidates.Where(fc => fc.TargetID != field.TargetID).Select(fc => fc.PropertyIdentifier)); - } - - var udtMemberNameConflictCandidates = _udtMemberCandidates.Where(udtm => udtm.TargetID != field.TargetID && udtm.EncapsulateFlag).Select(udtm => udtm.PropertyIdentifier); - - potentialDeclarationIdentifierConflicts.AddRange(udtMemberNameConflictCandidates); - - var identifierToCompare = IdentifierToCompare(field, declarationType); - - if (potentialDeclarationIdentifierConflicts.Any(m => m.IsEquivalentVBAIdentifierTo(identifierToCompare))) - { - errorMessage = RubberduckUI.EncapsulateField_NameConflictDetected; - return true; - } - return false; - } - - protected string IdentifierToCompare(IEncapsulateFieldCandidate field, DeclarationType declarationType) - { - return declarationType.HasFlag(DeclarationType.Property) - ? field.PropertyIdentifier - : field.BackingIdentifier; - } - - protected bool HasConflictingIdentifierIgnoreEncapsulationFlag(IEncapsulateFieldCandidate field, DeclarationType declarationType, out string errorMessage) - => InternalHasConflictingIdentifier(field, declarationType, true, out errorMessage); - - /// - ///The refactoring only inserts new code elements with the following Accessibilities: - ///Variables => Private - ///Properties => Public - ///UDTs => Private - /// - private bool IsAlwaysIgnoreNameConflictType(Declaration d, DeclarationType toEnapsulateDeclarationType) - { - //5.3.1.6 Each and must have a procedure - //name that is different from any other module variable name, module constant name, - //enum member name, or procedure name that is defined within the same module. - var NeverCauseNameConflictTypes = new List() - { - DeclarationType.Project, - DeclarationType.ProceduralModule, - DeclarationType.ClassModule, - DeclarationType.Parameter, - DeclarationType.EnumerationMember, - DeclarationType.Enumeration, - DeclarationType.UserDefinedType, - DeclarationType.UserDefinedTypeMember - }; - - if (toEnapsulateDeclarationType.HasFlag(DeclarationType.Variable)) - { - //5.2.3.4: An enum member name may not be the same as any variable name - //or constant name that is defined within the same module - NeverCauseNameConflictTypes.Remove(DeclarationType.EnumerationMember); - } - else if (toEnapsulateDeclarationType.HasFlag(DeclarationType.UserDefinedType)) - { - //5.2.3.3 If an is an element of a its - //UDT name cannot be the same as the enum name of any - //or the UDT name of any other within the same - NeverCauseNameConflictTypes.Remove(DeclarationType.UserDefinedType); - NeverCauseNameConflictTypes.Remove(DeclarationType.Enumeration); - } - else if (toEnapsulateDeclarationType.HasFlag(DeclarationType.Property)) - { - //Each < subroutine - declaration > and < function - declaration > must have a - //procedure name that is different from any other module variable name, - //module constant name, enum member name, or procedure name that is defined - //within the same module. - - NeverCauseNameConflictTypes.Remove(DeclarationType.EnumerationMember); - } - return d.IsLocalVariable() - || d.IsLocalConstant() - || NeverCauseNameConflictTypes.Contains(d.DeclarationType); - } - - private List PotentialConflictIdentifiers(IEncapsulateFieldCandidate candidate, DeclarationType declarationType) - { - var members = FindRelevantMembers(candidate); - - var nameConflictCandidates = members - .Where(d => !IsAlwaysIgnoreNameConflictType(d, declarationType)).ToList(); - - var localReferences = candidate.Declaration.References.Where(rf => rf.QualifiedModuleName == candidate.QualifiedModuleName); - - if (localReferences.Any()) - { - foreach (var idRef in localReferences) - { - var locals = members.Except(nameConflictCandidates) - .Where(localDec => localDec.ParentScopeDeclaration.Equals(idRef.ParentScoping)); - - nameConflictCandidates.AddRange(locals); - } - } - return nameConflictCandidates.Select(c => c.IdentifierName).ToList(); - } - - private List FlaggedCandidates - => _fieldCandidates.Where(f => f.EncapsulateFlag).ToList(); - - private bool IsConflictIdentifier(string fieldName, QualifiedModuleName qmn, DeclarationType declarationType) - { - var nameConflictCandidates = _declarationFinderProvider.DeclarationFinder.Members(qmn) - .Where(d => !IsAlwaysIgnoreNameConflictType(d, declarationType)).ToList(); - - return nameConflictCandidates.Any(m => m.IdentifierName.IsEquivalentVBAIdentifierTo(fieldName)); - } - } -} diff --git a/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldConflictFinderFactory.cs b/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldConflictFinderFactory.cs index 8ec8912702..ab950ebdc7 100644 --- a/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldConflictFinderFactory.cs +++ b/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldConflictFinderFactory.cs @@ -1,13 +1,13 @@ using Rubberduck.Parsing.VBA; using Rubberduck.Refactorings.EncapsulateField; using System.Collections.Generic; +using System.Linq; namespace Rubberduck.Refactorings { public interface IEncapsulateFieldConflictFinderFactory { - IEncapsulateFieldConflictFinder CreateEncapsulateFieldUseBackingFieldConflictFinder(IReadOnlyCollection candidates); - IEncapsulateFieldConflictFinder CreateEncapsulateFieldUseBackingUDTMemberConflictFinder(IReadOnlyCollection candidates, IReadOnlyCollection objectStateUDTs); + IEncapsulateFieldConflictFinder Create(IEncapsulateFieldCollectionsProvider collectionProvider); } public class EncapsulateFieldConflictFinderFactory : IEncapsulateFieldConflictFinderFactory @@ -18,14 +18,9 @@ public EncapsulateFieldConflictFinderFactory(IDeclarationFinderProvider declarat _declarationFinderProvider = declarationFinderProvider; } - public IEncapsulateFieldConflictFinder CreateEncapsulateFieldUseBackingFieldConflictFinder(IReadOnlyCollection candidates) + public IEncapsulateFieldConflictFinder Create(IEncapsulateFieldCollectionsProvider collectionProvider) { - return new EncapsulateFieldUseBackingFieldsConflictFinder(_declarationFinderProvider, candidates); - } - - public IEncapsulateFieldConflictFinder CreateEncapsulateFieldUseBackingUDTMemberConflictFinder(IReadOnlyCollection candidates, IReadOnlyCollection objectStateUDTs) - { - return new EncapsulateFieldUseBackingUDTMemberConflictFinder(_declarationFinderProvider, candidates, objectStateUDTs); + return new EncapsulateFieldConflictFinder(_declarationFinderProvider, collectionProvider.EncapsulateFieldCandidates, collectionProvider.ObjectStateUDTCandidates); } } } diff --git a/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldUseBackingFieldsConflictFinder.cs b/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldUseBackingFieldsConflictFinder.cs deleted file mode 100644 index b17a5cb92e..0000000000 --- a/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldUseBackingFieldsConflictFinder.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Rubberduck.Parsing.Symbols; -using Rubberduck.Parsing.VBA; -using System.Collections.Generic; -using System.Linq; - -namespace Rubberduck.Refactorings.EncapsulateField -{ - public class EncapsulateFieldUseBackingFieldsConflictFinder : EncapsulateFieldConflictFinderBase, IEncapsulateFieldConflictFinder - { - public EncapsulateFieldUseBackingFieldsConflictFinder(IDeclarationFinderProvider declarationFinderProvider, IEnumerable candidates) - : base(declarationFinderProvider, candidates) - { } - - protected override IEnumerable FindRelevantMembers(IEncapsulateFieldCandidate candidate) - => _declarationFinderProvider.DeclarationFinder.Members(candidate.QualifiedModuleName) - .Where(d => d != candidate.Declaration); - } -} diff --git a/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldUseBackingUDTMemberConflictFinder.cs b/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldUseBackingUDTMemberConflictFinder.cs deleted file mode 100644 index 4885b7d329..0000000000 --- a/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldUseBackingUDTMemberConflictFinder.cs +++ /dev/null @@ -1,122 +0,0 @@ -using Rubberduck.Parsing.Symbols; -using Rubberduck.Parsing.VBA; -using Rubberduck.Refactorings.EncapsulateField.Extensions; -using System.Collections.Generic; -using System.Linq; - -namespace Rubberduck.Refactorings.EncapsulateField -{ - public interface IEncapsulateFieldUseBackingUDTMemberConflictFinder : IEncapsulateFieldConflictFinder - { - IObjectStateUDT AssignNoConflictIdentifiers(IObjectStateUDT stateUDT); - } - - public class EncapsulateFieldUseBackingUDTMemberConflictFinder : EncapsulateFieldConflictFinderBase, IEncapsulateFieldUseBackingUDTMemberConflictFinder - { - private static DeclarationType[] _udtTypeIdentifierNonConflictTypes = new DeclarationType[] - { - DeclarationType.Project, - DeclarationType.Module, - DeclarationType.Property, - DeclarationType.Function, - DeclarationType.Procedure, - DeclarationType.Variable, - DeclarationType.Constant, - DeclarationType.UserDefinedTypeMember, - DeclarationType.EnumerationMember, - DeclarationType.Parameter - }; - - private List _objectStateUDTs; - public EncapsulateFieldUseBackingUDTMemberConflictFinder(IDeclarationFinderProvider declarationFinderProvider, IEnumerable candidates, IEnumerable objectStateUDTs) - : base(declarationFinderProvider, candidates) - { - _objectStateUDTs = objectStateUDTs.ToList(); - } - - public override bool TryValidateEncapsulationAttributes(IEncapsulateFieldCandidate field, out string errorMessage) - { - errorMessage = string.Empty; - if (!field.EncapsulateFlag) - { - return true; - } - - if (!base.TryValidateEncapsulationAttributes(field, out errorMessage)) - { - return false; - } - - //Compare to existing members...they cannot change - var objectStateUDT = _objectStateUDTs.SingleOrDefault(os => os.IsSelected); - return !ConflictsWithExistingUDTMembers(objectStateUDT, field.BackingIdentifier); - } - - public IObjectStateUDT AssignNoConflictIdentifiers(IObjectStateUDT stateUDT) - { - var members = _declarationFinderProvider.DeclarationFinder.Members(stateUDT.QualifiedModuleName); - var guard = 0; - while (guard++ < 10 && members.Any(m => m.IdentifierName.IsEquivalentVBAIdentifierTo(stateUDT.FieldIdentifier))) - { - stateUDT.FieldIdentifier = stateUDT.FieldIdentifier.IncrementEncapsulationIdentifier(); - } - - members = _declarationFinderProvider.DeclarationFinder.Members(stateUDT.QualifiedModuleName) - .Where(m => !_udtTypeIdentifierNonConflictTypes.Any(nct => m.DeclarationType.HasFlag(nct))); - - guard = 0; - while (guard++ < 10 && members.Any(m => m.IdentifierName.IsEquivalentVBAIdentifierTo(stateUDT.TypeIdentifier))) - { - stateUDT.TypeIdentifier = stateUDT.TypeIdentifier.IncrementEncapsulationIdentifier(); - } - return stateUDT; - } - - public override IEncapsulateFieldCandidate AssignNoConflictIdentifiers(IEncapsulateFieldCandidate candidate) - { - candidate = base.AssignNoConflictIdentifier(candidate, DeclarationType.Property); - - var objectStateUDT = _objectStateUDTs.SingleOrDefault(os => os.IsSelected); - var guard = 0; - while (guard++ < 10 && ConflictsWithExistingUDTMembers(objectStateUDT, candidate.PropertyIdentifier)) - { - candidate.PropertyIdentifier = candidate.PropertyIdentifier.IncrementEncapsulationIdentifier(); - } - return candidate; - } - - protected override IEncapsulateFieldCandidate AssignNoConflictIdentifier(IEncapsulateFieldCandidate candidate, DeclarationType declarationType) - { - candidate = base.AssignNoConflictIdentifier(candidate, declarationType); - - var objectStateUDT = _objectStateUDTs.SingleOrDefault(os => os.IsSelected); - var guard = 0; - while (guard++ < 10 && ConflictsWithExistingUDTMembers(objectStateUDT, candidate.BackingIdentifier)) - { - candidate.BackingIdentifier = candidate.BackingIdentifier.IncrementEncapsulationIdentifier(); - } - return candidate; - } - - private bool ConflictsWithExistingUDTMembers(IObjectStateUDT objectStateUDT, string identifier) - { - if (objectStateUDT is null) - { - return false; - } - - return objectStateUDT.ExistingMembers.Any(nm => nm.IdentifierName.IsEquivalentVBAIdentifierTo(identifier)); - } - - protected override IEnumerable FindRelevantMembers(IEncapsulateFieldCandidate candidate) - { - var members = _declarationFinderProvider.DeclarationFinder.Members(candidate.QualifiedModuleName) - .Where(d => d != candidate.Declaration); - - var membersToRemove = _fieldCandidates.Where(fc => fc.EncapsulateFlag && fc.Declaration.DeclarationType.HasFlag(DeclarationType.Variable)) - .Select(fc => fc.Declaration); - - return members.Except(membersToRemove); - } - } -} diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs index df415c61f5..ad20770d47 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs @@ -1,6 +1,6 @@ -using Rubberduck.Refactorings.Common; -using Rubberduck.Refactorings.EncapsulateFieldUseBackingField; +using Rubberduck.Refactorings.EncapsulateFieldUseBackingField; using Rubberduck.Refactorings.EncapsulateFieldUseBackingUDTMember; +using System; using System.Collections.Generic; using System.Linq; @@ -8,16 +8,16 @@ namespace Rubberduck.Refactorings.EncapsulateField { public class EncapsulateFieldModel : IRefactoringModel { - private readonly IObjectStateUDT _defaultObjectStateUDT; - public EncapsulateFieldModel(EncapsulateFieldUseBackingFieldModel backingFieldModel, - EncapsulateFieldUseBackingUDTMemberModel udtModel) + EncapsulateFieldUseBackingUDTMemberModel udtModel, + IEncapsulateFieldConflictFinder conflictFinder) { EncapsulateFieldUseBackingFieldModel = backingFieldModel; EncapsulateFieldUseBackingUDTMemberModel = udtModel; - ResetConflictDetection(EncapsulateFieldStrategy.UseBackingFields); ObjectStateUDTCandidates = udtModel.ObjectStateUDTCandidates; - _defaultObjectStateUDT = ObjectStateUDTCandidates.SingleOrDefault(os => !os.IsExistingDeclaration); + ConflictFinder = conflictFinder; + EncapsulateFieldUseBackingFieldModel.ConflictFinder = conflictFinder; + EncapsulateFieldUseBackingUDTMemberModel.ConflictFinder = conflictFinder; } public EncapsulateFieldUseBackingUDTMemberModel EncapsulateFieldUseBackingUDTMemberModel { get; } @@ -26,16 +26,22 @@ public class EncapsulateFieldModel : IRefactoringModel public IRefactoringPreviewProvider PreviewProvider { set; get; } + public Action StrategyChangedAction { set; get; } = (m) => { }; + + public Action ObjectStateUDTChangedAction { set; get; } = (m) => { }; + public IReadOnlyCollection ObjectStateUDTCandidates { private set; get; } + public IEncapsulateFieldConflictFinder ConflictFinder { set; get; } + public IObjectStateUDT ObjectStateUDTField { set { - EncapsulateFieldUseBackingUDTMemberModel.ObjectStateUDTField = value; - foreach (var candidate in EncapsulateFieldUseBackingUDTMemberModel.SelectedFieldCandidates) + if (EncapsulateFieldUseBackingUDTMemberModel.ObjectStateUDTField != value) { - EncapsulateFieldUseBackingUDTMemberModel.ConflictFinder.AssignNoConflictIdentifiers(candidate); + EncapsulateFieldUseBackingUDTMemberModel.ObjectStateUDTField = value; + ObjectStateUDTChangedAction(this); } } get => EncapsulateFieldStrategy == EncapsulateFieldStrategy.ConvertFieldsToUDTMembers @@ -48,49 +54,15 @@ public EncapsulateFieldStrategy EncapsulateFieldStrategy { set { - if (_strategy == value) + if (_strategy != value) { - return; + _strategy = value; + StrategyChangedAction(this); } - _strategy = value; - ResetConflictDetection(_strategy); } get => _strategy; } - private void ResetConflictDetection(EncapsulateFieldStrategy strategy) - { - var conflictFinder = - strategy == EncapsulateFieldStrategy.UseBackingFields - ? EncapsulateFieldUseBackingFieldModel.ConflictFinder - : EncapsulateFieldUseBackingUDTMemberModel.ConflictFinder; - - foreach (var candidate in EncapsulateFieldUseBackingFieldModel.EncapsulationCandidates) - { - candidate.ConflictFinder = conflictFinder; - ResolveConflict(conflictFinder, candidate); - } - - return; - } - - private void ResolveConflict(IEncapsulateFieldConflictFinder conflictFinder, IEncapsulateFieldCandidate candidate) - { - conflictFinder.AssignNoConflictIdentifiers(candidate); - if (candidate is IUserDefinedTypeCandidate udtCandidate) - { - foreach (var member in udtCandidate.Members) - { - conflictFinder.AssignNoConflictIdentifiers(member); - if (member.WrappedCandidate is IUserDefinedTypeCandidate childUDT - && childUDT.Declaration.AsTypeDeclaration.HasPrivateAccessibility()) - { - ResolveConflict(conflictFinder, childUDT); - } - } - } - } - public IReadOnlyCollection EncapsulationCandidates => EncapsulateFieldStrategy == EncapsulateFieldStrategy.UseBackingFields ? EncapsulateFieldUseBackingFieldModel.EncapsulationCandidates : EncapsulateFieldUseBackingUDTMemberModel.EncapsulationCandidates; diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModelFactory.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModelFactory.cs index bec50cebb5..655a78be8c 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModelFactory.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModelFactory.cs @@ -1,5 +1,4 @@ using Rubberduck.Parsing.Symbols; -using Rubberduck.Parsing.VBA; using Rubberduck.Refactorings.EncapsulateField; using System; using System.Collections.Generic; @@ -18,16 +17,19 @@ public class EncapsulateFieldModelFactory : IEncapsulateFieldModelFactory { private readonly IEncapsulateFieldUseBackingUDTMemberModelFactory _useBackingUDTMemberModelFactory; private readonly IEncapsulateFieldUseBackingFieldModelFactory _useBackingFieldModelFactory; - private readonly IEncapsulateFieldCandidateCollectionFactory _fieldCandidateCollectionFactory; + private readonly IEncapsulateFieldCollectionsProviderFactory _encapsulateFieldCollectionsProviderFactory; + private readonly IEncapsulateFieldConflictFinderFactory _encapsulateFieldConflictFinderFactory; public EncapsulateFieldModelFactory( IEncapsulateFieldUseBackingUDTMemberModelFactory encapsulateFieldUseBackingUDTMemberModelFactory, IEncapsulateFieldUseBackingFieldModelFactory encapsulateFieldUseBackingFieldModelFactory, - IEncapsulateFieldCandidateCollectionFactory encapsulateFieldCandidateCollectionFactory) + IEncapsulateFieldCollectionsProviderFactory encapsulateFieldCollectionsProviderFactory, + IEncapsulateFieldConflictFinderFactory encapsulateFieldConflictFinderFactory) { _useBackingUDTMemberModelFactory = encapsulateFieldUseBackingUDTMemberModelFactory as IEncapsulateFieldUseBackingUDTMemberModelFactory; _useBackingFieldModelFactory = encapsulateFieldUseBackingFieldModelFactory; - _fieldCandidateCollectionFactory = encapsulateFieldCandidateCollectionFactory; + _encapsulateFieldCollectionsProviderFactory = encapsulateFieldCollectionsProviderFactory; + _encapsulateFieldConflictFinderFactory = encapsulateFieldConflictFinderFactory; } public EncapsulateFieldModel Create(Declaration target) @@ -36,23 +38,29 @@ public EncapsulateFieldModel Create(Declaration target) { throw new ArgumentException(); } - - var fieldCandidates = _fieldCandidateCollectionFactory.Create(targetField.QualifiedModuleName); - var fieldEncapsulationModels = new List() { new FieldEncapsulationModel(targetField) }; + var fieldEncapsulationModels = new List() + { + new FieldEncapsulationModel(targetField) + }; - var useBackingFieldModel = _useBackingFieldModelFactory.Create(fieldCandidates, fieldEncapsulationModels); + var collectionsProvider = _encapsulateFieldCollectionsProviderFactory.Create(targetField.QualifiedModuleName); - var useBackingUDTMemberModel = _useBackingUDTMemberModelFactory.Create(fieldCandidates, fieldEncapsulationModels); + var useBackingFieldModel = _useBackingFieldModelFactory.Create(collectionsProvider, fieldEncapsulationModels); + + var useBackingUDTMemberModel = _useBackingUDTMemberModelFactory.Create(collectionsProvider, fieldEncapsulationModels); var initialStrategy = useBackingUDTMemberModel.ObjectStateUDTField.IsExistingDeclaration ? EncapsulateFieldStrategy.ConvertFieldsToUDTMembers : EncapsulateFieldStrategy.UseBackingFields; - return new EncapsulateFieldModel(useBackingFieldModel, useBackingUDTMemberModel) + var conflictFinder = _encapsulateFieldConflictFinderFactory.Create(collectionsProvider); + var model = new EncapsulateFieldModel(useBackingFieldModel, useBackingUDTMemberModel, conflictFinder) { EncapsulateFieldStrategy = initialStrategy, }; + + return model; } } } diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoring.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoring.cs index f6f6420d3e..42f600bdd1 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoring.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoring.cs @@ -1,5 +1,4 @@ using System.Linq; -using Rubberduck.Parsing.Rewriter; using Rubberduck.Parsing.Symbols; using Rubberduck.Parsing.VBA; using Rubberduck.Refactorings.Exceptions; @@ -17,7 +16,6 @@ public enum EncapsulateFieldStrategy public class EncapsulateFieldRefactoring : InteractiveRefactoringBase { private readonly ISelectedDeclarationProvider _selectedDeclarationProvider; - private readonly IRewritingManager _rewritingManager; private readonly EncapsulateFieldRefactoringAction _refactoringAction; private readonly EncapsulateFieldPreviewProvider _previewProvider; private readonly IEncapsulateFieldModelFactory _modelFactory; @@ -27,7 +25,6 @@ public class EncapsulateFieldRefactoring : InteractiveRefactoringBase userInteraction, - IRewritingManager rewritingManager, ISelectionProvider selectionProvider, ISelectedDeclarationProvider selectedDeclarationProvider) :base(selectionProvider, userInteraction) @@ -35,21 +32,18 @@ public class EncapsulateFieldRefactoring : InteractiveRefactoringBaseIEncapsulateFieldCandidate instances created by EncapsulateFieldCandidateCollectionFactory. /// This function is intended for exclusive use by EncapsulateFieldModelFactory /// - EncapsulateFieldUseBackingFieldModel Create(IReadOnlyCollection candidates, IEnumerable requests); + EncapsulateFieldUseBackingFieldModel Create(IEncapsulateFieldCollectionsProvider collectionsProvider, IEnumerable requests); } public class EncapsulateFieldUseBackingFieldModelFactory : IEncapsulateFieldUseBackingFieldModelFactory { - private readonly IEncapsulateFieldCandidateCollectionFactory _fieldCandidateCollectionFactory; + private readonly IEncapsulateFieldCollectionsProviderFactory _collectionProviderFactory; private readonly IEncapsulateFieldConflictFinderFactory _conflictFinderFactory; public EncapsulateFieldUseBackingFieldModelFactory( - IEncapsulateFieldCandidateCollectionFactory encapsulateFieldCandidateCollectionFactory, + IEncapsulateFieldCollectionsProviderFactory encapsulateFieldCollectionsProviderFactory, IEncapsulateFieldConflictFinderFactory encapsulateFieldConflictFinderFactory) { - _fieldCandidateCollectionFactory = encapsulateFieldCandidateCollectionFactory; _conflictFinderFactory = encapsulateFieldConflictFinderFactory; + _collectionProviderFactory = encapsulateFieldCollectionsProviderFactory; } public EncapsulateFieldUseBackingFieldModel Create(IEnumerable requests) @@ -41,13 +42,13 @@ public EncapsulateFieldUseBackingFieldModel Create(IEnumerable()); } - var fieldCandidates = _fieldCandidateCollectionFactory.Create(requests.First().Declaration.QualifiedModuleName); - return Create(fieldCandidates, requests); + var collectionsProvider = _collectionProviderFactory.Create(requests.First().Declaration.QualifiedModuleName); + return Create(collectionsProvider, requests); } - public EncapsulateFieldUseBackingFieldModel Create(IReadOnlyCollection candidates, IEnumerable requests) + public EncapsulateFieldUseBackingFieldModel Create(IEncapsulateFieldCollectionsProvider collectionsProvider, IEnumerable requests) { - var fieldCandidates = candidates.ToList(); + var fieldCandidates = collectionsProvider.EncapsulateFieldCandidates.ToList(); foreach (var request in requests) { @@ -60,7 +61,8 @@ public EncapsulateFieldUseBackingFieldModel Create(IReadOnlyCollection c.ConflictFinder = conflictsFinder); return new EncapsulateFieldUseBackingFieldModel(fieldCandidates) diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModelFactory.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModelFactory.cs index 7f5dcd357b..c18cbfeb22 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModelFactory.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModelFactory.cs @@ -20,23 +20,20 @@ public interface IEncapsulateFieldUseBackingUDTMemberModelFactory /// IEncapsulateFieldCandidate instances created by EncapsulateFieldCandidateCollectionFactory. /// This function is intended for exclusive use by EncapsulateFieldModelFactory /// - EncapsulateFieldUseBackingUDTMemberModel Create(IReadOnlyCollection candidates, IEnumerable requests, Declaration userDefinedTypeTarget = null); + EncapsulateFieldUseBackingUDTMemberModel Create(IEncapsulateFieldCollectionsProvider collectionsProvider, IEnumerable requests, Declaration userDefinedTypeTarget = null); } public class EncapsulateFieldUseBackingUDTMemberModelFactory : IEncapsulateFieldUseBackingUDTMemberModelFactory { - private readonly IEncapsulateFieldCandidateCollectionFactory _fieldCandidateCollectionFactory; - private readonly IObjectStateUserDefinedTypeFactory _objectStateUDTFactory; + private readonly IEncapsulateFieldCollectionsProviderFactory _encapsulateFieldCollectionsProviderFactory; private readonly IEncapsulateFieldConflictFinderFactory _conflictFinderFactory; public EncapsulateFieldUseBackingUDTMemberModelFactory( - IEncapsulateFieldCandidateCollectionFactory encapsulateFieldCandidateCollectionFactory, - IObjectStateUserDefinedTypeFactory objectStateUserDefinedTypeFactory, + IEncapsulateFieldCollectionsProviderFactory encapsulateFieldCollectionsProviderFactory, IEncapsulateFieldConflictFinderFactory encapsulateFieldConflictFinderFactory) { - _fieldCandidateCollectionFactory = encapsulateFieldCandidateCollectionFactory; - _objectStateUDTFactory = objectStateUserDefinedTypeFactory; _conflictFinderFactory = encapsulateFieldConflictFinderFactory; + _encapsulateFieldCollectionsProviderFactory = encapsulateFieldCollectionsProviderFactory; } public EncapsulateFieldUseBackingUDTMemberModel Create(IEnumerable requests, Declaration clientTarget) @@ -45,35 +42,32 @@ public EncapsulateFieldUseBackingUDTMemberModel Create(IEnumerable candidates, IEnumerable requests, Declaration clientTarget = null) + public EncapsulateFieldUseBackingUDTMemberModel Create(IEncapsulateFieldCollectionsProvider collectionsProvider, IEnumerable requests, Declaration clientTarget = null) { + var asUDTMemberCandidates = collectionsProvider.EncapsulateAsUserDefinedTypeMemberCandidates.ToList(); + if (clientTarget != null && (clientTarget.Accessibility != Accessibility.Private - || !candidates.Any(c => c.Declaration == clientTarget && c is IUserDefinedTypeCandidate))) + || !asUDTMemberCandidates.Any(c => c.Declaration == clientTarget && c.WrappedCandidate is IUserDefinedTypeCandidate))) { throw new ArgumentException("The object state Field must be a Private UserDefinedType"); } - var fieldCandidates = candidates.ToList(); + var objectStateUDTs = collectionsProvider.ObjectStateUDTCandidates; - var objectStateUDTs = fieldCandidates - .Where(c => c is IUserDefinedTypeCandidate udt && udt.IsObjectStateUDTCandidate) - .Select(udtc => _objectStateUDTFactory.Create(udtc as IUserDefinedTypeCandidate)) - .ToList(); - - var defaultObjectStateUDT = _objectStateUDTFactory.Create(fieldCandidates.First().QualifiedModuleName); - - objectStateUDTs.Add(defaultObjectStateUDT); + var defaultObjectStateUDT = objectStateUDTs.FirstOrDefault(os => !os.IsExistingDeclaration); var targetStateUDT = DetermineObjectStateUDTTarget(defaultObjectStateUDT, clientTarget, objectStateUDTs); foreach (var request in requests) { - var candidate = fieldCandidates.Single(c => c.Declaration.Equals(request.Declaration)); + var candidate = asUDTMemberCandidates.Single(c => c.Declaration.Equals(request.Declaration)); candidate.EncapsulateFlag = true; candidate.IsReadOnly = request.IsReadOnly; if (request.PropertyIdentifier != null) @@ -82,28 +76,24 @@ public EncapsulateFieldUseBackingUDTMemberModel Create(IReadOnlyCollection c.ConflictFinder = conflictsFinder); + asUDTMemberCandidates.ForEach(c => c.ConflictFinder = conflictsFinder); if (clientTarget == null && !targetStateUDT.IsExistingDeclaration) { conflictsFinder.AssignNoConflictIdentifiers(targetStateUDT); } - var udtMemberCandidates = - fieldCandidates.Select(c => new EncapsulateFieldAsUDTMemberCandidate(c, targetStateUDT)).ToList(); - - udtMemberCandidates.ForEach(c => conflictsFinder.AssignNoConflictIdentifiers(c)); + asUDTMemberCandidates.ForEach(c => conflictsFinder.AssignNoConflictIdentifiers(c)); - return new EncapsulateFieldUseBackingUDTMemberModel(targetStateUDT, udtMemberCandidates, objectStateUDTs) + return new EncapsulateFieldUseBackingUDTMemberModel(targetStateUDT, asUDTMemberCandidates, objectStateUDTs) { ConflictFinder = conflictsFinder }; } - IObjectStateUDT DetermineObjectStateUDTTarget(IObjectStateUDT defaultObjectStateUDT, Declaration clientTarget, List objectStateUDTs) + IObjectStateUDT DetermineObjectStateUDTTarget(IObjectStateUDT defaultObjectStateUDT, Declaration clientTarget, IReadOnlyCollection objectStateUDTs) { var targetStateUDT = defaultObjectStateUDT; diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ArrayCandidate.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ArrayCandidate.cs index 6ca449396f..b88b2eb718 100644 --- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ArrayCandidate.cs +++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ArrayCandidate.cs @@ -43,7 +43,10 @@ public override bool TryValidateEncapsulationAttributes(out string errorMessage) { return false; } - return ConflictFinder.TryValidateEncapsulationAttributes(this, out errorMessage); + + (bool IsValid, string ErrorMsg) = ConflictFinder?.ValidateEncapsulationAttributes(this) ?? (true, string.Empty); + errorMessage = ErrorMsg; + return IsValid; } public string UDTMemberDeclaration diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldAsUDTMemberCandidate.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldAsUDTMemberCandidate.cs index db8459345f..cfe5833879 100644 --- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldAsUDTMemberCandidate.cs +++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldAsUDTMemberCandidate.cs @@ -95,19 +95,20 @@ public IEncapsulateFieldConflictFinder ConflictFinder public bool TryValidateEncapsulationAttributes(out string errorMessage) { errorMessage = string.Empty; - if (!_wrapped.EncapsulateFlag) + if (!_wrapped.EncapsulateFlag || ConflictFinder is null) { return true; } - if (_wrapped is IArrayCandidate ac) + if (_wrapped is IArrayCandidate ac + && ac.HasExternalRedimOperation(out errorMessage)) { - if (ac.HasExternalRedimOperation(out errorMessage)) - { - return false; - } + return false; } - return ConflictFinder.TryValidateEncapsulationAttributes(this, out errorMessage); + + (bool IsValid, string ErrorMsg) = ConflictFinder.ValidateEncapsulationAttributes(this); + errorMessage = ErrorMsg; + return IsValid; } public override bool Equals(object obj) diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidate.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidate.cs index b5294fc02a..5a4c01e8e8 100644 --- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidate.cs +++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidate.cs @@ -90,8 +90,10 @@ public virtual string BackingIdentifier public virtual bool TryValidateEncapsulationAttributes(out string errorMessage) { - errorMessage = string.Empty; - return ConflictFinder?.TryValidateEncapsulationAttributes(this, out errorMessage) ?? true; + (bool IsValid, string ErrorMsg) = ConflictFinder?.ValidateEncapsulationAttributes(this) ?? (true, string.Empty); + + errorMessage = ErrorMsg; + return IsValid; } public virtual string TargetID => _target?.IdentifierName ?? IdentifierName; @@ -110,11 +112,10 @@ public virtual bool EncapsulateFlag if (!_encapsulateFlag) { PropertyIdentifier = _fieldAndProperty.DefaultPropertyName; + return; } - else if (ConflictFinder != null) - { - ConflictFinder.AssignNoConflictIdentifiers(this); - } + + ConflictFinder?.AssignNoConflictIdentifiers(this); } get => _encapsulateFlag; } @@ -137,7 +138,7 @@ public string PropertyIdentifier private void TryRestoreNewFieldNameAsOriginalFieldIdentifierName() { var canNowUseOriginalFieldName = !_fieldAndProperty.TargetFieldName.IsEquivalentVBAIdentifierTo(_fieldAndProperty.Property) - && !(ConflictFinder?.IsConflictingProposedIdentifier(_fieldAndProperty.TargetFieldName, this, DeclarationType.Variable) ?? false); + && !(ConflictFinder?.IsConflictingIdentifier(this, _fieldAndProperty.TargetFieldName, out _) ?? false); if (canNowUseOriginalFieldName) { @@ -148,12 +149,7 @@ private void TryRestoreNewFieldNameAsOriginalFieldIdentifierName() if (_fieldAndProperty.Field.IsEquivalentVBAIdentifierTo(_fieldAndProperty.TargetFieldName)) { _fieldAndProperty.Field = _fieldAndProperty.DefaultNewFieldName; - var isConflictingFieldIdentifier = ConflictFinder?.HasConflictingIdentifier(this, DeclarationType.Variable, out _) ?? false; - for (var count = 1; count < 10 && isConflictingFieldIdentifier; count++) - { - BackingIdentifier = BackingIdentifier.IncrementEncapsulationIdentifier(); - isConflictingFieldIdentifier = ConflictFinder?.HasConflictingIdentifier(this, DeclarationType.Variable, out _) ?? false; - } + ConflictFinder?.AssignNoConflictIdentifiers(this); } } diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidateCollectionFactory.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidateCollectionFactory.cs deleted file mode 100644 index 72b556ec1a..0000000000 --- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidateCollectionFactory.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Rubberduck.Parsing.Symbols; -using Rubberduck.Parsing.VBA; -using Rubberduck.Refactorings.EncapsulateField; -using Rubberduck.VBEditor; -using System.Collections.Generic; -using System.Linq; - -namespace Rubberduck.Refactorings -{ - public interface IEncapsulateFieldCandidateCollectionFactory - { - IReadOnlyCollection Create(QualifiedModuleName qualifiedModuleName); - } - - public class EncapsulateFieldCandidateCollectionFactory : IEncapsulateFieldCandidateCollectionFactory - { - private readonly IDeclarationFinderProvider _declarationFinderProvider; - private readonly IEncapsulateFieldCandidateFactory _fieldCandidateFactory; - public EncapsulateFieldCandidateCollectionFactory( - IDeclarationFinderProvider declarationFinderProvider, - IEncapsulateFieldCandidateFactory encapsulateFieldCandidateFactory) - { - _declarationFinderProvider = declarationFinderProvider; - _fieldCandidateFactory = encapsulateFieldCandidateFactory; - } - - public IReadOnlyCollection Create(QualifiedModuleName qualifiedModuleName) - { - return _declarationFinderProvider.DeclarationFinder.Members(qualifiedModuleName, DeclarationType.Variable) - .Where(v => v.ParentDeclaration is ModuleDeclaration - && !v.IsWithEvents) - .Select(f => _fieldCandidateFactory.Create(f)) - .ToList(); - } - } -} diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidateFactory.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidateFactory.cs index 3a41906e91..8e480d92e1 100644 --- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidateFactory.cs +++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidateFactory.cs @@ -14,12 +14,10 @@ public interface IEncapsulateFieldCandidateFactory public class EncapsulateFieldCandidateFactory : IEncapsulateFieldCandidateFactory { private readonly IDeclarationFinderProvider _declarationFinderProvider; - private readonly ICodeBuilder _codeBuilder; - public EncapsulateFieldCandidateFactory(IDeclarationFinderProvider declarationFinderProvider, ICodeBuilder codeBuilder) + public EncapsulateFieldCandidateFactory(IDeclarationFinderProvider declarationFinderProvider) { _declarationFinderProvider = declarationFinderProvider; - _codeBuilder = codeBuilder; } public IEncapsulateFieldCandidate Create(Declaration target) diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeMemberCandidate.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeMemberCandidate.cs index 3dafc2200a..bcb0329f70 100644 --- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeMemberCandidate.cs +++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeMemberCandidate.cs @@ -74,6 +74,7 @@ public bool EncapsulateFlag } return; } + var valueChanged = _encapsulateFlag != value; _encapsulateFlag = value; diff --git a/RubberduckTests/Commands/RefactorCommands/EncapsulateFieldCommandTests.cs b/RubberduckTests/Commands/RefactorCommands/EncapsulateFieldCommandTests.cs index 730e03defc..10d445d5a2 100644 --- a/RubberduckTests/Commands/RefactorCommands/EncapsulateFieldCommandTests.cs +++ b/RubberduckTests/Commands/RefactorCommands/EncapsulateFieldCommandTests.cs @@ -74,7 +74,6 @@ protected override CommandBase TestCommand(IVBE vbe, RubberduckParserState state resolver.Resolve(), resolver.Resolve(), userInteraction, - rewritingManager, selectionService, selectedDeclarationProvider); var notifier = new EncapsulateFieldFailedNotifier(msgBox); diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs index 7361a74e46..f2a90828fa 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs @@ -107,29 +107,31 @@ ResolveImpl() return new EncapsulateFieldModelFactory( ResolveImpl(), ResolveImpl(), - ResolveImpl() + ResolveImpl(), + ResolveImpl < IEncapsulateFieldConflictFinderFactory>() ) as T; case nameof(IEncapsulateFieldUseBackingUDTMemberModelFactory): return new EncapsulateFieldUseBackingUDTMemberModelFactory( - ResolveImpl(), - ResolveImpl(), + ResolveImpl(), ResolveImpl< IEncapsulateFieldConflictFinderFactory>()) as T; case nameof(IEncapsulateFieldUseBackingFieldModelFactory): return new EncapsulateFieldUseBackingFieldModelFactory( - ResolveImpl(), + ResolveImpl(), ResolveImpl< IEncapsulateFieldConflictFinderFactory>()) as T; + case nameof(IEncapsulateFieldCollectionsProviderFactory): + return new EncapsulateFieldCollectionsProviderFactory(_declarationFinderProvider, + ResolveImpl(), + ResolveImpl()) as T; + case nameof(IEncapsulateFieldCandidateFactory): - return new EncapsulateFieldCandidateFactory(_declarationFinderProvider, new CodeBuilder()) as T; + return new EncapsulateFieldCandidateFactory(_declarationFinderProvider) as T; case nameof(IObjectStateUserDefinedTypeFactory): return new ObjectStateUserDefinedTypeFactory() as T; - case nameof(IEncapsulateFieldCandidateCollectionFactory): - return new EncapsulateFieldCandidateCollectionFactory(_declarationFinderProvider, ResolveImpl()) as T; - case nameof(IEncapsulateFieldConflictFinderFactory): return new EncapsulateFieldConflictFinderFactory(_declarationFinderProvider) as T; diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFIeldTestSupport.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestSupport.cs similarity index 97% rename from RubberduckTests/Refactoring/EncapsulateField/EncapsulateFIeldTestSupport.cs rename to RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestSupport.cs index 44807b0c49..bb44648fd9 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFIeldTestSupport.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestSupport.cs @@ -111,19 +111,18 @@ public string RefactoredCode(CodeString codeString, Func RefactoredCode(codeString.Code, codeString.CaretPosition.ToOneBased(), presenterAdjustment, expectedException, executeViaActiveSelection); public IRefactoring SupportTestRefactoring( - IRewritingManager rewritingManager, + IRewritingManager rewritingManager, RubberduckParserState state, - RefactoringUserInteraction userInteraction, + RefactoringUserInteraction userInteraction, ISelectionService selectionService) { var resolver = new EncapsulateFieldTestComponentResolver(state, rewritingManager); var selectedDeclarationProvider = new SelectedDeclarationProvider(selectionService, state); - return new EncapsulateFieldRefactoring(resolver.Resolve(), - resolver.Resolve(), + return new EncapsulateFieldRefactoring(resolver.Resolve(), + resolver.Resolve(), resolver.Resolve(), - userInteraction, - rewritingManager, - selectionService, + userInteraction, + selectionService, selectedDeclarationProvider); } @@ -150,6 +149,8 @@ public IEncapsulateFieldCandidate RetrieveEncapsulateFieldCandidate(IVBE vbe, st var model = resolver.Resolve().Create(match); + model.ConflictFinder.AssignNoConflictIdentifiers(model[match.IdentifierName]); + return model[match.IdentifierName]; } } @@ -239,9 +240,9 @@ public void EncapsulateUsingUDTField(string targetID = null) public string StateUDT_FieldName { set; get; } - public TestEncapsulationAttributes this[string fieldName] + public TestEncapsulationAttributes this[string fieldName] => EncapsulateFieldAttributes.Where(efa => efa.TargetFieldName == fieldName).Single(); public IEnumerable EncapsulateFieldAttributes => _userInput; } -} +} \ No newline at end of file diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldValidatorTests.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldValidatorTests.cs index 7566b7bc61..f0ed03be4b 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldValidatorTests.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldValidatorTests.cs @@ -7,6 +7,7 @@ using Rubberduck.VBEditor.Utility; using Rubberduck.Parsing.Symbols; using Rubberduck.VBEditor.SafeComWrappers; +using System.Linq; namespace RubberduckTests.Refactoring.EncapsulateField { @@ -94,7 +95,7 @@ End Type End Function"; var candidate = Support.RetrieveEncapsulateFieldCandidate(inputCode, "myBar", DeclarationType.Variable); - var result = candidate.ConflictFinder.IsConflictingProposedIdentifier("First", candidate, DeclarationType.Property); + var result = candidate.ConflictFinder.IsConflictingIdentifier(candidate, "First", out _); Assert.AreEqual(true, result); } @@ -598,6 +599,102 @@ End Type Assert.AreEqual(false, model[fieldUT].TryValidateEncapsulationAttributes(out var errorMessage), errorMessage); } + [Test] + [Category("Refactorings")] + [Category("Encapsulate Field")] + public void AddedFieldConflictsWithExistingUDTMemberName() + { + var fieldUT = "mFirstValue"; + string inputCode = + $@" + +Private Type MyType + FirstValue As Integer + SecondValue As Integer +End Type + +Private {fieldUT} As Double + +Private myType As MyType +"; + + var vbe = MockVbeBuilder.BuildFromSingleStandardModule(inputCode, out _).Object; + var state = MockParser.CreateAndParse(vbe); + using (state) + { + var mTypeTarget = state.DeclarationFinder.DeclarationsWithType(DeclarationType.Variable) + .First(d => d.IdentifierName == "myType"); + + var mFirstTarget = state.DeclarationFinder.DeclarationsWithType(DeclarationType.Variable) + .First(d => d.IdentifierName == fieldUT); + + var resolver = new EncapsulateFieldTestComponentResolver(state, null); + + var collectionsProviderFactory = resolver.Resolve(); + var collectionsProvider = collectionsProviderFactory.Create(mTypeTarget.QualifiedModuleName); + + var encapsulateFieldCandidates = collectionsProvider.EncapsulateFieldCandidates; + + var finderFactory = resolver.Resolve(); + var conflictFinder = finderFactory.Create(collectionsProvider); + + foreach (var candidate in encapsulateFieldCandidates) + { + candidate.ConflictFinder = conflictFinder; + } + + var mTypeCandidate = encapsulateFieldCandidates.Single(c => c.Declaration == mTypeTarget); + mTypeCandidate.EncapsulateFlag = true; + + var mFirstCandidate = encapsulateFieldCandidates.Single(c => c.Declaration == mFirstTarget); + + foreach (var candidate in encapsulateFieldCandidates) + { + candidate.EncapsulateFlag = true; + } + + var result = mFirstCandidate.TryValidateEncapsulationAttributes(out var errorMessage); + Assert.IsTrue(result, errorMessage); + } + } + + [Test] + [Category("Refactorings")] + [Category("Encapsulate Field")] + public void ObjectStateUDTFieldConflictsWithAssignedProperty() + { + var fieldUT = "mFirstValue"; + string inputCode = + $@" + +Private {fieldUT} As Double +"; + + var vbe = MockVbeBuilder.BuildFromSingleStandardModule(inputCode, out _).Object; + var state = MockParser.CreateAndParse(vbe); + using (state) + { + var mFirstTarget = state.DeclarationFinder.DeclarationsWithType(DeclarationType.Variable) + .First(d => d.IdentifierName == fieldUT) as VariableDeclaration; + + var resolver = new EncapsulateFieldTestComponentResolver(state, null); + + var modelFactory = resolver.Resolve(); + var model = modelFactory.Create(mFirstTarget); + var mFirstCandidate = model[mFirstTarget.IdentifierName]; + + mFirstCandidate.EncapsulateFlag = true; + mFirstCandidate.PropertyIdentifier = "This"; + + model.EncapsulateFieldStrategy = EncapsulateFieldStrategy.ConvertFieldsToUDTMembers; + var objectStateUDT = model.ObjectStateUDTField; + + model.ConflictFinder.AssignNoConflictIdentifiers(objectStateUDT); + + StringAssert.AreEqualIgnoringCase("this_1", objectStateUDT.IdentifierName); + } + } + protected override IRefactoring TestRefactoring( IRewritingManager rewritingManager, RubberduckParserState state, From 29d46807670594e16a61724876da86cc2b33af0e Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Sun, 20 Sep 2020 15:23:31 -0700 Subject: [PATCH 42/67] Rework and reduce IEncapsulateFieldCandidate --- .../EncapsulateFieldConflictFinder.cs | 117 +++++++++----- .../FieldCandidates/ArrayCandidate.cs | 56 +------ .../EncapsulateFieldAsUDTMemberCandidate.cs | 64 +++----- .../EncapsulateFieldCandidate.cs | 153 ++++++------------ .../EncapsulateFieldCandidateFactory.cs | 11 +- .../UserDefinedTypeCandidate.cs | 35 ++-- .../UserDefinedTypeMemberCandidate.cs | 21 +-- .../ObjectStateUDT/ObjectStateUDT.cs | 38 ++--- .../PropertyAttributeSetsGenerator.cs | 7 +- 9 files changed, 185 insertions(+), 317 deletions(-) diff --git a/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldConflictFinder.cs b/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldConflictFinder.cs index d92f976977..cf1126ee17 100644 --- a/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldConflictFinder.cs +++ b/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldConflictFinder.cs @@ -1,4 +1,6 @@ -using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing; +using Rubberduck.Parsing.Grammar; +using Rubberduck.Parsing.Symbols; using Rubberduck.Parsing.VBA; using Rubberduck.Refactorings.Common; using Rubberduck.Refactorings.EncapsulateField.Extensions; @@ -16,6 +18,7 @@ public interface IEncapsulateFieldConflictFinder void AssignNoConflictIdentifiers(IEncapsulateFieldCandidate candidate); void AssignNoConflictIdentifiers(IObjectStateUDT stateUDT); void AssignNoConflictIdentifiers(IEnumerable candidates); + void AssignNoConflictBackingFieldIdentifier(IEncapsulateFieldCandidate candidate); } public class EncapsulateFieldConflictFinder : IEncapsulateFieldConflictFinder @@ -47,7 +50,8 @@ public EncapsulateFieldConflictFinder(IDeclarationFinderProvider declarationFind _fieldCandidates = candidates.ToList(); _udtMemberCandidates = new List(); - _fieldCandidates.ForEach(c => LoadUDTMembers(c)); + + _fieldCandidates.ForEach(c => LoadUDTMemberCandidates(c, _udtMemberCandidates)); _allCandidates = _fieldCandidates.Concat(_udtMemberCandidates).ToList(); @@ -75,13 +79,23 @@ public EncapsulateFieldConflictFinder(IDeclarationFinderProvider declarationFind var errorMessage = string.Empty; - var hasInvalidIdentifierOrHasConflicts = - VBAIdentifierValidator.TryMatchInvalidIdentifierCriteria(field.PropertyIdentifier, declarationType, out errorMessage, field.Declaration.IsArray) - || IsConflictingIdentifier(field, field.PropertyIdentifier, out errorMessage) - || IsConflictingIdentifier(field, field.BackingIdentifier, out errorMessage) - || field is IEncapsulateFieldAsUDTMemberCandidate && ConflictsWithExistingUDTMembers(SelectedObjectStateUDT(), field.BackingIdentifier, out errorMessage); + if (field.Declaration.IsArray) + { + if (field.Declaration.References.Any(rf => rf.QualifiedModuleName != field.QualifiedModuleName + && rf.Context.TryGetAncestor(out _))) + { + errorMessage = string.Format(RubberduckUI.EncapsulateField_ArrayHasExternalRedimFormat, field.IdentifierName); + } + return (!string.IsNullOrEmpty(errorMessage), errorMessage); + } - return (string.IsNullOrEmpty(errorMessage), errorMessage); + var hasConflictFreeValidIdentifiers = + !VBAIdentifierValidator.TryMatchInvalidIdentifierCriteria(field.PropertyIdentifier, declarationType, out errorMessage, field.Declaration.IsArray) + && !IsConflictingIdentifier(field, field.PropertyIdentifier, out errorMessage) + && !IsConflictingIdentifier(field, field.BackingIdentifier, out errorMessage) + && !(field is IEncapsulateFieldAsUDTMemberCandidate && ConflictsWithExistingUDTMembers(SelectedObjectStateUDT(), field.BackingIdentifier, out errorMessage)); + + return (hasConflictFreeValidIdentifiers, errorMessage); } public bool IsConflictingIdentifier(IEncapsulateFieldCandidate field, string identifierToCompare, out string errorMessage) @@ -98,20 +112,20 @@ public void AssignNoConflictIdentifiers(IEnumerable { foreach (var candidate in candidates.Where(c => c.EncapsulateFlag)) { - ResolveFieldConflicts(candidate); + ResolveIdentifierConflicts(candidate); } } - private void ResolveFieldConflicts(IEncapsulateFieldCandidate candidate) + private void ResolveIdentifierConflicts(IEncapsulateFieldCandidate candidate) { AssignNoConflictIdentifiers(candidate); if (candidate is IUserDefinedTypeCandidate udtCandidate) { - ResolveUDTMemberConflicts(udtCandidate.Members); + ResolveUDTMemberIdentifierConflicts(udtCandidate.Members); } } - private void ResolveUDTMemberConflicts(IEnumerable members) + private void ResolveUDTMemberIdentifierConflicts(IEnumerable members) { foreach (var member in members) { @@ -119,18 +133,18 @@ private void ResolveUDTMemberConflicts(IEnumerable ConflictsWithExistingUDTMembers(SelectedObjectStateUDT(), candidate.PropertyIdentifier, out _), - () => IncrementPropertyIdentifier(candidate)); + () => ConflictsWithExistingUDTMembers(SelectedObjectStateUDT(), udtMember.UserDefinedTypeMemberIdentifier, out _), + () => udtMember.UserDefinedTypeMemberIdentifier = udtMember.UserDefinedTypeMemberIdentifier.IncrementEncapsulationIdentifier()); return; } @@ -141,15 +155,16 @@ public void AssignNoConflictIdentifiers(IEncapsulateFieldCandidate candidate) public void AssignNoConflictIdentifiers(IObjectStateUDT stateUDT) { AssignIdentifier( - () => HasConflictingFieldIdentifier(stateUDT, stateUDT.FieldIdentifier), - () => stateUDT.FieldIdentifier = stateUDT.FieldIdentifier.IncrementEncapsulationIdentifier()); + () => _existingUserUDTsAndEnums.Any(m => m.IdentifierName.IsEquivalentVBAIdentifierTo(stateUDT.TypeIdentifier)), + () => stateUDT.TypeIdentifier = stateUDT.TypeIdentifier.IncrementEncapsulationIdentifier()); AssignIdentifier( - () => _existingUserUDTsAndEnums.Any(m => m.IdentifierName.IsEquivalentVBAIdentifierTo(stateUDT.TypeIdentifier)), - () => stateUDT.TypeIdentifier = stateUDT.TypeIdentifier.IncrementEncapsulationIdentifier()); + () => HasConflictingFieldIdentifier(stateUDT, stateUDT.FieldIdentifier), + () => stateUDT.FieldIdentifier = stateUDT.FieldIdentifier.IncrementEncapsulationIdentifier()); } - private IObjectStateUDT SelectedObjectStateUDT() => _objectStateUDTs.SingleOrDefault(os => os.IsSelected); + private IObjectStateUDT SelectedObjectStateUDT() + => _objectStateUDTs.SingleOrDefault(os => os.IsSelected); private static bool ConflictsWithExistingUDTMembers(IObjectStateUDT objectStateUDT, string identifier, out string errorMessage) { @@ -161,26 +176,20 @@ private static bool ConflictsWithExistingUDTMembers(IObjectStateUDT objectStateU return !string.IsNullOrEmpty(errorMessage); } - private void IncrementPropertyIdentifier(IEncapsulateFieldCandidate candidate) - => candidate.PropertyIdentifier = candidate.PropertyIdentifier.IncrementEncapsulationIdentifier(); - private void AssignNoConflictPropertyIdentifier(IEncapsulateFieldCandidate candidate) { AssignIdentifier( () => IsConflictingIdentifier(candidate, candidate.PropertyIdentifier, out _), - () => IncrementPropertyIdentifier(candidate)); + () => candidate.PropertyIdentifier = candidate.PropertyIdentifier.IncrementEncapsulationIdentifier()); } - private void AssignNoConflictBackingFieldIdentifier(IEncapsulateFieldCandidate candidate) + public void AssignNoConflictBackingFieldIdentifier(IEncapsulateFieldCandidate candidate) { - //Private UserDefinedTypes are never used directly as a backing field - so never change their identifier. - //The backing fields for an encapsulated Private UDT are its members. - if (!(candidate is UserDefinedTypeMemberCandidate - || candidate is IUserDefinedTypeCandidate udtCandidate && udtCandidate.TypeDeclarationIsPrivate)) + if (candidate.BackingIdentifierMutator != null) { AssignIdentifier( () => IsConflictingIdentifier(candidate, candidate.BackingIdentifier, out _), - () => candidate.BackingIdentifier = candidate.BackingIdentifier.IncrementEncapsulationIdentifier()); + () => candidate.BackingIdentifierMutator(candidate.BackingIdentifier.IncrementEncapsulationIdentifier())); } } @@ -200,13 +209,24 @@ private static void AssignIdentifier(Func hasConflict, Action incrementIde private bool HasConflictIdentifiers(IEncapsulateFieldCandidate candidate, string identifierToCompare) { - if (_allCandidates.Where(c => c.TargetID != candidate.TargetID + return HasInternalPropertyAndBackingFieldConflict(candidate) + || HasConflictsWithOtherEncapsulationPropertyIdentifiers(candidate, identifierToCompare) + || HasConflictsWithUnmodifiedPropertyAndFieldIdentifiers(candidate, identifierToCompare) + || HasConflictWithLocalDeclarationIdentifiers(candidate, identifierToCompare); + } + + private bool HasInternalPropertyAndBackingFieldConflict(IEncapsulateFieldCandidate candidate) + => candidate.BackingIdentifierMutator != null + && candidate.EncapsulateFlag + && candidate.PropertyIdentifier.IsEquivalentVBAIdentifierTo(candidate.BackingIdentifier); + + private bool HasConflictsWithOtherEncapsulationPropertyIdentifiers(IEncapsulateFieldCandidate candidate, string identifierToCompare) + => _allCandidates.Where(c => c.TargetID != candidate.TargetID && c.EncapsulateFlag - && c.PropertyIdentifier.IsEquivalentVBAIdentifierTo(identifierToCompare)).Any()) - { - return true; - } + && c.PropertyIdentifier.IsEquivalentVBAIdentifierTo(identifierToCompare)).Any(); + private bool HasConflictsWithUnmodifiedPropertyAndFieldIdentifiers(IEncapsulateFieldCandidate candidate, string identifierToCompare) + { var membersToEvaluate = _members.Where(d => d != candidate.Declaration); if (candidate is IEncapsulateFieldAsUDTMemberCandidate) @@ -219,13 +239,22 @@ private bool HasConflictIdentifiers(IEncapsulateFieldCandidate candidate, string var nameConflictCandidates = membersToEvaluate.Where(member => !(member.IsLocalVariable() || member.IsLocalConstant() || _declarationTypesThatNeverConflictWithFieldAndPropertyIdentifiers.Contains(member.DeclarationType))); - if (nameConflictCandidates.Any(m => m.IdentifierName.IsEquivalentVBAIdentifierTo(identifierToCompare))) + return nameConflictCandidates.Any(m => m.IdentifierName.IsEquivalentVBAIdentifierTo(identifierToCompare)); + } + + private bool HasConflictWithLocalDeclarationIdentifiers(IEncapsulateFieldCandidate candidate, string identifierToCompare) + { + var membersToEvaluate = _members.Where(d => d != candidate.Declaration); + + if (candidate is IEncapsulateFieldAsUDTMemberCandidate) { - return true; + membersToEvaluate = membersToEvaluate.Except( + _fieldCandidates.Where(fc => fc.EncapsulateFlag && fc.Declaration.DeclarationType.HasFlag(DeclarationType.Variable)) + .Select(f => f.Declaration)); } - //Only check IdentifierReferences in the declaring module because IdentifierReferences in - //other modules will be module-qualified. + //Only check IdentifierReferences in the declaring module because encapsulated field + //references in other modules will be module-qualified. var candidateLocalReferences = candidate.Declaration.References.Where(rf => rf.QualifiedModuleName == candidate.QualifiedModuleName); var localDeclarationConflictCandidates = membersToEvaluate.Where(localDec => candidateLocalReferences @@ -251,7 +280,7 @@ private bool HasConflictingFieldIdentifier(IObjectStateUDT candidate, string ide .Where(fc => fc.EncapsulateFlag && fc.Declaration.DeclarationType.HasFlag(DeclarationType.Variable)) .Select(fc => fc.Declaration); - var nameConflictCandidates = + var nameConflictCandidates = _members.Except(fieldsToRemoveFromConflictCandidates) .Where(member => !(member.IsLocalVariable() || member.IsLocalConstant() || _declarationTypesThatNeverConflictWithFieldAndPropertyIdentifiers.Contains(member.DeclarationType))); @@ -259,7 +288,7 @@ private bool HasConflictingFieldIdentifier(IObjectStateUDT candidate, string ide return nameConflictCandidates.Any(m => m.IdentifierName.IsEquivalentVBAIdentifierTo(identifierToCompare)); } - private void LoadUDTMembers(IEncapsulateFieldCandidate candidate) + private void LoadUDTMemberCandidates(IEncapsulateFieldCandidate candidate, List udtMemberCandidates) { if (!(candidate is IUserDefinedTypeCandidate udtCandidate)) { @@ -268,13 +297,13 @@ private void LoadUDTMembers(IEncapsulateFieldCandidate candidate) foreach (var member in udtCandidate.Members) { - _udtMemberCandidates.Add(member); + udtMemberCandidates.Add(member); if (member.WrappedCandidate is IUserDefinedTypeCandidate childUDT && childUDT.Declaration.AsTypeDeclaration.HasPrivateAccessibility()) { //recursive till a non-UserDefinedType member is found - LoadUDTMembers(childUDT); + LoadUDTMemberCandidates(childUDT, udtMemberCandidates); } } } diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ArrayCandidate.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ArrayCandidate.cs index b88b2eb718..3396bd7ce1 100644 --- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ArrayCandidate.cs +++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ArrayCandidate.cs @@ -1,67 +1,15 @@ -using Rubberduck.Parsing; -using Rubberduck.Parsing.Grammar; +using Rubberduck.Parsing.Grammar; using Rubberduck.Parsing.Symbols; -using Rubberduck.Resources; -using System.Linq; namespace Rubberduck.Refactorings.EncapsulateField { - public interface IArrayCandidate : IEncapsulateFieldCandidate + public class ArrayCandidate : EncapsulateFieldCandidate { - string UDTMemberDeclaration { get;} - bool HasExternalRedimOperation(out string errorMessage); - } - - public class ArrayCandidate : EncapsulateFieldCandidate, IArrayCandidate - { - private string _subscripts; public ArrayCandidate(Declaration declaration) :base(declaration) { - ImplementLet = false; - ImplementSet = false; PropertyAsTypeName = Tokens.Variant; - CanBeReadWrite = false; IsReadOnly = true; - - _subscripts = string.Empty; - if (declaration.Context.TryGetChildContext(out var ctxt)) - { - _subscripts = ctxt.GetText(); - } - } - - public override bool TryValidateEncapsulationAttributes(out string errorMessage) - { - errorMessage = string.Empty; - if (!EncapsulateFlag) - { - return true; - } - - if (HasExternalRedimOperation(out errorMessage)) - { - return false; - } - - (bool IsValid, string ErrorMsg) = ConflictFinder?.ValidateEncapsulationAttributes(this) ?? (true, string.Empty); - errorMessage = ErrorMsg; - return IsValid; - } - - public string UDTMemberDeclaration - => $"{PropertyIdentifier}({_subscripts}) {Tokens.As} {Declaration.AsTypeName}"; - - public bool HasExternalRedimOperation(out string errorMessage) - { - errorMessage = string.Empty; - if (Declaration.References.Any(rf => rf.QualifiedModuleName != QualifiedModuleName - && rf.Context.TryGetAncestor(out _))) - { - errorMessage = string.Format(RubberduckUI.EncapsulateField_ArrayHasExternalRedimFormat, IdentifierName); - return true; - } - return false; } } } diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldAsUDTMemberCandidate.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldAsUDTMemberCandidate.cs index cfe5833879..afd20ae7f4 100644 --- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldAsUDTMemberCandidate.cs +++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldAsUDTMemberCandidate.cs @@ -1,5 +1,6 @@ using Rubberduck.Parsing.Symbols; using Rubberduck.VBEditor; +using System; namespace Rubberduck.Refactorings.EncapsulateField { @@ -7,20 +8,23 @@ public interface IEncapsulateFieldAsUDTMemberCandidate : IEncapsulateFieldCandid { IObjectStateUDT ObjectStateUDT { set; get; } IEncapsulateFieldCandidate WrappedCandidate { get; } + string UserDefinedTypeMemberIdentifier { set; get; } } + /// + /// EncapsulateFieldAsUDTMemberCandidate wraps an IEncapusulateFieldCandidate instance + /// for the purposes of declaring its backing field as a UserDefinedTypeMember + /// within an existing or new UserDefinedType + /// public class EncapsulateFieldAsUDTMemberCandidate : IEncapsulateFieldAsUDTMemberCandidate { - private int _hashCode; - private readonly string _uniqueID; + private readonly int _hashCode; private IEncapsulateFieldCandidate _wrapped; public EncapsulateFieldAsUDTMemberCandidate(IEncapsulateFieldCandidate candidate, IObjectStateUDT objStateUDT) { _wrapped = candidate; - PropertyIdentifier = _wrapped.PropertyIdentifier; ObjectStateUDT = objStateUDT; - _uniqueID = BuildUniqueID(candidate, objStateUDT); - _hashCode = _uniqueID.GetHashCode(); + _hashCode = $"{candidate.QualifiedModuleName.Name}.{candidate.IdentifierName}".GetHashCode(); } public IEncapsulateFieldCandidate WrappedCandidate => _wrapped; @@ -31,9 +35,10 @@ public IObjectStateUDT ObjectStateUDT set { _objectStateUDT = value; - if (_objectStateUDT?.Declaration == Declaration) + if (_objectStateUDT?.Declaration == _wrapped.Declaration) { - EncapsulateFlag = false; + //Cannot wrap itself if it is used as the ObjectStateUDT + _wrapped.EncapsulateFlag = false; } } get => _objectStateUDT; @@ -49,30 +54,25 @@ public bool EncapsulateFlag get => _wrapped.EncapsulateFlag; } + public string UserDefinedTypeMemberIdentifier + { + set => PropertyIdentifier = value; + get => PropertyIdentifier; + } + public string PropertyIdentifier { set => _wrapped.PropertyIdentifier = value; get => _wrapped.PropertyIdentifier; } - public string PropertyAsTypeName => _wrapped.PropertyAsTypeName; + public virtual Action BackingIdentifierMutator { get; } = null; - public string BackingIdentifier - { - set { } - get => PropertyIdentifier; - } - public string BackingAsTypeName => Declaration.AsTypeName; + public string BackingIdentifier => PropertyIdentifier; - public bool CanBeReadWrite - { - set => _wrapped.CanBeReadWrite = value; - get => _wrapped.CanBeReadWrite; - } - - public bool ImplementLet => _wrapped.ImplementLet; + public string PropertyAsTypeName => _wrapped.PropertyAsTypeName; - public bool ImplementSet => _wrapped.ImplementSet; + public bool CanBeReadWrite => !_wrapped.Declaration.IsArray; public bool IsReadOnly { @@ -94,19 +94,7 @@ public IEncapsulateFieldConflictFinder ConflictFinder public bool TryValidateEncapsulationAttributes(out string errorMessage) { - errorMessage = string.Empty; - if (!_wrapped.EncapsulateFlag || ConflictFinder is null) - { - return true; - } - - if (_wrapped is IArrayCandidate ac - && ac.HasExternalRedimOperation(out errorMessage)) - { - return false; - } - - (bool IsValid, string ErrorMsg) = ConflictFinder.ValidateEncapsulationAttributes(this); + (bool IsValid, string ErrorMsg) = ConflictFinder?.ValidateEncapsulationAttributes(this) ?? (true, string.Empty); errorMessage = ErrorMsg; return IsValid; } @@ -115,12 +103,10 @@ public override bool Equals(object obj) { return obj != null && obj is EncapsulateFieldAsUDTMemberCandidate convertWrapper - && BuildUniqueID(convertWrapper, convertWrapper.ObjectStateUDT) == _uniqueID; + && convertWrapper.QualifiedModuleName == QualifiedModuleName + && convertWrapper.IdentifierName == IdentifierName; } public override int GetHashCode() => _hashCode; - - private static string BuildUniqueID(IEncapsulateFieldCandidate candidate, IObjectStateUDT field) - => $"{candidate.QualifiedModuleName.Name}.{field.IdentifierName}.{candidate.IdentifierName}"; } } diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidate.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidate.cs index 5a4c01e8e8..990d6ac4e2 100644 --- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidate.cs +++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidate.cs @@ -1,8 +1,8 @@ using Rubberduck.Parsing.Grammar; using Rubberduck.Parsing.Symbols; using Rubberduck.Refactorings.Common; -using Rubberduck.Refactorings.EncapsulateField.Extensions; using Rubberduck.VBEditor; +using System; namespace Rubberduck.Refactorings.EncapsulateField { @@ -18,13 +18,11 @@ public interface IEncapsulateFieldCandidate : IEncapsulateFieldRefactoringElemen string TargetID { get; } Declaration Declaration { get; } bool EncapsulateFlag { get; set; } - string BackingIdentifier { set; get; } - string BackingAsTypeName { get; } + string BackingIdentifier { get; } + Action BackingIdentifierMutator { get; } string PropertyIdentifier { set; get; } string PropertyAsTypeName { get; } - bool CanBeReadWrite { set; get; } - bool ImplementLet { get; } - bool ImplementSet { get; } + bool CanBeReadWrite { get; } bool IsReadOnly { set; get; } IEncapsulateFieldConflictFinder ConflictFinder { set; get; } bool TryValidateEncapsulationAttributes(out string errorMessage); @@ -32,62 +30,49 @@ public interface IEncapsulateFieldCandidate : IEncapsulateFieldRefactoringElemen public class EncapsulateFieldCandidate : IEncapsulateFieldCandidate { - protected Declaration _target; - protected QualifiedModuleName _qmn; - protected readonly string _uniqueID; - protected int _hashCode; - private string _identifierName; + protected readonly int _hashCode; protected EncapsulationIdentifiers _fieldAndProperty; - private string _rhsParameterIdentifierName; public EncapsulateFieldCandidate(Declaration declaration) { - _target = declaration; - _rhsParameterIdentifierName = Resources.Refactorings.Refactorings.CodeBuilder_DefaultPropertyRHSParam; + Declaration = declaration; + AsTypeName = declaration.AsTypeName; _fieldAndProperty = new EncapsulationIdentifiers(declaration.IdentifierName); + BackingIdentifierMutator = (value) => _fieldAndProperty.Field = value; + IdentifierName = declaration.IdentifierName; - PropertyAsTypeName = declaration.AsTypeName; - _qmn = declaration.QualifiedModuleName; - CanBeReadWrite = true; + TargetID = IdentifierName; - _uniqueID = $"{_qmn.Name}.{declaration.IdentifierName}"; - _hashCode = _uniqueID.GetHashCode(); + QualifiedModuleName = declaration.QualifiedModuleName; - ImplementLet = true; - ImplementSet = false; - if (_target.IsEnumField() && _target.AsTypeDeclaration.HasPrivateAccessibility()) - { - //5.3.1 The declared type of a function declaration may not be a private enum. - PropertyAsTypeName = Tokens.Long; - } - else if (_target.AsTypeName.Equals(Tokens.Variant) - && !_target.IsArray) - { - ImplementSet = true; - } - else if (Declaration.IsObject) - { - ImplementLet = false; - ImplementSet = true; - } + //5.3.1 The declared type of a function declaration may not be a private enum. + PropertyAsTypeName = declaration.IsEnumField() && declaration.AsTypeDeclaration.HasPrivateAccessibility() + ? Tokens.Long + : declaration.AsTypeName; + + CanBeReadWrite = !Declaration.IsArray; + + _hashCode = $"{QualifiedModuleName.Name}.{declaration.IdentifierName}".GetHashCode(); } - public Declaration Declaration => _target; + public Declaration Declaration { get; } - public string AsTypeName => _target.AsTypeName; + public string IdentifierName { get; } - public virtual string BackingIdentifier - { - get => _fieldAndProperty.Field; - set => _fieldAndProperty.Field = value; - } + public string AsTypeName { get; } - public string BackingAsTypeName => Declaration.AsTypeName; + public bool CanBeReadWrite { get; } + + public virtual bool IsReadOnly { set; get; } public virtual IEncapsulateFieldConflictFinder ConflictFinder { set; get; } + public string PropertyAsTypeName { get; set; } + + public QualifiedModuleName QualifiedModuleName { get; } + public virtual bool TryValidateEncapsulationAttributes(out string errorMessage) { (bool IsValid, string ErrorMsg) = ConflictFinder?.ValidateEncapsulationAttributes(this) ?? (true, string.Empty); @@ -96,92 +81,54 @@ public virtual bool TryValidateEncapsulationAttributes(out string errorMessage) return IsValid; } - public virtual string TargetID => _target?.IdentifierName ?? IdentifierName; + public virtual string TargetID { get; } protected bool _encapsulateFlag; public virtual bool EncapsulateFlag { set { - if (_encapsulateFlag == value) + if (_encapsulateFlag != value) { - return; + _encapsulateFlag = value; + if (!_encapsulateFlag) + { + PropertyIdentifier = _fieldAndProperty.DefaultPropertyName; + return; + } + + ConflictFinder?.AssignNoConflictIdentifiers(this); } - - _encapsulateFlag = value; - if (!_encapsulateFlag) - { - PropertyIdentifier = _fieldAndProperty.DefaultPropertyName; - return; - } - - ConflictFinder?.AssignNoConflictIdentifiers(this); } get => _encapsulateFlag; } - public virtual bool IsReadOnly { set; get; } - - public bool CanBeReadWrite { set; get; } - public string PropertyIdentifier { get => _fieldAndProperty.Property; set { - _fieldAndProperty.Property = value; - - TryRestoreNewFieldNameAsOriginalFieldIdentifierName(); - } - } - - private void TryRestoreNewFieldNameAsOriginalFieldIdentifierName() - { - var canNowUseOriginalFieldName = !_fieldAndProperty.TargetFieldName.IsEquivalentVBAIdentifierTo(_fieldAndProperty.Property) - && !(ConflictFinder?.IsConflictingIdentifier(this, _fieldAndProperty.TargetFieldName, out _) ?? false); - - if (canNowUseOriginalFieldName) - { - _fieldAndProperty.Field = _fieldAndProperty.TargetFieldName; - return; - } + if (_fieldAndProperty.Property != value) + { + _fieldAndProperty.Property = value; - if (_fieldAndProperty.Field.IsEquivalentVBAIdentifierTo(_fieldAndProperty.TargetFieldName)) - { - _fieldAndProperty.Field = _fieldAndProperty.DefaultNewFieldName; - ConflictFinder?.AssignNoConflictIdentifiers(this); + //Reset the backing field identifier + _fieldAndProperty.Field = _fieldAndProperty.TargetFieldName; + ConflictFinder?.AssignNoConflictBackingFieldIdentifier(this); + } } } - public string PropertyAsTypeName { get; set; } - - public QualifiedModuleName QualifiedModuleName => _qmn; - - public string IdentifierName - { - get => Declaration?.IdentifierName ?? _identifierName; - set => _identifierName = value; - } + public virtual string BackingIdentifier => _fieldAndProperty.Field; - private bool _implLet; - public bool ImplementLet - { - get => !IsReadOnly && _implLet; - set => _implLet = value; - } - - private bool _implSet; - public bool ImplementSet - { - get => !IsReadOnly && _implSet; - set => _implSet = value; - } + public virtual Action BackingIdentifierMutator { get; } public override bool Equals(object obj) { return obj != null && obj is IEncapsulateFieldCandidate efc - && $"{efc.QualifiedModuleName.Name}.{efc.IdentifierName}" == _uniqueID; + && efc.QualifiedModuleName == QualifiedModuleName + && efc.IdentifierName == IdentifierName; } public override int GetHashCode() => _hashCode; diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidateFactory.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidateFactory.cs index 8e480d92e1..3640f3c094 100644 --- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidateFactory.cs +++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidateFactory.cs @@ -33,23 +33,14 @@ public IEncapsulateFieldCandidate Create(Declaration target) foreach (var udtMemberDeclaration in udtMembers) { var candidateUDTMember = new UserDefinedTypeMemberCandidate(Create(udtMemberDeclaration), udtField) as IUserDefinedTypeMemberCandidate; - udtField.AddMember(candidateUDTMember); } - var udtVariablesOfSameType = _declarationFinderProvider.DeclarationFinder.UserDeclarations(DeclarationType.Variable) - .Where(v => v.AsTypeDeclaration == udtField.Declaration.AsTypeDeclaration); - - udtField.IsObjectStateUDTCandidate = udtField.TypeDeclarationIsPrivate - && udtField.Declaration.HasPrivateAccessibility() - && udtVariablesOfSameType.Count() == 1; - return udtField; } else if (target.IsArray) { - var arrayCandidate = new ArrayCandidate(target); - return arrayCandidate; + return new ArrayCandidate(target); } return new EncapsulateFieldCandidate(target); diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeCandidate.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeCandidate.cs index 9e9b442731..96fe2d9950 100644 --- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeCandidate.cs +++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeCandidate.cs @@ -1,5 +1,6 @@ using Rubberduck.Parsing.Symbols; using Rubberduck.Refactorings.Common; +using System; using System.Collections.Generic; namespace Rubberduck.Refactorings.EncapsulateField @@ -9,15 +10,17 @@ public interface IUserDefinedTypeCandidate : IEncapsulateFieldCandidate IEnumerable Members { get; } void AddMember(IUserDefinedTypeMemberCandidate member); bool TypeDeclarationIsPrivate { get; } - bool IsObjectStateUDTCandidate { set; get; } - bool IsSelectedObjectStateUDT { set; get; } } public class UserDefinedTypeCandidate : EncapsulateFieldCandidate, IUserDefinedTypeCandidate { public UserDefinedTypeCandidate(Declaration declaration) : base(declaration) - {} + { + BackingIdentifierMutator = Declaration.AsTypeDeclaration.HasPrivateAccessibility() + ? null + : base.BackingIdentifierMutator; + } public void AddMember(IUserDefinedTypeMemberCandidate member) { @@ -30,15 +33,12 @@ public void AddMember(IUserDefinedTypeMemberCandidate member) public bool TypeDeclarationIsPrivate => Declaration.AsTypeDeclaration?.HasPrivateAccessibility() ?? false; - public bool IsSelectedObjectStateUDT { set; get; } - - public bool IsObjectStateUDTCandidate { set; get; } + public override string BackingIdentifier => + BackingIdentifierMutator is null + ? _fieldAndProperty.TargetFieldName + : _fieldAndProperty.Field; - public override string BackingIdentifier - { - get => TypeDeclarationIsPrivate ? _fieldAndProperty.TargetFieldName : _fieldAndProperty.Field; - set => _fieldAndProperty.Field = value; - } + public override Action BackingIdentifierMutator { get; } private IEncapsulateFieldConflictFinder _conflictsFinder; public override IEncapsulateFieldConflictFinder ConflictFinder @@ -85,17 +85,8 @@ public override bool EncapsulateFlag } public override bool Equals(object obj) - { - if (obj is IUserDefinedTypeCandidate udt) - { - return udt.TargetID.Equals(TargetID); - } - return false; - } + => (obj is IUserDefinedTypeCandidate udt && udt.TargetID.Equals(TargetID)); - public override int GetHashCode() - { - return base.GetHashCode(); - } + public override int GetHashCode() => base.GetHashCode(); } } diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeMemberCandidate.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeMemberCandidate.cs index bcb0329f70..58712073a7 100644 --- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeMemberCandidate.cs +++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeMemberCandidate.cs @@ -1,5 +1,6 @@ using Rubberduck.Parsing.Symbols; using Rubberduck.VBEditor; +using System; namespace Rubberduck.Refactorings.EncapsulateField { @@ -26,10 +27,6 @@ public UserDefinedTypeMemberCandidate(IEncapsulateFieldCandidate candidate, IUse public string AsTypeName => WrappedCandidate.AsTypeName; - public string BackingIdentifier { set; get; } - - public string BackingAsTypeName => Declaration.AsTypeName; - public IUserDefinedTypeCandidate UDTField { private set; get; } public IEncapsulateFieldConflictFinder ConflictFinder @@ -45,6 +42,10 @@ public string IdentifierForReference(IdentifierReference idRef) public string PropertyIdentifier { set; get; } + public string BackingIdentifier { get; } + + public Action BackingIdentifierMutator { get; } = null; + public Declaration Declaration => WrappedCandidate.Declaration; public string IdentifierName => WrappedCandidate.IdentifierName; @@ -79,6 +80,7 @@ public bool EncapsulateFlag _encapsulateFlag = value; PropertyIdentifier = WrappedCandidate.PropertyIdentifier; + if (_encapsulateFlag && valueChanged && ConflictFinder != null) { ConflictFinder.AssignNoConflictIdentifiers(this); @@ -93,11 +95,8 @@ public bool EncapsulateFlag get => _encapsulateFlag; } - public bool CanBeReadWrite - { - set => WrappedCandidate.CanBeReadWrite = value; - get => WrappedCandidate.CanBeReadWrite; - } + public bool CanBeReadWrite => !Declaration.IsArray; + public bool HasValidEncapsulationAttributes => true; public QualifiedModuleName QualifiedModuleName @@ -105,10 +104,6 @@ public QualifiedModuleName QualifiedModuleName public string PropertyAsTypeName => WrappedCandidate.PropertyAsTypeName; - public bool ImplementLet => WrappedCandidate.ImplementLet; - - public bool ImplementSet => WrappedCandidate.ImplementSet; - public override bool Equals(object obj) { return obj != null diff --git a/Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT/ObjectStateUDT.cs b/Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT/ObjectStateUDT.cs index e081c733a6..ea0a5697f2 100644 --- a/Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT/ObjectStateUDT.cs +++ b/Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT/ObjectStateUDT.cs @@ -13,7 +13,6 @@ public interface IObjectStateUDT : IEncapsulateFieldRefactoringElement { Declaration Declaration { get; } string TypeIdentifier { set; get; } - string FieldDeclarationBlock { get; } string FieldIdentifier { set; get; } bool IsExistingDeclaration { get; } Declaration AsTypeDeclaration { get; } @@ -33,10 +32,9 @@ public class ObjectStateUDT : IObjectStateUDT { private static string _defaultNewFieldName = "this"; private readonly IUserDefinedTypeCandidate _wrappedUDTField; - private readonly int _hashCode; public ObjectStateUDT(IUserDefinedTypeCandidate udtField) - : this(udtField.Declaration.AsTypeName) + : this(udtField.IdentifierName, udtField.Declaration.AsTypeName) { if (!udtField.TypeDeclarationIsPrivate) { @@ -44,26 +42,21 @@ public ObjectStateUDT(IUserDefinedTypeCandidate udtField) } QualifiedModuleName = udtField.QualifiedModuleName; - FieldIdentifier = udtField.IdentifierName; _wrappedUDTField = udtField; - _hashCode = ($"{QualifiedModuleName.Name}.{_wrappedUDTField.IdentifierName}").GetHashCode(); } public ObjectStateUDT(QualifiedModuleName qualifiedModuleName) - :this($"T{qualifiedModuleName.ComponentName.CapitalizeFirstLetter()}") + :this(_defaultNewFieldName, $"T{qualifiedModuleName.ComponentName.CapitalizeFirstLetter()}") { QualifiedModuleName = qualifiedModuleName; } - private ObjectStateUDT(string typeIdentifier) + private ObjectStateUDT(string fieldIdentifier, string typeIdentifier) { - FieldIdentifier = _defaultNewFieldName; + FieldIdentifier = fieldIdentifier; TypeIdentifier = typeIdentifier; } - public string FieldDeclarationBlock - => $"{Accessibility.Private} {IdentifierName} {Tokens.As} {AsTypeName}"; - public string IdentifierName => _wrappedUDTField?.IdentifierName ?? FieldIdentifier; public Declaration Declaration => _wrappedUDTField?.Declaration; @@ -76,13 +69,9 @@ public bool IsSelected set { _isSelected = value; - if (_wrappedUDTField != null) + if (_isSelected && IsExistingDeclaration) { - _wrappedUDTField.IsSelectedObjectStateUDT = value; - if (_isSelected && IsExistingDeclaration) - { - _wrappedUDTField.EncapsulateFlag = false; - } + _wrappedUDTField.EncapsulateFlag = false; } } get => _isSelected; @@ -103,19 +92,10 @@ public IReadOnlyCollection ExistingMembers public override bool Equals(object obj) { - if (obj is IObjectStateUDT stateUDT && stateUDT.FieldIdentifier == FieldIdentifier) - { - return true; - } - - if (obj is IEncapsulateFieldRefactoringElement fd && fd.IdentifierName == IdentifierName) - { - return true; - } - - return false; + return (obj is IObjectStateUDT stateUDT && stateUDT.FieldIdentifier == FieldIdentifier) + || (obj is IEncapsulateFieldRefactoringElement fd && fd.IdentifierName == IdentifierName); } - public override int GetHashCode() => _hashCode; + public override int GetHashCode() => $"{QualifiedModuleName.Name}.{FieldIdentifier}".GetHashCode(); } } diff --git a/Rubberduck.Refactorings/EncapsulateField/PropertyAttributeSetsGenerator.cs b/Rubberduck.Refactorings/EncapsulateField/PropertyAttributeSetsGenerator.cs index 60d7b67668..bfac7adc98 100644 --- a/Rubberduck.Refactorings/EncapsulateField/PropertyAttributeSetsGenerator.cs +++ b/Rubberduck.Refactorings/EncapsulateField/PropertyAttributeSetsGenerator.cs @@ -1,4 +1,5 @@ -using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.Grammar; +using Rubberduck.Parsing.Symbols; using System; using System.Collections.Generic; using System.Linq; @@ -132,8 +133,8 @@ private PropertyAttributeSet CreatePropertyAttributeSet(IEncapsulateFieldCandida BackingField = candidate.BackingIdentifier, AsTypeName = candidate.PropertyAsTypeName, RHSParameterIdentifier = Resources.Refactorings.Refactorings.CodeBuilder_DefaultPropertyRHSParam, - GeneratePropertyLet = candidate.ImplementLet, - GeneratePropertySet = candidate.ImplementSet, + GeneratePropertyLet = !candidate.IsReadOnly && !candidate.Declaration.IsObject && !candidate.Declaration.IsArray, + GeneratePropertySet = !candidate.IsReadOnly && !candidate.Declaration.IsArray && (candidate.Declaration.IsObject || candidate.Declaration.AsTypeName == Tokens.Variant), UsesSetAssignment = candidate.Declaration.IsObject, Declaration = candidate.Declaration }; From b771e1f2162742d017d637280f4b3b01e7caee33 Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Mon, 21 Sep 2020 10:17:50 -0700 Subject: [PATCH 43/67] Update XMLDoc content --- .../CreateUDTMemberRefactoringAction.cs | 5 +++++ .../EncapsulateFieldCollectionsProvider.cs | 9 ++------ ...apsulateFieldCollectionsProviderFactory.cs | 6 ----- .../EncapsulateFieldCodeBuilder.cs | 4 ++++ ...lateFieldInsertNewCodeRefactoringAction.cs | 4 ++++ .../NewContentAggregator.cs | 22 +++++++++++++++++++ .../EncapsulateField/EncapsulateFieldModel.cs | 5 +++++ ...apsulateFieldRefactoringActionsProvider.cs | 4 ++++ ...psulateFieldUseBackingFieldModelFactory.cs | 6 ++--- ...ateFieldUseBackingUDTMemberModelFactory.cs | 4 ++-- .../EncapsulateFieldAsUDTMemberCandidate.cs | 2 +- .../UserDefinedTypeCandidate.cs | 2 +- .../FieldEncapsulationModel.cs | 6 ++--- .../ObjectStateUDT/ObjectStateUDT.cs | 6 ++--- .../PropertyAttributeSetsGenerator.cs | 10 +++++++++ 15 files changed, 67 insertions(+), 28 deletions(-) diff --git a/Rubberduck.Refactorings/CreateUDTMember/CreateUDTMemberRefactoringAction.cs b/Rubberduck.Refactorings/CreateUDTMember/CreateUDTMemberRefactoringAction.cs index 6e8e1822e5..ffc2a95138 100644 --- a/Rubberduck.Refactorings/CreateUDTMember/CreateUDTMemberRefactoringAction.cs +++ b/Rubberduck.Refactorings/CreateUDTMember/CreateUDTMemberRefactoringAction.cs @@ -8,6 +8,11 @@ using System.Linq; namespace Rubberduck.Refactorings.CreateUDTMember { + /// + /// CreateUDTMemberRefactoringAction adds a UserDefinedTypeMember declaration (based on a + /// prototype declaation) to a UserDefinedType declaration. The indentation of the first + /// existing member is used by the inserted members. The caller is responsible for identifier validation and name collision anaysis. + /// public class CreateUDTMemberRefactoringAction : CodeOnlyRefactoringActionBase { private readonly IDeclarationFinderProvider _declarationFinderProvider; diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldCollectionsProvider.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldCollectionsProvider.cs index 5de7b494b0..31c7c89459 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldCollectionsProvider.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldCollectionsProvider.cs @@ -14,15 +14,10 @@ public interface IEncapsulateFieldCollectionsProvider } /// - /// EncapsulateFieldCollectionsProvider generates collections IEncapsulateFieldCandidate + /// EncapsulateFieldCollectionsProvider generates collections of IEncapsulateFieldCandidate /// instances, IEncapsulateFieldAsUDTMemberCandidate instances, and IObjectStateUDT instances. - /// It provides these collections to the various objects of the EncapsulateFieldRefactoring - /// so that they all operate in the same object sets. + /// It provides these collection instances to the various objects of the EncapsulateFieldRefactoring. /// - /// - /// The EncapsulateFieldCollectionsProvider is the source of these collections for the various objects of - /// the EncapsulateFieldRefactoring so that they all operate in the same set of object instances. - /// public class EncapsulateFieldCollectionsProvider : IEncapsulateFieldCollectionsProvider { private readonly IDeclarationFinderProvider _declarationFinderProvider; diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldCollectionsProviderFactory.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldCollectionsProviderFactory.cs index 6049f830bf..e5fbf8f142 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldCollectionsProviderFactory.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldCollectionsProviderFactory.cs @@ -1,11 +1,6 @@ using Rubberduck.Parsing.VBA; using Rubberduck.Refactorings.EncapsulateField; using Rubberduck.VBEditor; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Rubberduck.Refactorings { @@ -39,5 +34,4 @@ public IEncapsulateFieldCollectionsProvider Create(QualifiedModuleName qualified qualifiedModuleName); } } - } diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldCodeBuilder.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldCodeBuilder.cs index 17f77cd3fc..0e14d0c77c 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldCodeBuilder.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldCodeBuilder.cs @@ -14,6 +14,10 @@ public interface IEncapsulateFieldCodeBuilder string BuildFieldDeclaration(Declaration target, string identifier); } + /// + /// EncapsulateFieldCodeBuilder wraps an ICodeBuilder instance to extend it for the + /// specific needs of an EncapsulateField refactoring action. + /// public class EncapsulateFieldCodeBuilder : IEncapsulateFieldCodeBuilder { private const string FourSpaces = " "; diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeRefactoringAction.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeRefactoringAction.cs index dfea910b89..a157dd6303 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeRefactoringAction.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeRefactoringAction.cs @@ -11,6 +11,10 @@ namespace Rubberduck.Refactorings.EncapsulateFieldInsertNewCode { + /// + /// EncapsulateFieldInsertNewCodeRefactoringAction is a refactoring action dedicated to + /// the insertion of new code content generated by EncapsulateField refactoring actions + /// public class EncapsulateFieldInsertNewCodeRefactoringAction : CodeOnlyRefactoringActionBase { private readonly IDeclarationFinderProvider _declarationFinderProvider; diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/NewContentAggregator.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/NewContentAggregator.cs index 6bc74c875e..54d15de064 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/NewContentAggregator.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/NewContentAggregator.cs @@ -13,13 +13,35 @@ public enum NewContentType public interface INewContentAggregator { + /// + /// Allows gouping content blocks by NewContentType. + /// void AddNewContent(NewContentType contentType, string block); + /// + /// Allows gouping content blocks by an adhoc identifier. + /// void AddNewContent(string contentIdentifier, string block); + /// + /// Retrieves a block of content aggregated by NewContentType. + /// + /// NewContentType blocks to aggregate string RetrieveBlock(params NewContentType[] newContentTypes); + /// + /// Retrieves a block of content aggregated by a user-determined identifier. + /// + /// NewContentType blocks to aggregate string RetrieveBlock(params string[] contentIdentifiers); + /// + /// Sets default number of NewLines between blocks of code after + /// all retrieving block(s) of code. The default value is 2. + /// int NewLineLimit { set; get; } } + /// + /// NewContentAggregator provides a repository for caching generated code blocks + /// and retrieving them as an aggregated single block of code organized by NewContentType. + /// public class NewContentAggregator : INewContentAggregator { private readonly static string _doubleSpace = $"{Environment.NewLine}{Environment.NewLine}"; diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs index ad20770d47..df9c00474f 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs @@ -6,6 +6,11 @@ namespace Rubberduck.Refactorings.EncapsulateField { + /// + /// The EncapsulateFieldModel provides a facade to the EncapsulateFieldRefactoring + /// by aggregating and simplifying access to the EncapsulateFieldUseBackingFieldModel + /// and the EncapsulateFieldUseBackingUDTMemberModel. + /// public class EncapsulateFieldModel : IRefactoringModel { public EncapsulateFieldModel(EncapsulateFieldUseBackingFieldModel backingFieldModel, diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringActionsProvider.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringActionsProvider.cs index 99577767ae..b6a47bb177 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringActionsProvider.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringActionsProvider.cs @@ -15,6 +15,10 @@ public interface IEncapsulateFieldRefactoringActionsProvider ICodeOnlyRefactoringAction EncapsulateFieldInsertNewCode { get; } } + /// + /// EncapsulateFieldRefactoringActionsProvider reduces the number of EncapsulateField refactoring action + /// constructor parameters providing refactoring actions common to the aggregated EncapsulateFieldRefactoringActions + /// public class EncapsulateFieldRefactoringActionsProvider : IEncapsulateFieldRefactoringActionsProvider { private readonly ReplaceReferencesRefactoringAction _replaceReferences; diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldModelFactory.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldModelFactory.cs index 04f7b6d246..60fbed9c09 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldModelFactory.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldModelFactory.cs @@ -1,5 +1,4 @@ -using Rubberduck.Parsing.Symbols; -using Rubberduck.Refactorings.EncapsulateField; +using Rubberduck.Refactorings.EncapsulateField; using Rubberduck.Refactorings.EncapsulateFieldUseBackingField; using System.Collections.Generic; using System.Linq; @@ -11,13 +10,12 @@ public interface IEncapsulateFieldUseBackingFieldModelFactory /// /// Creates an EncapsulateFieldUseBackingFieldModel used by the EncapsulateFieldUseBackingFieldRefactoringAction. /// - /// Optional: UserDefinedType Field to include the Encapsulated Field(s) EncapsulateFieldUseBackingFieldModel Create(IEnumerable requests); /// /// Creates an EncapsulateFieldUseBackingFieldModel based upon collection of /// IEncapsulateFieldCandidate instances created by EncapsulateFieldCandidateCollectionFactory. - /// This function is intended for exclusive use by EncapsulateFieldModelFactory + /// This function is intended for exclusive use by the EncapsulateFieldModelFactory /// EncapsulateFieldUseBackingFieldModel Create(IEncapsulateFieldCollectionsProvider collectionsProvider, IEnumerable requests); } diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModelFactory.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModelFactory.cs index c18cbfeb22..ab5794d19b 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModelFactory.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModelFactory.cs @@ -12,13 +12,13 @@ public interface IEncapsulateFieldUseBackingUDTMemberModelFactory /// /// Creates an EncapsulateFieldUseBackingUDTMemberModel used by the EncapsulateFieldUseBackingUDTMemberRefactoringAction. /// - /// Optional: UserDefinedType Field to include the Encapsulated Field(s) + /// Optional: UserDefinedType Field to host the Encapsulated Field(s) EncapsulateFieldUseBackingUDTMemberModel Create(IEnumerable requests, Declaration userDefinedTypeTarget = null); /// /// Creates an EncapsulateFieldUseBackingUDTMemberModel based upon collection of /// IEncapsulateFieldCandidate instances created by EncapsulateFieldCandidateCollectionFactory. - /// This function is intended for exclusive use by EncapsulateFieldModelFactory + /// This function is intended for exclusive use by the EncapsulateFieldModelFactory /// EncapsulateFieldUseBackingUDTMemberModel Create(IEncapsulateFieldCollectionsProvider collectionsProvider, IEnumerable requests, Declaration userDefinedTypeTarget = null); } diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldAsUDTMemberCandidate.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldAsUDTMemberCandidate.cs index afd20ae7f4..0e2ccaee2b 100644 --- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldAsUDTMemberCandidate.cs +++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldAsUDTMemberCandidate.cs @@ -13,7 +13,7 @@ public interface IEncapsulateFieldAsUDTMemberCandidate : IEncapsulateFieldCandid /// /// EncapsulateFieldAsUDTMemberCandidate wraps an IEncapusulateFieldCandidate instance - /// for the purposes of declaring its backing field as a UserDefinedTypeMember + /// for the purposes of declaring it as a new UserDefinedTypeMember /// within an existing or new UserDefinedType /// public class EncapsulateFieldAsUDTMemberCandidate : IEncapsulateFieldAsUDTMemberCandidate diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeCandidate.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeCandidate.cs index 96fe2d9950..1694544d33 100644 --- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeCandidate.cs +++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/UserDefinedTypeCandidate.cs @@ -72,6 +72,7 @@ public override bool EncapsulateFlag { set { + base.EncapsulateFlag = value; if (TypeDeclarationIsPrivate) { foreach (var member in Members) @@ -79,7 +80,6 @@ public override bool EncapsulateFlag member.EncapsulateFlag = value; } } - base.EncapsulateFlag = value; } get => base.EncapsulateFlag; } diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldEncapsulationModel.cs b/Rubberduck.Refactorings/EncapsulateField/FieldEncapsulationModel.cs index 0e8336fe31..6372dec9dc 100644 --- a/Rubberduck.Refactorings/EncapsulateField/FieldEncapsulationModel.cs +++ b/Rubberduck.Refactorings/EncapsulateField/FieldEncapsulationModel.cs @@ -3,13 +3,13 @@ namespace Rubberduck.Refactorings.EncapsulateField { /// - /// FieldEncapsulationModel contains attributes necessary for the EncapsulateFieldUseBackingFieldRefactoringAction + /// FieldEncapsulationModel consolidates attributes necessary for the EncapsulateFieldUseBackingFieldRefactoringAction /// and the EncapsulateFieldUseBackingUDTMemberRefactoringAction. /// /// - /// There is no validation or conflict checking performed for non-UserDefinedTypes. + /// There is no identifier validation or conflict checking performed. /// If the target is a UserDefinedType Field and the UserDefinedType is Private, - /// then the propertyIdentifier parameter is ignored and PropertyIdentifiers for each UserDefinedTypeMember + /// then the propertyIdentifier parameter is ignored and PropertyIdentifiers for each UserDefinedTypeMember /// are generated by the refactoring action. /// public class FieldEncapsulationModel diff --git a/Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT/ObjectStateUDT.cs b/Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT/ObjectStateUDT.cs index ea0a5697f2..61c1208049 100644 --- a/Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT/ObjectStateUDT.cs +++ b/Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT/ObjectStateUDT.cs @@ -4,8 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; -using Rubberduck.Resources; -using Rubberduck.Parsing.Grammar; namespace Rubberduck.Refactorings.EncapsulateField { @@ -22,11 +20,11 @@ public interface IObjectStateUDT : IEncapsulateFieldRefactoringElement /// /// ObjectStateUDT is a Private UserDefinedType whose UserDefinedTypeMembers represent - /// object state in lieu of (or in addition to) a set of Private fields. + /// a consolidated grouping of object state in lieu of (or in addition to) a set of Private fields. /// /// /// Within the EncapsulateField refactoring, the ObjectStateUDT can be an existing - /// UserDefinedType or an identifier that will be used to generate a new UserDefinedType + /// UserDefinedType or a new UserDefinedType generated by the refactoring. /// public class ObjectStateUDT : IObjectStateUDT { diff --git a/Rubberduck.Refactorings/EncapsulateField/PropertyAttributeSetsGenerator.cs b/Rubberduck.Refactorings/EncapsulateField/PropertyAttributeSetsGenerator.cs index bfac7adc98..7d6e6a14e7 100644 --- a/Rubberduck.Refactorings/EncapsulateField/PropertyAttributeSetsGenerator.cs +++ b/Rubberduck.Refactorings/EncapsulateField/PropertyAttributeSetsGenerator.cs @@ -24,6 +24,16 @@ public interface IPropertyAttributeSetsGenerator IReadOnlyCollection GeneratePropertyAttributeSets(IEncapsulateFieldCandidate candidate); } + /// + /// PropertyAttributeSetsGenerator operates on an IEncapsulateFieldCandidate instance to + /// generate a collection of PropertyAttributeSets used by the EncapsulateField refactoring + /// actions to generate Property Let/Set/Get code blocks. + /// + /// + /// Typically there is only a single PropertyAttributeSet in the collection. + /// In the case of a Private UserDefinedType, there will be a PropertyAttributeSet + /// for each UserDefinedTypeMember. + /// public class PropertyAttributeSetsGenerator : IPropertyAttributeSetsGenerator { private Func _backingFieldQualifierFunc; From 3a74f8d043c13c0af931121f00452f2be0646239 Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Mon, 21 Sep 2020 12:30:35 -0700 Subject: [PATCH 44/67] Restore modified UI accessors Restored EncapsulateFieldModel and IObjectStateUDT accessors used by the UI to keep all changes for this PR within the RefactoringActions. --- .../EncapsulateField/EncapsulateFieldViewModel.cs | 2 +- .../EncapsulateField/EncapsulateFieldModel.cs | 2 ++ .../EncapsulateField/ObjectStateUDT/ObjectStateUDT.cs | 5 +++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Rubberduck.Core/UI/Refactorings/EncapsulateField/EncapsulateFieldViewModel.cs b/Rubberduck.Core/UI/Refactorings/EncapsulateField/EncapsulateFieldViewModel.cs index 4316fc7550..5275c21590 100644 --- a/Rubberduck.Core/UI/Refactorings/EncapsulateField/EncapsulateFieldViewModel.cs +++ b/Rubberduck.Core/UI/Refactorings/EncapsulateField/EncapsulateFieldViewModel.cs @@ -198,7 +198,7 @@ public bool ConvertFieldsToUDTMembers private bool _selectionHasValidEncapsulationAttributes; public bool SelectionHasValidEncapsulationAttributes => _selectionHasValidEncapsulationAttributes; - public string PropertiesPreview => Model.PreviewProvider?.Preview(Model) ?? string.Empty; + public string PropertiesPreview => Model.PreviewRefactoring(); public CommandBase SelectAllCommand { get; } diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs index df9c00474f..283ed2164d 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs @@ -29,6 +29,8 @@ public class EncapsulateFieldModel : IRefactoringModel public EncapsulateFieldUseBackingFieldModel EncapsulateFieldUseBackingFieldModel { get; } + public string PreviewRefactoring() => PreviewProvider?.Preview(this) ?? string.Empty; + public IRefactoringPreviewProvider PreviewProvider { set; get; } public Action StrategyChangedAction { set; get; } = (m) => { }; diff --git a/Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT/ObjectStateUDT.cs b/Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT/ObjectStateUDT.cs index 61c1208049..e4245f104a 100644 --- a/Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT/ObjectStateUDT.cs +++ b/Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT/ObjectStateUDT.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Rubberduck.Parsing.Grammar; namespace Rubberduck.Refactorings.EncapsulateField { @@ -12,6 +13,7 @@ public interface IObjectStateUDT : IEncapsulateFieldRefactoringElement Declaration Declaration { get; } string TypeIdentifier { set; get; } string FieldIdentifier { set; get; } + string FieldDeclarationBlock { get; } bool IsExistingDeclaration { get; } Declaration AsTypeDeclaration { get; } bool IsSelected { set; get; } @@ -61,6 +63,9 @@ private ObjectStateUDT(string fieldIdentifier, string typeIdentifier) public string AsTypeName => _wrappedUDTField?.AsTypeName ?? TypeIdentifier; + public string FieldDeclarationBlock + => $"{Accessibility.Private} {IdentifierName} {Tokens.As} {AsTypeName}"; + private bool _isSelected; public bool IsSelected { From b227806b8ec5423de331e57684b05ad04546cb5b Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Mon, 21 Sep 2020 12:35:55 -0700 Subject: [PATCH 45/67] Fix array identifier validation bug --- .../ConflictDetection/EncapsulateFieldConflictFinder.cs | 8 +++++++- .../EncapsulateArrayFieldTests.cs | 3 ++- .../EncapsulateField/EncapsulateFieldValidatorTests.cs | 6 +++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldConflictFinder.cs b/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldConflictFinder.cs index cf1126ee17..00eedd424f 100644 --- a/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldConflictFinder.cs +++ b/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldConflictFinder.cs @@ -85,8 +85,14 @@ public EncapsulateFieldConflictFinder(IDeclarationFinderProvider declarationFind && rf.Context.TryGetAncestor(out _))) { errorMessage = string.Format(RubberduckUI.EncapsulateField_ArrayHasExternalRedimFormat, field.IdentifierName); + return (false, errorMessage); + } + + if (field is IEncapsulateFieldAsUDTMemberCandidate udtMember + && VBAIdentifierValidator.TryMatchInvalidIdentifierCriteria(udtMember.UserDefinedTypeMemberIdentifier, declarationType, out errorMessage, true)) + { + return (false, errorMessage); } - return (!string.IsNullOrEmpty(errorMessage), errorMessage); } var hasConflictFreeValidIdentifiers = diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateArrayFieldTests.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateArrayFieldTests.cs index 424dcf7d54..78d8f9b0f8 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateArrayFieldTests.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateArrayFieldTests.cs @@ -203,11 +203,12 @@ End Sub var field = model[fieldUT]; - field.TryValidateEncapsulationAttributes(out var errorMessage); + var result = field.TryValidateEncapsulationAttributes(out var errorMessage); var expectedError = string.Format(RubberduckUI.EncapsulateField_ArrayHasExternalRedimFormat, field.IdentifierName); StringAssert.AreEqualIgnoringCase(expectedError, errorMessage); + Assert.IsFalse(result); } protected override IRefactoring TestRefactoring( diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldValidatorTests.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldValidatorTests.cs index f0ed03be4b..7a9de08672 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldValidatorTests.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldValidatorTests.cs @@ -8,6 +8,7 @@ using Rubberduck.Parsing.Symbols; using Rubberduck.VBEditor.SafeComWrappers; using System.Linq; +using Rubberduck.Resources; namespace RubberduckTests.Refactoring.EncapsulateField { @@ -502,7 +503,10 @@ public void UDTReservedMemberArrayIdentifier() var model = Support.RetrieveUserModifiedModelPriorToRefactoring(vbe, fieldName, DeclarationType.Variable, presenterAction); - Assert.AreEqual(false, model[fieldName].TryValidateEncapsulationAttributes(out var message), message); + Assert.AreEqual(false, model[fieldName].TryValidateEncapsulationAttributes(out var errorMessage), errorMessage); + + var expectedMessage = string.Format(RubberduckUI.InvalidNameCriteria_IsReservedKeywordFormat, fieldName); + Assert.AreEqual(expectedMessage, errorMessage); } [Test] From 105cbdd82e1834dc41628e6ddf0f05ac64215c60 Mon Sep 17 00:00:00 2001 From: Max Doerner Date: Thu, 1 Oct 2020 00:38:41 +0200 Subject: [PATCH 46/67] Get document supertypes from user com projects We use the user com projects to get the names of the implemented interfaces; the type hierarchy pass does the rest. There is one hack in this: we suppress the underscore in front of the implemented interfaces in order to emulate the VBE's behaviour to let type checks pass relative to the coclass instead of the interface. This is entirely based on convention. Moreover, this lacks unit test since the com projects cannot be mocked at this stage. --- .../CompilationPasses/TypeHierarchyPass.cs | 4 +- .../ReferenceResolveRunner.cs | 13 +- .../ReferenceResolveRunnerBase.cs | 128 +++++++----------- .../SynchronousReferenceResolveRunner.cs | 13 +- RubberduckTests/Mocks/MockParser.cs | 3 +- 5 files changed, 66 insertions(+), 95 deletions(-) diff --git a/Rubberduck.Parsing/VBA/ReferenceManagement/CompilationPasses/TypeHierarchyPass.cs b/Rubberduck.Parsing/VBA/ReferenceManagement/CompilationPasses/TypeHierarchyPass.cs index 000bfb55d5..87ffef3bd2 100644 --- a/Rubberduck.Parsing/VBA/ReferenceManagement/CompilationPasses/TypeHierarchyPass.cs +++ b/Rubberduck.Parsing/VBA/ReferenceManagement/CompilationPasses/TypeHierarchyPass.cs @@ -31,12 +31,12 @@ public TypeHierarchyPass(DeclarationFinder declarationFinder, VBAExpressionParse public void Execute(IReadOnlyCollection modules) { - var toRelsolveSupertypesFor = _declarationFinder.UserDeclarations(DeclarationType.ClassModule) + var toResolveSupertypesFor = _declarationFinder.UserDeclarations(DeclarationType.ClassModule) .Concat(_declarationFinder.UserDeclarations(DeclarationType.Document)) .Concat(_declarationFinder.UserDeclarations(DeclarationType.UserForm)) .Where(decl => modules.Contains(decl.QualifiedName.QualifiedModuleName)) .Concat(_declarationFinder.BuiltInDeclarations(DeclarationType.ClassModule)); - foreach (var declaration in toRelsolveSupertypesFor) + foreach (var declaration in toResolveSupertypesFor) { AddImplementedInterface(declaration); } diff --git a/Rubberduck.Parsing/VBA/ReferenceManagement/ReferenceResolveRunner.cs b/Rubberduck.Parsing/VBA/ReferenceManagement/ReferenceResolveRunner.cs index d9047ad98d..b37aa0b666 100644 --- a/Rubberduck.Parsing/VBA/ReferenceManagement/ReferenceResolveRunner.cs +++ b/Rubberduck.Parsing/VBA/ReferenceManagement/ReferenceResolveRunner.cs @@ -5,6 +5,7 @@ using System.Threading; using System.Threading.Tasks; using Antlr4.Runtime.Tree; +using Rubberduck.Parsing.ComReflection; using Rubberduck.Parsing.VBA.DeclarationCaching; using Rubberduck.VBEditor; @@ -17,13 +18,15 @@ public class ReferenceResolveRunner : ReferenceResolveRunnerBase public ReferenceResolveRunner( RubberduckParserState state, IParserStateManager parserStateManager, - IModuleToModuleReferenceManager moduletToModuleReferenceManager, - IReferenceRemover referenceRemover) + IModuleToModuleReferenceManager moduleToModuleReferenceManager, + IReferenceRemover referenceRemover, + IUserComProjectProvider userComProjectProvider) :base(state, parserStateManager, - moduletToModuleReferenceManager, - referenceRemover) - { } + moduleToModuleReferenceManager, + referenceRemover, + userComProjectProvider) + {} protected override void ResolveReferences(ICollection> toResolve, CancellationToken token) diff --git a/Rubberduck.Parsing/VBA/ReferenceManagement/ReferenceResolveRunnerBase.cs b/Rubberduck.Parsing/VBA/ReferenceManagement/ReferenceResolveRunnerBase.cs index 31b8577a3d..fc55a4d21c 100644 --- a/Rubberduck.Parsing/VBA/ReferenceManagement/ReferenceResolveRunnerBase.cs +++ b/Rubberduck.Parsing/VBA/ReferenceManagement/ReferenceResolveRunnerBase.cs @@ -6,6 +6,7 @@ using Antlr4.Runtime.Tree; using NLog; using Rubberduck.Parsing.Common; +using Rubberduck.Parsing.ComReflection; using Rubberduck.Parsing.Symbols; using Rubberduck.Parsing.VBA.DeclarationCaching; using Rubberduck.Parsing.VBA.Extensions; @@ -29,12 +30,14 @@ public abstract class ReferenceResolveRunnerBase : IReferenceResolveRunner protected readonly IParserStateManager _parserStateManager; private readonly IModuleToModuleReferenceManager _moduleToModuleReferenceManager; private readonly IReferenceRemover _referenceRemover; + private readonly IUserComProjectProvider _userComProjectProvider; public ReferenceResolveRunnerBase( RubberduckParserState state, IParserStateManager parserStateManager, - IModuleToModuleReferenceManager moduletToModuleReferenceManager, - IReferenceRemover referenceRemover) + IModuleToModuleReferenceManager moduleToModuleReferenceManager, + IReferenceRemover referenceRemover, + IUserComProjectProvider userComProjectProvider) { if (state == null) { @@ -44,19 +47,24 @@ public abstract class ReferenceResolveRunnerBase : IReferenceResolveRunner { throw new ArgumentNullException(nameof(parserStateManager)); } - if (moduletToModuleReferenceManager == null) + if (moduleToModuleReferenceManager == null) { - throw new ArgumentNullException(nameof(moduletToModuleReferenceManager)); + throw new ArgumentNullException(nameof(moduleToModuleReferenceManager)); } if (referenceRemover == null) { throw new ArgumentNullException(nameof(referenceRemover)); } + if (userComProjectProvider == null) + { + throw new ArgumentNullException(nameof(userComProjectProvider)); + } _state = state; _parserStateManager = parserStateManager; - _moduleToModuleReferenceManager = moduletToModuleReferenceManager; + _moduleToModuleReferenceManager = moduleToModuleReferenceManager; _referenceRemover = referenceRemover; + _userComProjectProvider = userComProjectProvider; } @@ -81,18 +89,18 @@ public void ResolveReferences(IReadOnlyCollection toResolve var parsingStageTimer = ParsingStageTimer.StartNew(); - ExecuteCompilationPasses(_toResolve.AsReadOnly(), token); + AddSuperTypeNamesForDocumentModules(_toResolve.AsReadOnly(), _state, _userComProjectProvider); token.ThrowIfCancellationRequested(); parsingStageTimer.Stop(); - parsingStageTimer.Log("Executed compilation passes in {0}ms."); - parsingStageTimer.Restart(); + parsingStageTimer.Log("Added supertypes for document modules in {0}ms."); - AddSupertypesForDocumentModules(_toResolve.AsReadOnly(), _state); + ExecuteCompilationPasses(_toResolve.AsReadOnly(), token); token.ThrowIfCancellationRequested(); parsingStageTimer.Stop(); - parsingStageTimer.Log("Added supertypes for document modules in {0}ms."); + parsingStageTimer.Log("Executed compilation passes in {0}ms."); + parsingStageTimer.Restart(); var parseTreesToResolve = _state.ParseTrees.Where(kvp => _toResolve.Contains(kvp.Key)).ToList(); token.ThrowIfCancellationRequested(); @@ -151,90 +159,46 @@ private void ExecuteCompilationPasses(IReadOnlyCollection m } } - private void AddSupertypesForDocumentModules(IReadOnlyCollection modules, RubberduckParserState state) - { - var documentModuleDeclarations = state.DeclarationFinder.UserDeclarations(DeclarationType.Document) - .OfType() - .Where(declaration => modules.Contains(declaration.QualifiedName.QualifiedModuleName)); - - foreach (var documentDeclaration in documentModuleDeclarations) - { - var documentSupertype = SupertypeForDocument(documentDeclaration.QualifiedName.QualifiedModuleName, state); - if (documentSupertype != null) - { - documentDeclaration.AddSupertype(documentSupertype); - } - } - } + // skip IDispatch.. just about everything implements it and RD doesn't need to care about it; don't care about IUnknown either + private static readonly HashSet IgnoredComInterfaces = new HashSet(new[] { "IDispatch", "IUnknown" }); - private Declaration SupertypeForDocument(QualifiedModuleName module, RubberduckParserState state) + private void AddSuperTypeNamesForDocumentModules(IReadOnlyCollection modules, RubberduckParserState state, IUserComProjectProvider userComProjectProvider) { - if(module.ComponentType != ComponentType.Document) - { - return null; - } + //todo: Figure out how to unit test this. - var component = _state.ProjectsProvider.Component(module); - if (component == null || component.IsWrappingNullReference) - { - return null; - } + var documentModuleDeclarationsByProject = state.DeclarationFinder.UserDeclarations(DeclarationType.Document) + .OfType() + .Where(declaration => modules.Contains(declaration.QualifiedName.QualifiedModuleName)) + .GroupBy(declaration => declaration.ProjectId); - Declaration superType = null; - // TODO: Replace with TypeLibAPI call, require a solution regarding thread synchronization or caching - /* - using (var properties = component.Properties) + foreach (var projectGroup in documentModuleDeclarationsByProject) { - int documentPropertyCount = 0; - try + var userComProject = userComProjectProvider.UserProject(projectGroup.Key); + var documents = projectGroup.ToDictionary(module => module.IdentifierName); + foreach (var comModule in userComProject.Members) { - if (properties == null || properties.IsWrappingNullReference) + if (!(documents.TryGetValue(comModule.Name, out var document))) { - return null; + continue; } - documentPropertyCount = properties.Count; - } - catch(COMException) - { - return null; - } - - foreach (var coclass in state.CoClasses) - { - try - { - if (coclass.Key.Count != documentPropertyCount) - { - continue; - } - - var allNamesMatch = true; - for (var i = 0; i < coclass.Key.Count; i++) - { - using (var property = properties[i+1]) - { - if (coclass.Key[i] != property?.Name) - { - allNamesMatch = false; - break; - } - } - } - - if (allNamesMatch) - { - superType = coclass.Value; - break; - } - } - catch (COMException) + + var inheritedInterfaces = comModule is ComCoClass documentCoClass + ? documentCoClass.ImplementedInterfaces + : (comModule as ComInterface)?.InheritedInterfaces; + + //todo: Find a way to deal with the VBE's document type assignment behaviour not relying on an assumption about an interface naming convention. + var superTypeNames = inheritedInterfaces? + .Where(i => !i.IsRestricted && !IgnoredComInterfaces.Contains(i.Name)) + .Select(i => i.Name) + .Select(name => name.StartsWith("_") ? name.Substring(1) : name) //This emulates the VBE's behaviour to allow assignment to the coclass type instead on the interface. + ?? Enumerable.Empty(); + + foreach (var superTypeName in superTypeNames) { + document.AddSupertypeName(superTypeName); } } } - */ - - return superType; } protected void ResolveReferences(DeclarationFinder finder, QualifiedModuleName module, IParseTree tree, CancellationToken token) diff --git a/Rubberduck.Parsing/VBA/ReferenceManagement/SynchronousReferenceResolveRunner.cs b/Rubberduck.Parsing/VBA/ReferenceManagement/SynchronousReferenceResolveRunner.cs index 9b09f2cd73..04ff280f64 100644 --- a/Rubberduck.Parsing/VBA/ReferenceManagement/SynchronousReferenceResolveRunner.cs +++ b/Rubberduck.Parsing/VBA/ReferenceManagement/SynchronousReferenceResolveRunner.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Threading; using Antlr4.Runtime.Tree; +using Rubberduck.Parsing.ComReflection; using Rubberduck.Parsing.VBA.DeclarationCaching; using Rubberduck.VBEditor; @@ -12,13 +13,15 @@ public class SynchronousReferenceResolveRunner : ReferenceResolveRunnerBase public SynchronousReferenceResolveRunner( RubberduckParserState state, IParserStateManager parserStateManager, - IModuleToModuleReferenceManager moduletToModuleReferenceManager, - IReferenceRemover referenceRemover) + IModuleToModuleReferenceManager moduleToModuleReferenceManager, + IReferenceRemover referenceRemover, + IUserComProjectProvider userComProjectProvider) : base(state, parserStateManager, - moduletToModuleReferenceManager, - referenceRemover) - { } + moduleToModuleReferenceManager, + referenceRemover, + userComProjectProvider) + {} protected override void ResolveReferences(ICollection> toResolve, CancellationToken token) diff --git a/RubberduckTests/Mocks/MockParser.cs b/RubberduckTests/Mocks/MockParser.cs index 222fb32f19..cd5a64e5d9 100644 --- a/RubberduckTests/Mocks/MockParser.cs +++ b/RubberduckTests/Mocks/MockParser.cs @@ -122,7 +122,8 @@ public static (SynchronousParseCoordinator parser, IRewritingManager rewritingMa state, parserStateManager, moduleToModuleReferenceManager, - referenceRemover); + referenceRemover, + userComProjectsRepository); var parsingStageService = new ParsingStageService( comSynchronizer, builtInDeclarationLoader, From 650d5635c934662b8dd6231dc2d6a93453b10859 Mon Sep 17 00:00:00 2001 From: Max Doerner Date: Thu, 1 Oct 2020 21:17:43 +0200 Subject: [PATCH 47/67] Change the document module supertype behaviour to adding supertypes without underscore This seems safer in case we actually have a declaration for the interface with an underscore. --- .../ReferenceResolveRunnerBase.cs | 34 +++++++++++++------ 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/Rubberduck.Parsing/VBA/ReferenceManagement/ReferenceResolveRunnerBase.cs b/Rubberduck.Parsing/VBA/ReferenceManagement/ReferenceResolveRunnerBase.cs index fc55a4d21c..36916638e4 100644 --- a/Rubberduck.Parsing/VBA/ReferenceManagement/ReferenceResolveRunnerBase.cs +++ b/Rubberduck.Parsing/VBA/ReferenceManagement/ReferenceResolveRunnerBase.cs @@ -182,16 +182,7 @@ private void AddSuperTypeNamesForDocumentModules(IReadOnlyCollection !i.IsRestricted && !IgnoredComInterfaces.Contains(i.Name)) - .Select(i => i.Name) - .Select(name => name.StartsWith("_") ? name.Substring(1) : name) //This emulates the VBE's behaviour to allow assignment to the coclass type instead on the interface. - ?? Enumerable.Empty(); + var superTypeNames = SuperTypeNamesForDocumentFromComType(comModule); foreach (var superTypeName in superTypeNames) { @@ -201,6 +192,29 @@ private void AddSuperTypeNamesForDocumentModules(IReadOnlyCollection SuperTypeNamesForDocumentFromComType(IComType comModule) + { + var inheritedInterfaces = comModule is ComCoClass documentCoClass + ? documentCoClass.ImplementedInterfaces + : (comModule as ComInterface)?.InheritedInterfaces; + + //todo: Find a way to deal with the VBE's document type assignment behaviour not relying on an assumption about an interface naming convention. + var superTypeNames = (inheritedInterfaces? + .Where(i => !i.IsRestricted && !IgnoredComInterfaces.Contains(i.Name)) + .Select(i => i.Name) + ?? Enumerable.Empty()) + .ToList(); + + //This emulates the VBE's behaviour to allow assignment to the coclass type instead on the interface. + var additionalSuperTypes = superTypeNames + .Where(name => name.StartsWith("_")) + .Select(name => name.Substring(1)) + .ToList(); + + superTypeNames.AddRange(additionalSuperTypes); + return superTypeNames; + } + protected void ResolveReferences(DeclarationFinder finder, QualifiedModuleName module, IParseTree tree, CancellationToken token) { token.ThrowIfCancellationRequested(); From 80e13479f626808f0155a3822bcd4823738aeded Mon Sep 17 00:00:00 2001 From: Max Doerner Date: Fri, 2 Oct 2020 01:36:04 +0200 Subject: [PATCH 48/67] Fix NRE if the user com project for a document module does not exist An appropriate guard clause was missing in the previous two commits. --- .../VBA/ReferenceManagement/ReferenceResolveRunnerBase.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Rubberduck.Parsing/VBA/ReferenceManagement/ReferenceResolveRunnerBase.cs b/Rubberduck.Parsing/VBA/ReferenceManagement/ReferenceResolveRunnerBase.cs index 36916638e4..8c3bd6eda1 100644 --- a/Rubberduck.Parsing/VBA/ReferenceManagement/ReferenceResolveRunnerBase.cs +++ b/Rubberduck.Parsing/VBA/ReferenceManagement/ReferenceResolveRunnerBase.cs @@ -14,7 +14,6 @@ using Rubberduck.Parsing.VBA.ReferenceManagement.CompilationPasses; using Rubberduck.VBEditor; using Rubberduck.VBEditor.Extensions; -using Rubberduck.VBEditor.SafeComWrappers; namespace Rubberduck.Parsing.VBA.ReferenceManagement { @@ -174,6 +173,12 @@ private void AddSuperTypeNamesForDocumentModules(IReadOnlyCollection module.IdentifierName); foreach (var comModule in userComProject.Members) { From 9a7d34c5c8b8dc3b321dd10639ad749b10c1c635 Mon Sep 17 00:00:00 2001 From: Max Doerner Date: Fri, 2 Oct 2020 02:01:50 +0200 Subject: [PATCH 49/67] Add ImplicitContainingWorkbookReferenceInspection and ImplicitContainingWorksheetReferenceInspection These two find unqualified references inside the corresponding document modules to certain members that point to the ActiveWorkbook or ActiveSheet when used unqualified outside the appropriate document modules. That the reference is actually to the containing document can be surprising. Because of that, they should be qualified with Me. This also removes the corresponding false-positives for the ImplicitActiveSheetReferenceInspection and ImplicitActiveWorkbookReferenceInspection. Technically, both the containing and active workbook/worksheet types now share a common base inspection handling (most of) the selection of the declarations whose references can be suspicious. --- .../ImplicitSheetReferenceInspectionBase.cs | 46 ++++ ...ImplicitWorkbookReferenceInspectionBase.cs | 46 ++++ .../ImplicitActiveSheetReferenceInspection.cs | 38 +--- ...plicitActiveWorkbookReferenceInspection.cs | 33 +-- ...itContainingWorkbookReferenceInspection.cs | 72 +++++++ ...tContainingWorksheetReferenceInspection.cs | 59 ++++++ .../CodeInspectionDefaults.Designer.cs | 116 +++++----- .../CodeInspectionDefaults.settings | 2 + .../Inspections/InspectionInfo.Designer.cs | 20 +- .../Inspections/InspectionInfo.de.resx | 6 + .../Inspections/InspectionInfo.resx | 6 + .../Inspections/InspectionNames.Designer.cs | 20 +- .../Inspections/InspectionNames.de.resx | 6 + .../Inspections/InspectionNames.resx | 6 + .../Inspections/InspectionResults.Designer.cs | 20 +- .../Inspections/InspectionResults.de.resx | 6 + .../Inspections/InspectionResults.resx | 7 + ...icitActiveSheetReferenceInspectionTests.cs | 56 +++++ ...tActiveWorkbookReferenceInspectionTests.cs | 83 ++++++++ ...ContainingSheetreferenceInspectionTests.cs | 142 +++++++++++++ ...tainingWorkbookReferenceInspectionTests.cs | 200 ++++++++++++++++++ 21 files changed, 873 insertions(+), 117 deletions(-) create mode 100644 Rubberduck.CodeAnalysis/Inspections/Abstract/ImplicitSheetReferenceInspectionBase.cs create mode 100644 Rubberduck.CodeAnalysis/Inspections/Abstract/ImplicitWorkbookReferenceInspectionBase.cs create mode 100644 Rubberduck.CodeAnalysis/Inspections/Concrete/ImplicitContainingWorkbookReferenceInspection.cs create mode 100644 Rubberduck.CodeAnalysis/Inspections/Concrete/ImplicitContainingWorksheetReferenceInspection.cs create mode 100644 RubberduckTests/Inspections/ImplicitContainingSheetreferenceInspectionTests.cs create mode 100644 RubberduckTests/Inspections/ImplicitContainingWorkbookReferenceInspectionTests.cs diff --git a/Rubberduck.CodeAnalysis/Inspections/Abstract/ImplicitSheetReferenceInspectionBase.cs b/Rubberduck.CodeAnalysis/Inspections/Abstract/ImplicitSheetReferenceInspectionBase.cs new file mode 100644 index 0000000000..7160af6643 --- /dev/null +++ b/Rubberduck.CodeAnalysis/Inspections/Abstract/ImplicitSheetReferenceInspectionBase.cs @@ -0,0 +1,46 @@ +using System.Collections.Generic; +using System.Linq; +using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.VBA; +using Rubberduck.Parsing.VBA.DeclarationCaching; + +namespace Rubberduck.CodeAnalysis.Inspections.Abstract +{ + internal abstract class ImplicitSheetReferenceInspectionBase : IdentifierReferenceInspectionFromDeclarationsBase + { + public ImplicitSheetReferenceInspectionBase(IDeclarationFinderProvider declarationFinderProvider) + : base(declarationFinderProvider) + { } + + protected override IEnumerable ObjectionableDeclarations(DeclarationFinder finder) + { + var excel = finder.Projects + .SingleOrDefault(item => !item.IsUserDefined + && item.IdentifierName == "Excel"); + if (excel == null) + { + return Enumerable.Empty(); + } + + var globalModules = GlobalObjectClassNames + .Select(className => finder.FindClassModule(className, excel, true)) + .OfType(); + + return globalModules + .SelectMany(moduleClass => moduleClass.Members) + .Where(declaration => TargetMemberNames.Contains(declaration.IdentifierName) + && declaration.DeclarationType.HasFlag(DeclarationType.Member) + && declaration.AsTypeName == "Range"); + } + + private static readonly string[] GlobalObjectClassNames = + { + "Global", "_Global" + }; + + private static readonly string[] TargetMemberNames = + { + "Cells", "Range", "Columns", "Rows" + }; + } +} \ No newline at end of file diff --git a/Rubberduck.CodeAnalysis/Inspections/Abstract/ImplicitWorkbookReferenceInspectionBase.cs b/Rubberduck.CodeAnalysis/Inspections/Abstract/ImplicitWorkbookReferenceInspectionBase.cs new file mode 100644 index 0000000000..3fd009e56b --- /dev/null +++ b/Rubberduck.CodeAnalysis/Inspections/Abstract/ImplicitWorkbookReferenceInspectionBase.cs @@ -0,0 +1,46 @@ +using System.Collections.Generic; +using System.Linq; +using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.VBA; +using Rubberduck.Parsing.VBA.DeclarationCaching; + +namespace Rubberduck.CodeAnalysis.Inspections.Abstract +{ + internal abstract class ImplicitWorkbookReferenceInspectionBase : IdentifierReferenceInspectionFromDeclarationsBase + { + internal ImplicitWorkbookReferenceInspectionBase(IDeclarationFinderProvider declarationFinderProvider) + : base(declarationFinderProvider) + { } + + private static readonly string[] InterestingMembers = + { + "Worksheets", "Sheets", "Names" + }; + + private static readonly string[] InterestingClasses = + { + "_Global", "_Application", "Global", "Application" + }; + + protected override IEnumerable ObjectionableDeclarations(DeclarationFinder finder) + { + var excel = finder.Projects + .SingleOrDefault(project => project.IdentifierName == "Excel" && !project.IsUserDefined); + if (excel == null) + { + return Enumerable.Empty(); + } + + var relevantClasses = InterestingClasses + .Select(className => finder.FindClassModule(className, excel, true)) + .OfType(); + + var relevantProperties = relevantClasses + .SelectMany(classDeclaration => classDeclaration.Members) + .OfType() + .Where(member => InterestingMembers.Contains(member.IdentifierName)); + + return relevantProperties; + } + } +} \ No newline at end of file diff --git a/Rubberduck.CodeAnalysis/Inspections/Concrete/ImplicitActiveSheetReferenceInspection.cs b/Rubberduck.CodeAnalysis/Inspections/Concrete/ImplicitActiveSheetReferenceInspection.cs index 4f2cf28f4e..b7026a6ea7 100644 --- a/Rubberduck.CodeAnalysis/Inspections/Concrete/ImplicitActiveSheetReferenceInspection.cs +++ b/Rubberduck.CodeAnalysis/Inspections/Concrete/ImplicitActiveSheetReferenceInspection.cs @@ -1,5 +1,4 @@ -using System.Collections.Generic; -using System.Linq; +using System.Linq; using Rubberduck.CodeAnalysis.Inspections.Abstract; using Rubberduck.CodeAnalysis.Inspections.Attributes; using Rubberduck.Parsing.Symbols; @@ -10,7 +9,7 @@ namespace Rubberduck.CodeAnalysis.Inspections.Concrete { /// - /// Locates unqualified Worksheet.Range/Cells/Columns/Rows member calls that implicitly refer to ActiveSheet. + /// Locates unqualified Worksheet.Range/Cells/Columns/Rows member calls inside worksheet modules. /// /// /// @@ -42,43 +41,18 @@ namespace Rubberduck.CodeAnalysis.Inspections.Concrete /// /// [RequiredLibrary("Excel")] - internal sealed class ImplicitActiveSheetReferenceInspection : IdentifierReferenceInspectionFromDeclarationsBase + internal sealed class ImplicitActiveSheetReferenceInspection : ImplicitSheetReferenceInspectionBase { public ImplicitActiveSheetReferenceInspection(IDeclarationFinderProvider declarationFinderProvider) : base(declarationFinderProvider) {} - protected override IEnumerable ObjectionableDeclarations(DeclarationFinder finder) + protected override bool IsResultReference(IdentifierReference reference, DeclarationFinder finder) { - var excel = finder.Projects - .SingleOrDefault(item => !item.IsUserDefined - && item.IdentifierName == "Excel"); - if (excel == null) - { - return Enumerable.Empty(); - } - - var globalModules = GlobalObjectClassNames - .Select(className => finder.FindClassModule(className, excel, true)) - .OfType(); - - return globalModules - .SelectMany(moduleClass => moduleClass.Members) - .Where(declaration => TargetMemberNames.Contains(declaration.IdentifierName) - && declaration.DeclarationType.HasFlag(DeclarationType.Member) - && declaration.AsTypeName == "Range"); + return !(Declaration.GetModuleParent(reference.ParentNonScoping) is DocumentModuleDeclaration document) + || !document.SupertypeNames.Contains("Worksheet"); } - private static readonly string[] GlobalObjectClassNames = - { - "Global", "_Global" - }; - - private static readonly string[] TargetMemberNames = - { - "Cells", "Range", "Columns", "Rows" - }; - protected override string ResultDescription(IdentifierReference reference) { return string.Format( diff --git a/Rubberduck.CodeAnalysis/Inspections/Concrete/ImplicitActiveWorkbookReferenceInspection.cs b/Rubberduck.CodeAnalysis/Inspections/Concrete/ImplicitActiveWorkbookReferenceInspection.cs index 0f976c810c..f90b6011ff 100644 --- a/Rubberduck.CodeAnalysis/Inspections/Concrete/ImplicitActiveWorkbookReferenceInspection.cs +++ b/Rubberduck.CodeAnalysis/Inspections/Concrete/ImplicitActiveWorkbookReferenceInspection.cs @@ -40,41 +40,22 @@ namespace Rubberduck.CodeAnalysis.Inspections.Concrete /// /// [RequiredLibrary("Excel")] - internal sealed class ImplicitActiveWorkbookReferenceInspection : IdentifierReferenceInspectionFromDeclarationsBase + internal sealed class ImplicitActiveWorkbookReferenceInspection : ImplicitWorkbookReferenceInspectionBase { public ImplicitActiveWorkbookReferenceInspection(IDeclarationFinderProvider declarationFinderProvider) : base(declarationFinderProvider) {} - private static readonly string[] InterestingMembers = + private static readonly List _alwaysActiveWorkbookReferenceParents = new List { - "Worksheets", "Sheets", "Names" + "_Application", "Application" }; - private static readonly string[] InterestingClasses = + protected override bool IsResultReference(IdentifierReference reference, DeclarationFinder finder) { - "_Global", "_Application", "Global", "Application" - }; - - protected override IEnumerable ObjectionableDeclarations(DeclarationFinder finder) - { - var excel = finder.Projects - .SingleOrDefault(project => project.IdentifierName == "Excel" && !project.IsUserDefined); - if (excel == null) - { - return Enumerable.Empty(); - } - - var relevantClasses = InterestingClasses - .Select(className => finder.FindClassModule(className, excel, true)) - .OfType(); - - var relevantProperties = relevantClasses - .SelectMany(classDeclaration => classDeclaration.Members) - .OfType() - .Where(member => InterestingMembers.Contains(member.IdentifierName)); - - return relevantProperties; + return !(Declaration.GetModuleParent(reference.ParentNonScoping) is DocumentModuleDeclaration document) + || !document.SupertypeNames.Contains("Workbook") + || _alwaysActiveWorkbookReferenceParents.Contains(reference.Declaration.ParentDeclaration.IdentifierName); } protected override string ResultDescription(IdentifierReference reference) diff --git a/Rubberduck.CodeAnalysis/Inspections/Concrete/ImplicitContainingWorkbookReferenceInspection.cs b/Rubberduck.CodeAnalysis/Inspections/Concrete/ImplicitContainingWorkbookReferenceInspection.cs new file mode 100644 index 0000000000..cc2ab5efd6 --- /dev/null +++ b/Rubberduck.CodeAnalysis/Inspections/Concrete/ImplicitContainingWorkbookReferenceInspection.cs @@ -0,0 +1,72 @@ +using System.Collections.Generic; +using System.Linq; +using Rubberduck.CodeAnalysis.Inspections.Abstract; +using Rubberduck.CodeAnalysis.Inspections.Attributes; +using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.VBA; +using Rubberduck.Parsing.VBA.DeclarationCaching; +using Rubberduck.Resources.Inspections; + +namespace Rubberduck.CodeAnalysis.Inspections.Concrete +{ + /// + /// Locates unqualified Workbook.Worksheets/Sheets/Names member calls inside workbook document modules that implicitly refer to the containing workbook. + /// + /// + /// + /// Implicit references inside a workbook document module can be mistakes for implicit references to the active workbook, which is the behavior in all other modules + /// By explicitly qualifying these member calls with Me, the ambiguity can be resolved. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + [RequiredLibrary("Excel")] + internal sealed class ImplicitContainingWorkbookReferenceInspection : ImplicitWorkbookReferenceInspectionBase + { + public ImplicitContainingWorkbookReferenceInspection(IDeclarationFinderProvider declarationFinderProvider) + : base(declarationFinderProvider) + { } + + private static readonly List _alwaysActiveWorkbookReferenceParents = new List + { + "_Application", "Application" + }; + + protected override IEnumerable ObjectionableDeclarations(DeclarationFinder finder) + { + return base.ObjectionableDeclarations(finder) + .Where(declaration => !_alwaysActiveWorkbookReferenceParents.Contains(declaration.ParentDeclaration.IdentifierName)); + } + + protected override bool IsResultReference(IdentifierReference reference, DeclarationFinder finder) + { + return Declaration.GetModuleParent(reference.ParentNonScoping) is DocumentModuleDeclaration document + && document.SupertypeNames.Contains("Workbook"); + } + + protected override string ResultDescription(IdentifierReference reference) + { + var referenceText = reference.Context.GetText(); + return string.Format( + InspectionResults.ImplicitContainingWorkbookReferenceInspection, + referenceText); + } + } +} \ No newline at end of file diff --git a/Rubberduck.CodeAnalysis/Inspections/Concrete/ImplicitContainingWorksheetReferenceInspection.cs b/Rubberduck.CodeAnalysis/Inspections/Concrete/ImplicitContainingWorksheetReferenceInspection.cs new file mode 100644 index 0000000000..2e2038dfde --- /dev/null +++ b/Rubberduck.CodeAnalysis/Inspections/Concrete/ImplicitContainingWorksheetReferenceInspection.cs @@ -0,0 +1,59 @@ +using System.Linq; +using Rubberduck.CodeAnalysis.Inspections.Abstract; +using Rubberduck.CodeAnalysis.Inspections.Attributes; +using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.VBA; +using Rubberduck.Parsing.VBA.DeclarationCaching; +using Rubberduck.Resources.Inspections; + +namespace Rubberduck.CodeAnalysis.Inspections.Concrete +{ + /// + /// Locates unqualified Worksheet.Range/Cells/Columns/Rows member calls inside worksheet modules that implicitly refer to the containing sheet. + /// + /// + /// + /// Implicit references inside a worksheet document module can be mistakes for implicit references to the active worksheet, which is the behavior in all other places. + /// By explicitly qualifying these member calls with Me, the ambiguity can be resolved. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + [RequiredLibrary("Excel")] + internal sealed class ImplicitContainingWorksheetReferenceInspection : ImplicitSheetReferenceInspectionBase + { + public ImplicitContainingWorksheetReferenceInspection(IDeclarationFinderProvider declarationFinderProvider) + : base(declarationFinderProvider) + {} + + protected override bool IsResultReference(IdentifierReference reference, DeclarationFinder finder) + { + return Declaration.GetModuleParent(reference.ParentNonScoping) is DocumentModuleDeclaration document + && document.SupertypeNames.Contains("Worksheet"); + } + + protected override string ResultDescription(IdentifierReference reference) + { + return string.Format( + InspectionResults.ImplicitContainingWorksheetReferenceInspection, + reference.Declaration.IdentifierName); + } + } +} \ No newline at end of file diff --git a/Rubberduck.CodeAnalysis/Properties/CodeInspectionDefaults.Designer.cs b/Rubberduck.CodeAnalysis/Properties/CodeInspectionDefaults.Designer.cs index 999c5f7299..c7d0c0aed7 100644 --- a/Rubberduck.CodeAnalysis/Properties/CodeInspectionDefaults.Designer.cs +++ b/Rubberduck.CodeAnalysis/Properties/CodeInspectionDefaults.Designer.cs @@ -92,63 +92,67 @@ public sealed partial class CodeInspectionDefaults : global::System.Configuratio "odeInspection Name=\"HungarianNotationInspection\" Severity=\"Suggestion\" Inspectio" + "nType=\"MaintainabilityAndReadabilityIssues\" />\r\n \r\n \r\n \r\n \r\n " + - " \r\n \r\n " + - "\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n <" + - "CodeInspection Name=\"ModuleWithoutFolderInspection\" Severity=\"Suggestion\" Inspec" + - "tionType=\"RubberduckOpportunities\" />\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n true\r\n")] + "odeInspection Name=\"ImplicitPublicMemberInspection\" Severity=\"Hint\" InspectionTy" + + "pe=\"LanguageOpportunities\" />\r\n \r\n \r\n " + + "\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n <" + + "CodeInspection Name=\"IsMissingWithNonArgumentParameterInspection\" Severity=\"Warn" + + "ing\" InspectionType=\"CodeQualityIssues\" />\r\n \r\n" + + " \r\n \r\n \r\n \r\n true\r\n")] public global::Rubberduck.CodeAnalysis.Settings.CodeInspectionSettings CodeInspectionSettings { get { return ((global::Rubberduck.CodeAnalysis.Settings.CodeInspectionSettings)(this["CodeInspectionSettings"])); diff --git a/Rubberduck.CodeAnalysis/Properties/CodeInspectionDefaults.settings b/Rubberduck.CodeAnalysis/Properties/CodeInspectionDefaults.settings index 864f41f743..d0b41da20e 100644 --- a/Rubberduck.CodeAnalysis/Properties/CodeInspectionDefaults.settings +++ b/Rubberduck.CodeAnalysis/Properties/CodeInspectionDefaults.settings @@ -48,7 +48,9 @@ <CodeInspection Name="HostSpecificExpressionInspection" Severity="Warning" InspectionType="LanguageOpportunities" /> <CodeInspection Name="HungarianNotationInspection" Severity="Suggestion" InspectionType="MaintainabilityAndReadabilityIssues" /> <CodeInspection Name="ImplicitActiveSheetReferenceInspection" Severity="Warning" InspectionType="LanguageOpportunities" /> + <CodeInspection Name="ImplicitContainingSheetReferenceInspection" Severity="Suggestion" InspectionType="LanguageOpportunities" /> <CodeInspection Name="ImplicitActiveWorkbookReferenceInspection" Severity="Warning" InspectionType="LanguageOpportunities" /> + <CodeInspection Name="ImplicitContainingWorkbookReferenceInspection" Severity="Suggestion" InspectionType="LanguageOpportunities" /> <CodeInspection Name="ImplicitDefaultMemberAssignmentInspection" Severity="Suggestion" InspectionType="LanguageOpportunities" /> <CodeInspection Name="ImplicitPublicMemberInspection" Severity="Hint" InspectionType="LanguageOpportunities" /> <CodeInspection Name="ImplicitVariantReturnTypeInspection" Severity="Hint" InspectionType="LanguageOpportunities" /> diff --git a/Rubberduck.Resources/Inspections/InspectionInfo.Designer.cs b/Rubberduck.Resources/Inspections/InspectionInfo.Designer.cs index 5456788d54..7f9fc2bc26 100644 --- a/Rubberduck.Resources/Inspections/InspectionInfo.Designer.cs +++ b/Rubberduck.Resources/Inspections/InspectionInfo.Designer.cs @@ -19,7 +19,7 @@ namespace Rubberduck.Resources.Inspections { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public class InspectionInfo { @@ -357,6 +357,24 @@ public class InspectionInfo { } } + /// + /// Looks up a localized string similar to Implicit references to workbook members inside a workbook document module can be mistakes for implicit references to the active workbook, which is the behavior in all other modules. By explicitly qualifying these member calls with Me, the ambiguity can be resolved.. + /// + public static string ImplicitContainingWorkbookReferenceInspection { + get { + return ResourceManager.GetString("ImplicitContainingWorkbookReferenceInspection", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Implicit references to worksheet members inside a worksheet document module can be mistakes for implicit references to the active worksheet, which is the behavior in all other modules. By explicitly qualifying these member calls with Me, the ambiguity can be resolved.. + /// + public static string ImplicitContainingWorksheetReferenceInspection { + get { + return ResourceManager.GetString("ImplicitContainingWorksheetReferenceInspection", resourceCulture); + } + } + /// /// Looks up a localized string similar to Default member accesses hide away the actually called member. This is especially misleading if there is no indication in the expression that such a call is made. It can cause errors in which a member was forgotten to be called to go unnoticed.. /// diff --git a/Rubberduck.Resources/Inspections/InspectionInfo.de.resx b/Rubberduck.Resources/Inspections/InspectionInfo.de.resx index cee0d8c7be..2473ca2af4 100644 --- a/Rubberduck.Resources/Inspections/InspectionInfo.de.resx +++ b/Rubberduck.Resources/Inspections/InspectionInfo.de.resx @@ -442,4 +442,10 @@ Falls der Parameter 'null' sein kann, bitte dieses Auftreten ignorieren. 'null' Eine Annotation hat mehr Argumente als erlaubt. Die überzähligen Argumente werden ignoriert. + + Implizite Referenzen zu Elementen eines Workbooks in einem Workbook-Modul können mit Referenzen zum aktiven Workbook verwechselt werden, da dies das Verhalten für solche Referenzen ist überall außerhalb von Workbook-Modulen. Die Uneindeutigkeit kann aufgelöst werden, indem die Referenzen mit 'Me' qualifiziert werden. + + + Implizite Referenzen zu Elementen eines Worksheets in einem Worksheet-Modul können mit Referenzen zum aktiven Worksheet verwechselt werden, da dies das Verhalten für solche Referenzen ist überall außerhalb von Worksheet-Modulen. Die Uneindeutigkeit kann aufgelöst werden, indem die Referenzen mit 'Me' qualifiziert werden. + \ No newline at end of file diff --git a/Rubberduck.Resources/Inspections/InspectionInfo.resx b/Rubberduck.Resources/Inspections/InspectionInfo.resx index 48142a3ca8..f2c24146ba 100644 --- a/Rubberduck.Resources/Inspections/InspectionInfo.resx +++ b/Rubberduck.Resources/Inspections/InspectionInfo.resx @@ -442,4 +442,10 @@ If the parameter can be null, ignore this inspection result; passing a null valu An annotation has more arguments than allowed; superfluous arguments are ignored. + + Implicit references to workbook members inside a workbook document module can be mistakes for implicit references to the active workbook, which is the behavior in all other modules. By explicitly qualifying these member calls with Me, the ambiguity can be resolved. + + + Implicit references to worksheet members inside a worksheet document module can be mistakes for implicit references to the active worksheet, which is the behavior in all other modules. By explicitly qualifying these member calls with Me, the ambiguity can be resolved. + \ No newline at end of file diff --git a/Rubberduck.Resources/Inspections/InspectionNames.Designer.cs b/Rubberduck.Resources/Inspections/InspectionNames.Designer.cs index e5f58a3536..5b076eef8e 100644 --- a/Rubberduck.Resources/Inspections/InspectionNames.Designer.cs +++ b/Rubberduck.Resources/Inspections/InspectionNames.Designer.cs @@ -19,7 +19,7 @@ namespace Rubberduck.Resources.Inspections { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public class InspectionNames { @@ -357,6 +357,24 @@ public class InspectionNames { } } + /// + /// Looks up a localized string similar to Implicit reference to the containing Workbook module. + /// + public static string ImplicitContainingWorkbookReferenceInspection { + get { + return ResourceManager.GetString("ImplicitContainingWorkbookReferenceInspection", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Implicit reference to containing Worksheet module. + /// + public static string ImplicitContainingWorksheetReferenceInspection { + get { + return ResourceManager.GetString("ImplicitContainingWorksheetReferenceInspection", resourceCulture); + } + } + /// /// Looks up a localized string similar to Implicit default member access. /// diff --git a/Rubberduck.Resources/Inspections/InspectionNames.de.resx b/Rubberduck.Resources/Inspections/InspectionNames.de.resx index 8301974bb3..f129a82bbf 100644 --- a/Rubberduck.Resources/Inspections/InspectionNames.de.resx +++ b/Rubberduck.Resources/Inspections/InspectionNames.de.resx @@ -438,4 +438,10 @@ Name, der ein geschütztes Leerzeichen enthält + + Implizite Referenz zum umgebenden Worksheet-Modul + + + Implizite Referenz zum umgebenden Workbook-Modul + \ No newline at end of file diff --git a/Rubberduck.Resources/Inspections/InspectionNames.resx b/Rubberduck.Resources/Inspections/InspectionNames.resx index 2b7f7de244..454a369b70 100644 --- a/Rubberduck.Resources/Inspections/InspectionNames.resx +++ b/Rubberduck.Resources/Inspections/InspectionNames.resx @@ -446,4 +446,10 @@ Superfluous annotation arguments + + Implicit reference to containing Worksheet module + + + Implicit reference to the containing Workbook module + \ No newline at end of file diff --git a/Rubberduck.Resources/Inspections/InspectionResults.Designer.cs b/Rubberduck.Resources/Inspections/InspectionResults.Designer.cs index 00e2338b40..10a437fb01 100644 --- a/Rubberduck.Resources/Inspections/InspectionResults.Designer.cs +++ b/Rubberduck.Resources/Inspections/InspectionResults.Designer.cs @@ -19,7 +19,7 @@ namespace Rubberduck.Resources.Inspections { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public class InspectionResults { @@ -366,6 +366,24 @@ public class InspectionResults { } } + /// + /// Looks up a localized string similar to Member '{0}' implicitly references the containing workbook document module.. + /// + public static string ImplicitContainingWorkbookReferenceInspection { + get { + return ResourceManager.GetString("ImplicitContainingWorkbookReferenceInspection", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Member '{0}' implicitly references the containing worksheet document module.. + /// + public static string ImplicitContainingWorksheetReferenceInspection { + get { + return ResourceManager.GetString("ImplicitContainingWorksheetReferenceInspection", resourceCulture); + } + } + /// /// Looks up a localized string similar to On the expression '{0}', there is an implicit default member access to '{1}'.. /// diff --git a/Rubberduck.Resources/Inspections/InspectionResults.de.resx b/Rubberduck.Resources/Inspections/InspectionResults.de.resx index 9f65f6db82..14d64a6d77 100644 --- a/Rubberduck.Resources/Inspections/InspectionResults.de.resx +++ b/Rubberduck.Resources/Inspections/InspectionResults.de.resx @@ -467,4 +467,10 @@ In Memoriam, 1972-2018 Die Annotation '{0}' erwartet weniger Argumente. + + Element '{0}' referenziert implizit auf das umgebende Workbook-Modul. + + + Element '{0}' referenziert implizit auf das umgebende Worksheet-Modul. + \ No newline at end of file diff --git a/Rubberduck.Resources/Inspections/InspectionResults.resx b/Rubberduck.Resources/Inspections/InspectionResults.resx index aa86d062a7..71e550e9b8 100644 --- a/Rubberduck.Resources/Inspections/InspectionResults.resx +++ b/Rubberduck.Resources/Inspections/InspectionResults.resx @@ -513,4 +513,11 @@ In memoriam, 1972-2018 The annotation '{0}' was expected to have less arguments. {0} annotation name + + Member '{0}' implicitly references the containing workbook document module. + {0} member name + + + Member '{0}' implicitly references the containing worksheet document module. + \ No newline at end of file diff --git a/RubberduckTests/Inspections/ImplicitActiveSheetReferenceInspectionTests.cs b/RubberduckTests/Inspections/ImplicitActiveSheetReferenceInspectionTests.cs index 83553f15ad..b450db962f 100644 --- a/RubberduckTests/Inspections/ImplicitActiveSheetReferenceInspectionTests.cs +++ b/RubberduckTests/Inspections/ImplicitActiveSheetReferenceInspectionTests.cs @@ -1,7 +1,9 @@ using System.Linq; +using System.Threading; using NUnit.Framework; using Rubberduck.CodeAnalysis.Inspections; using Rubberduck.CodeAnalysis.Inspections.Concrete; +using Rubberduck.Parsing.Symbols; using Rubberduck.Parsing.VBA; using Rubberduck.VBEditor.SafeComWrappers; using RubberduckTests.Mocks; @@ -67,6 +69,60 @@ End Sub Assert.AreEqual(1, InspectionResultsForModules(modules, ReferenceLibrary.Excel).Count()); } + [Test] + [Category("Inspections")] + public void ImplicitActiveSheetReference_DoesNotReportInWorkSheetModules() + { + const string inputCode = + @"Sub foo() + Dim arr1() As Variant + arr1 = Cells(1,2) +End Sub +"; + var module = ("Sheet1", inputCode, ComponentType.Document); + var vbe = MockVbeBuilder.BuildFromModules(module, ReferenceLibrary.Excel).Object; + + using (var state = MockParser.CreateAndParse(vbe)) + { + var documentModule = state.DeclarationFinder.UserDeclarations(DeclarationType.Document) + .OfType() + .Single(); + documentModule.AddSupertypeName("Worksheet"); + + var inspection = InspectionUnderTest(state); + var inspectionResults = inspection.GetInspectionResults(CancellationToken.None); + + Assert.AreEqual(0, inspectionResults.Count()); + } + } + + [Test] + [Category("Inspections")] + public void ImplicitActiveSheetReference_ReportsInWorkbookModules() + { + const string inputCode = + @"Sub foo() + Dim arr1() As Variant + arr1 = Cells(1,2) +End Sub +"; + var module = ("Sheet1", inputCode, ComponentType.Document); + var vbe = MockVbeBuilder.BuildFromModules(module, ReferenceLibrary.Excel).Object; + + using (var state = MockParser.CreateAndParse(vbe)) + { + var documentModule = state.DeclarationFinder.UserDeclarations(DeclarationType.Document) + .OfType() + .Single(); + documentModule.AddSupertypeName("Workbook"); + + var inspection = InspectionUnderTest(state); + var inspectionResults = inspection.GetInspectionResults(CancellationToken.None); + + Assert.AreEqual(1, inspectionResults.Count()); + } + } + [Test] [Category("Inspections")] public void ImplicitActiveSheetReference_Ignored_DoesNotReportRange() diff --git a/RubberduckTests/Inspections/ImplicitActiveWorkbookReferenceInspectionTests.cs b/RubberduckTests/Inspections/ImplicitActiveWorkbookReferenceInspectionTests.cs index 6e2a196150..a92889d39e 100644 --- a/RubberduckTests/Inspections/ImplicitActiveWorkbookReferenceInspectionTests.cs +++ b/RubberduckTests/Inspections/ImplicitActiveWorkbookReferenceInspectionTests.cs @@ -1,7 +1,9 @@ using System.Linq; +using System.Threading; using NUnit.Framework; using Rubberduck.CodeAnalysis.Inspections; using Rubberduck.CodeAnalysis.Inspections.Concrete; +using Rubberduck.Parsing.Symbols; using Rubberduck.Parsing.VBA; using Rubberduck.VBEditor.SafeComWrappers; using RubberduckTests.Mocks; @@ -160,6 +162,87 @@ Sub foo() Assert.AreEqual(expected, actual); } + [Test] + [Category("Inspections")] + public void ImplicitActiveWorkbookReference_DoesNotReportUnqualifiedInWorkbookModules() + { + const string inputCode = + @" +Sub foo() + Dim sheet As Worksheet + Set sheet = Worksheets(""Sheet1"") +End Sub"; + var module = ("SomeWorkbook", inputCode, ComponentType.Document); + var vbe = MockVbeBuilder.BuildFromModules(module, ReferenceLibrary.Excel).Object; + + using (var state = MockParser.CreateAndParse(vbe)) + { + var documentModule = state.DeclarationFinder.UserDeclarations(DeclarationType.Document) + .OfType() + .Single(); + documentModule.AddSupertypeName("Workbook"); + + var inspection = InspectionUnderTest(state); + var inspectionResults = inspection.GetInspectionResults(CancellationToken.None); + + Assert.AreEqual(0, inspectionResults.Count()); + } + } + + [Test] + [Category("Inspections")] + public void ImplicitActiveWorkbookReference_ReportsApplicationQualifiedInWorkbookModules() + { + const string inputCode = + @" +Sub foo() + Dim sheet As Worksheet + Set sheet = Application.Worksheets(""Sheet1"") +End Sub"; + var module = ("SomeWorkbook", inputCode, ComponentType.Document); + var vbe = MockVbeBuilder.BuildFromModules(module, ReferenceLibrary.Excel).Object; + + using (var state = MockParser.CreateAndParse(vbe)) + { + var documentModule = state.DeclarationFinder.UserDeclarations(DeclarationType.Document) + .OfType() + .Single(); + documentModule.AddSupertypeName("Workbook"); + + var inspection = InspectionUnderTest(state); + var inspectionResults = inspection.GetInspectionResults(CancellationToken.None); + + Assert.AreEqual(1, inspectionResults.Count()); + } + } + + [Test] + [Category("Inspections")] + public void ImplicitActiveWorkbookReference_ReportsInWorksheetModules() + { + const string inputCode = + @" +Sub foo() + Dim sheet As Worksheet + Set sheet = Worksheets(""Sheet1"") +End Sub"; + var module = ("Sheet1", inputCode, ComponentType.Document); + var vbe = MockVbeBuilder.BuildFromModules(module, ReferenceLibrary.Excel).Object; + + using (var state = MockParser.CreateAndParse(vbe)) + { + var documentModule = state.DeclarationFinder.UserDeclarations(DeclarationType.Document) + .OfType() + .Single(); + documentModule.AddSupertypeName("Worksheet"); + + var inspection = InspectionUnderTest(state); + var inspectionResults = inspection.GetInspectionResults(CancellationToken.None); + + Assert.AreEqual(1, inspectionResults.Count()); + } + } + [Test] [Category("Inspections")] public void ImplicitActiveWorkbookReference_Ignored_DoesNotReportRange() diff --git a/RubberduckTests/Inspections/ImplicitContainingSheetreferenceInspectionTests.cs b/RubberduckTests/Inspections/ImplicitContainingSheetreferenceInspectionTests.cs new file mode 100644 index 0000000000..79aea689d1 --- /dev/null +++ b/RubberduckTests/Inspections/ImplicitContainingSheetreferenceInspectionTests.cs @@ -0,0 +1,142 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using NUnit.Framework; +using Rubberduck.CodeAnalysis.Inspections; +using Rubberduck.CodeAnalysis.Inspections.Concrete; +using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.VBA; +using Rubberduck.VBEditor.SafeComWrappers; +using RubberduckTests.Mocks; + +namespace RubberduckTests.Inspections +{ + [TestFixture] + public class ImplicitContainingSheetReferenceInspectionTests : InspectionTestsBase + { + [Test] + [Category("Inspections")] + public void ImplicitContainingSheetReference_ReportsRangeInWorksheets() + { + const string inputCode = +@"Sub foo() + Dim arr1() As Variant + arr1 = Range(""A1:B2"") +End Sub +"; + Assert.AreEqual(1, InspectionResultsInWorksheet(inputCode).Count()); + } + + [Test] + [Category("Inspections")] + public void ImplicitContainingSheetReference_ReportsCellsInWorksheets() + { + const string inputCode = + @"Sub foo() + Dim arr1() As Variant + arr1 = Cells(1,2) +End Sub +"; + Assert.AreEqual(1, InspectionResultsInWorksheet(inputCode).Count()); + } + + [Test] + [Category("Inspections")] + public void ImplicitContainingSheetReference_ReportsColumnsInWorksheets() + { + const string inputCode = + @"Sub foo() + Dim arr1() As Variant + arr1 = Columns(3) +End Sub +"; + Assert.AreEqual(1, InspectionResultsInWorksheet(inputCode).Count()); + } + + [Test] + [Category("Inspections")] + public void ImplicitContainingSheetReference_ReportsRowsInWorksheets() + { + const string inputCode = + @"Sub foo() + Dim arr1() As Variant + arr1 = Rows(3) +End Sub +"; + Assert.AreEqual(1, InspectionResultsInWorksheet(inputCode).Count()); + } + + [Test] + [Category("Inspections")] + public void ImplicitContainingSheetReference_DoesNotReportsMembersQualifiedWithMe() + { + const string inputCode = + @"Sub foo() + Dim arr1() As Variant + arr1 = Me.Rows(3) +End Sub +"; + Assert.AreEqual(0, InspectionResultsInWorksheet(inputCode).Count()); + } + + [Test] + [Category("Inspections")] + public void ImplicitContainingSheetReference_DoesNotReportOutsideWorkSheetModules() + { + const string inputCode = + @"Sub foo() + Dim arr1() As Variant + arr1 = Cells(1,2) +End Sub +"; + var modules = new (string, string, ComponentType)[] { ("Class1", inputCode, ComponentType.ClassModule) }; + Assert.AreEqual(0, InspectionResultsForModules(modules, ReferenceLibrary.Excel).Count()); + } + + [Test] + [Category("Inspections")] + public void ImplicitContainingSheetReference_Ignored_DoesNotReportRange() + { + const string inputCode = +@"Sub foo() + Dim arr1() As Variant + + '@Ignore ImplicitContainingWorksheetReference + arr1 = Range(""A1:B2"") +End Sub +"; + Assert.AreEqual(0, InspectionResultsInWorksheet(inputCode).Count()); + } + + [Test] + [Category("Inspections")] + public void InspectionName() + { + var inspection = new ImplicitContainingWorksheetReferenceInspection(null); + + Assert.AreEqual(nameof(ImplicitContainingWorksheetReferenceInspection), inspection.Name); + } + + private IEnumerable InspectionResultsInWorksheet(string inputCode) + { + var module = ("Sheet1", inputCode, ComponentType.Document); + var vbe = MockVbeBuilder.BuildFromModules(module, ReferenceLibrary.Excel).Object; + + using (var state = MockParser.CreateAndParse(vbe)) + { + var documentModule = state.DeclarationFinder.UserDeclarations(DeclarationType.Document) + .OfType() + .Single(); + documentModule.AddSupertypeName("Worksheet"); + + var inspection = InspectionUnderTest(state); + return inspection.GetInspectionResults(CancellationToken.None); + } + } + + protected override IInspection InspectionUnderTest(RubberduckParserState state) + { + return new ImplicitContainingWorksheetReferenceInspection(state); + } + } +} \ No newline at end of file diff --git a/RubberduckTests/Inspections/ImplicitContainingWorkbookReferenceInspectionTests.cs b/RubberduckTests/Inspections/ImplicitContainingWorkbookReferenceInspectionTests.cs new file mode 100644 index 0000000000..29ecc902c3 --- /dev/null +++ b/RubberduckTests/Inspections/ImplicitContainingWorkbookReferenceInspectionTests.cs @@ -0,0 +1,200 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using NUnit.Framework; +using Rubberduck.CodeAnalysis.Inspections; +using Rubberduck.CodeAnalysis.Inspections.Concrete; +using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.VBA; +using Rubberduck.VBEditor.SafeComWrappers; +using RubberduckTests.Mocks; + +namespace RubberduckTests.Inspections +{ + [TestFixture] + public class ImplicitContainingWorkbookReferenceInspectionTests : InspectionTestsBase + { + [Test] + [Category("Inspections")] + public void ImplicitContainingWorkbookReference_ReportsWorksheets() + { + const string inputCode = + @" +Sub foo() + Dim sheet As Worksheet + Set sheet = Worksheets(""Sheet1"") +End Sub"; + Assert.AreEqual(1, InspectionResultsInWorkbook(inputCode).Count()); + } + + [Test] + [Category("Inspections")] + public void ImplicitContainingWorkbookReference_ExplicitApplication() + { + const string inputCode = + @" +Sub foo() + Dim sheet As Worksheet + Set sheet = Application.Worksheets(""Sheet1"") +End Sub"; + Assert.AreEqual(0, InspectionResultsInWorkbook(inputCode).Count()); + ; + } + + [Test] + [Category("Inspections")] + public void ImplicitContainingWorkbookReference_ReportsSheets() + { + const string inputCode = + @" +Sub foo() + Dim sheet As Worksheet + Set sheet = Sheets(""Sheet1"") +End Sub"; + Assert.AreEqual(1, InspectionResultsInWorkbook(inputCode).Count()); + } + + [Test] + [Category("Inspections")] + public void ImplicitContainingWorkbookReference_ReportsNames() + { + const string inputCode = + @" +Sub foo() + Names.Add ""foo"", Rows(1) +End Sub"; + Assert.AreEqual(1, InspectionResultsInWorkbook(inputCode).Count()); + } + + [Test] + [Category("Inspections")] + public void ImplicitContainingWorkbookReference_ExplicitReference_NotReported() + { + const string inputCode = + @" +Sub foo() + Dim book As Workbook + Dim sheet As Worksheet + Set sheet = book.Worksheets(1) +End Sub"; + Assert.AreEqual(0, InspectionResultsInWorkbook(inputCode).Count()); + } + + [Test] + [Category("Inspections")] + public void ImplicitContainingWorkbookReference_MeReference_NotReported() + { + const string inputCode = + @" +Sub foo() + Dim sheet As Worksheet + Set sheet = Me.Worksheets(1) +End Sub"; + Assert.AreEqual(0, InspectionResultsInWorkbook(inputCode).Count()); + } + + [Test] + [Category("Inspections")] + public void ImplicitContainingWorkbookReference_DimAsTypeWorksheets_NotReported() + { + const string inputCode = + @" +Sub foo() + Dim allSheets As Worksheets +End Sub"; + Assert.AreEqual(0, InspectionResultsInWorkbook(inputCode).Count()); + } + + [Test] + [Category("Inspections")] + public void ImplicitContainingWorkbookReference_DimAsTypeSheets_NotReported() + { + const string inputCode = + @" +Sub foo() + Dim allSheets As Sheets +End Sub"; + Assert.AreEqual(0, InspectionResultsInWorkbook(inputCode).Count()); + } + + [Test] + [Category("Inspections")] + public void ImplicitContainingWorkbookReference_DimAsTypeNames_NotReported() + { + const string inputCode = + @" +Sub foo() + Dim allNames As Names +End Sub"; + Assert.AreEqual(0, InspectionResultsInWorkbook(inputCode).Count()); + } + + [Test] + [Category("Inspections")] + public void ImplicitContainingWorkbookReference_DoesNotReportUnqualifiedOutsideWorkbookModules() + { + const string inputCode = + @" +Sub foo() + Dim sheet As Worksheet + Set sheet = Worksheets(""Sheet1"") +End Sub"; + const int expected = 0; + var actual = ArrangeOutsideWorkbookAndGetInspectionCount(inputCode); + + Assert.AreEqual(expected, actual); + } + + [Test] + [Category("Inspections")] + public void ImplicitContainingWorkbookReference_Ignored_DoesNotReportRange() + { + const string inputCode = + @" +Sub foo() + Dim sheet As Worksheet + + '@Ignore ImplicitContainingWorkbookReference + Set sheet = Worksheets(""Sheet1"") +End Sub"; + Assert.AreEqual(0, InspectionResultsInWorkbook(inputCode).Count()); + } + + private int ArrangeOutsideWorkbookAndGetInspectionCount(string code) + { + var modules = new (string, string, ComponentType)[] {("Module1", code, ComponentType.StandardModule)}; + return InspectionResultsForModules(modules, ReferenceLibrary.Excel).Count(); + } + + [Test] + [Category("Inspections")] + public void InspectionName() + { + var inspection = new ImplicitContainingWorkbookReferenceInspection(null); + + Assert.AreEqual(nameof(ImplicitContainingWorkbookReferenceInspection), inspection.Name); + } + + private IEnumerable InspectionResultsInWorkbook(string inputCode) + { + var module = ("SomeWorkbook", inputCode, ComponentType.Document); + var vbe = MockVbeBuilder.BuildFromModules(module, ReferenceLibrary.Excel).Object; + + using (var state = MockParser.CreateAndParse(vbe)) + { + var documentModule = state.DeclarationFinder.UserDeclarations(DeclarationType.Document) + .OfType() + .Single(); + documentModule.AddSupertypeName("Workbook"); + + var inspection = InspectionUnderTest(state); + return inspection.GetInspectionResults(CancellationToken.None); + } + } + + protected override IInspection InspectionUnderTest(RubberduckParserState state) + { + return new ImplicitContainingWorkbookReferenceInspection(state); + } + } +} \ No newline at end of file From a5b115e7610cb3753692a4f715bba15e5e76a70f Mon Sep 17 00:00:00 2001 From: Max Doerner Date: Fri, 2 Oct 2020 02:36:32 +0200 Subject: [PATCH 50/67] Adjust com interface discovery for documents with separate interface Hosts like Access insert an additional interface for document modules between the document and the built-in one. Since we do not have a declaration for the separate interface, we also add the interface two levels op to the supertypes of the document. --- .../ReferenceResolveRunnerBase.cs | 39 +++++++++++++------ 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/Rubberduck.Parsing/VBA/ReferenceManagement/ReferenceResolveRunnerBase.cs b/Rubberduck.Parsing/VBA/ReferenceManagement/ReferenceResolveRunnerBase.cs index 8c3bd6eda1..315f2a149a 100644 --- a/Rubberduck.Parsing/VBA/ReferenceManagement/ReferenceResolveRunnerBase.cs +++ b/Rubberduck.Parsing/VBA/ReferenceManagement/ReferenceResolveRunnerBase.cs @@ -200,23 +200,40 @@ private void AddSuperTypeNamesForDocumentModules(IReadOnlyCollection SuperTypeNamesForDocumentFromComType(IComType comModule) { var inheritedInterfaces = comModule is ComCoClass documentCoClass - ? documentCoClass.ImplementedInterfaces - : (comModule as ComInterface)?.InheritedInterfaces; - - //todo: Find a way to deal with the VBE's document type assignment behaviour not relying on an assumption about an interface naming convention. - var superTypeNames = (inheritedInterfaces? - .Where(i => !i.IsRestricted && !IgnoredComInterfaces.Contains(i.Name)) - .Select(i => i.Name) - ?? Enumerable.Empty()) + ? documentCoClass.ImplementedInterfaces.ToList() + : (comModule as ComInterface)?.InheritedInterfaces.ToList(); + + if (inheritedInterfaces == null) + { + return Enumerable.Empty(); + } + + var relevantInterfaces = inheritedInterfaces + .Where(i => !i.IsRestricted && !IgnoredComInterfaces.Contains(i.Name)) + .ToList(); + + //todo: Find a way to deal with the VBE's document type assignment and interface behaviour not relying on an assumption about an interface naming conventions. + + //Some hosts like Access chose to have a separate hidden interface for each document module and only let that inherit the built-in base interface. + //Since we do not have a declaration for the hidden interface, we have to go one more step up the hierarchy. + var additionalInterfaces = relevantInterfaces + .Where(i => i.Name.Equals("_" + comModule.Name)) + .SelectMany(i => i.InheritedInterfaces); + + relevantInterfaces.AddRange(additionalInterfaces); + + var superTypeNames = relevantInterfaces + .Select(i => i.Name) .ToList(); //This emulates the VBE's behaviour to allow assignment to the coclass type instead on the interface. - var additionalSuperTypes = superTypeNames + var additionalSuperTypeNames = superTypeNames .Where(name => name.StartsWith("_")) .Select(name => name.Substring(1)) + .Where(name => !name.Equals(comModule.Name)) .ToList(); - superTypeNames.AddRange(additionalSuperTypes); + superTypeNames.AddRange(additionalSuperTypeNames); return superTypeNames; } @@ -240,7 +257,7 @@ protected void ResolveReferences(DeclarationFinder finder, QualifiedModuleName m Logger.Debug("Binding resolution done for component '{0}' in {1}ms (thread {2})", module.Name, watch.ElapsedMilliseconds, Thread.CurrentThread.ManagedThreadId); - //Evaluation of the overall status has to be defered to allow processing of undeclared variables before setting the ready state. + //Evaluation of the overall status has to be deferred to allow processing of undeclared variables before setting the ready state. _parserStateManager.SetModuleState(module, ParserState.Ready, token, false); } catch (OperationCanceledException) From 10d173f83d4dcba92769d1929a8eaaa82363d768 Mon Sep 17 00:00:00 2001 From: Max Doerner Date: Fri, 2 Oct 2020 19:13:59 +0200 Subject: [PATCH 51/67] Add QualifyWithMeQuickFix for ImplicitContainingWorkbook/WorksheetReferenceInspection It does what the name suggests. --- .../Concrete/QualifyWithMeQuickFix.cs | 61 +++++++++ .../Inspections/QuickFixes.Designer.cs | 11 +- .../Inspections/QuickFixes.de.resx | 3 + .../Inspections/QuickFixes.resx | 3 + .../QuickFixes/QualifyWithMeQuickFixTests.cs | 124 ++++++++++++++++++ 5 files changed, 201 insertions(+), 1 deletion(-) create mode 100644 Rubberduck.CodeAnalysis/QuickFixes/Concrete/QualifyWithMeQuickFix.cs create mode 100644 RubberduckTests/QuickFixes/QualifyWithMeQuickFixTests.cs diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/QualifyWithMeQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/QualifyWithMeQuickFix.cs new file mode 100644 index 0000000000..32734ccddb --- /dev/null +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/QualifyWithMeQuickFix.cs @@ -0,0 +1,61 @@ +using Rubberduck.CodeAnalysis.Inspections; +using Rubberduck.CodeAnalysis.Inspections.Concrete; +using Rubberduck.CodeAnalysis.QuickFixes.Abstract; +using Rubberduck.Parsing.Grammar; +using Rubberduck.Parsing.Rewriter; + +namespace Rubberduck.CodeAnalysis.QuickFixes.Concrete +{ + /// + /// Qualifies an implicit reference with 'Me'. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + internal class QualifyWithMeQuickFix : QuickFixBase + { + public QualifyWithMeQuickFix() + : base(typeof(ImplicitContainingWorkbookReferenceInspection), + typeof(ImplicitContainingWorksheetReferenceInspection)) + {} + + public override void Fix(IInspectionResult result, IRewriteSession rewriteSession) + { + var rewriter = rewriteSession.CheckOutModuleRewriter(result.QualifiedSelection.QualifiedName); + + var context = result.Context; + rewriter.InsertBefore(context.Start.TokenIndex, $"{Tokens.Me}."); + } + + public override string Description(IInspectionResult result) + { + return Resources.Inspections.QuickFixes.QualifyWithMeQuickFix; + } + + public override bool CanFixMultiple => true; + public override bool CanFixInProcedure => true; + public override bool CanFixInModule => true; + public override bool CanFixInProject => true; + public override bool CanFixAll => true; + } +} \ No newline at end of file diff --git a/Rubberduck.Resources/Inspections/QuickFixes.Designer.cs b/Rubberduck.Resources/Inspections/QuickFixes.Designer.cs index 8c676de34f..6692c3adbd 100644 --- a/Rubberduck.Resources/Inspections/QuickFixes.Designer.cs +++ b/Rubberduck.Resources/Inspections/QuickFixes.Designer.cs @@ -19,7 +19,7 @@ namespace Rubberduck.Resources.Inspections { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public class QuickFixes { @@ -339,6 +339,15 @@ public class QuickFixes { } } + /// + /// Looks up a localized string similar to Qualify reference with 'Me'.. + /// + public static string QualifyWithMeQuickFix { + get { + return ResourceManager.GetString("QualifyWithMeQuickFix", resourceCulture); + } + } + /// /// Looks up a localized string similar to Remove 'ByRef' modifier. /// diff --git a/Rubberduck.Resources/Inspections/QuickFixes.de.resx b/Rubberduck.Resources/Inspections/QuickFixes.de.resx index 7a59cda349..cbf9dac78f 100644 --- a/Rubberduck.Resources/Inspections/QuickFixes.de.resx +++ b/Rubberduck.Resources/Inspections/QuickFixes.de.resx @@ -300,4 +300,7 @@ In Modul ignorieren + + Qualifiziere die Referenz mit 'Me'. + \ No newline at end of file diff --git a/Rubberduck.Resources/Inspections/QuickFixes.resx b/Rubberduck.Resources/Inspections/QuickFixes.resx index 081c9a2825..88642f1b08 100644 --- a/Rubberduck.Resources/Inspections/QuickFixes.resx +++ b/Rubberduck.Resources/Inspections/QuickFixes.resx @@ -300,4 +300,7 @@ Ignore in module + + Qualify reference with 'Me'. + \ No newline at end of file diff --git a/RubberduckTests/QuickFixes/QualifyWithMeQuickFixTests.cs b/RubberduckTests/QuickFixes/QualifyWithMeQuickFixTests.cs new file mode 100644 index 0000000000..1320b5b384 --- /dev/null +++ b/RubberduckTests/QuickFixes/QualifyWithMeQuickFixTests.cs @@ -0,0 +1,124 @@ +using System.Linq; +using System.Threading; +using NUnit.Framework; +using Rubberduck.CodeAnalysis.Inspections.Concrete; +using Rubberduck.CodeAnalysis.QuickFixes; +using Rubberduck.CodeAnalysis.QuickFixes.Concrete; +using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.VBA; +using Rubberduck.VBEditor.SafeComWrappers; +using RubberduckTests.Mocks; + +namespace RubberduckTests.QuickFixes +{ + [TestFixture] + public class QualifyWithMeQuickFixTests : QuickFixTestBase + { + [Test] + [Category("QuickFixes")] + public void QualifiesImplicitWorkbookReferencesInWorkbooks() + { + const string inputCode = + @" +Sub foo() + Dim sheet As Worksheet + Set sheet = Worksheets(""Sheet1"") +End Sub"; + + const string expectedCode = + @" +Sub foo() + Dim sheet As Worksheet + Set sheet = Me.Worksheets(""Sheet1"") +End Sub"; + + var actualCode = ApplyQuickFixToFirstInspectionResultForImplicitWorkbookInspection(inputCode); + Assert.AreEqual(expectedCode, actualCode); + } + + private string ApplyQuickFixToFirstInspectionResultForImplicitWorkbookInspection(string inputCode) + { + var inputModule = ("SomeWorkbook", inputCode, ComponentType.Document); + var vbe = MockVbeBuilder.BuildFromModules(inputModule, ReferenceLibrary.Excel).Object; + + var (state, rewriteManager) = MockParser.CreateAndParseWithRewritingManager(vbe); + using (state) + { + var documentModule = state.DeclarationFinder.UserDeclarations(DeclarationType.Document) + .OfType() + .Single(); + documentModule.AddSupertypeName("Workbook"); + + var inspection = new ImplicitContainingWorkbookReferenceInspection(state); + var inspectionResults = inspection.GetInspectionResults(CancellationToken.None); + + var rewriteSession = rewriteManager.CheckOutCodePaneSession(); + + var quickFix = QuickFix(state); + + var resultToFix = inspectionResults.First(); + quickFix.Fix(resultToFix, rewriteSession); + + var module = state.DeclarationFinder.AllModules.First(qmn => qmn.ComponentName == "SomeWorkbook"); + + return rewriteSession.CheckOutModuleRewriter(module).GetText(); + } + } + + [Test] + [Category("QuickFixes")] + public void QualifiesImplicitWorksheetReferencesInWorksheets() + { + const string inputCode = + @" +Private Sub Example() + Dim foo As Range + Set foo = Range(""A1"") +End Sub"; + + const string expectedCode = + @" +Private Sub Example() + Dim foo As Range + Set foo = Me.Range(""A1"") +End Sub"; + + var actualCode = ApplyQuickFixToFirstInspectionResultForImplicitWorksheetInspection(inputCode); + Assert.AreEqual(expectedCode, actualCode); + } + + private string ApplyQuickFixToFirstInspectionResultForImplicitWorksheetInspection(string inputCode) + { + var inputModule = ("Sheet1", inputCode, ComponentType.Document); + var vbe = MockVbeBuilder.BuildFromModules(inputModule, ReferenceLibrary.Excel).Object; + + var (state, rewriteManager) = MockParser.CreateAndParseWithRewritingManager(vbe); + using (state) + { + var documentModule = state.DeclarationFinder.UserDeclarations(DeclarationType.Document) + .OfType() + .Single(); + documentModule.AddSupertypeName("Worksheet"); + + var inspection = new ImplicitContainingWorksheetReferenceInspection(state); + var inspectionResults = inspection.GetInspectionResults(CancellationToken.None); + + var rewriteSession = rewriteManager.CheckOutCodePaneSession(); + + var quickFix = QuickFix(state); + + var resultToFix = inspectionResults.First(); + quickFix.Fix(resultToFix, rewriteSession); + + var module = state.DeclarationFinder.AllModules.First(qmn => qmn.ComponentName == "Sheet1"); + + return rewriteSession.CheckOutModuleRewriter(module).GetText(); + } + } + + protected override IQuickFix QuickFix(RubberduckParserState state) + { + return new QualifyWithMeQuickFix(); + } + } +} From 660aa9a9cc1a7c281a3866f447a3218ff9b756d6 Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Fri, 2 Oct 2020 15:02:28 -0700 Subject: [PATCH 52/67] Add MisleadingByRefParameterInspection --- .../MisleadingByRefParameterInspection.cs | 72 +++++++++++++++++++ .../Concrete/PassParameterByValueQuickFix.cs | 2 +- .../Inspections/InspectionInfo.Designer.cs | 11 ++- .../Inspections/InspectionInfo.resx | 3 + .../Inspections/InspectionNames.Designer.cs | 11 ++- .../Inspections/InspectionNames.resx | 7 +- .../Inspections/InspectionResults.Designer.cs | 11 ++- .../Inspections/InspectionResults.resx | 4 ++ ...MisleadingByRefParameterInspectionTests.cs | 72 +++++++++++++++++++ .../PassParameterByValueQuickFixTests.cs | 22 ++++++ 10 files changed, 207 insertions(+), 8 deletions(-) create mode 100644 Rubberduck.CodeAnalysis/Inspections/Concrete/MisleadingByRefParameterInspection.cs create mode 100644 RubberduckTests/Inspections/MisleadingByRefParameterInspectionTests.cs diff --git a/Rubberduck.CodeAnalysis/Inspections/Concrete/MisleadingByRefParameterInspection.cs b/Rubberduck.CodeAnalysis/Inspections/Concrete/MisleadingByRefParameterInspection.cs new file mode 100644 index 0000000000..57f65acd52 --- /dev/null +++ b/Rubberduck.CodeAnalysis/Inspections/Concrete/MisleadingByRefParameterInspection.cs @@ -0,0 +1,72 @@ +using Rubberduck.CodeAnalysis.Inspections.Abstract; +using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.VBA; +using Rubberduck.Parsing.VBA.DeclarationCaching; +using Rubberduck.Resources.Inspections; +using System.Linq; + +namespace Rubberduck.CodeAnalysis.Inspections.Concrete +{ + /// + /// Flags the value-parameter of a property mutators that are declared with an explict ByRef modifier. + /// + /// + /// Regardless of the presence or absence of an explicit ByRef or ByVal modifier, the value-parameter + /// of a property mutator is always treated as though it had an explicit ByVal modifier. + /// Exception: UserDefinedType parameters are always passed by reference. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + internal sealed class MisleadingByRefParameterInspection : DeclarationInspectionBase + { + public MisleadingByRefParameterInspection(IDeclarationFinderProvider declarationFinderProvider) + : base(declarationFinderProvider, DeclarationType.Parameter) + { } + + protected override bool IsResultDeclaration(Declaration declaration, DeclarationFinder finder) + { + if ((declaration is ParameterDeclaration parameter) + && parameter.ParentDeclaration is ModuleBodyElementDeclaration enclosingMethod + && (enclosingMethod.DeclarationType.HasFlag(DeclarationType.PropertyLet) + || enclosingMethod.DeclarationType.HasFlag(DeclarationType.PropertySet)) + && enclosingMethod.Parameters.Last() == parameter + && !(parameter.AsTypeDeclaration?.DeclarationType.HasFlag(DeclarationType.UserDefinedType) ?? false)) + { + return parameter.IsByRef && !parameter.IsImplicitByRef; + } + + return false; + } + + protected override string ResultDescription(Declaration declaration) + { + return string.Format( + InspectionResults.MisleadingByRefParameterInspection, + declaration.IdentifierName); + } + } +} diff --git a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/PassParameterByValueQuickFix.cs b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/PassParameterByValueQuickFix.cs index a036e0e318..90e2ae795f 100644 --- a/Rubberduck.CodeAnalysis/QuickFixes/Concrete/PassParameterByValueQuickFix.cs +++ b/Rubberduck.CodeAnalysis/QuickFixes/Concrete/PassParameterByValueQuickFix.cs @@ -38,7 +38,7 @@ internal sealed class PassParameterByValueQuickFix : QuickFixBase private readonly IDeclarationFinderProvider _declarationFinderProvider; public PassParameterByValueQuickFix(IDeclarationFinderProvider declarationFinderProvider) - : base(typeof(ParameterCanBeByValInspection)) + : base(typeof(ParameterCanBeByValInspection), typeof(MisleadingByRefParameterInspection)) { _declarationFinderProvider = declarationFinderProvider; } diff --git a/Rubberduck.Resources/Inspections/InspectionInfo.Designer.cs b/Rubberduck.Resources/Inspections/InspectionInfo.Designer.cs index 5456788d54..a6e9ba4553 100644 --- a/Rubberduck.Resources/Inspections/InspectionInfo.Designer.cs +++ b/Rubberduck.Resources/Inspections/InspectionInfo.Designer.cs @@ -19,7 +19,7 @@ namespace Rubberduck.Resources.Inspections { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public class InspectionInfo { @@ -501,6 +501,15 @@ public class InspectionInfo { } } + /// + /// Looks up a localized string similar to The last parameter (the 'Value' parameter) of property mutators are always passed by value. This is true regardless of the presence or absence of a ByVal modifier. Exception: UserDefinedType parameters must always be passed by reference in all cases.. + /// + public static string MisleadingByRefParameterInspection { + get { + return ResourceManager.GetString("MisleadingByRefParameterInspection", resourceCulture); + } + } + /// /// Looks up a localized string similar to An annotation parameter is missing or incorrectly specified. The correct syntax is : '@Annotation([parameter])\nExample: '@Folder("Parent.Child"). /// diff --git a/Rubberduck.Resources/Inspections/InspectionInfo.resx b/Rubberduck.Resources/Inspections/InspectionInfo.resx index 48142a3ca8..f9a1b9b469 100644 --- a/Rubberduck.Resources/Inspections/InspectionInfo.resx +++ b/Rubberduck.Resources/Inspections/InspectionInfo.resx @@ -442,4 +442,7 @@ If the parameter can be null, ignore this inspection result; passing a null valu An annotation has more arguments than allowed; superfluous arguments are ignored. + + The last parameter (the 'Value' parameter) of property mutators are always passed by value. This is true regardless of the presence or absence of a ByVal modifier. Exception: UserDefinedType parameters must always be passed by reference in all cases. + \ No newline at end of file diff --git a/Rubberduck.Resources/Inspections/InspectionNames.Designer.cs b/Rubberduck.Resources/Inspections/InspectionNames.Designer.cs index e5f58a3536..20a7ff299b 100644 --- a/Rubberduck.Resources/Inspections/InspectionNames.Designer.cs +++ b/Rubberduck.Resources/Inspections/InspectionNames.Designer.cs @@ -19,7 +19,7 @@ namespace Rubberduck.Resources.Inspections { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public class InspectionNames { @@ -501,6 +501,15 @@ public class InspectionNames { } } + /// + /// Looks up a localized string similar to ByRef is a misleading modifier because the parameter will be passed ByVal.. + /// + public static string MisleadingByRefParameterInspection { + get { + return ResourceManager.GetString("MisleadingByRefParameterInspection", resourceCulture); + } + } + /// /// Looks up a localized string similar to Missing annotation parameter. /// diff --git a/Rubberduck.Resources/Inspections/InspectionNames.resx b/Rubberduck.Resources/Inspections/InspectionNames.resx index 2b7f7de244..8fd97a1efe 100644 --- a/Rubberduck.Resources/Inspections/InspectionNames.resx +++ b/Rubberduck.Resources/Inspections/InspectionNames.resx @@ -362,19 +362,15 @@ Keyword used as member name - Line continuation between keywords - Identifier containing a non-breaking space - Negative line number - OnErrorGoto -1 @@ -446,4 +442,7 @@ Superfluous annotation arguments + + ByRef is a misleading modifier because the parameter will be passed ByVal. + \ No newline at end of file diff --git a/Rubberduck.Resources/Inspections/InspectionResults.Designer.cs b/Rubberduck.Resources/Inspections/InspectionResults.Designer.cs index 00e2338b40..8663085b24 100644 --- a/Rubberduck.Resources/Inspections/InspectionResults.Designer.cs +++ b/Rubberduck.Resources/Inspections/InspectionResults.Designer.cs @@ -19,7 +19,7 @@ namespace Rubberduck.Resources.Inspections { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public class InspectionResults { @@ -519,6 +519,15 @@ public class InspectionResults { } } + /// + /// Looks up a localized string similar to Misleading ByRef modifier used for Parameter '{0}'.. + /// + public static string MisleadingByRefParameterInspection { + get { + return ResourceManager.GetString("MisleadingByRefParameterInspection", resourceCulture); + } + } + /// /// Looks up a localized string similar to The annotation '{0}' was expected to have more arguments.. /// diff --git a/Rubberduck.Resources/Inspections/InspectionResults.resx b/Rubberduck.Resources/Inspections/InspectionResults.resx index aa86d062a7..34de378b6e 100644 --- a/Rubberduck.Resources/Inspections/InspectionResults.resx +++ b/Rubberduck.Resources/Inspections/InspectionResults.resx @@ -513,4 +513,8 @@ In memoriam, 1972-2018 The annotation '{0}' was expected to have less arguments. {0} annotation name + + Misleading ByRef modifier used for Parameter '{0}'. + {0} Parameter name + \ No newline at end of file diff --git a/RubberduckTests/Inspections/MisleadingByRefParameterInspectionTests.cs b/RubberduckTests/Inspections/MisleadingByRefParameterInspectionTests.cs new file mode 100644 index 0000000000..f43812bc68 --- /dev/null +++ b/RubberduckTests/Inspections/MisleadingByRefParameterInspectionTests.cs @@ -0,0 +1,72 @@ +using NUnit.Framework; +using Rubberduck.CodeAnalysis.Inspections; +using Rubberduck.CodeAnalysis.Inspections.Concrete; +using Rubberduck.Parsing.VBA; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RubberduckTests.Inspections +{ + [TestFixture] + public class MisleadingByRefParameterInspectionTests : InspectionTestsBase + { + [TestCase("Property Let Fizz(ByRef arg1 As Integer)\r\nEnd Property", 1)] + [TestCase("Property Let Fizz(arg1 As Integer)\r\nEnd Property", 0)] + [TestCase("Property Let Fizz(ByVal arg1 As Integer)\r\nEnd Property", 0)] + [TestCase("Property Set Fizz(ByRef arg1 As Object)\r\nEnd Property", 1)] + [TestCase("Property Set Fizz(arg1 As Object)\r\nEnd Property", 0)] + [TestCase("Property Set Fizz(ByVal arg1 As Object)\r\nEnd Property", 0)] + [Category("QuickFixes")] + [Category(nameof(MisleadingByRefParameterInspection))] + public void AllParamMechanisms(string inputCode, int expectedCount) + { + Assert.AreEqual(expectedCount, InspectionResultsForStandardModule(inputCode).Count()); + } + + [TestCase("arg")] + [TestCase("ByRef arg")] + [Category("QuickFixes")] + [Category(nameof(MisleadingByRefParameterInspection))] + public void UserDefinedTypeEdgeCase(string parameterMechanismAndParam) + { + var inputCode = +$@" +Option Explicit + +Public Type TestType + FirstValue As Long +End Type + +Private this As TestType + +Public Property Get UserDefinedType() As TestType + UserDefinedType = this +End Property + +Public Property Let UserDefinedType({parameterMechanismAndParam} As TestType) + this = arg +End Property +"; + + Assert.AreEqual(0, InspectionResultsForStandardModule(inputCode).Count()); + } + + [Test] + [Category("QuickFixes")] + [Category(nameof(MisleadingByRefParameterInspection))] + public void InspectionName() + { + var inspection = new MisleadingByRefParameterInspection(null); + + Assert.AreEqual(nameof(MisleadingByRefParameterInspection), inspection.Name); + } + + protected override IInspection InspectionUnderTest(RubberduckParserState state) + { + return new MisleadingByRefParameterInspection(state); + } + } +} diff --git a/RubberduckTests/QuickFixes/PassParameterByValueQuickFixTests.cs b/RubberduckTests/QuickFixes/PassParameterByValueQuickFixTests.cs index 6957b4a256..fc158bf106 100644 --- a/RubberduckTests/QuickFixes/PassParameterByValueQuickFixTests.cs +++ b/RubberduckTests/QuickFixes/PassParameterByValueQuickFixTests.cs @@ -296,6 +296,28 @@ Debug.Print foo Assert.AreEqual(expectedCode, actualCode); } + [Test] + [Category("QuickFixes")] + [Category(nameof(MisleadingByRefParameterInspection))] + public void CorrectsMisleadingByRefPropertyMutatorParameter() + { + const string inputCode = +@" +Option Explicit + +Private fizzField As Long + +Public Property Get Fizz() As Long + Fizz = fizzField +End Property + +Public Property Let Fizz(ByRef arg As Long) + fizzField = arg +End Property +"; + var actualCode = ApplyQuickFixToFirstInspectionResult(inputCode, state => new MisleadingByRefParameterInspection(state)); + StringAssert.Contains("Public Property Let Fizz(ByVal arg As Long)", actualCode); + } protected override IQuickFix QuickFix(RubberduckParserState state) { From 9ba28b2ec0bd680c42ba3f0077fecd7a0d01d092 Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Fri, 25 Sep 2020 11:52:24 -0700 Subject: [PATCH 53/67] Ignore Property mutator Value param --- .../ImplicitByRefModifierInspection.cs | 40 +++++++++++++------ .../ImplicitByRefModifierInspectionTests.cs | 37 +++++++++++++++++ 2 files changed, 65 insertions(+), 12 deletions(-) diff --git a/Rubberduck.CodeAnalysis/Inspections/Concrete/ImplicitByRefModifierInspection.cs b/Rubberduck.CodeAnalysis/Inspections/Concrete/ImplicitByRefModifierInspection.cs index 686a8fb83d..833d778a1f 100644 --- a/Rubberduck.CodeAnalysis/Inspections/Concrete/ImplicitByRefModifierInspection.cs +++ b/Rubberduck.CodeAnalysis/Inspections/Concrete/ImplicitByRefModifierInspection.cs @@ -3,6 +3,7 @@ using Rubberduck.Parsing.VBA; using Rubberduck.Parsing.VBA.DeclarationCaching; using Rubberduck.Resources.Inspections; +using System.Linq; namespace Rubberduck.CodeAnalysis.Inspections.Concrete { @@ -10,8 +11,11 @@ namespace Rubberduck.CodeAnalysis.Inspections.Concrete /// Highlights implicit ByRef modifiers in user code. /// /// - /// In modern VB (VB.NET), the implicit modifier is ByVal, as it is in most other programming languages. - /// Making the ByRef modifiers explicit can help surface potentially unexpected language defaults. + /// VBA parameters are implicitly ByRef, which differs from modern VB (VB.NET) and most other programming languages which are implicitly ByVal. + /// So, explicitly identifing VBA parameter mechanisms (the ByRef and ByVal modifiers) can help surface potentially unexpected language results. + /// The inspection does not flag an implicit parameter mechanism for the last parameter of Property mutators (Let or Set). + /// VBA applies a ByVal parameter mechanism to the last parameter in the absence (or presence!) of a modifier. + /// Exception: UserDefinedType parameters must always be passed as ByRef. /// /// /// @@ -31,6 +35,16 @@ namespace Rubberduck.CodeAnalysis.Inspections.Concrete /// ]]> /// /// + /// + /// + /// + /// + /// internal sealed class ImplicitByRefModifierInspection : DeclarationInspectionBase { public ImplicitByRefModifierInspection(IDeclarationFinderProvider declarationFinderProvider) @@ -41,21 +55,23 @@ protected override bool IsResultDeclaration(Declaration declaration, Declaration { if (!(declaration is ParameterDeclaration parameter) || !parameter.IsImplicitByRef - || parameter.IsParamArray) + || parameter.IsParamArray + //Exclude parameters of Declare statements + || !(parameter.ParentDeclaration is ModuleBodyElementDeclaration enclosingMethod)) { return false; } - var parentDeclaration = parameter.ParentDeclaration; - - if (parentDeclaration is ModuleBodyElementDeclaration enclosingMethod) - { - return !enclosingMethod.IsInterfaceImplementation - && !finder.FindEventHandlers().Contains(enclosingMethod); - } + return !IsPropertyMutatorRHSParameter(enclosingMethod, parameter) + && !enclosingMethod.IsInterfaceImplementation + && !finder.FindEventHandlers().Contains(enclosingMethod); + } - return parentDeclaration.DeclarationType != DeclarationType.LibraryFunction - && parentDeclaration.DeclarationType != DeclarationType.LibraryProcedure; + private bool IsPropertyMutatorRHSParameter(ModuleBodyElementDeclaration enclosingMethod, ParameterDeclaration implicitByRefParameter) + { + return (enclosingMethod.DeclarationType.HasFlag(DeclarationType.PropertyLet) + || enclosingMethod.DeclarationType.HasFlag(DeclarationType.PropertySet)) + && enclosingMethod.Parameters.Last().Equals(implicitByRefParameter); } protected override string ResultDescription(Declaration declaration) diff --git a/RubberduckTests/Inspections/ImplicitByRefModifierInspectionTests.cs b/RubberduckTests/Inspections/ImplicitByRefModifierInspectionTests.cs index caa05cfe5d..7cd4f2c85a 100644 --- a/RubberduckTests/Inspections/ImplicitByRefModifierInspectionTests.cs +++ b/RubberduckTests/Inspections/ImplicitByRefModifierInspectionTests.cs @@ -17,13 +17,26 @@ public class ImplicitByRefModifierInspectionTests : InspectionTestsBase [TestCase("Sub Foo(arg1 As Integer, ByRef arg2 As Date)\r\nEnd Sub", 1)] [TestCase("Sub Foo(ParamArray arg1 As Integer)\r\nEnd Sub", 0)] [Category("QuickFixes")] + [Category(nameof(ImplicitByRefModifierInspection))] public void ImplicitByRefModifier_SimpleScenarios(string inputCode, int expectedCount) { Assert.AreEqual(expectedCount, InspectionResultsForStandardModule(inputCode).Count()); } + [TestCase("Property Let Fizz(RHS As Integer)\r\nEnd Property", 0)] + [TestCase("Property Set Fizz(RHS As Object)\r\nEnd Property", 0)] + [TestCase("Property Let Fizz(index As Integer, RHS As Integer)\r\nEnd Property", 1)] + [TestCase("Property Set Fizz(index As Integer, RHS As Object)\r\nEnd Property", 1)] + [Category("QuickFixes")] + [Category(nameof(ImplicitByRefModifierInspection))] + public void ImplicitByRefModifier_PropertyMutatorRHSParameter(string inputCode, int expectedCount) + { + Assert.AreEqual(expectedCount, InspectionResultsForStandardModule(inputCode).Count()); + } + [Test] [Category("QuickFixes")] + [Category(nameof(ImplicitByRefModifierInspection))] public void ImplicitByRefModifier_ReturnsResult_InterfaceImplementation() { const string inputCode1 = @@ -47,6 +60,7 @@ Sub IClass1_Foo(arg1 As Integer) [Test] [Category("QuickFixes")] + [Category(nameof(ImplicitByRefModifierInspection))] public void ImplicitByRefModifier_ReturnsResult_MultipleInterfaceImplementations() { const string inputCode1 = @@ -77,6 +91,7 @@ Sub IClass1_Foo(arg1 As Integer) [Test] [Category("QuickFixes")] + [Category(nameof(ImplicitByRefModifierInspection))] public void ImplicitByRefModifier_Ignored_DoesNotReturnResult() { const string inputCode = @@ -86,8 +101,30 @@ Sub Foo(arg1 As Integer) Assert.AreEqual(0, InspectionResultsForStandardModule(inputCode).Count()); } + [TestCase(@"Public Declare PtrSafe Sub LibProcedure Lib ""MyLib""(arg As Long)", "LibProcedure 2000")] + [TestCase(@"Public Declare PtrSafe Function LibProcedure Lib ""MyLib""(arg As Long) As Long", "test = LibProcedure(2000)")] + [Category("QuickFixes")] + [Category(nameof(ImplicitByRefModifierInspection))] + public void ImplicitByRefModifier_IgnoresDeclareStatement(string declareStatement, string libraryCall) + { + var inputCode = +$@" +Option Explicit + +Private test As Long + +{declareStatement} + +Public Sub CallTheLib() + {libraryCall} +End Sub"; + + Assert.AreEqual(0, InspectionResultsForStandardModule(inputCode).Count()); + } + [Test] [Category("QuickFixes")] + [Category(nameof(ImplicitByRefModifierInspection))] public void InspectionName() { var inspection = new ImplicitByRefModifierInspection(null); From 3d2ed907d650261b34d5a6df99b25a007e272850 Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Mon, 5 Oct 2020 17:54:01 -0700 Subject: [PATCH 54/67] Replace accessibility parameter string with enum Also added more content to xmldocs. --- Rubberduck.Refactorings/Common/CodeBuilder.cs | 50 +++++++++---------- ...terfaceImplementationsRefactoringAction.cs | 8 +-- RubberduckTests/CodeBuilderTests.cs | 20 ++++---- 3 files changed, 39 insertions(+), 39 deletions(-) diff --git a/Rubberduck.Refactorings/Common/CodeBuilder.cs b/Rubberduck.Refactorings/Common/CodeBuilder.cs index cc155e9fc0..50c4deb106 100644 --- a/Rubberduck.Refactorings/Common/CodeBuilder.cs +++ b/Rubberduck.Refactorings/Common/CodeBuilder.cs @@ -21,7 +21,7 @@ public interface ICodeBuilder /// Main body content/logic of the member string BuildMemberBlockFromPrototype(ModuleBodyElementDeclaration declaration, string content = null, - string accessibility = null, + Accessibility accessibility = Accessibility.Public, string newIdentifier = null); /// @@ -35,24 +35,23 @@ public interface ICodeBuilder /// Generates a Property Get codeblock based on the prototype declaration /// /// DeclarationType with flags: Variable, Constant, UserDefinedTypeMember, or Function - /// Member body content. Formatting is the responsibility of the caller - /// Defaults to 'Value' unless otherwise specified + /// Member body content. Null results in an empty body. Formatting is the responsibility of the caller bool TryBuildPropertyGetCodeBlock(Declaration prototype, string propertyIdentifier, out string codeBlock, - string accessibility = null, + Accessibility accessibility = Accessibility.Public, string content = null); /// /// Generates a Property Let codeblock based on the prototype declaration /// /// DeclarationType with flags: Variable, Constant, UserDefinedTypeMember, or Function - /// Member body content. Formatting is the responsibility of the caller - /// Defaults to 'Value' unless otherwise specified + /// Member body content. Null results in an empty body. Formatting is the responsibility of the caller + /// Defaults to 'RHS' unless otherwise specified bool TryBuildPropertyLetCodeBlock(Declaration prototype, string propertyIdentifier, out string codeBlock, - string accessibility = null, + Accessibility accessibility = Accessibility.Public, string content = null, string parameterIdentifier = null); @@ -60,12 +59,12 @@ public interface ICodeBuilder /// Generates a Property Set codeblock based on the prototype declaration /// /// DeclarationType with flags: Variable, Constant, UserDefinedTypeMember, or Function - /// Member body content. Formatting is the responsibility of the caller - /// Defaults to 'Value' unless otherwise specified + /// Member body content. Null results in an empty body. Formatting is the responsibility of the caller + /// Defaults to 'RHS' unless otherwise specified bool TryBuildPropertySetCodeBlock(Declaration prototype, string propertyIdentifier, out string codeBlock, - string accessibility = null, + Accessibility accessibility = Accessibility.Public, string content = null, string parameterIdentifier = null); @@ -82,7 +81,7 @@ public interface ICodeBuilder /// Generates a UserDefinedTypeMember declaration expression based on the prototype declaration /// /// DeclarationType with flags: Variable, Constant, UserDefinedTypeMember, or Function - /// Defaults is 4 spaces + /// If left null, 4 spaces of indentation are applied bool TryBuildUDTMemberDeclaration(string identifier, Declaration prototype, out string declaration, string indentation = null); } @@ -90,7 +89,7 @@ public class CodeBuilder : ICodeBuilder { public string BuildMemberBlockFromPrototype(ModuleBodyElementDeclaration declaration, string content = null, - string accessibility = null, + Accessibility accessibility = Accessibility.Public, string newIdentifier = null) { @@ -105,16 +104,16 @@ public class CodeBuilder : ICodeBuilder return string.Concat(elements); } - public bool TryBuildPropertyGetCodeBlock(Declaration prototype, string propertyIdentifier, out string codeBlock, string accessibility = null, string content = null) + public bool TryBuildPropertyGetCodeBlock(Declaration prototype, string propertyIdentifier, out string codeBlock, Accessibility accessibility = Accessibility.Public, string content = null) => TryBuildPropertyBlockFromTarget(prototype, DeclarationType.PropertyGet, propertyIdentifier, out codeBlock, accessibility, content); - public bool TryBuildPropertyLetCodeBlock(Declaration prototype, string propertyIdentifier, out string codeBlock, string accessibility = null, string content = null, string parameterIdentifier = null) + public bool TryBuildPropertyLetCodeBlock(Declaration prototype, string propertyIdentifier, out string codeBlock, Accessibility accessibility = Accessibility.Public, string content = null, string parameterIdentifier = null) => TryBuildPropertyBlockFromTarget(prototype, DeclarationType.PropertyLet, propertyIdentifier, out codeBlock, accessibility, content, parameterIdentifier); - public bool TryBuildPropertySetCodeBlock(Declaration prototype, string propertyIdentifier, out string codeBlock, string accessibility = null, string content = null, string parameterIdentifier = null) + public bool TryBuildPropertySetCodeBlock(Declaration prototype, string propertyIdentifier, out string codeBlock, Accessibility accessibility = Accessibility.Public, string content = null, string parameterIdentifier = null) => TryBuildPropertyBlockFromTarget(prototype, DeclarationType.PropertySet, propertyIdentifier, out codeBlock, accessibility, content, parameterIdentifier); - private bool TryBuildPropertyBlockFromTarget(T prototype, DeclarationType letSetGetType, string propertyIdentifier, out string codeBlock, string accessibility = null, string content = null, string parameterIdentifier = null) where T : Declaration + private bool TryBuildPropertyBlockFromTarget(T prototype, DeclarationType letSetGetType, string propertyIdentifier, out string codeBlock, Accessibility accessibility, string content = null, string parameterIdentifier = null) where T : Declaration { codeBlock = string.Empty; if (!letSetGetType.HasFlag(DeclarationType.Property)) @@ -142,27 +141,23 @@ public bool TryBuildPropertySetCodeBlock(Declaration prototype, string propertyI var letSetParamExpression = $"{paramMechanism} {propertyValueParam} {asTypeClause}"; codeBlock = letSetGetType.HasFlag(DeclarationType.PropertyGet) - ? string.Join(Environment.NewLine, $"{accessibility ?? Tokens.Public} {TypeToken(letSetGetType)} {propertyIdentifier}() {asTypeClause}", content, EndStatement(letSetGetType)) - : string.Join(Environment.NewLine, $"{accessibility ?? Tokens.Public} {TypeToken(letSetGetType)} {propertyIdentifier}({letSetParamExpression})", content, EndStatement(letSetGetType)); + ? string.Join(Environment.NewLine, $"{AccessibilityToken(accessibility)} {TypeToken(letSetGetType)} {propertyIdentifier}() {asTypeClause}", content, EndStatement(letSetGetType)) + : string.Join(Environment.NewLine, $"{AccessibilityToken(accessibility)} {TypeToken(letSetGetType)} {propertyIdentifier}({letSetParamExpression})", content, EndStatement(letSetGetType)); return true; } public string ImprovedFullMemberSignature(ModuleBodyElementDeclaration declaration) - => ImprovedFullMemberSignatureInternal(declaration); + => ImprovedFullMemberSignatureInternal(declaration, declaration.Accessibility); - private string ImprovedFullMemberSignatureInternal(ModuleBodyElementDeclaration declaration, string accessibility = null, string newIdentifier = null) + private string ImprovedFullMemberSignatureInternal(ModuleBodyElementDeclaration declaration, Accessibility accessibility, string newIdentifier = null) { - var accessibilityToken = declaration.Accessibility.Equals(Accessibility.Implicit) - ? Tokens.Public - : $"{declaration.Accessibility.ToString()}"; - var asTypeName = string.IsNullOrEmpty(declaration.AsTypeName) ? string.Empty : $" {Tokens.As} {declaration.AsTypeName}"; var elements = new List() { - accessibility ?? accessibilityToken, + AccessibilityToken(accessibility), $" {TypeToken(declaration.DeclarationType)} ", newIdentifier ?? declaration.IdentifierName, $"({ImprovedArgumentList(declaration)})", @@ -297,6 +292,11 @@ private static string BuildUDTMemberDeclaration(string udtMemberIdentifier, Decl return $"{indentation ?? " "}{identifierExpression} {Tokens.As} {prototype.AsTypeName}"; } + private static string AccessibilityToken(Accessibility accessibility) + => accessibility.Equals(Accessibility.Implicit) + ? Tokens.Public + : $"{accessibility.ToString()}"; + private static bool IsValidPrototypeDeclarationType(DeclarationType declarationType) { return declarationType.HasFlag(DeclarationType.Variable) diff --git a/Rubberduck.Refactorings/ImplementInterface/AddInterfaceImplementations/AddInterfaceImplementationsRefactoringAction.cs b/Rubberduck.Refactorings/ImplementInterface/AddInterfaceImplementations/AddInterfaceImplementationsRefactoringAction.cs index 0c3091b84a..e2f475521a 100644 --- a/Rubberduck.Refactorings/ImplementInterface/AddInterfaceImplementations/AddInterfaceImplementationsRefactoringAction.cs +++ b/Rubberduck.Refactorings/ImplementInterface/AddInterfaceImplementations/AddInterfaceImplementationsRefactoringAction.cs @@ -38,12 +38,12 @@ private string GetInterfaceMember(Declaration member, string interfaceName) { if (member is ModuleBodyElementDeclaration mbed) { - return _codeBuilder.BuildMemberBlockFromPrototype(mbed, accessibility: Tokens.Private, newIdentifier: $"{interfaceName}_{member.IdentifierName}", content: _memberBody); + return _codeBuilder.BuildMemberBlockFromPrototype(mbed, accessibility: Accessibility.Private, newIdentifier: $"{interfaceName}_{member.IdentifierName}", content: _memberBody); } if (member is VariableDeclaration variable) { - if (!_codeBuilder.TryBuildPropertyGetCodeBlock(variable, $"{interfaceName}_{variable.IdentifierName}", out var propertyGet, Tokens.Private, _memberBody)) + if (!_codeBuilder.TryBuildPropertyGetCodeBlock(variable, $"{interfaceName}_{variable.IdentifierName}", out var propertyGet, Accessibility.Private, _memberBody)) { throw new InvalidOperationException(); } @@ -52,7 +52,7 @@ private string GetInterfaceMember(Declaration member, string interfaceName) if (variable.AsTypeName.Equals(Tokens.Variant) || !variable.IsObject) { - if (!_codeBuilder.TryBuildPropertyLetCodeBlock(variable, $"{interfaceName}_{variable.IdentifierName}", out var propertyLet, Tokens.Private, _memberBody)) + if (!_codeBuilder.TryBuildPropertyLetCodeBlock(variable, $"{interfaceName}_{variable.IdentifierName}", out var propertyLet, Accessibility.Private, _memberBody)) { throw new InvalidOperationException(); } @@ -61,7 +61,7 @@ private string GetInterfaceMember(Declaration member, string interfaceName) if (variable.AsTypeName.Equals(Tokens.Variant) || variable.IsObject) { - if (!_codeBuilder.TryBuildPropertySetCodeBlock(variable, $"{interfaceName}_{variable.IdentifierName}", out var propertySet, Tokens.Private, _memberBody)) + if (!_codeBuilder.TryBuildPropertySetCodeBlock(variable, $"{interfaceName}_{variable.IdentifierName}", out var propertySet, Accessibility.Private, _memberBody)) { throw new InvalidOperationException(); } diff --git a/RubberduckTests/CodeBuilderTests.cs b/RubberduckTests/CodeBuilderTests.cs index 581b1d5810..a4481302ec 100644 --- a/RubberduckTests/CodeBuilderTests.cs +++ b/RubberduckTests/CodeBuilderTests.cs @@ -56,12 +56,12 @@ End Enum StringAssert.Contains($"Property Get {testParams.Identifier}() As {typeName}", result); } - [TestCase("fizz", DeclarationType.Variable, "Integer", "Public")] - [TestCase("FirstValue", DeclarationType.UserDefinedTypeMember, "Long", "Public")] - [TestCase("fazz", DeclarationType.Variable, "Long", "Public")] - [TestCase("fuzz", DeclarationType.Variable, "ETestType2", "Private")] + [TestCase("fizz", DeclarationType.Variable, "Integer", Accessibility.Public)] + [TestCase("FirstValue", DeclarationType.UserDefinedTypeMember, "Long", Accessibility.Public)] + [TestCase("fazz", DeclarationType.Variable, "Long", Accessibility.Public)] + [TestCase("fuzz", DeclarationType.Variable, "ETestType2", Accessibility.Private)] [Category(nameof(CodeBuilder))] - public void PropertyBlockFromPrototype_PropertyGetAccessibility(string targetIdentifier, DeclarationType declarationType, string typeName, string accessibility) + public void PropertyBlockFromPrototype_PropertyGetAccessibility(string targetIdentifier, DeclarationType declarationType, string typeName, Accessibility accessibility) { var testParams = new PropertyBlockFromPrototypeParams("Bazz", DeclarationType.PropertyGet, accessibility); string inputCode = @@ -643,7 +643,7 @@ private static string ImprovedArgumentListTest(ModuleBodyElementDeclaration mbed => new CodeBuilder().ImprovedArgumentList(mbed); private static string MemberBlockFromPrototypeTest(ModuleBodyElementDeclaration mbed, MemberBlockFromPrototypeTestParams testParams) - => new CodeBuilder().BuildMemberBlockFromPrototype(mbed, testParams.Accessibility, testParams.Content, testParams.NewIdentifier); + => new CodeBuilder().BuildMemberBlockFromPrototype(mbed, testParams.Content, testParams.Accessibility, testParams.NewIdentifier); private (string procType, string endStmt) ProcedureTypeIdentifier(DeclarationType declarationType) { @@ -666,7 +666,7 @@ private static string MemberBlockFromPrototypeTest(ModuleBodyElementDeclaration private struct PropertyBlockFromPrototypeParams { - public PropertyBlockFromPrototypeParams(string identifier, DeclarationType propertyType, string accessibility = null, string content = null, string paramIdentifier = null) + public PropertyBlockFromPrototypeParams(string identifier, DeclarationType propertyType, Accessibility accessibility = Accessibility.Public, string content = null, string paramIdentifier = null) { Identifier = identifier; DeclarationType = propertyType; @@ -676,21 +676,21 @@ public PropertyBlockFromPrototypeParams(string identifier, DeclarationType prope } public DeclarationType DeclarationType { get; } public string Identifier { get; } - public string Accessibility {get; } + public Accessibility Accessibility {get; } public string Content { get; } public string WriteParam { get; } } private struct MemberBlockFromPrototypeTestParams { - public MemberBlockFromPrototypeTestParams(ModuleBodyElementDeclaration mbed, string accessibility = null, string content = null, string newIdentifier = null) + public MemberBlockFromPrototypeTestParams(ModuleBodyElementDeclaration mbed, Accessibility accessibility = Accessibility.Public, string content = null, string newIdentifier = null) { Accessibility = accessibility; Content = content; NewIdentifier = newIdentifier; } - public string Accessibility { get; } + public Accessibility Accessibility { get; } public string Content { get; } public string NewIdentifier { get; } } From 361948a2330526a3050ba2d0d637e87df239967b Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Wed, 7 Oct 2020 03:21:35 -0700 Subject: [PATCH 55/67] Modify CodeBuilder to use Indenter settings --- Rubberduck.Refactorings/Common/CodeBuilder.cs | 49 +++++--- .../CreateUDTMemberRefactoringAction.cs | 4 +- .../EncapsulateFieldCodeBuilder.cs | 67 +++++----- RubberduckTests/CodeBuilderTests.cs | 37 ++++-- .../CodeExplorer/MockedCodeExplorer.cs | 15 ++- .../ExtractInterfaceCommandTests.cs | 17 ++- .../ImplementInterfaceCommandTests.cs | 15 ++- .../CreateUDTMemberRefactoringActionTests.cs | 15 ++- .../EncapsulateFieldTestComponentResolver.cs | 21 +++- .../ExtractInterfaceRefactoringActionTests.cs | 17 ++- .../ExtractInterface/ExtractInterfaceTests.cs | 23 +++- ...mplementInterfaceRefactoringActionTests.cs | 117 +++++++++++------- .../ImplementInterfaceTests.cs | 15 ++- 13 files changed, 279 insertions(+), 133 deletions(-) diff --git a/Rubberduck.Refactorings/Common/CodeBuilder.cs b/Rubberduck.Refactorings/Common/CodeBuilder.cs index cc155e9fc0..768643baa7 100644 --- a/Rubberduck.Refactorings/Common/CodeBuilder.cs +++ b/Rubberduck.Refactorings/Common/CodeBuilder.cs @@ -1,6 +1,7 @@ using Rubberduck.Parsing; using Rubberduck.Parsing.Grammar; using Rubberduck.Parsing.Symbols; +using Rubberduck.SmartIndenter; using System; using System.Collections.Generic; using System.Linq; @@ -35,7 +36,7 @@ public interface ICodeBuilder /// Generates a Property Get codeblock based on the prototype declaration /// /// DeclarationType with flags: Variable, Constant, UserDefinedTypeMember, or Function - /// Member body content. Formatting is the responsibility of the caller + /// Member body content. /// Defaults to 'Value' unless otherwise specified bool TryBuildPropertyGetCodeBlock(Declaration prototype, string propertyIdentifier, @@ -47,7 +48,7 @@ public interface ICodeBuilder /// Generates a Property Let codeblock based on the prototype declaration /// /// DeclarationType with flags: Variable, Constant, UserDefinedTypeMember, or Function - /// Member body content. Formatting is the responsibility of the caller + /// Member body content. /// Defaults to 'Value' unless otherwise specified bool TryBuildPropertyLetCodeBlock(Declaration prototype, string propertyIdentifier, @@ -60,7 +61,7 @@ public interface ICodeBuilder /// Generates a Property Set codeblock based on the prototype declaration /// /// DeclarationType with flags: Variable, Constant, UserDefinedTypeMember, or Function - /// Member body content. Formatting is the responsibility of the caller + /// Member body content. /// Defaults to 'Value' unless otherwise specified bool TryBuildPropertySetCodeBlock(Declaration prototype, string propertyIdentifier, @@ -73,7 +74,8 @@ public interface ICodeBuilder /// Generates a UserDefinedType (UDT) declaration using the prototype declarations for /// creating the UserDefinedTypeMember declarations. /// - /// No validation or conflict analysis is applied to the identifiers. + /// + /// No validation or conflict analysis is applied to the identifiers. /// /// DeclarationTypes with flags: Variable, Constant, UserDefinedTypeMember, or Function bool TryBuildUserDefinedTypeDeclaration(string udtIdentifier, IEnumerable<(Declaration Prototype, string UDTMemberIdentifier)> memberPrototypes, out string declaration, Accessibility accessibility = Accessibility.Private); @@ -81,19 +83,27 @@ public interface ICodeBuilder /// /// Generates a UserDefinedTypeMember declaration expression based on the prototype declaration /// + /// + /// No validation or conflict analysis is applied to the identifiers. + /// /// DeclarationType with flags: Variable, Constant, UserDefinedTypeMember, or Function - /// Defaults is 4 spaces - bool TryBuildUDTMemberDeclaration(string identifier, Declaration prototype, out string declaration, string indentation = null); + bool TryBuildUDTMemberDeclaration(string identifier, Declaration prototype, out string declaration); } public class CodeBuilder : ICodeBuilder { + private readonly IIndenter _indenter; + + public CodeBuilder(IIndenter indenter) + { + _indenter = indenter; + } + public string BuildMemberBlockFromPrototype(ModuleBodyElementDeclaration declaration, string content = null, string accessibility = null, string newIdentifier = null) { - var elements = new List() { ImprovedFullMemberSignatureInternal(declaration, accessibility, newIdentifier), @@ -117,12 +127,8 @@ public bool TryBuildPropertySetCodeBlock(Declaration prototype, string propertyI private bool TryBuildPropertyBlockFromTarget(T prototype, DeclarationType letSetGetType, string propertyIdentifier, out string codeBlock, string accessibility = null, string content = null, string parameterIdentifier = null) where T : Declaration { codeBlock = string.Empty; - if (!letSetGetType.HasFlag(DeclarationType.Property)) - { - throw new ArgumentException(); - } - - if (!IsValidPrototypeDeclarationType(prototype.DeclarationType)) + if (!letSetGetType.HasFlag(DeclarationType.Property) + || !IsValidPrototypeDeclarationType(prototype.DeclarationType)) { return false; } @@ -144,6 +150,9 @@ public bool TryBuildPropertySetCodeBlock(Declaration prototype, string propertyI codeBlock = letSetGetType.HasFlag(DeclarationType.PropertyGet) ? string.Join(Environment.NewLine, $"{accessibility ?? Tokens.Public} {TypeToken(letSetGetType)} {propertyIdentifier}() {asTypeClause}", content, EndStatement(letSetGetType)) : string.Join(Environment.NewLine, $"{accessibility ?? Tokens.Public} {TypeToken(letSetGetType)} {propertyIdentifier}({letSetParamExpression})", content, EndStatement(letSetGetType)); + + codeBlock = string.Join(Environment.NewLine, _indenter.Indent(codeBlock)); + return true; } @@ -168,8 +177,8 @@ private string ImprovedFullMemberSignatureInternal(ModuleBodyElementDeclaration $"({ImprovedArgumentList(declaration)})", asTypeName }; - return string.Concat(elements).Trim(); + return string.Concat(elements).Trim(); } public string ImprovedArgumentList(ModuleBodyElementDeclaration declaration) @@ -185,6 +194,7 @@ public string ImprovedArgumentList(ModuleBodyElementDeclaration declaration) && declaration.DeclarationType.HasFlag(DeclarationType.Property) && !declaration.DeclarationType.Equals(DeclarationType.PropertyGet))); } + return $"{string.Join(", ", arguments)}"; } @@ -265,11 +275,12 @@ public bool TryBuildUserDefinedTypeDeclaration(string udtIdentifier, IEnumerable blockLines.Add($"{Tokens.End} {Tokens.Type}"); - declaration = string.Join(Environment.NewLine, blockLines); + declaration = string.Join(Environment.NewLine, _indenter.Indent(blockLines)); + return true; } - public bool TryBuildUDTMemberDeclaration(string udtMemberIdentifier, Declaration prototype, out string declaration, string indentation = null) + public bool TryBuildUDTMemberDeclaration(string udtMemberIdentifier, Declaration prototype, out string declaration) { declaration = string.Empty; @@ -280,11 +291,11 @@ public bool TryBuildUDTMemberDeclaration(string udtMemberIdentifier, Declaration return false; } - declaration = BuildUDTMemberDeclaration(udtMemberIdentifier, prototype, indentation); + declaration = BuildUDTMemberDeclaration(udtMemberIdentifier, prototype); return true; } - private static string BuildUDTMemberDeclaration(string udtMemberIdentifier, Declaration prototype, string indentation = null) + private static string BuildUDTMemberDeclaration(string udtMemberIdentifier, Declaration prototype) { var identifierExpression = udtMemberIdentifier; if (prototype.IsArray) @@ -294,7 +305,7 @@ private static string BuildUDTMemberDeclaration(string udtMemberIdentifier, Decl : $"{udtMemberIdentifier}()"; } - return $"{indentation ?? " "}{identifierExpression} {Tokens.As} {prototype.AsTypeName}"; + return $"{identifierExpression} {Tokens.As} {prototype.AsTypeName}"; } private static bool IsValidPrototypeDeclarationType(DeclarationType declarationType) diff --git a/Rubberduck.Refactorings/CreateUDTMember/CreateUDTMemberRefactoringAction.cs b/Rubberduck.Refactorings/CreateUDTMember/CreateUDTMemberRefactoringAction.cs index ffc2a95138..245ac619bf 100644 --- a/Rubberduck.Refactorings/CreateUDTMember/CreateUDTMemberRefactoringAction.cs +++ b/Rubberduck.Refactorings/CreateUDTMember/CreateUDTMemberRefactoringAction.cs @@ -70,9 +70,9 @@ private IEnumerable GenerateUserDefinedMemberDeclarations(IEnumerable<(D var declarations = new List(); foreach (var (Prototype, UDTMemberIdentifier) in newMemberPairs) { - if (_codeBuilder.TryBuildUDTMemberDeclaration(UDTMemberIdentifier, Prototype, out var declaration, indentation)) + if (_codeBuilder.TryBuildUDTMemberDeclaration(UDTMemberIdentifier, Prototype, out var declaration)) { - declarations.Add(declaration); + declarations.Add($"{indentation}{declaration}"); } } return declarations; diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldCodeBuilder.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldCodeBuilder.cs index 0e14d0c77c..944bf911f1 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldCodeBuilder.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldCodeBuilder.cs @@ -20,9 +20,6 @@ public interface IEncapsulateFieldCodeBuilder /// public class EncapsulateFieldCodeBuilder : IEncapsulateFieldCodeBuilder { - private const string FourSpaces = " "; - private static string _doubleSpace = $"{Environment.NewLine}{Environment.NewLine}"; - private readonly ICodeBuilder _codeBuilder; public EncapsulateFieldCodeBuilder(ICodeBuilder codeBuilder) @@ -32,68 +29,60 @@ public EncapsulateFieldCodeBuilder(ICodeBuilder codeBuilder) public (string Get, string Let, string Set) BuildPropertyBlocks(PropertyAttributeSet propertyAttributes) { - string propertyLet = null; - string propertySet = null; + if (!(propertyAttributes.Declaration.DeclarationType.HasFlag(DeclarationType.Variable) + || propertyAttributes.Declaration.DeclarationType.HasFlag(DeclarationType.UserDefinedTypeMember))) + { + throw new ArgumentException("Invalid prototype DeclarationType", nameof(propertyAttributes)); + } + + (string Get, string Let, string Set) blocks = (string.Empty, string.Empty, string.Empty); + + var mutatorBody = $"{propertyAttributes.BackingField} = {propertyAttributes.RHSParameterIdentifier}"; if (propertyAttributes.GeneratePropertyLet) { - var letterContent = $"{FourSpaces}{propertyAttributes.BackingField} = {propertyAttributes.RHSParameterIdentifier}"; - if (!_codeBuilder.TryBuildPropertyLetCodeBlock(propertyAttributes.Declaration, propertyAttributes.PropertyName, out propertyLet, content: letterContent)) - { - throw new ArgumentException(); - } + _codeBuilder.TryBuildPropertyLetCodeBlock(propertyAttributes.Declaration, propertyAttributes.PropertyName, out blocks.Let, content: mutatorBody); } if (propertyAttributes.GeneratePropertySet) { - var setterContent = $"{FourSpaces}{Tokens.Set} {propertyAttributes.BackingField} = {propertyAttributes.RHSParameterIdentifier}"; - if (!_codeBuilder.TryBuildPropertySetCodeBlock(propertyAttributes.Declaration, propertyAttributes.PropertyName, out propertySet, content: setterContent)) - { - throw new ArgumentException(); - } + _codeBuilder.TryBuildPropertySetCodeBlock(propertyAttributes.Declaration, propertyAttributes.PropertyName, out blocks.Set, content: $"{Tokens.Set} {mutatorBody}"); } - var getterContent = $"{propertyAttributes.PropertyName} = {propertyAttributes.BackingField}"; - if (propertyAttributes.UsesSetAssignment) - { - getterContent = $"{Tokens.Set} {getterContent}"; - } + var propertyGetBody = propertyAttributes.UsesSetAssignment + ? $"{Tokens.Set} {propertyAttributes.PropertyName} = {propertyAttributes.BackingField}" + : $"{propertyAttributes.PropertyName} = {propertyAttributes.BackingField}"; if (propertyAttributes.AsTypeName.Equals(Tokens.Variant) && !propertyAttributes.Declaration.IsArray) { - getterContent = string.Join(Environment.NewLine, + propertyGetBody = string.Join( $"{Tokens.If} IsObject({propertyAttributes.BackingField}) {Tokens.Then}", - $"{FourSpaces}{Tokens.Set} {propertyAttributes.PropertyName} = {propertyAttributes.BackingField}", + $"{Tokens.Set} {propertyAttributes.PropertyName} = {propertyAttributes.BackingField}", Tokens.Else, - $"{FourSpaces}{propertyAttributes.PropertyName} = {propertyAttributes.BackingField}", - $"{Tokens.End} {Tokens.If}", - Environment.NewLine); + $"{propertyAttributes.PropertyName} = {propertyAttributes.BackingField}", + $"{Tokens.End} {Tokens.If}"); } - if (!_codeBuilder.TryBuildPropertyGetCodeBlock(propertyAttributes.Declaration, propertyAttributes.PropertyName, out var propertyGet, content: $"{FourSpaces}{getterContent}")) - { - throw new ArgumentException(); - } + _codeBuilder.TryBuildPropertyGetCodeBlock(propertyAttributes.Declaration, propertyAttributes.PropertyName, out blocks.Get, content: propertyGetBody); - return (propertyGet, propertyLet, propertySet); + return (blocks.Get, blocks.Let, blocks.Set); } public string BuildUserDefinedTypeDeclaration(IObjectStateUDT objectStateUDT, IEnumerable candidates) { - var selected = candidates.Where(c => c.EncapsulateFlag); - - var newUDTMembers = selected + var newUDTMembers = candidates.Where(c => c.EncapsulateFlag) .Select(m => (m.Declaration, m.BackingIdentifier)); - _codeBuilder.TryBuildUserDefinedTypeDeclaration(objectStateUDT.AsTypeName, newUDTMembers, out var declaration); + if (_codeBuilder.TryBuildUserDefinedTypeDeclaration(objectStateUDT.AsTypeName, newUDTMembers, out var declaration)) + { + return declaration; + } - return declaration ?? string.Empty; + return string.Empty; } - public string BuildObjectStateFieldDeclaration(IObjectStateUDT objectStateUDT) - { - return $"{Accessibility.Private} {objectStateUDT.IdentifierName} {Tokens.As} {objectStateUDT.AsTypeName}"; - } + public string BuildObjectStateFieldDeclaration(IObjectStateUDT objectStateUDT) + => $"{Accessibility.Private} {objectStateUDT.IdentifierName} {Tokens.As} {objectStateUDT.AsTypeName}"; public string BuildFieldDeclaration(Declaration target, string identifier) { diff --git a/RubberduckTests/CodeBuilderTests.cs b/RubberduckTests/CodeBuilderTests.cs index 581b1d5810..04f8ecd7ec 100644 --- a/RubberduckTests/CodeBuilderTests.cs +++ b/RubberduckTests/CodeBuilderTests.cs @@ -2,7 +2,9 @@ using Rubberduck.Common; using Rubberduck.Parsing.Symbols; using Rubberduck.Refactorings; +using Rubberduck.SmartIndenter; using RubberduckTests.Mocks; +using RubberduckTests.Settings; using System; using System.Collections.Generic; using System.Linq; @@ -493,7 +495,7 @@ public void UDT_NullUDTIdentifierBuildUDT_NoResult() .Where(d => d.IdentifierName == "test") .Select(d => (d, d.IdentifierName)); - var result = new CodeBuilder().TryBuildUserDefinedTypeDeclaration(null, targets, out var declaration); + var result = CreateCodeBuilder().TryBuildUserDefinedTypeDeclaration(null, targets, out var declaration); Assert.IsFalse(result); Assert.IsTrue(string.IsNullOrEmpty(declaration)); @@ -504,7 +506,7 @@ public void UDT_NullUDTIdentifierBuildUDT_NoResult() [Category(nameof(CodeBuilder))] public void UDT_EmptyPrototypeList_NoResult() { - var result = new CodeBuilder().TryBuildUserDefinedTypeDeclaration(_defaultUDTIdentifier, Enumerable.Empty<(Declaration, string)>(), out var declaration); + var result = CreateCodeBuilder().TryBuildUserDefinedTypeDeclaration(_defaultUDTIdentifier, Enumerable.Empty<(Declaration, string)>(), out var declaration); Assert.IsFalse(result); Assert.IsTrue(string.IsNullOrEmpty(declaration)); } @@ -514,7 +516,7 @@ public void UDT_EmptyPrototypeList_NoResult() public void UDT_NullDeclarationInPrototypeList_NoResult() { var nullInList = new List<(Declaration, string)>() { (null, "Fizz") }; - var result = new CodeBuilder().TryBuildUserDefinedTypeDeclaration(_defaultUDTIdentifier, nullInList, out var declaration); + var result = CreateCodeBuilder().TryBuildUserDefinedTypeDeclaration(_defaultUDTIdentifier, nullInList, out var declaration); Assert.IsFalse(result); Assert.IsTrue(string.IsNullOrEmpty(declaration)); } @@ -532,7 +534,7 @@ public void UDT_NullIdentifierInPrototypeList_NoResult() .Where(d => d.IdentifierName == "test") .Select(d => (d, nullIdentifier)); - var result = new CodeBuilder().TryBuildUserDefinedTypeDeclaration("TestType", targets, out var declaration); + var result = CreateCodeBuilder().TryBuildUserDefinedTypeDeclaration("TestType", targets, out var declaration); Assert.IsFalse(result); Assert.IsTrue(string.IsNullOrEmpty(declaration)); @@ -543,7 +545,7 @@ public void UDT_NullIdentifierInPrototypeList_NoResult() [Category(nameof(CodeBuilder))] public void UDT_NullPrototype_NoResult() { - var result = new CodeBuilder().TryBuildUDTMemberDeclaration(_defaultUDTIdentifier, null, out var declaration); + var result = CreateCodeBuilder().TryBuildUDTMemberDeclaration(_defaultUDTIdentifier, null, out var declaration); Assert.IsFalse(result); Assert.IsTrue(string.IsNullOrEmpty(declaration)); } @@ -559,7 +561,7 @@ public void UDT_NullUDTIdentifierBuildUDTMember_NoResult() var target = state.DeclarationFinder.DeclarationsWithType(DeclarationType.Variable) .Single(d => d.IdentifierName == "test"); - var result = new CodeBuilder().TryBuildUDTMemberDeclaration(null, target, out var declaration); + var result = CreateCodeBuilder().TryBuildUDTMemberDeclaration(null, target, out var declaration); Assert.IsFalse(result); Assert.IsTrue(string.IsNullOrEmpty(declaration)); @@ -576,7 +578,7 @@ private string CodeBuilderUDTResult(string inputCode, DeclarationType declaratio .Where(d => prototypes.Contains(d.IdentifierName)) .Select(prototype => (prototype, prototype.IdentifierName.CapitalizeFirstLetter())); - return new CodeBuilder().TryBuildUserDefinedTypeDeclaration(_defaultUDTIdentifier, targets, out string declaration) + return CreateCodeBuilder().TryBuildUserDefinedTypeDeclaration(_defaultUDTIdentifier, targets, out string declaration) ? declaration : string.Empty; } @@ -623,27 +625,38 @@ private string ParseAndTest(string inputCode, string targetIdentifier, Declar private static string PropertyGetBlockFromPrototypeTest(T target, PropertyBlockFromPrototypeParams testParams) where T : Declaration { - new CodeBuilder().TryBuildPropertyGetCodeBlock(target, testParams.Identifier, out string result, testParams.Accessibility, testParams.Content); + CreateCodeBuilder().TryBuildPropertyGetCodeBlock(target, testParams.Identifier, out string result, testParams.Accessibility, testParams.Content); return result; } private static string PropertyLetBlockFromPrototypeTest(T target, PropertyBlockFromPrototypeParams testParams) where T : Declaration { - new CodeBuilder().TryBuildPropertyLetCodeBlock(target, testParams.Identifier, out string result, testParams.Accessibility, testParams.Content, testParams.WriteParam); + CreateCodeBuilder().TryBuildPropertyLetCodeBlock(target, testParams.Identifier, out string result, testParams.Accessibility, testParams.Content, testParams.WriteParam); return result; } private static string PropertySetBlockFromPrototypeTest(T target, PropertyBlockFromPrototypeParams testParams) where T : Declaration { - new CodeBuilder().TryBuildPropertySetCodeBlock(target, testParams.Identifier, out string result, testParams.Accessibility, testParams.Content, testParams.WriteParam); + CreateCodeBuilder().TryBuildPropertySetCodeBlock(target, testParams.Identifier, out string result, testParams.Accessibility, testParams.Content, testParams.WriteParam); return result; } private static string ImprovedArgumentListTest(ModuleBodyElementDeclaration mbed) - => new CodeBuilder().ImprovedArgumentList(mbed); + => CreateCodeBuilder().ImprovedArgumentList(mbed); private static string MemberBlockFromPrototypeTest(ModuleBodyElementDeclaration mbed, MemberBlockFromPrototypeTestParams testParams) - => new CodeBuilder().BuildMemberBlockFromPrototype(mbed, testParams.Accessibility, testParams.Content, testParams.NewIdentifier); + => CreateCodeBuilder().BuildMemberBlockFromPrototype(mbed, testParams.Accessibility, testParams.Content, testParams.NewIdentifier); + + private static ICodeBuilder CreateCodeBuilder() + => new CodeBuilder(new Indenter(null, CreateIndenterSettings)); + + private static IndenterSettings CreateIndenterSettings() + { + var s = IndenterSettingsTests.GetMockIndenterSettings(); + s.VerticallySpaceProcedures = true; + s.LinesBetweenProcedures = 1; + return s; + } private (string procType, string endStmt) ProcedureTypeIdentifier(DeclarationType declarationType) { diff --git a/RubberduckTests/CodeExplorer/MockedCodeExplorer.cs b/RubberduckTests/CodeExplorer/MockedCodeExplorer.cs index f37cfef86f..5e1df21a40 100644 --- a/RubberduckTests/CodeExplorer/MockedCodeExplorer.cs +++ b/RubberduckTests/CodeExplorer/MockedCodeExplorer.cs @@ -507,16 +507,27 @@ public MockedCodeExplorer ImplementIndenterCommand() public MockedCodeExplorer ImplementExtractInterfaceCommand() { - var addImplementationsBaseRefactoring = new AddInterfaceImplementationsRefactoringAction(null, new CodeBuilder()); + var addImplementationsBaseRefactoring = new AddInterfaceImplementationsRefactoringAction(null, CreateCodeBuilder()); var addComponentService = TestAddComponentService(State.ProjectsProvider); var extractInterfaceBaseRefactoring = new ExtractInterfaceRefactoringAction(addImplementationsBaseRefactoring, State, State, null, State.ProjectsProvider, addComponentService); var userInteraction = new RefactoringUserInteraction(null, _uiDispatcher.Object); ViewModel.CodeExplorerExtractInterfaceCommand = new CodeExplorerExtractInterfaceCommand( - new ExtractInterfaceRefactoring(extractInterfaceBaseRefactoring, State, userInteraction, null, new CodeBuilder()), + new ExtractInterfaceRefactoring(extractInterfaceBaseRefactoring, State, userInteraction, null, CreateCodeBuilder()), State, null, VbeEvents.Object); return this; } + private ICodeBuilder CreateCodeBuilder() + => new CodeBuilder(new Indenter(Vbe.Object, CreateIndenterSettings)); + + private static IndenterSettings CreateIndenterSettings() + { + var s = IndenterSettingsTests.GetMockIndenterSettings(); + s.VerticallySpaceProcedures = true; + s.LinesBetweenProcedures = 1; + return s; + } + private static IAddComponentService TestAddComponentService(IProjectsProvider projectsProvider) { var sourceCodeHandler = new CodeModuleComponentSourceCodeHandler(); diff --git a/RubberduckTests/Commands/RefactorCommands/ExtractInterfaceCommandTests.cs b/RubberduckTests/Commands/RefactorCommands/ExtractInterfaceCommandTests.cs index d0678ec0bf..722d782c8d 100644 --- a/RubberduckTests/Commands/RefactorCommands/ExtractInterfaceCommandTests.cs +++ b/RubberduckTests/Commands/RefactorCommands/ExtractInterfaceCommandTests.cs @@ -8,6 +8,7 @@ using Rubberduck.Refactorings; using Rubberduck.Refactorings.AddInterfaceImplementations; using Rubberduck.Refactorings.ExtractInterface; +using Rubberduck.SmartIndenter; using Rubberduck.UI.Command; using Rubberduck.UI.Command.Refactorings; using Rubberduck.UI.Command.Refactorings.Notifiers; @@ -18,6 +19,7 @@ using Rubberduck.VBEditor.SourceCodeHandling; using Rubberduck.VBEditor.Utility; using RubberduckTests.Mocks; +using RubberduckTests.Settings; namespace RubberduckTests.Commands.RefactorCommands { @@ -173,15 +175,26 @@ protected override CommandBase TestCommand(IVBE vbe, RubberduckParserState state uiDispatcherMock .Setup(m => m.Invoke(It.IsAny())) .Callback((Action action) => action.Invoke()); - var addImplementationsBaseRefactoring = new AddInterfaceImplementationsRefactoringAction(rewritingManager, new CodeBuilder()); + var addImplementationsBaseRefactoring = new AddInterfaceImplementationsRefactoringAction(rewritingManager, CreateCodeBuilder()); var addComponentService = TestAddComponentService(state.ProjectsProvider); var baseRefactoring = new ExtractInterfaceRefactoringAction(addImplementationsBaseRefactoring, state, state, rewritingManager, state.ProjectsProvider, addComponentService); var userInteraction = new RefactoringUserInteraction(factory, uiDispatcherMock.Object); - var refactoring = new ExtractInterfaceRefactoring(baseRefactoring, state, userInteraction, selectionService, new CodeBuilder()); + var refactoring = new ExtractInterfaceRefactoring(baseRefactoring, state, userInteraction, selectionService, CreateCodeBuilder()); var notifier = new ExtractInterfaceFailedNotifier(msgBox); return new RefactorExtractInterfaceCommand(refactoring, notifier, state, selectionService); } + private static ICodeBuilder CreateCodeBuilder() + => new CodeBuilder(new Indenter(null, CreateIndenterSettings)); + + private static IndenterSettings CreateIndenterSettings() + { + var s = IndenterSettingsTests.GetMockIndenterSettings(); + s.VerticallySpaceProcedures = true; + s.LinesBetweenProcedures = 1; + return s; + } + private static IAddComponentService TestAddComponentService(IProjectsProvider projectsProvider) { var sourceCodeHandler = new CodeModuleComponentSourceCodeHandler(); diff --git a/RubberduckTests/Commands/RefactorCommands/ImplementInterfaceCommandTests.cs b/RubberduckTests/Commands/RefactorCommands/ImplementInterfaceCommandTests.cs index aa5d7590f7..95d6cb07e9 100644 --- a/RubberduckTests/Commands/RefactorCommands/ImplementInterfaceCommandTests.cs +++ b/RubberduckTests/Commands/RefactorCommands/ImplementInterfaceCommandTests.cs @@ -6,6 +6,7 @@ using Rubberduck.Refactorings; using Rubberduck.Refactorings.AddInterfaceImplementations; using Rubberduck.Refactorings.ImplementInterface; +using Rubberduck.SmartIndenter; using Rubberduck.UI.Command; using Rubberduck.UI.Command.Refactorings; using Rubberduck.UI.Command.Refactorings.Notifiers; @@ -14,6 +15,7 @@ using Rubberduck.VBEditor.SafeComWrappers.Abstract; using Rubberduck.VBEditor.Utility; using RubberduckTests.Mocks; +using RubberduckTests.Settings; namespace RubberduckTests.Commands.RefactorCommands { @@ -58,7 +60,7 @@ public void ImplementInterface_CanExecute_ImplementsInterfaceSelected() protected override CommandBase TestCommand(IVBE vbe, RubberduckParserState state, IRewritingManager rewritingManager, ISelectionService selectionService) { var msgBox = new Mock().Object; - var addImplementationsBaseRefactoring = new AddInterfaceImplementationsRefactoringAction(rewritingManager, new CodeBuilder()); + var addImplementationsBaseRefactoring = new AddInterfaceImplementationsRefactoringAction(rewritingManager, CreateCodeBuilder()); var baseRefactoring = new ImplementInterfaceRefactoringAction(addImplementationsBaseRefactoring, rewritingManager); var refactoring = new ImplementInterfaceRefactoring(baseRefactoring, state, selectionService); var notifier = new ImplementInterfaceFailedNotifier(msgBox); @@ -78,5 +80,16 @@ protected override IVBE SetupAllowingExecution() return builder.AddProject(project).Build().Object; } + + private static ICodeBuilder CreateCodeBuilder() + => new CodeBuilder(new Indenter(null, CreateIndenterSettings)); + + private static IndenterSettings CreateIndenterSettings() + { + var s = IndenterSettingsTests.GetMockIndenterSettings(); + s.VerticallySpaceProcedures = true; + s.LinesBetweenProcedures = 1; + return s; + } } } \ No newline at end of file diff --git a/RubberduckTests/Refactoring/CreateUDTMember/CreateUDTMemberRefactoringActionTests.cs b/RubberduckTests/Refactoring/CreateUDTMember/CreateUDTMemberRefactoringActionTests.cs index 1e0e326fd0..9ea3806800 100644 --- a/RubberduckTests/Refactoring/CreateUDTMember/CreateUDTMemberRefactoringActionTests.cs +++ b/RubberduckTests/Refactoring/CreateUDTMember/CreateUDTMemberRefactoringActionTests.cs @@ -4,6 +4,8 @@ using Rubberduck.Parsing.VBA; using Rubberduck.Refactorings; using Rubberduck.Refactorings.CreateUDTMember; +using Rubberduck.SmartIndenter; +using RubberduckTests.Settings; using System.Collections.Generic; using System.Linq; @@ -171,7 +173,18 @@ private static Declaration GetUniquelyNamedDeclaration(IDeclarationFinderProvide protected override IRefactoringAction TestBaseRefactoring(RubberduckParserState state, IRewritingManager rewritingManager) { - return new CreateUDTMemberRefactoringAction(state, rewritingManager, new CodeBuilder()); + return new CreateUDTMemberRefactoringAction(state, rewritingManager, CreateCodeBuilder()); + } + + private static ICodeBuilder CreateCodeBuilder() + => new CodeBuilder(new Indenter(null, CreateIndenterSettings)); + + private static IndenterSettings CreateIndenterSettings() + { + var s = IndenterSettingsTests.GetMockIndenterSettings(); + s.VerticallySpaceProcedures = true; + s.LinesBetweenProcedures = 1; + return s; } } } diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs index f2a90828fa..c681265b60 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs @@ -10,6 +10,9 @@ using Rubberduck.Refactorings.EncapsulateFieldUseBackingField; using Rubberduck.Refactorings.EncapsulateFieldInsertNewCode; using System; +using Rubberduck.SmartIndenter; +using Rubberduck.VBEditor.SafeComWrappers.Abstract; +using RubberduckTests.Settings; namespace RubberduckTests.Refactoring.EncapsulateField { @@ -83,8 +86,8 @@ ResolveImpl() case nameof(CreateUDTMemberRefactoringAction): return new CreateUDTMemberRefactoringAction( _declarationFinderProvider, - _rewritingManager, - new CodeBuilder()) as T; + _rewritingManager, + ResolveImpl()) as T; case nameof(EncapsulateFieldPreviewProvider): return new EncapsulateFieldPreviewProvider( @@ -139,9 +142,21 @@ ResolveImpl() return new NewContentAggregatorFactory() as T; case nameof(IEncapsulateFieldCodeBuilderFactory): - return new EncapsulateFieldCodeBuilderFactory(new CodeBuilder()) as T; + return new EncapsulateFieldCodeBuilderFactory(ResolveImpl()) as T; + case nameof(ICodeBuilder): + return new CodeBuilder(ResolveImpl()) as T; + case nameof(IIndenter): + return new Indenter(null, CreateIndenterSettings) as T; } throw new ArgumentException($"Unable to resolve {typeof(T).Name}") ; } + + private static IndenterSettings CreateIndenterSettings() + { + var s = IndenterSettingsTests.GetMockIndenterSettings(); + s.VerticallySpaceProcedures = true; + s.LinesBetweenProcedures = 1; + return s; + } } } diff --git a/RubberduckTests/Refactoring/ExtractInterface/ExtractInterfaceRefactoringActionTests.cs b/RubberduckTests/Refactoring/ExtractInterface/ExtractInterfaceRefactoringActionTests.cs index 349300abc7..fec9afe208 100644 --- a/RubberduckTests/Refactoring/ExtractInterface/ExtractInterfaceRefactoringActionTests.cs +++ b/RubberduckTests/Refactoring/ExtractInterface/ExtractInterfaceRefactoringActionTests.cs @@ -7,10 +7,12 @@ using Rubberduck.Refactorings; using Rubberduck.Refactorings.AddInterfaceImplementations; using Rubberduck.Refactorings.ExtractInterface; +using Rubberduck.SmartIndenter; using Rubberduck.VBEditor.ComManagement; using Rubberduck.VBEditor.SafeComWrappers; using Rubberduck.VBEditor.SourceCodeHandling; using Rubberduck.VBEditor.Utility; +using RubberduckTests.Settings; namespace RubberduckTests.Refactoring { @@ -755,13 +757,13 @@ private static ExtractInterfaceModel TestModel(IDeclarationFinderProvider state, var targetClass = finder.UserDeclarations(DeclarationType.ClassModule) .OfType() .Single(module => module.IdentifierName == "Class"); - var model = new ExtractInterfaceModel(state, targetClass, new CodeBuilder()); + var model = new ExtractInterfaceModel(state, targetClass, CreateCodeBuilder()); return modelAdjustment(model); } protected override IRefactoringAction TestBaseRefactoring(RubberduckParserState state, IRewritingManager rewritingManager) { - var addInterfaceImplementationsAction = new AddInterfaceImplementationsRefactoringAction(rewritingManager, new CodeBuilder()); + var addInterfaceImplementationsAction = new AddInterfaceImplementationsRefactoringAction(rewritingManager, CreateCodeBuilder()); var addComponentService = TestAddComponentService(state?.ProjectsProvider); return new ExtractInterfaceRefactoringAction(addInterfaceImplementationsAction, state, state, rewritingManager, state?.ProjectsProvider, addComponentService); } @@ -771,5 +773,16 @@ private static IAddComponentService TestAddComponentService(IProjectsProvider pr var sourceCodeHandler = new CodeModuleComponentSourceCodeHandler(); return new AddComponentService(projectsProvider, sourceCodeHandler, sourceCodeHandler); } + + private static ICodeBuilder CreateCodeBuilder() + => new CodeBuilder(new Indenter(null, CreateIndenterSettings)); + + private static IndenterSettings CreateIndenterSettings() + { + var s = IndenterSettingsTests.GetMockIndenterSettings(); + s.VerticallySpaceProcedures = true; + s.LinesBetweenProcedures = 1; + return s; + } } } \ No newline at end of file diff --git a/RubberduckTests/Refactoring/ExtractInterface/ExtractInterfaceTests.cs b/RubberduckTests/Refactoring/ExtractInterface/ExtractInterfaceTests.cs index 47970b8836..25d49e5937 100644 --- a/RubberduckTests/Refactoring/ExtractInterface/ExtractInterfaceTests.cs +++ b/RubberduckTests/Refactoring/ExtractInterface/ExtractInterfaceTests.cs @@ -11,12 +11,14 @@ using Rubberduck.Refactorings.AddInterfaceImplementations; using Rubberduck.Refactorings.Exceptions; using Rubberduck.Refactorings.ExtractInterface; +using Rubberduck.SmartIndenter; using Rubberduck.VBEditor; using Rubberduck.VBEditor.ComManagement; using Rubberduck.VBEditor.SafeComWrappers; using Rubberduck.VBEditor.SourceCodeHandling; using Rubberduck.VBEditor.Utility; using RubberduckTests.Mocks; +using RubberduckTests.Settings; namespace RubberduckTests.Refactoring { @@ -150,7 +152,7 @@ public void ExtractInterfaceRefactoring_IgnoresField() .First(); //Specify Params to remove - var model = new ExtractInterfaceModel(state, target, new CodeBuilder()); + var model = new ExtractInterfaceModel(state, target, CreateCodeBuilder()); Assert.AreEqual(0, model.Members.Count); } } @@ -178,7 +180,7 @@ public void ExtractInterfaceRefactoring_DefaultsToPublicInterfaceForExposedImple .First(); //Specify Params to remove - var model = new ExtractInterfaceModel(state, target, new CodeBuilder()); + var model = new ExtractInterfaceModel(state, target, CreateCodeBuilder()); Assert.AreEqual(ClassInstancing.Public, model.InterfaceInstancing); } } @@ -204,7 +206,7 @@ public void ExtractInterfaceRefactoring_DefaultsToPrivateInterfaceForNonExposedI .First(); //Specify Params to remove - var model = new ExtractInterfaceModel(state, target, new CodeBuilder()); + var model = new ExtractInterfaceModel(state, target, CreateCodeBuilder()); Assert.AreEqual(ClassInstancing.Private, model.InterfaceInstancing); } } @@ -309,10 +311,10 @@ End Sub RefactoringUserInteraction userInteraction, ISelectionService selectionService) { - var addImplementationsBaseRefactoring = new AddInterfaceImplementationsRefactoringAction(rewritingManager, new CodeBuilder()); + var addImplementationsBaseRefactoring = new AddInterfaceImplementationsRefactoringAction(rewritingManager, CreateCodeBuilder()); var addComponentService = TestAddComponentService(state?.ProjectsProvider); var baseRefactoring = new ExtractInterfaceRefactoringAction(addImplementationsBaseRefactoring, state, state, rewritingManager, state?.ProjectsProvider, addComponentService); - return new ExtractInterfaceRefactoring(baseRefactoring, state, userInteraction, selectionService, new CodeBuilder()); + return new ExtractInterfaceRefactoring(baseRefactoring, state, userInteraction, selectionService, CreateCodeBuilder()); } private static IAddComponentService TestAddComponentService(IProjectsProvider projectsProvider) @@ -320,5 +322,16 @@ private static IAddComponentService TestAddComponentService(IProjectsProvider pr var sourceCodeHandler = new CodeModuleComponentSourceCodeHandler(); return new AddComponentService(projectsProvider, sourceCodeHandler, sourceCodeHandler); } + + private static ICodeBuilder CreateCodeBuilder() + => new CodeBuilder(new Indenter(null, CreateIndenterSettings)); + + private static IndenterSettings CreateIndenterSettings() + { + var s = IndenterSettingsTests.GetMockIndenterSettings(); + s.VerticallySpaceProcedures = true; + s.LinesBetweenProcedures = 1; + return s; + } } } \ No newline at end of file diff --git a/RubberduckTests/Refactoring/ImplementInterface/ImplementInterfaceRefactoringActionTests.cs b/RubberduckTests/Refactoring/ImplementInterface/ImplementInterfaceRefactoringActionTests.cs index d66dadc086..ba1d61e6da 100644 --- a/RubberduckTests/Refactoring/ImplementInterface/ImplementInterfaceRefactoringActionTests.cs +++ b/RubberduckTests/Refactoring/ImplementInterface/ImplementInterfaceRefactoringActionTests.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System; +using System.Linq; using NUnit.Framework; using Rubberduck.Parsing.Rewriter; using Rubberduck.Parsing.Symbols; @@ -6,14 +7,18 @@ using Rubberduck.Refactorings; using Rubberduck.Refactorings.AddInterfaceImplementations; using Rubberduck.Refactorings.ImplementInterface; +using Rubberduck.SmartIndenter; using Rubberduck.VBEditor.SafeComWrappers; +using RubberduckTests.Settings; namespace RubberduckTests.Refactoring { [TestFixture] public class ImplementInterfaceRefactoringActionTests : RefactoringActionTestBase { - private string _todoImplementMessage = "Err.Raise 5 'TODO implement interface member"; + private string _errorRaiseStmt = "Err.Raise 5"; + private string _todoStmt = "'TODO implement interface member"; + private string ErrRaiseAndComment => $"{_errorRaiseStmt} {_todoStmt}"; private static string _rhsIdentifier = Rubberduck.Resources.Refactorings.Refactorings.CodeBuilder_DefaultPropertyRHSParam; @@ -35,7 +40,7 @@ public void ImplementInterface_Procedure() $@"Implements Interface1 Private Sub Interface1_Foo() - {_todoImplementMessage} + {ErrRaiseAndComment} End Sub "; ExecuteTest(classCode, interfaceCode, expectedCode); @@ -65,7 +70,7 @@ public void ImplementInterface_Procedure_ClassHasOtherProcedure() End Sub Private Sub Interface1_Foo() - {_todoImplementMessage} + {ErrRaiseAndComment} End Sub "; ExecuteTest(classCode, interfaceCode, expectedCode); @@ -101,15 +106,15 @@ End Property End Property Private Property Get Interface1_a() As String - {_todoImplementMessage} + {ErrRaiseAndComment} End Property Private Property Let Interface1_a(ByVal RHS As String) - {_todoImplementMessage} + {ErrRaiseAndComment} End Property Private Property Get Interface1_b() As String - {_todoImplementMessage} + {ErrRaiseAndComment} End Property "; ExecuteTest(classCode, interfaceCode, expectedCode); @@ -133,7 +138,7 @@ public void ImplementInterface_Procedure_WithParams() $@"Implements Interface1 Private Sub Interface1_Foo(ByVal a As Integer, ByRef b As Variant, c As Variant, d As Long) - {_todoImplementMessage} + {ErrRaiseAndComment} End Sub "; ExecuteTest(classCode, interfaceCode, expectedCode); @@ -157,7 +162,7 @@ public void ImplementInterface_Function() $@"Implements Interface1 Private Function Interface1_Foo() As Integer - {_todoImplementMessage} + {ErrRaiseAndComment} End Function "; ExecuteTest(classCode, interfaceCode, expectedCode); @@ -181,7 +186,7 @@ public void ImplementInterface_Function_WithImplicitType() $@"Implements Interface1 Private Function Interface1_Foo() As Variant - {_todoImplementMessage} + {ErrRaiseAndComment} End Function "; ExecuteTest(classCode, interfaceCode, expectedCode); @@ -205,7 +210,7 @@ public void ImplementInterface_Function_WithParam() $@"Implements Interface1 Private Function Interface1_Foo(a As Variant) As Variant - {_todoImplementMessage} + {ErrRaiseAndComment} End Function "; ExecuteTest(classCode, interfaceCode, expectedCode); @@ -229,7 +234,7 @@ public void ImplementInterface_PropertyGet() $@"Implements Interface1 Private Property Get Interface1_Foo() As Integer - {_todoImplementMessage} + {ErrRaiseAndComment} End Property "; ExecuteTest(classCode, interfaceCode, expectedCode); @@ -253,7 +258,7 @@ public void ImplementInterface_PropertyGet_WithImplicitType() $@"Implements Interface1 Private Property Get Interface1_Foo() As Variant - {_todoImplementMessage} + {ErrRaiseAndComment} End Property "; ExecuteTest(classCode, interfaceCode, expectedCode); @@ -277,7 +282,7 @@ public void ImplementInterface_PropertyGet_WithParam() $@"Implements Interface1 Private Property Get Interface1_Foo(a As Variant) As Variant - {_todoImplementMessage} + {ErrRaiseAndComment} End Property "; ExecuteTest(classCode, interfaceCode, expectedCode); @@ -301,7 +306,7 @@ public void ImplementInterface_PropertyLet() $@"Implements Interface1 Private Property Let Interface1_Foo(ByVal value As Long) - {_todoImplementMessage} + {ErrRaiseAndComment} End Property "; ExecuteTest(classCode, interfaceCode, expectedCode); @@ -325,7 +330,7 @@ public void ImplementInterface_PropertyLet_WithParam() $@"Implements Interface1 Private Property Let Interface1_Foo(ByVal a As Variant) - {_todoImplementMessage} + {ErrRaiseAndComment} End Property "; ExecuteTest(classCode, interfaceCode, expectedCode); @@ -349,7 +354,7 @@ public void ImplementInterface_PropertySet() $@"Implements Interface1 Private Property Set Interface1_Foo(ByVal value As Variant) - {_todoImplementMessage} + {ErrRaiseAndComment} End Property "; ExecuteTest(classCode, interfaceCode, expectedCode); @@ -373,7 +378,7 @@ public void ImplementInterface_PropertySet_WithParam() $@"Implements Interface1 Private Property Set Interface1_Foo(ByVal a As Variant) - {_todoImplementMessage} + {ErrRaiseAndComment} End Property "; ExecuteTest(classCode, interfaceCode, expectedCode); @@ -406,19 +411,19 @@ End Property $@"Implements Interface1 Private Sub Interface1_Foo() - {_todoImplementMessage} + {ErrRaiseAndComment} End Sub Private Function Interface1_Bar(ByVal a As Integer) As Boolean - {_todoImplementMessage} + {ErrRaiseAndComment} End Function Private Property Get Interface1_Buz(ByVal a As Boolean) As Integer - {_todoImplementMessage} + {ErrRaiseAndComment} End Property Private Property Let Interface1_Buz(ByVal a As Boolean, ByVal value As Integer) - {_todoImplementMessage} + {ErrRaiseAndComment} End Property "; ExecuteTest(classCode, interfaceCode, expectedCode); @@ -454,23 +459,23 @@ End Property $@"Implements Interface1 Private Sub Interface1_Foo(ByVal arg1 As Integer, ByVal arg2 As String) - {_todoImplementMessage} + {ErrRaiseAndComment} End Sub Private Function Interface1_Fizz(b As Variant) As Variant - {_todoImplementMessage} + {ErrRaiseAndComment} End Function Private Property Get Interface1_Buzz() As Variant - {_todoImplementMessage} + {ErrRaiseAndComment} End Property Private Property Let Interface1_Buzz(ByVal value As Variant) - {_todoImplementMessage} + {ErrRaiseAndComment} End Property Private Property Set Interface1_Buzz(ByVal value As Variant) - {_todoImplementMessage} + {ErrRaiseAndComment} End Property "; ExecuteTest(classCode, interfaceCode, expectedCode); @@ -491,11 +496,11 @@ public void ImplementInterface_PublicIntrinsic(string interfaceCode) $@"Implements Interface1 Private Property Get Interface1_Foo() As Long - {_todoImplementMessage} + {ErrRaiseAndComment} End Property Private Property Let Interface1_Foo(ByVal {_rhsIdentifier} As Long) - {_todoImplementMessage} + {ErrRaiseAndComment} End Property "; ExecuteTest(classCode, interfaceCode, expectedCode); @@ -516,11 +521,11 @@ public void ImplementInterface_PublicObject(string interfaceCode) $@"Implements Interface1 Private Property Get Interface1_Foo() As Object - {_todoImplementMessage} + {ErrRaiseAndComment} End Property Private Property Set Interface1_Foo(ByVal {_rhsIdentifier} As Object) - {_todoImplementMessage} + {ErrRaiseAndComment} End Property "; ExecuteTest(classCode, interfaceCode, expectedCode); @@ -543,15 +548,15 @@ public void ImplementInterface_PublicVariant(string interfaceCode) $@"Implements Interface1 Private Property Get Interface1_Foo() As Variant - {_todoImplementMessage} + {ErrRaiseAndComment} End Property Private Property Let Interface1_Foo(ByVal {_rhsIdentifier} As Variant) - {_todoImplementMessage} + {ErrRaiseAndComment} End Property Private Property Set Interface1_Foo(ByVal {_rhsIdentifier} As Variant) - {_todoImplementMessage} + {ErrRaiseAndComment} End Property "; ExecuteTest(classCode, interfaceCode, expectedCode); @@ -575,7 +580,7 @@ public void ImplementInterface_ImplicitByRefParameter() $@"Implements Interface1 Private Sub Interface1_Foo(arg As Variant) - {_todoImplementMessage} + {ErrRaiseAndComment} End Sub "; ExecuteTest(classCode, interfaceCode, expectedCode); @@ -599,7 +604,7 @@ public void ImplementInterface_ExplicitByRefParameter() $@"Implements Interface1 Private Sub Interface1_Foo(ByRef arg As Variant) - {_todoImplementMessage} + {ErrRaiseAndComment} End Sub "; ExecuteTest(classCode, interfaceCode, expectedCode); @@ -623,7 +628,7 @@ public void ImplementInterface_ByValParameter() $@"Implements Interface1 Private Sub Interface1_Foo(ByVal arg As Variant) - {_todoImplementMessage} + {ErrRaiseAndComment} End Sub "; ExecuteTest(classCode, interfaceCode, expectedCode); @@ -647,7 +652,7 @@ public void ImplementInterface_OptionalParameter_WoDefault() $@"Implements Interface1 Private Sub Interface1_Foo(Optional arg As Variant) - {_todoImplementMessage} + {ErrRaiseAndComment} End Sub "; ExecuteTest(classCode, interfaceCode, expectedCode); @@ -671,7 +676,7 @@ public void ImplementInterface_OptionalParameter_WithDefault() $@"Implements Interface1 Private Sub Interface1_Foo(Optional arg As Variant = 42) - {_todoImplementMessage} + {ErrRaiseAndComment} End Sub "; ExecuteTest(classCode, interfaceCode, expectedCode); @@ -695,7 +700,7 @@ public void ImplementInterface_ParamArray() $@"Implements Interface1 Private Sub Interface1_Foo(arg1 As Long, ParamArray args() As Variant) - {_todoImplementMessage} + {ErrRaiseAndComment} End Sub "; ExecuteTest(classCode, interfaceCode, expectedCode); @@ -719,7 +724,7 @@ public void ImplementInterface_MakesMissingAsTypesExplicit() $@"Implements Interface1 Private Sub Interface1_Foo(arg1 As Variant) - {_todoImplementMessage} + {ErrRaiseAndComment} End Sub "; ExecuteTest(classCode, interfaceCode, expectedCode); @@ -743,7 +748,7 @@ public void ImplementInterface_Array() $@"Implements Interface1 Private Sub Interface1_Foo(arg1() As Long) - {_todoImplementMessage} + {ErrRaiseAndComment} End Sub "; ExecuteTest(classCode, interfaceCode, expectedCode); @@ -756,7 +761,20 @@ private void ExecuteTest(string classCode, string interfaceCode, string expected ("Class1", classCode,ComponentType.ClassModule), ("Interface1", interfaceCode, ComponentType.ClassModule)); - Assert.AreEqual(expectedClassCode.Trim(), refactoredCode["Class1"].Trim()); + //Remove Indenter formatting effects from refactoring results evaluation + var expected = expectedClassCode.Trim().Split(new string[] { Environment.NewLine }, StringSplitOptions.None); + var refactored = refactoredCode["Class1"].Trim().Split(new string[] { Environment.NewLine }, StringSplitOptions.None); + Assert.AreEqual(expected.Count(), refactored.Count()); + for (var idx = 0; idx < expected.Count(); idx++) + { + if (expected[idx].Contains(_errorRaiseStmt)) + { + StringAssert.Contains(_errorRaiseStmt, refactored[idx]); + StringAssert.Contains(_todoStmt, refactored[idx]); + continue; + } + Assert.AreEqual(expected[idx], refactored[idx]); + } } private static ImplementInterfaceModel TestModel(RubberduckParserState state) @@ -773,8 +791,19 @@ private static ImplementInterfaceModel TestModel(RubberduckParserState state) protected override IRefactoringAction TestBaseRefactoring(RubberduckParserState state, IRewritingManager rewritingManager) { - var addInterfaceImplementationsAction = new AddInterfaceImplementationsRefactoringAction(rewritingManager, new CodeBuilder()); + var addInterfaceImplementationsAction = new AddInterfaceImplementationsRefactoringAction(rewritingManager, CreateCodeBuilder()); return new ImplementInterfaceRefactoringAction(addInterfaceImplementationsAction, rewritingManager); } + + private static ICodeBuilder CreateCodeBuilder() + => new CodeBuilder(new Indenter(null, CreateIndenterSettings)); + + private static IndenterSettings CreateIndenterSettings() + { + var s = IndenterSettingsTests.GetMockIndenterSettings(); + s.VerticallySpaceProcedures = true; + s.LinesBetweenProcedures = 1; + return s; + } } } \ No newline at end of file diff --git a/RubberduckTests/Refactoring/ImplementInterface/ImplementInterfaceTests.cs b/RubberduckTests/Refactoring/ImplementInterface/ImplementInterfaceTests.cs index aefaaa1aac..fdfe628eaa 100644 --- a/RubberduckTests/Refactoring/ImplementInterface/ImplementInterfaceTests.cs +++ b/RubberduckTests/Refactoring/ImplementInterface/ImplementInterfaceTests.cs @@ -8,10 +8,12 @@ using Rubberduck.Refactorings.AddInterfaceImplementations; using Rubberduck.Refactorings.Exceptions.ImplementInterface; using Rubberduck.Refactorings.ImplementInterface; +using Rubberduck.SmartIndenter; using Rubberduck.VBEditor; using Rubberduck.VBEditor.SafeComWrappers; using Rubberduck.VBEditor.Utility; using RubberduckTests.Mocks; +using RubberduckTests.Settings; namespace RubberduckTests.Refactoring { @@ -217,10 +219,21 @@ End Sub protected override IRefactoring TestRefactoring(IRewritingManager rewritingManager, RubberduckParserState state, ISelectionService selectionService) { - var addImplementationsBaseRefactoring = new AddInterfaceImplementationsRefactoringAction(rewritingManager, new CodeBuilder()); + var addImplementationsBaseRefactoring = new AddInterfaceImplementationsRefactoringAction(rewritingManager, CreateCodeBuilder()); var baseRefactoring = new ImplementInterfaceRefactoringAction(addImplementationsBaseRefactoring, rewritingManager); return new ImplementInterfaceRefactoring(baseRefactoring, state, selectionService); } + + private static ICodeBuilder CreateCodeBuilder() + => new CodeBuilder(new Indenter(null, CreateIndenterSettings)); + + private static IndenterSettings CreateIndenterSettings() + { + var s = IndenterSettingsTests.GetMockIndenterSettings(); + s.VerticallySpaceProcedures = true; + s.LinesBetweenProcedures = 1; + return s; + } } } From 51ce8a7e3e8acafb16597be5aab9dd4e03437ff6 Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Wed, 7 Oct 2020 03:46:51 -0700 Subject: [PATCH 56/67] Expose IIndenter on ICodeBuilder Also corrects merge error and minor changes to signatures and comments --- Rubberduck.Refactorings/Common/CodeBuilder.cs | 19 ++++++++++++------- RubberduckTests/CodeBuilderTests.cs | 6 +++--- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/Rubberduck.Refactorings/Common/CodeBuilder.cs b/Rubberduck.Refactorings/Common/CodeBuilder.cs index b3ae5a4395..aff28204e7 100644 --- a/Rubberduck.Refactorings/Common/CodeBuilder.cs +++ b/Rubberduck.Refactorings/Common/CodeBuilder.cs @@ -87,18 +87,20 @@ public interface ICodeBuilder /// No validation or conflict analysis is applied to the identifiers. /// /// DeclarationType with flags: Variable, Constant, UserDefinedTypeMember, or Function - bool TryBuildUDTMemberDeclaration(string identifier, Declaration prototype, out string declaration); + bool TryBuildUDTMemberDeclaration(Declaration prototype, string identifier, out string declaration); + + IIndenter Indenter { get; } } public class CodeBuilder : ICodeBuilder { - private readonly IIndenter _indenter; - public CodeBuilder(IIndenter indenter) { - _indenter = indenter; + Indenter = indenter; } + public IIndenter Indenter { get; } + public string BuildMemberBlockFromPrototype(ModuleBodyElementDeclaration declaration, string content = null, Accessibility accessibility = Accessibility.Public, @@ -135,6 +137,9 @@ public bool TryBuildPropertySetCodeBlock(Declaration prototype, string propertyI var propertyValueParam = parameterIdentifier ?? Resources.Refactorings.Refactorings.CodeBuilder_DefaultPropertyRHSParam; + //TODO: Improve generated Array properties + //Add logic to conditionally return Arrays or Variant depending on Office version. + //Ability to return an Array from a Function or Property was added in Office 2000 http://www.cpearson.com/excel/passingandreturningarrays.htm var asType = prototype.IsArray ? $"{Tokens.Variant}" : IsEnumField(prototype) && prototype.AsTypeDeclaration.Accessibility.Equals(Accessibility.Private) @@ -151,7 +156,7 @@ public bool TryBuildPropertySetCodeBlock(Declaration prototype, string propertyI ? string.Join(Environment.NewLine, $"{AccessibilityToken(accessibility)} {TypeToken(letSetGetType)} {propertyIdentifier}() {asTypeClause}", content, EndStatement(letSetGetType)) : string.Join(Environment.NewLine, $"{AccessibilityToken(accessibility)} {TypeToken(letSetGetType)} {propertyIdentifier}({letSetParamExpression})", content, EndStatement(letSetGetType)); - codeBlock = string.Join(Environment.NewLine, _indenter.Indent(codeBlock)); + codeBlock = string.Join(Environment.NewLine, Indenter.Indent(codeBlock)); return true; } @@ -270,12 +275,12 @@ public bool TryBuildUserDefinedTypeDeclaration(string udtIdentifier, IEnumerable blockLines.Add($"{Tokens.End} {Tokens.Type}"); - declaration = string.Join(Environment.NewLine, _indenter.Indent(blockLines)); + declaration = string.Join(Environment.NewLine, Indenter.Indent(blockLines)); return true; } - public bool TryBuildUDTMemberDeclaration(string udtMemberIdentifier, Declaration prototype, out string declaration) + public bool TryBuildUDTMemberDeclaration(Declaration prototype, string udtMemberIdentifier, out string declaration) { declaration = string.Empty; diff --git a/RubberduckTests/CodeBuilderTests.cs b/RubberduckTests/CodeBuilderTests.cs index e98062c61e..e91af28627 100644 --- a/RubberduckTests/CodeBuilderTests.cs +++ b/RubberduckTests/CodeBuilderTests.cs @@ -545,7 +545,7 @@ public void UDT_NullIdentifierInPrototypeList_NoResult() [Category(nameof(CodeBuilder))] public void UDT_NullPrototype_NoResult() { - var result = CreateCodeBuilder().TryBuildUDTMemberDeclaration(_defaultUDTIdentifier, null, out var declaration); + var result = CreateCodeBuilder().TryBuildUDTMemberDeclaration(null, _defaultUDTIdentifier, out var declaration); Assert.IsFalse(result); Assert.IsTrue(string.IsNullOrEmpty(declaration)); } @@ -561,7 +561,7 @@ public void UDT_NullUDTIdentifierBuildUDTMember_NoResult() var target = state.DeclarationFinder.DeclarationsWithType(DeclarationType.Variable) .Single(d => d.IdentifierName == "test"); - var result = CreateCodeBuilder().TryBuildUDTMemberDeclaration(null, target, out var declaration); + var result = CreateCodeBuilder().TryBuildUDTMemberDeclaration(target, null, out var declaration); Assert.IsFalse(result); Assert.IsTrue(string.IsNullOrEmpty(declaration)); @@ -645,7 +645,7 @@ private static string ImprovedArgumentListTest(ModuleBodyElementDeclaration mbed => CreateCodeBuilder().ImprovedArgumentList(mbed); private static string MemberBlockFromPrototypeTest(ModuleBodyElementDeclaration mbed, MemberBlockFromPrototypeTestParams testParams) - => CreateCodeBuilder().BuildMemberBlockFromPrototype(mbed, testParams.Accessibility, testParams.Content, testParams.NewIdentifier); + => CreateCodeBuilder().BuildMemberBlockFromPrototype(mbed, testParams.Content, testParams.Accessibility, testParams.NewIdentifier); private static ICodeBuilder CreateCodeBuilder() => new CodeBuilder(new Indenter(null, CreateIndenterSettings)); From f36df0de679207ab4ada5580aa7f1ad589757343 Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Wed, 7 Oct 2020 16:42:19 -0700 Subject: [PATCH 57/67] Add ModifyUserDefinedTypeRefactoringAction Replaces CreateUDTMemberRefactoringAction. Leverages ICodeBuilder and IIndenter to rewrite the entire UDT declaration after adding (or removing) UDT Members. --- .../CreateUDTMember/CreateUDTMemberModel.cs | 67 ------ .../CreateUDTMemberRefactoringAction.cs | 81 ------- ...apsulateFieldRefactoringActionsProvider.cs | 18 +- ...eldUseBackingUDTMemberRefactoringAction.cs | 17 +- .../ModifyUserDefinedTypeModel.cs | 60 +++++ .../ModifyUserDefinedTypeRefactoringAction.cs | 62 +++++ .../CreateUDTMemberRefactoringActionTests.cs | 190 --------------- .../EncapsulateFieldTestComponentResolver.cs | 11 +- ...fyUserDefinedTypeRefactoringActionTests.cs | 220 ++++++++++++++++++ 9 files changed, 366 insertions(+), 360 deletions(-) delete mode 100644 Rubberduck.Refactorings/CreateUDTMember/CreateUDTMemberModel.cs delete mode 100644 Rubberduck.Refactorings/CreateUDTMember/CreateUDTMemberRefactoringAction.cs create mode 100644 Rubberduck.Refactorings/ModifyUserDefinedType/ModifyUserDefinedTypeModel.cs create mode 100644 Rubberduck.Refactorings/ModifyUserDefinedType/ModifyUserDefinedTypeRefactoringAction.cs delete mode 100644 RubberduckTests/Refactoring/CreateUDTMember/CreateUDTMemberRefactoringActionTests.cs create mode 100644 RubberduckTests/Refactoring/ModifyUserDefinedType/ModifyUserDefinedTypeRefactoringActionTests.cs diff --git a/Rubberduck.Refactorings/CreateUDTMember/CreateUDTMemberModel.cs b/Rubberduck.Refactorings/CreateUDTMember/CreateUDTMemberModel.cs deleted file mode 100644 index 5bdfdae598..0000000000 --- a/Rubberduck.Refactorings/CreateUDTMember/CreateUDTMemberModel.cs +++ /dev/null @@ -1,67 +0,0 @@ -using Rubberduck.Parsing.Symbols; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Rubberduck.Refactorings.CreateUDTMember -{ - public class CreateUDTMemberModel : IRefactoringModel - { - private Dictionary> _targets { get; } = new Dictionary>(); - - public CreateUDTMemberModel() - { } - - public CreateUDTMemberModel(Declaration userDefinedType, IEnumerable<(Declaration prototype, string UserDefinedTypeMemberIdentifier)> conversionModels) - { - if (conversionModels.Any(cm => !IsValidPrototypeDeclarationType(cm.prototype.DeclarationType))) - { - throw new ArgumentException(); - } - - foreach ((Declaration prototype, string UDTMemberIdentifier) in conversionModels) - { - AssignPrototypeToUserDefinedType(userDefinedType, prototype, UDTMemberIdentifier); - } - } - - public IReadOnlyCollection UserDefinedTypeTargets => _targets.Keys; - - public IEnumerable<(Declaration prototype, string userDefinedTypeMemberIdentifier)> this[Declaration udt] - => _targets[udt].Select(pr => (pr.prototype, pr.UDTMemberIdentifier)); - - private void AssignPrototypeToUserDefinedType(Declaration udt, Declaration prototype, string udtMemberIdentifierName = null) - { - if (!udt.DeclarationType.HasFlag(DeclarationType.UserDefinedType)) - { - throw new ArgumentException(); - } - - if (!(_targets.TryGetValue(udt, out var memberPrototypes))) - { - _targets.Add(udt, new List<(Declaration, string)>()); - } - else - { - var hasDuplicateMemberNames = memberPrototypes - .Select(pr => pr.UDTMemberIdentifier?.ToUpperInvariant() ?? pr.prototype.IdentifierName) - .GroupBy(uc => uc).Any(g => g.Count() > 1); - - if (hasDuplicateMemberNames) - { - throw new ArgumentException(); - } - } - - _targets[udt].Add((prototype, udtMemberIdentifierName ?? prototype.IdentifierName)); - } - - private static bool IsValidPrototypeDeclarationType(DeclarationType declarationType) - { - return declarationType.HasFlag(DeclarationType.Variable) - || declarationType.HasFlag(DeclarationType.UserDefinedTypeMember) - || declarationType.HasFlag(DeclarationType.Constant) - || declarationType.HasFlag(DeclarationType.Function); - } - } -} diff --git a/Rubberduck.Refactorings/CreateUDTMember/CreateUDTMemberRefactoringAction.cs b/Rubberduck.Refactorings/CreateUDTMember/CreateUDTMemberRefactoringAction.cs deleted file mode 100644 index 245ac619bf..0000000000 --- a/Rubberduck.Refactorings/CreateUDTMember/CreateUDTMemberRefactoringAction.cs +++ /dev/null @@ -1,81 +0,0 @@ -using Rubberduck.Parsing; -using Rubberduck.Parsing.Grammar; -using Rubberduck.Parsing.Rewriter; -using Rubberduck.Parsing.Symbols; -using Rubberduck.Parsing.VBA; -using System; -using System.Collections.Generic; -using System.Linq; -namespace Rubberduck.Refactorings.CreateUDTMember -{ - /// - /// CreateUDTMemberRefactoringAction adds a UserDefinedTypeMember declaration (based on a - /// prototype declaation) to a UserDefinedType declaration. The indentation of the first - /// existing member is used by the inserted members. The caller is responsible for identifier validation and name collision anaysis. - /// - public class CreateUDTMemberRefactoringAction : CodeOnlyRefactoringActionBase - { - private readonly IDeclarationFinderProvider _declarationFinderProvider; - private readonly ICodeBuilder _codeBuilder; - - public CreateUDTMemberRefactoringAction(IDeclarationFinderProvider declarationFinderProvider,IRewritingManager rewritingManager, ICodeBuilder codeBuilder) - : base(rewritingManager) - { - _declarationFinderProvider = declarationFinderProvider; - _codeBuilder = codeBuilder; - } - - public override void Refactor(CreateUDTMemberModel model, IRewriteSession rewriteSession) - { - if (model.UserDefinedTypeTargets.Any( udt => !(udt.Context is VBAParser.UdtDeclarationContext))) - { - throw new ArgumentException(); - } - - var rewriter = rewriteSession.CheckOutModuleRewriter(model.UserDefinedTypeTargets.First().QualifiedModuleName); - - foreach (var udt in model.UserDefinedTypeTargets) - { - var newMembersBlock = BuildNewMembersBlock(udt, model); - - var insertionIndex = (udt.Context as VBAParser.UdtDeclarationContext) - .END_TYPE().Symbol.TokenIndex - 1; - - rewriter.InsertBefore(insertionIndex, $"{newMembersBlock}"); - } - } - - private string BuildNewMembersBlock(Declaration udt, CreateUDTMemberModel model) - { - var indentation = DetermineIndentationFromLastMember(udt); - - var newMemberStatements = GenerateUserDefinedMemberDeclarations(model[udt], indentation); - - return string.Concat(newMemberStatements); - } - - private string DetermineIndentationFromLastMember(Declaration udt) - { - var lastMember = _declarationFinderProvider.DeclarationFinder - .UserDeclarations(DeclarationType.UserDefinedTypeMember) - .Where(utm => udt == utm.ParentDeclaration) - .Last(); - - lastMember.Context.TryGetPrecedingContext(out var endOfStatementContextPrototype); - return endOfStatementContextPrototype.GetText(); - } - - private IEnumerable GenerateUserDefinedMemberDeclarations(IEnumerable<(Declaration Prototype, string UDTMemberIdentifier)> newMemberPairs, string indentation) - { - var declarations = new List(); - foreach (var (Prototype, UDTMemberIdentifier) in newMemberPairs) - { - if (_codeBuilder.TryBuildUDTMemberDeclaration(UDTMemberIdentifier, Prototype, out var declaration)) - { - declarations.Add($"{indentation}{declaration}"); - } - } - return declarations; - } - } -} diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringActionsProvider.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringActionsProvider.cs index b6a47bb177..4e73441eeb 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringActionsProvider.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoringActionsProvider.cs @@ -1,8 +1,8 @@ -using Rubberduck.Refactorings.CreateUDTMember; -using Rubberduck.Refactorings.ReplaceDeclarationIdentifier; +using Rubberduck.Refactorings.ReplaceDeclarationIdentifier; using Rubberduck.Refactorings.ReplaceReferences; using Rubberduck.Refactorings.ReplacePrivateUDTMemberReferences; using Rubberduck.Refactorings.EncapsulateFieldInsertNewCode; +using Rubberduck.Refactorings.ModifyUserDefinedType; namespace Rubberduck.Refactorings.EncapsulateField { @@ -11,33 +11,33 @@ public interface IEncapsulateFieldRefactoringActionsProvider ICodeOnlyRefactoringAction ReplaceReferences { get; } ICodeOnlyRefactoringAction ReplaceUDTMemberReferences { get; } ICodeOnlyRefactoringAction ReplaceDeclarationIdentifiers { get; } - ICodeOnlyRefactoringAction CreateUDTMember { get; } + ICodeOnlyRefactoringAction ModifyUserDefinedType { get; } ICodeOnlyRefactoringAction EncapsulateFieldInsertNewCode { get; } } /// /// EncapsulateFieldRefactoringActionsProvider reduces the number of EncapsulateField refactoring action - /// constructor parameters providing refactoring actions common to the aggregated EncapsulateFieldRefactoringActions + /// constructor parameters. It provides Refactoring Actions common to the EncapsulateFieldRefactoringActions /// public class EncapsulateFieldRefactoringActionsProvider : IEncapsulateFieldRefactoringActionsProvider { private readonly ReplaceReferencesRefactoringAction _replaceReferences; private readonly ReplaceDeclarationIdentifierRefactoringAction _replaceDeclarationIdentifiers; private readonly ReplacePrivateUDTMemberReferencesRefactoringAction _replaceUDTMemberReferencesRefactoringAction; - private readonly CreateUDTMemberRefactoringAction _createUDTMemberRefactoringAction; + private readonly ModifyUserDefinedTypeRefactoringAction _modifyUDTRefactoringAction; private readonly EncapsulateFieldInsertNewCodeRefactoringAction _encapsulateFieldInsertNewCodeRefactoringAction; public EncapsulateFieldRefactoringActionsProvider( ReplaceReferencesRefactoringAction replaceReferencesRefactoringAction, ReplacePrivateUDTMemberReferencesRefactoringAction replaceUDTMemberReferencesRefactoringAction, ReplaceDeclarationIdentifierRefactoringAction replaceDeclarationIdentifierRefactoringAction, - CreateUDTMemberRefactoringAction createUDTMemberRefactoringActionRefactoringAction, + ModifyUserDefinedTypeRefactoringAction modifyUserDefinedTypeRefactoringAction, EncapsulateFieldInsertNewCodeRefactoringAction encapsulateFieldInsertNewCodeRefactoringAction) { _replaceReferences = replaceReferencesRefactoringAction; _replaceUDTMemberReferencesRefactoringAction = replaceUDTMemberReferencesRefactoringAction; _replaceDeclarationIdentifiers = replaceDeclarationIdentifierRefactoringAction; - _createUDTMemberRefactoringAction = createUDTMemberRefactoringActionRefactoringAction; + _modifyUDTRefactoringAction = modifyUserDefinedTypeRefactoringAction; _encapsulateFieldInsertNewCodeRefactoringAction = encapsulateFieldInsertNewCodeRefactoringAction; } @@ -50,8 +50,8 @@ public ICodeOnlyRefactoringAction ReplaceDecl public ICodeOnlyRefactoringAction ReplaceUDTMemberReferences => _replaceUDTMemberReferencesRefactoringAction; - public ICodeOnlyRefactoringAction CreateUDTMember - => _createUDTMemberRefactoringAction; + public ICodeOnlyRefactoringAction ModifyUserDefinedType + => _modifyUDTRefactoringAction; public ICodeOnlyRefactoringAction EncapsulateFieldInsertNewCode => _encapsulateFieldInsertNewCodeRefactoringAction; diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs index 05797b2d1d..cbe900967f 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs @@ -1,19 +1,19 @@ using Rubberduck.Parsing.Rewriter; using Rubberduck.Parsing.Symbols; using Rubberduck.Refactorings.Common; -using Rubberduck.Refactorings.CreateUDTMember; using Rubberduck.Refactorings.ReplaceReferences; using Rubberduck.Refactorings.ReplacePrivateUDTMemberReferences; using System.Linq; using Rubberduck.Refactorings.EncapsulateField; using Rubberduck.Refactorings.EncapsulateFieldInsertNewCode; using System.Collections.Generic; +using Rubberduck.Refactorings.ModifyUserDefinedType; namespace Rubberduck.Refactorings.EncapsulateFieldUseBackingUDTMember { public class EncapsulateFieldUseBackingUDTMemberRefactoringAction : CodeOnlyRefactoringActionBase { - private readonly ICodeOnlyRefactoringAction _createUDTMemberRefactoringAction; + private readonly ICodeOnlyRefactoringAction _modifyUDTRefactoringAction; private readonly ICodeOnlyRefactoringAction _replacePrivateUDTMemberReferencesRefactoringAction; private readonly ICodeOnlyRefactoringAction _replaceReferencesRefactoringAction; private readonly ICodeOnlyRefactoringAction _encapsulateFieldInsertNewCodeRefactoringAction; @@ -29,7 +29,7 @@ public class EncapsulateFieldUseBackingUDTMemberRefactoringAction : CodeOnlyRefa IEncapsulateFieldCodeBuilderFactory encapsulateFieldCodeBuilderFactory) : base(rewritingManager) { - _createUDTMemberRefactoringAction = refactoringActionsProvider.CreateUDTMember; + _modifyUDTRefactoringAction = refactoringActionsProvider.ModifyUserDefinedType; _replacePrivateUDTMemberReferencesRefactoringAction = refactoringActionsProvider.ReplaceUDTMemberReferences; _replaceReferencesRefactoringAction = refactoringActionsProvider.ReplaceReferences; _encapsulateFieldInsertNewCodeRefactoringAction = refactoringActionsProvider.EncapsulateFieldInsertNewCode; @@ -58,11 +58,14 @@ private void ModifyFields(EncapsulateFieldUseBackingUDTMemberModel encapsulateFi if (encapsulateFieldModel.ObjectStateUDTField.IsExistingDeclaration) { - var conversionPairs = encapsulateFieldModel.SelectedFieldCandidates - .Select(c => (c.Declaration, c.BackingIdentifier)); + var model = new ModifyUserDefinedTypeModel(encapsulateFieldModel.ObjectStateUDTField.AsTypeDeclaration); - var model = new CreateUDTMemberModel(encapsulateFieldModel.ObjectStateUDTField.AsTypeDeclaration, conversionPairs); - _createUDTMemberRefactoringAction.Refactor(model, rewriteSession); + foreach (var candidate in encapsulateFieldModel.SelectedFieldCandidates) + { + model.AddNewMemberPrototype(candidate.Declaration, candidate.BackingIdentifier); + } + + _modifyUDTRefactoringAction.Refactor(model,rewriteSession); } rewriter.RemoveVariables(encapsulateFieldModel.SelectedFieldCandidates.Select(f => f.Declaration) diff --git a/Rubberduck.Refactorings/ModifyUserDefinedType/ModifyUserDefinedTypeModel.cs b/Rubberduck.Refactorings/ModifyUserDefinedType/ModifyUserDefinedTypeModel.cs new file mode 100644 index 0000000000..bd42405a89 --- /dev/null +++ b/Rubberduck.Refactorings/ModifyUserDefinedType/ModifyUserDefinedTypeModel.cs @@ -0,0 +1,60 @@ +using Rubberduck.Parsing.Grammar; +using Rubberduck.Parsing.Symbols; +using System; +using System.Collections.Generic; + +namespace Rubberduck.Refactorings.ModifyUserDefinedType +{ + public class ModifyUserDefinedTypeModel : IRefactoringModel + { + private List<(Declaration, string)> _newMembers; + private List _membersToRemove; + + public ModifyUserDefinedTypeModel(Declaration target) + { + if (!target.DeclarationType.HasFlag(DeclarationType.UserDefinedType)) + { + throw new ArgumentException(); + } + + Target = target; + _newMembers = new List<(Declaration, string)>(); + _membersToRemove = new List(); + InsertionIndex = (Target.Context as VBAParser.UdtDeclarationContext).END_TYPE().Symbol.TokenIndex - 1; + } + + public Declaration Target { get; } + + public int InsertionIndex { get; } + + public void AddNewMemberPrototype(Declaration prototype, string memberIdentifier) + { + if (!IsValidPrototypeDeclarationType(prototype.DeclarationType)) + { + throw new ArgumentException("Invalid prototype DeclarationType"); + } + _newMembers.Add((prototype, memberIdentifier)); + } + + public void RemoveMember(Declaration member) + { + if (!member.DeclarationType.HasFlag(DeclarationType.UserDefinedTypeMember)) + { + throw new ArgumentException(); + } + _membersToRemove.Add(member); + } + + public IEnumerable<(Declaration, string)> MembersToAdd => _newMembers; + + public IEnumerable MembersToRemove => _membersToRemove; + + private static bool IsValidPrototypeDeclarationType(DeclarationType declarationType) + { + return declarationType.HasFlag(DeclarationType.Variable) + || declarationType.HasFlag(DeclarationType.UserDefinedTypeMember) + || declarationType.HasFlag(DeclarationType.Constant) + || declarationType.HasFlag(DeclarationType.Function); + } + } +} diff --git a/Rubberduck.Refactorings/ModifyUserDefinedType/ModifyUserDefinedTypeRefactoringAction.cs b/Rubberduck.Refactorings/ModifyUserDefinedType/ModifyUserDefinedTypeRefactoringAction.cs new file mode 100644 index 0000000000..881d227fc2 --- /dev/null +++ b/Rubberduck.Refactorings/ModifyUserDefinedType/ModifyUserDefinedTypeRefactoringAction.cs @@ -0,0 +1,62 @@ +using Rubberduck.Parsing; +using Rubberduck.Parsing.Grammar; +using Rubberduck.Parsing.Rewriter; +using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.VBA; +using Rubberduck.SmartIndenter; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Rubberduck.Refactorings.ModifyUserDefinedType +{ + public class ModifyUserDefinedTypeRefactoringAction : CodeOnlyRefactoringActionBase + { + private readonly IDeclarationFinderProvider _declarationFinderProvider; + private readonly IRewritingManager _rewritingManager; + private readonly ICodeBuilder _codeBuilder; + + /// + /// Removes or adds UserDefinedTypeMember declarations to an existing UserDefinedType. + /// Adding a UDTMember is based on a Declaration prototype (typically a variable declaration but can a UserDefinedTypeMember, Constant, or Function). + /// + /// + /// The refactoring actions does not modify the prototype declaration or its references. + /// The refactoring actions does not modify references for removed UDTMembers. + /// The refactoring action does not provide any identifier validation or conflictAnalysis + /// + public ModifyUserDefinedTypeRefactoringAction(IDeclarationFinderProvider declarationFinderProvider, IRewritingManager rewritingManager, ICodeBuilder codeBuilder) + :base(rewritingManager) + { + _declarationFinderProvider = declarationFinderProvider; + _rewritingManager = rewritingManager; + _codeBuilder = codeBuilder; + } + + public override void Refactor(ModifyUserDefinedTypeModel model, IRewriteSession rewriteSession) + { + var newMembers = new List(); + foreach ((Declaration Prototype, string Identifier) in model.MembersToAdd) + { + _codeBuilder.TryBuildUDTMemberDeclaration(Prototype, Identifier, out var udtMemberDeclaration); + newMembers.Add(udtMemberDeclaration); + } + + var scratchPad = _rewritingManager.CheckOutCodePaneSession().CheckOutModuleRewriter(model.Target.QualifiedModuleName); + scratchPad.InsertBefore(model.InsertionIndex, $"{Environment.NewLine}{string.Join(Environment.NewLine, newMembers)}"); + + foreach (var member in model.MembersToRemove) + { + scratchPad.Remove(member); + } + + var udtDeclarationContext = model.Target.Context as VBAParser.UdtDeclarationContext; + var newBlock = scratchPad.GetText(udtDeclarationContext.Start.TokenIndex, udtDeclarationContext.Stop.TokenIndex); + var udtLines = newBlock.Split(new string[] { Environment.NewLine }, StringSplitOptions.None) + .Where(ul => !string.IsNullOrEmpty(ul.Trim())); + + var rewriter = rewriteSession.CheckOutModuleRewriter(model.Target.QualifiedModuleName); + rewriter.Replace(udtDeclarationContext, string.Join(Environment.NewLine, _codeBuilder.Indenter.Indent(udtLines))); + } + } +} diff --git a/RubberduckTests/Refactoring/CreateUDTMember/CreateUDTMemberRefactoringActionTests.cs b/RubberduckTests/Refactoring/CreateUDTMember/CreateUDTMemberRefactoringActionTests.cs deleted file mode 100644 index 9ea3806800..0000000000 --- a/RubberduckTests/Refactoring/CreateUDTMember/CreateUDTMemberRefactoringActionTests.cs +++ /dev/null @@ -1,190 +0,0 @@ -using NUnit.Framework; -using Rubberduck.Parsing.Rewriter; -using Rubberduck.Parsing.Symbols; -using Rubberduck.Parsing.VBA; -using Rubberduck.Refactorings; -using Rubberduck.Refactorings.CreateUDTMember; -using Rubberduck.SmartIndenter; -using RubberduckTests.Settings; -using System.Collections.Generic; -using System.Linq; - -namespace RubberduckTests.Refactoring.CreateUDTMember -{ - [TestFixture] - public class CreateUDTMemberRefactoringActionTests : RefactoringActionTestBase - { - [TestCase(4)] - [TestCase(2)] - [Category("Refactorings")] - [Category("Encapsulate Field")] - [Category(nameof(CreateUDTMemberRefactoringAction))] - public void FormatSingleExistingMember(int indentionLevel) - { - var indention = string.Concat(Enumerable.Repeat(" ", indentionLevel)); - - string inputCode = -$@" -Option Explicit - -Private mTest As Long - -Private Type TestType -{indention}FirstValue As String -End Type -"; - var expectedUDT = -$@" -Private Type TestType -{indention}FirstValue As String -{indention}Test As Long -End Type -"; - - var results = ExecuteTest(inputCode, "TestType", ("mTest", "Test")); - StringAssert.Contains(expectedUDT, results); - } - - [TestCase(4)] - [TestCase(2)] - [Category("Refactorings")] - [Category("Encapsulate Field")] - [Category(nameof(CreateUDTMemberRefactoringAction))] - public void FormatMatchesLastMemberIndent(int indentionLevel) - { - var indention = string.Concat(Enumerable.Repeat(" ", indentionLevel)); - var indentionFirstMember = string.Concat(Enumerable.Repeat(" ", 10)); - - string inputCode = -$@" -Option Explicit - -Private mTest As Long - -Private Type TestType -{indentionFirstMember}FirstValue As String -{indention}SecondValue As Double -End Type -"; - var expectedUDT = -$@" -Private Type TestType -{indentionFirstMember}FirstValue As String -{indention}SecondValue As Double -{indention}Test As Long -End Type -"; - - var results = ExecuteTest(inputCode, "TestType", ("mTest", "Test")); - StringAssert.Contains(expectedUDT, results); - } - - [Test] - [Category("Refactorings")] - [Category("Encapsulate Field")] - [Category(nameof(CreateUDTMemberRefactoringAction))] - public void FormatPreservesComments() - { - var indention = string.Concat(Enumerable.Repeat(" ", 2)); - - string inputCode = -$@" -Option Explicit - -Private mTest As Long - -Private Type TestType -{indention}FirstValue As String -{indention}SecondValue As Double 'This is a comment -End Type -"; - var expectedUDT = -$@" -Private Type TestType -{indention}FirstValue As String -{indention}SecondValue As Double 'This is a comment -{indention}Test As Long -End Type -"; - - var results = ExecuteTest(inputCode, "TestType", ("mTest", "Test")); - StringAssert.Contains(expectedUDT, results); - } - - [Test] - [Category("Refactorings")] - [Category("Encapsulate Field")] - [Category(nameof(CreateUDTMemberRefactoringAction))] - public void FormatMultipleInsertions() - { - string inputCode = -$@" -Option Explicit - -Private mTest As Long -Private mTest1 As Long -Private mTest2 As Long - -Private Type TestType - FirstValue As String - SecondValue As Double -End Type -"; - var expectedUDT = -$@" -Private Type TestType - FirstValue As String - SecondValue As Double - Test As Long - Test1 As Long - Test2 As Long -End Type -"; - - var results = ExecuteTest(inputCode, "TestType", ("mTest", "Test"), ("mTest1", "Test1"), ("mTest2", "Test2")); - StringAssert.Contains(expectedUDT, results); - } - - private string ExecuteTest(string inputCode, string udtIdentifier, params (string, string)[] fieldConversions) - { - return RefactoredCode(inputCode, state => TestModel(state, udtIdentifier, fieldConversions)); - } - - private CreateUDTMemberModel TestModel(RubberduckParserState state, string udtIdentifier, params (string fieldID, string udtMemberID)[] fieldConversions) - { - var udtDeclaration = GetUniquelyNamedDeclaration(state, DeclarationType.UserDefinedType, udtIdentifier); - - var conversionPairs = new List<(Declaration, string)>(); - foreach (var (fieldID, udtMemberID) in fieldConversions) - { - var fieldDeclaration = GetUniquelyNamedDeclaration(state, DeclarationType.Variable, fieldID) as VariableDeclaration; - conversionPairs.Add((fieldDeclaration, udtMemberID)); - } - - var model = new CreateUDTMemberModel(udtDeclaration, conversionPairs.ToArray()); - - return model; - } - - private static Declaration GetUniquelyNamedDeclaration(IDeclarationFinderProvider declarationFinderProvider, DeclarationType declarationType, string identifier) - { - return declarationFinderProvider.DeclarationFinder.UserDeclarations(declarationType).Single(d => d.IdentifierName.Equals(identifier)); - } - - protected override IRefactoringAction TestBaseRefactoring(RubberduckParserState state, IRewritingManager rewritingManager) - { - return new CreateUDTMemberRefactoringAction(state, rewritingManager, CreateCodeBuilder()); - } - - private static ICodeBuilder CreateCodeBuilder() - => new CodeBuilder(new Indenter(null, CreateIndenterSettings)); - - private static IndenterSettings CreateIndenterSettings() - { - var s = IndenterSettingsTests.GetMockIndenterSettings(); - s.VerticallySpaceProcedures = true; - s.LinesBetweenProcedures = 1; - return s; - } - } -} diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs index c681265b60..a1bc160983 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs @@ -1,7 +1,6 @@ using Rubberduck.Parsing.Rewriter; using Rubberduck.Parsing.VBA; using Rubberduck.Refactorings; -using Rubberduck.Refactorings.CreateUDTMember; using Rubberduck.Refactorings.EncapsulateField; using Rubberduck.Refactorings.ReplaceReferences; using Rubberduck.Refactorings.ReplacePrivateUDTMemberReferences; @@ -11,8 +10,8 @@ using Rubberduck.Refactorings.EncapsulateFieldInsertNewCode; using System; using Rubberduck.SmartIndenter; -using Rubberduck.VBEditor.SafeComWrappers.Abstract; using RubberduckTests.Settings; +using Rubberduck.Refactorings.ModifyUserDefinedType; namespace RubberduckTests.Refactoring.EncapsulateField { @@ -61,7 +60,7 @@ public EncapsulateFieldTestComponentResolver(IDeclarationFinderProvider declarat ResolveImpl(), ResolveImpl(), ResolveImpl(), - ResolveImpl(), + ResolveImpl(), ResolveImpl() ) as T; @@ -83,9 +82,9 @@ ResolveImpl() case nameof(IReplacePrivateUDTMemberReferencesModelFactory): return new ReplacePrivateUDTMemberReferencesModelFactory(_declarationFinderProvider) as T; - case nameof(CreateUDTMemberRefactoringAction): - return new CreateUDTMemberRefactoringAction( - _declarationFinderProvider, + case nameof(ModifyUserDefinedTypeRefactoringAction): + return new ModifyUserDefinedTypeRefactoringAction( + _declarationFinderProvider, _rewritingManager, ResolveImpl()) as T; diff --git a/RubberduckTests/Refactoring/ModifyUserDefinedType/ModifyUserDefinedTypeRefactoringActionTests.cs b/RubberduckTests/Refactoring/ModifyUserDefinedType/ModifyUserDefinedTypeRefactoringActionTests.cs new file mode 100644 index 0000000000..c816711891 --- /dev/null +++ b/RubberduckTests/Refactoring/ModifyUserDefinedType/ModifyUserDefinedTypeRefactoringActionTests.cs @@ -0,0 +1,220 @@ +using NUnit.Framework; +using Rubberduck.Parsing.Rewriter; +using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.VBA; +using Rubberduck.Refactorings; +using Rubberduck.Refactorings.ModifyUserDefinedType; +using Rubberduck.SmartIndenter; +using RubberduckTests.Settings; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace RubberduckTests.Refactoring.ModifyUserDefinedType +{ + [TestFixture] + public class ModifyUserDefinedTypeRefactoringActionTests : RefactoringActionTestBase + { + [TestCase("Private mTest As Long", DeclarationType.Variable)] + [TestCase("Private Const mTest As Long = 10", DeclarationType.Constant)] + [TestCase("Private Function mTest() As Long\r\nEnd Function", DeclarationType.Function)] + [TestCase("Private Property Get mTest() As Long\r\nEnd Property", DeclarationType.PropertyGet)] + [TestCase("Private Type ProtoType\r\n mTest As Long\r\nEnd Type", DeclarationType.UserDefinedTypeMember)] + [Category("Refactorings")] + [Category("Encapsulate Field")] + [Category(nameof(ModifyUserDefinedTypeRefactoringAction))] + public void AddSingleMember(string prototypeDeclaration, DeclarationType declarationType) + { + string inputCode = +$@" +Option Explicit + +Private Type TestType + FirstValue As String +End Type + +{prototypeDeclaration} +"; + var expectedUDT = +$@" +Private Type TestType + FirstValue As String + Test As Long +End Type +"; + + ExecuteTest(inputCode, "TestType", expectedUDT, ("mTest", "Test", declarationType)); + } + + [Test] + [Category("Refactorings")] + [Category("Encapsulate Field")] + [Category(nameof(ModifyUserDefinedTypeRefactoringAction))] + public void MultipleNewMembers() + { + string inputCode = +$@" +Option Explicit + +Private mTest As Long +Private mTest1 As Long +Private mTest2 As Long + +Private Type TestType + FirstValue As String + SecondValue As Double +End Type +"; + var expectedUDT = +$@" +Private Type TestType + FirstValue As String + SecondValue As Double + Test As Long + Test1 As Long + Test2 As Long +End Type +"; + + ExecuteTest(inputCode, "TestType", expectedUDT, ("mTest", "Test", DeclarationType.Variable), ("mTest1", "Test1", DeclarationType.Variable), ("mTest2", "Test2", DeclarationType.Variable)); + } + + [Test] + [Category("Refactorings")] + [Category("Encapsulate Field")] + [Category(nameof(ModifyUserDefinedTypeRefactoringAction))] + public void MultipleNewMembersRemoveMultiple() + { + string inputCode = +$@" +Option Explicit + +Private mTest As Long +Private mTest1 As Long +Private mTest2 As Long + +Private Type TestType + FirstValue As String + SecondValue As Double + ThirdValue As Byte +End Type +"; + var expectedUDT = +$@" +Private Type TestType + SecondValue As Double + Test As Long + Test1 As Long + Test2 As Long +End Type +"; + var adds = new List<(string, string, DeclarationType)>() + { + ("mTest", "Test", DeclarationType.Variable), + ("mTest1", "Test1", DeclarationType.Variable), + ("mTest2", "Test2", DeclarationType.Variable) + }; + + var removes = new List() + { + "FirstValue", + "ThirdValue" + }; + + ExecuteTest(inputCode, "TestType", expectedUDT, adds, removes); + } + + [Test] + [Category("Refactorings")] + [Category("Encapsulate Field")] + [Category(nameof(ModifyUserDefinedTypeRefactoringAction))] + public void RemoveOnlyMultiple() + { + string inputCode = +$@" +Option Explicit + +Private Type TestType + FirstValue As String + SecondValue As Double + ThirdValue As Byte + FourthValue As Integer +End Type +"; + var expectedUDT = +$@" +Private Type TestType + FirstValue As String + SecondValue As Double +End Type +"; + var removes = new List() + { + "FourthValue", + "ThirdValue" + }; + + ExecuteTest(inputCode, "TestType", expectedUDT, Enumerable.Empty<(string,string,DeclarationType)>(), removes); + } + + private void ExecuteTest(string inputCode, string udtIdentifier, string expectedUDT, params (string, string, DeclarationType)[] fieldConversions) + { + ExecuteTest(inputCode, udtIdentifier, expectedUDT, (IEnumerable<(string, string, DeclarationType)>)fieldConversions); + } + + private void ExecuteTest(string inputCode, string udtIdentifier, string expectedUDT, IEnumerable<(string, string, DeclarationType)> fieldConversions, IEnumerable udtMemberIdentifiers = null) + { + var results = RefactoredCode(inputCode, state => TestModel(state, udtIdentifier, fieldConversions, udtMemberIdentifiers ?? Enumerable.Empty())); + + var refactoredCode = results.Trim().Split(new string[] { Environment.NewLine }, StringSplitOptions.None); + + var refactored = refactoredCode.SkipWhile(r => !r.Contains("Private Type")); + + var expected = expectedUDT.Trim().Split(new string[] { Environment.NewLine }, StringSplitOptions.None); + for (var idx = 0; idx < expected.Count(); idx++) + { + //Remove Indenter formatting effects from refactoring results evaluation + Assert.AreEqual(expected[idx].Trim(), refactored.ElementAt(idx).Trim()); + } + } + + private ModifyUserDefinedTypeModel TestModel(RubberduckParserState state, string udtIdentifier, IEnumerable<(string fieldID, string udtMemberID, DeclarationType declarationType)> fieldConversions, IEnumerable removals) + { + var udtDeclaration = GetUniquelyNamedDeclaration(state, DeclarationType.UserDefinedType, udtIdentifier); + var model = new ModifyUserDefinedTypeModel(udtDeclaration); + + foreach (var (fieldID, udtMemberID, declarationType) in fieldConversions) + { + var fieldDeclaration = GetUniquelyNamedDeclaration(state, declarationType, fieldID); + model.AddNewMemberPrototype(fieldDeclaration, udtMemberID); + } + + foreach (var udtMemberIdentifier in removals) + { + var udtMember = GetUniquelyNamedDeclaration(state, DeclarationType.UserDefinedTypeMember, udtMemberIdentifier); + model.RemoveMember(udtMember); + } + + return model; + } + + private static Declaration GetUniquelyNamedDeclaration(IDeclarationFinderProvider declarationFinderProvider, DeclarationType declarationType, string identifier) + => declarationFinderProvider.DeclarationFinder.UserDeclarations(declarationType).Single(d => d.IdentifierName.Equals(identifier)); + + protected override IRefactoringAction TestBaseRefactoring(RubberduckParserState state, IRewritingManager rewritingManager) + { + return new ModifyUserDefinedTypeRefactoringAction(state, rewritingManager, CreateCodeBuilder()); + } + + private static ICodeBuilder CreateCodeBuilder() + => new CodeBuilder(new Indenter(null, CreateIndenterSettings)); + + private static IndenterSettings CreateIndenterSettings() + { + var s = IndenterSettingsTests.GetMockIndenterSettings(); + s.VerticallySpaceProcedures = true; + s.LinesBetweenProcedures = 1; + return s; + } + } +} From 25b963c3c9d9d27aefbf3248f9ac8582dd2ca8fa Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Fri, 9 Oct 2020 14:25:12 -0700 Subject: [PATCH 58/67] Rework EncapsulateFieldRefactoring factories Let CW supply factories where possible. Resulted in removal of several explicitly defined factory class files...which led to a lot of renames, refactoring, and some file structure changes. --- .../Root/RubberduckIoCInstaller.cs | 10 -- .../EncapsulateFieldConflictFinder.cs | 7 ++ .../EncapsulateFieldConflictFinderFactory.cs | 26 ------ .../EncapsulateFieldCandidateSetsProvider.cs | 65 +++++++++++++ .../EncapsulateFieldCollectionsProvider.cs | 92 ------------------- ...apsulateFieldCollectionsProviderFactory.cs | 37 -------- .../EncapsulateFieldCodeBuilderFactory.cs | 22 ----- ...lateFieldInsertNewCodeRefactoringAction.cs | 4 +- .../NewContentAggregator.cs | 5 + .../NewContentAggregatorFactory.cs | 16 ---- .../EncapsulateField/EncapsulateFieldModel.cs | 4 +- .../EncapsulateFieldModelFactory.cs | 26 ++++-- .../EncapsulateFieldRefactoring.cs | 10 +- ...psulateFieldUseBackingFieldModelFactory.cs | 49 ++++++---- ...ateFieldUseBackingUDTMemberModelFactory.cs | 81 ++++++++-------- ...eldUseBackingUDTMemberRefactoringAction.cs | 5 +- .../FieldCandidates/ArrayCandidate.cs | 15 --- .../EncapsulateFieldCandidateFactory.cs | 49 ++++++---- .../ObjectStateFieldCandidate.cs} | 8 +- .../ObjectStateUserDefinedTypeFactory.cs | 31 ------- .../EncapsulateFieldTestComponentResolver.cs | 81 +++++++++++----- ...ldUseBackingFieldRefactoringActionTests.cs | 4 +- ...ncapsulateFieldUseBackingUDTMemberTests.cs | 29 +++--- .../EncapsulateFieldValidatorTests.cs | 11 ++- .../PropertyAttributeSetsGeneratorTests.cs | 14 ++- 25 files changed, 306 insertions(+), 395 deletions(-) delete mode 100644 Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldConflictFinderFactory.cs create mode 100644 Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldCandidateSetsProvider.cs delete mode 100644 Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldCollectionsProvider.cs delete mode 100644 Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldCollectionsProviderFactory.cs delete mode 100644 Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldCodeBuilderFactory.cs delete mode 100644 Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/NewContentAggregatorFactory.cs delete mode 100644 Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ArrayCandidate.cs rename Rubberduck.Refactorings/EncapsulateField/{ObjectStateUDT/ObjectStateUDT.cs => FieldCandidates/ObjectStateFieldCandidate.cs} (92%) delete mode 100644 Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT/ObjectStateUserDefinedTypeFactory.cs diff --git a/Rubberduck.Main/Root/RubberduckIoCInstaller.cs b/Rubberduck.Main/Root/RubberduckIoCInstaller.cs index 2235cabd8b..46d84e9990 100644 --- a/Rubberduck.Main/Root/RubberduckIoCInstaller.cs +++ b/Rubberduck.Main/Root/RubberduckIoCInstaller.cs @@ -411,18 +411,8 @@ private void RegisterEncapsulateFieldRefactoringFactories(IWindsorContainer cont container.Register(Component.For() .ImplementedBy() .LifestyleSingleton()); - container.Register(Component.For() - .ImplementedBy() - .LifestyleSingleton()); - container.Register(Component.For() - .ImplementedBy() - .LifestyleSingleton()); - container.Register(Component.For() - .ImplementedBy() - .LifestyleSingleton()); } - private void RegisterQuickFixes(IWindsorContainer container, Assembly[] assembliesToRegister) { foreach (var assembly in assembliesToRegister) diff --git a/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldConflictFinder.cs b/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldConflictFinder.cs index 00eedd424f..f73143112d 100644 --- a/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldConflictFinder.cs +++ b/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldConflictFinder.cs @@ -11,6 +11,13 @@ namespace Rubberduck.Refactorings.EncapsulateField { + public interface IEncapsulateFieldConflictFinderFactory + { + IEncapsulateFieldConflictFinder Create(IDeclarationFinderProvider declarationFinderProvider, + IEnumerable candidates, + IEnumerable objectStateUDTs); + } + public interface IEncapsulateFieldConflictFinder { bool IsConflictingIdentifier(IEncapsulateFieldCandidate field, string identifierToCompare, out string errorMessage); diff --git a/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldConflictFinderFactory.cs b/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldConflictFinderFactory.cs deleted file mode 100644 index ab950ebdc7..0000000000 --- a/Rubberduck.Refactorings/EncapsulateField/ConflictDetection/EncapsulateFieldConflictFinderFactory.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Rubberduck.Parsing.VBA; -using Rubberduck.Refactorings.EncapsulateField; -using System.Collections.Generic; -using System.Linq; - -namespace Rubberduck.Refactorings -{ - public interface IEncapsulateFieldConflictFinderFactory - { - IEncapsulateFieldConflictFinder Create(IEncapsulateFieldCollectionsProvider collectionProvider); - } - - public class EncapsulateFieldConflictFinderFactory : IEncapsulateFieldConflictFinderFactory - { - private readonly IDeclarationFinderProvider _declarationFinderProvider; - public EncapsulateFieldConflictFinderFactory(IDeclarationFinderProvider declarationFinderProvider) - { - _declarationFinderProvider = declarationFinderProvider; - } - - public IEncapsulateFieldConflictFinder Create(IEncapsulateFieldCollectionsProvider collectionProvider) - { - return new EncapsulateFieldConflictFinder(_declarationFinderProvider, collectionProvider.EncapsulateFieldCandidates, collectionProvider.ObjectStateUDTCandidates); - } - } -} diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldCandidateSetsProvider.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldCandidateSetsProvider.cs new file mode 100644 index 0000000000..f5a705d79c --- /dev/null +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldCandidateSetsProvider.cs @@ -0,0 +1,65 @@ +using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.VBA; +using Rubberduck.VBEditor; +using System.Collections.Generic; +using System.Linq; + +namespace Rubberduck.Refactorings.EncapsulateField +{ + public interface IEncapsulateFieldCandidateSetsProviderFactory + { + IEncapsulateFieldCandidateSetsProvider Create(IDeclarationFinderProvider declarationFinderProvider, + IEncapsulateFieldCandidateFactory encapsulateFieldCandidateFactory, + QualifiedModuleName qualifiedModuleName); + } + + public interface IEncapsulateFieldCandidateSetsProvider + { + IReadOnlyCollection EncapsulateFieldUseBackingFieldCandidates { get; } + IReadOnlyCollection EncapsulateFieldUseBackingUDTMemberCandidates { get; } + IReadOnlyCollection ObjectStateFieldCandidates { get; } + } + + /// + /// EncapsulateFieldCandidateSetsProvider provides access to a sets of + /// EncapsulateField candidate instances to be shared among EncapsulateFieldRefactoringActions. + /// + public class EncapsulateFieldCandidateSetsProvider : IEncapsulateFieldCandidateSetsProvider + { + public EncapsulateFieldCandidateSetsProvider(IDeclarationFinderProvider declarationFinderProvider, + IEncapsulateFieldCandidateFactory encapsulateFieldCandidateFactory, + QualifiedModuleName qualifiedModuleName) + { + EncapsulateFieldUseBackingFieldCandidates = declarationFinderProvider.DeclarationFinder.Members(qualifiedModuleName, DeclarationType.Variable) + .Where(v => v.ParentDeclaration is ModuleDeclaration + && !v.IsWithEvents) + .Select(f => encapsulateFieldCandidateFactory.CreateFieldCandidate(f)) + .ToList(); + + var objectStateUDTCandidates = EncapsulateFieldUseBackingFieldCandidates + .OfType() + .Where(fc => fc.Declaration.Accessibility == Accessibility.Private + && fc.Declaration.AsTypeDeclaration.Accessibility == Accessibility.Private) + .Select(udtc => encapsulateFieldCandidateFactory.CreateObjectStateField(udtc)) + //If multiple fields of the same UserDefinedType exist, they are all disqualified as candidates to host a module's state. + .ToLookup(objectStateUDTCandidate => objectStateUDTCandidate.Declaration.AsTypeDeclaration.IdentifierName) + .Where(osc => osc.Count() == 1) + .SelectMany(osc => osc) + .ToList(); + + var defaultObjectStateUDT = encapsulateFieldCandidateFactory.CreateDefaultObjectStateField(qualifiedModuleName); + objectStateUDTCandidates.Add(defaultObjectStateUDT); + ObjectStateFieldCandidates = objectStateUDTCandidates; + + EncapsulateFieldUseBackingUDTMemberCandidates = EncapsulateFieldUseBackingFieldCandidates + .Select(fc => encapsulateFieldCandidateFactory.CreateUDTMemberCandidate(fc, defaultObjectStateUDT)) + .ToList(); + } + + public IReadOnlyCollection EncapsulateFieldUseBackingFieldCandidates { get; } + + public IReadOnlyCollection EncapsulateFieldUseBackingUDTMemberCandidates { get; } + + public IReadOnlyCollection ObjectStateFieldCandidates { get; } + } +} diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldCollectionsProvider.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldCollectionsProvider.cs deleted file mode 100644 index 31c7c89459..0000000000 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldCollectionsProvider.cs +++ /dev/null @@ -1,92 +0,0 @@ -using Rubberduck.Parsing.Symbols; -using Rubberduck.Parsing.VBA; -using Rubberduck.VBEditor; -using System.Collections.Generic; -using System.Linq; - -namespace Rubberduck.Refactorings.EncapsulateField -{ - public interface IEncapsulateFieldCollectionsProvider - { - IReadOnlyCollection EncapsulateFieldCandidates { get; } - IReadOnlyCollection EncapsulateAsUserDefinedTypeMemberCandidates { get; } - IReadOnlyCollection ObjectStateUDTCandidates { get; } - } - - /// - /// EncapsulateFieldCollectionsProvider generates collections of IEncapsulateFieldCandidate - /// instances, IEncapsulateFieldAsUDTMemberCandidate instances, and IObjectStateUDT instances. - /// It provides these collection instances to the various objects of the EncapsulateFieldRefactoring. - /// - public class EncapsulateFieldCollectionsProvider : IEncapsulateFieldCollectionsProvider - { - private readonly IDeclarationFinderProvider _declarationFinderProvider; - private readonly IEncapsulateFieldCandidateFactory _encapsulateFieldCandidateFactory; - private readonly IObjectStateUserDefinedTypeFactory _objectStateUserDefinedTypeFactory; - - public EncapsulateFieldCollectionsProvider( - IDeclarationFinderProvider declarationFinderProvider, - IEncapsulateFieldCandidateFactory encapsulateFieldCandidateFactory, - IObjectStateUserDefinedTypeFactory objectStateUserDefinedTypeFactory, - QualifiedModuleName qualifiedModuleName) - { - _declarationFinderProvider = declarationFinderProvider; - _encapsulateFieldCandidateFactory = encapsulateFieldCandidateFactory; - _objectStateUserDefinedTypeFactory = objectStateUserDefinedTypeFactory; - - EncapsulateFieldCandidates = _declarationFinderProvider.DeclarationFinder.Members(qualifiedModuleName, DeclarationType.Variable) - .Where(v => v.ParentDeclaration is ModuleDeclaration - && !v.IsWithEvents) - .Select(f => _encapsulateFieldCandidateFactory.Create(f)) - .ToList(); - - ObjectStateUDTCandidates = LoadObjectStateUDTCandidates(EncapsulateFieldCandidates, _objectStateUserDefinedTypeFactory, qualifiedModuleName); - - EncapsulateAsUserDefinedTypeMemberCandidates = LoadAsUDTMemberCandidates(EncapsulateFieldCandidates, ObjectStateUDTCandidates); - } - - public IReadOnlyCollection EncapsulateFieldCandidates { get; } - - public IReadOnlyCollection EncapsulateAsUserDefinedTypeMemberCandidates { get; } - - public IReadOnlyCollection ObjectStateUDTCandidates { get; } - - private static List LoadObjectStateUDTCandidates(IReadOnlyCollection fieldCandidates, IObjectStateUserDefinedTypeFactory factory, QualifiedModuleName qmn) - { - var objectStateUDTs = new List(); - objectStateUDTs = fieldCandidates - .OfType() - .Where(fc => fc.Declaration.Accessibility == Accessibility.Private - && fc.Declaration.AsTypeDeclaration.Accessibility == Accessibility.Private) - .Select(udtc => factory.Create(udtc)) - .ToList(); - - //If more than one instance of a UserDefinedType is available, it is disqualified as - //a field to host the module's state. - var multipleFieldsOfTheSameUDT = objectStateUDTs.ToLookup(os => os.Declaration.AsTypeDeclaration.IdentifierName); - foreach (var duplicate in multipleFieldsOfTheSameUDT.Where(d => d.Count() > 1)) - { - objectStateUDTs.RemoveAll(os => duplicate.Contains(os)); - } - - var defaultObjectStateUDT = factory.Create(qmn); - objectStateUDTs.Add(defaultObjectStateUDT); - - return objectStateUDTs; - } - - private static List LoadAsUDTMemberCandidates(IReadOnlyCollection fieldCandidates, IReadOnlyCollection objectStateUDTCandidates) - { - var encapsulateAsUDTMembers = new List(); - var defaultObjectStateUDT = objectStateUDTCandidates.FirstOrDefault(os => !os.IsExistingDeclaration); - - foreach (var field in fieldCandidates) - { - var asUDT = new EncapsulateFieldAsUDTMemberCandidate(field, defaultObjectStateUDT); - encapsulateAsUDTMembers.Add(asUDT); - } - - return encapsulateAsUDTMembers; - } - } -} diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldCollectionsProviderFactory.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldCollectionsProviderFactory.cs deleted file mode 100644 index e5fbf8f142..0000000000 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldCollectionsProviderFactory.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Rubberduck.Parsing.VBA; -using Rubberduck.Refactorings.EncapsulateField; -using Rubberduck.VBEditor; - -namespace Rubberduck.Refactorings -{ - public interface IEncapsulateFieldCollectionsProviderFactory - { - IEncapsulateFieldCollectionsProvider Create(QualifiedModuleName qualifiedModuleName); - } - - public class EncapsulateFieldCollectionsProviderFactory : IEncapsulateFieldCollectionsProviderFactory - { - private readonly IDeclarationFinderProvider _declarationFinderProvider; - private readonly IEncapsulateFieldCandidateFactory _encapsulateFieldCandidateFactory; - private readonly IObjectStateUserDefinedTypeFactory _objectStateUserDefinedTypeFactory; - - public EncapsulateFieldCollectionsProviderFactory( - IDeclarationFinderProvider declarationFinderProvider, - IEncapsulateFieldCandidateFactory encapsulateFieldCandidateFactory, - IObjectStateUserDefinedTypeFactory objectStateUserDefinedTypeFactory) - { - _declarationFinderProvider = declarationFinderProvider; - _encapsulateFieldCandidateFactory = encapsulateFieldCandidateFactory; - _objectStateUserDefinedTypeFactory = objectStateUserDefinedTypeFactory; - } - - public IEncapsulateFieldCollectionsProvider Create(QualifiedModuleName qualifiedModuleName) - { - return new EncapsulateFieldCollectionsProvider( - _declarationFinderProvider, - _encapsulateFieldCandidateFactory, - _objectStateUserDefinedTypeFactory, - qualifiedModuleName); - } - } -} diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldCodeBuilderFactory.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldCodeBuilderFactory.cs deleted file mode 100644 index e903e81127..0000000000 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldCodeBuilderFactory.cs +++ /dev/null @@ -1,22 +0,0 @@ - -namespace Rubberduck.Refactorings.EncapsulateField -{ - public interface IEncapsulateFieldCodeBuilderFactory - { - IEncapsulateFieldCodeBuilder Create(); - } - - public class EncapsulateFieldCodeBuilderFactory : IEncapsulateFieldCodeBuilderFactory - { - private readonly ICodeBuilder _codeBuilder; - public EncapsulateFieldCodeBuilderFactory(ICodeBuilder codeBuilder) - { - _codeBuilder = codeBuilder; - } - - public IEncapsulateFieldCodeBuilder Create() - { - return new EncapsulateFieldCodeBuilder(_codeBuilder); - } - } -} diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeRefactoringAction.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeRefactoringAction.cs index a157dd6303..c40b5f3aa0 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeRefactoringAction.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeRefactoringAction.cs @@ -25,12 +25,12 @@ public class EncapsulateFieldInsertNewCodeRefactoringAction : CodeOnlyRefactorin IDeclarationFinderProvider declarationFinderProvider, IRewritingManager rewritingManager, IPropertyAttributeSetsGenerator propertyAttributeSetsGenerator, - IEncapsulateFieldCodeBuilderFactory encapsulateFieldCodeBuilderFactory) + IEncapsulateFieldCodeBuilder encapsulateFieldCodeBuilder) : base(rewritingManager) { _declarationFinderProvider = declarationFinderProvider; _propertyAttributeSetsGenerator = propertyAttributeSetsGenerator; - _encapsulateFieldCodeBuilder = encapsulateFieldCodeBuilderFactory.Create(); + _encapsulateFieldCodeBuilder = encapsulateFieldCodeBuilder; } public override void Refactor(EncapsulateFieldInsertNewCodeModel model, IRewriteSession rewriteSession) diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/NewContentAggregator.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/NewContentAggregator.cs index 54d15de064..fc649081ca 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/NewContentAggregator.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/NewContentAggregator.cs @@ -11,6 +11,11 @@ public enum NewContentType CodeSectionBlock, } + public interface INewContentAggregatorFactory + { + INewContentAggregator Create(); + } + public interface INewContentAggregator { /// diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/NewContentAggregatorFactory.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/NewContentAggregatorFactory.cs deleted file mode 100644 index 05f07e972a..0000000000 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/NewContentAggregatorFactory.cs +++ /dev/null @@ -1,16 +0,0 @@ - -namespace Rubberduck.Refactorings.EncapsulateField -{ - public interface INewContentAggregatorFactory - { - INewContentAggregator Create(); - } - - public class NewContentAggregatorFactory : INewContentAggregatorFactory - { - public INewContentAggregator Create() - { - return new NewContentAggregator(); - } - } -} diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs index 283ed2164d..181a743582 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModel.cs @@ -35,7 +35,7 @@ public class EncapsulateFieldModel : IRefactoringModel public Action StrategyChangedAction { set; get; } = (m) => { }; - public Action ObjectStateUDTChangedAction { set; get; } = (m) => { }; + public Action ObjectStateFieldChangedAction { set; get; } = (m) => { }; public IReadOnlyCollection ObjectStateUDTCandidates { private set; get; } @@ -48,7 +48,7 @@ public IObjectStateUDT ObjectStateUDTField if (EncapsulateFieldUseBackingUDTMemberModel.ObjectStateUDTField != value) { EncapsulateFieldUseBackingUDTMemberModel.ObjectStateUDTField = value; - ObjectStateUDTChangedAction(this); + ObjectStateFieldChangedAction(this); } } get => EncapsulateFieldStrategy == EncapsulateFieldStrategy.ConvertFieldsToUDTMembers diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModelFactory.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModelFactory.cs index 655a78be8c..5e7a97d221 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModelFactory.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldModelFactory.cs @@ -1,4 +1,5 @@ using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.VBA; using Rubberduck.Refactorings.EncapsulateField; using System; using System.Collections.Generic; @@ -15,20 +16,25 @@ public interface IEncapsulateFieldModelFactory public class EncapsulateFieldModelFactory : IEncapsulateFieldModelFactory { + private readonly IDeclarationFinderProvider _declarationFinderProvider; + private readonly IEncapsulateFieldCandidateFactory _candidatesFactory; private readonly IEncapsulateFieldUseBackingUDTMemberModelFactory _useBackingUDTMemberModelFactory; private readonly IEncapsulateFieldUseBackingFieldModelFactory _useBackingFieldModelFactory; - private readonly IEncapsulateFieldCollectionsProviderFactory _encapsulateFieldCollectionsProviderFactory; + private readonly IEncapsulateFieldCandidateSetsProviderFactory _candidateSetsFactory; private readonly IEncapsulateFieldConflictFinderFactory _encapsulateFieldConflictFinderFactory; - public EncapsulateFieldModelFactory( + public EncapsulateFieldModelFactory(IDeclarationFinderProvider declarationFinderProvider, + IEncapsulateFieldCandidateFactory candidatesFactory, IEncapsulateFieldUseBackingUDTMemberModelFactory encapsulateFieldUseBackingUDTMemberModelFactory, IEncapsulateFieldUseBackingFieldModelFactory encapsulateFieldUseBackingFieldModelFactory, - IEncapsulateFieldCollectionsProviderFactory encapsulateFieldCollectionsProviderFactory, + IEncapsulateFieldCandidateSetsProviderFactory candidateSetsProviderFactory, IEncapsulateFieldConflictFinderFactory encapsulateFieldConflictFinderFactory) { + _declarationFinderProvider = declarationFinderProvider; + _candidatesFactory = candidatesFactory; _useBackingUDTMemberModelFactory = encapsulateFieldUseBackingUDTMemberModelFactory as IEncapsulateFieldUseBackingUDTMemberModelFactory; _useBackingFieldModelFactory = encapsulateFieldUseBackingFieldModelFactory; - _encapsulateFieldCollectionsProviderFactory = encapsulateFieldCollectionsProviderFactory; + _candidateSetsFactory = candidateSetsProviderFactory; _encapsulateFieldConflictFinderFactory = encapsulateFieldConflictFinderFactory; } @@ -44,17 +50,19 @@ public EncapsulateFieldModel Create(Declaration target) new FieldEncapsulationModel(targetField) }; - var collectionsProvider = _encapsulateFieldCollectionsProviderFactory.Create(targetField.QualifiedModuleName); + var contextCollections = _candidateSetsFactory.Create(_declarationFinderProvider, _candidatesFactory, target.QualifiedModuleName); - var useBackingFieldModel = _useBackingFieldModelFactory.Create(collectionsProvider, fieldEncapsulationModels); - - var useBackingUDTMemberModel = _useBackingUDTMemberModelFactory.Create(collectionsProvider, fieldEncapsulationModels); + var useBackingFieldModel = _useBackingFieldModelFactory.Create(contextCollections, fieldEncapsulationModels); + var useBackingUDTMemberModel = _useBackingUDTMemberModelFactory.Create(contextCollections, fieldEncapsulationModels); var initialStrategy = useBackingUDTMemberModel.ObjectStateUDTField.IsExistingDeclaration ? EncapsulateFieldStrategy.ConvertFieldsToUDTMembers : EncapsulateFieldStrategy.UseBackingFields; - var conflictFinder = _encapsulateFieldConflictFinderFactory.Create(collectionsProvider); + var conflictFinder = _encapsulateFieldConflictFinderFactory.Create(_declarationFinderProvider, + contextCollections.EncapsulateFieldUseBackingFieldCandidates, + contextCollections.ObjectStateFieldCandidates); + var model = new EncapsulateFieldModel(useBackingFieldModel, useBackingUDTMemberModel, conflictFinder) { EncapsulateFieldStrategy = initialStrategy, diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoring.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoring.cs index 42f600bdd1..20e3c2143e 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoring.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldRefactoring.cs @@ -64,7 +64,7 @@ protected override EncapsulateFieldModel InitializeModel(Declaration target) model.StrategyChangedAction = OnStrategyChanged; - model.ObjectStateUDTChangedAction = OnObjectStateUDTChanged; + model.ObjectStateFieldChangedAction = OnObjectStateUDTChanged; model.ConflictFinder.AssignNoConflictIdentifiers(model.EncapsulationCandidates); @@ -83,6 +83,14 @@ protected override void RefactorImpl(EncapsulateFieldModel model) private void OnStrategyChanged(EncapsulateFieldModel model) { + if (model.EncapsulateFieldStrategy == EncapsulateFieldStrategy.UseBackingFields) + { + foreach (var objectStateCandidate in model.EncapsulateFieldUseBackingUDTMemberModel.ObjectStateUDTCandidates) + { + objectStateCandidate.IsSelected = !objectStateCandidate.IsExistingDeclaration; + } + } + var candidates = model.EncapsulateFieldStrategy == EncapsulateFieldStrategy.UseBackingFields ? model.EncapsulateFieldUseBackingFieldModel.EncapsulationCandidates : model.EncapsulateFieldUseBackingUDTMemberModel.EncapsulationCandidates; diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldModelFactory.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldModelFactory.cs index 60fbed9c09..9c7a81bbc3 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldModelFactory.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldModelFactory.cs @@ -1,4 +1,5 @@ -using Rubberduck.Refactorings.EncapsulateField; +using Rubberduck.Parsing.VBA; +using Rubberduck.Refactorings.EncapsulateField; using Rubberduck.Refactorings.EncapsulateFieldUseBackingField; using System.Collections.Generic; using System.Linq; @@ -10,56 +11,64 @@ public interface IEncapsulateFieldUseBackingFieldModelFactory /// /// Creates an EncapsulateFieldUseBackingFieldModel used by the EncapsulateFieldUseBackingFieldRefactoringAction. /// - EncapsulateFieldUseBackingFieldModel Create(IEnumerable requests); + EncapsulateFieldUseBackingFieldModel Create(IEnumerable fieldModels); /// /// Creates an EncapsulateFieldUseBackingFieldModel based upon collection of - /// IEncapsulateFieldCandidate instances created by EncapsulateFieldCandidateCollectionFactory. + /// IEncapsulateFieldCandidate instances. /// This function is intended for exclusive use by the EncapsulateFieldModelFactory /// - EncapsulateFieldUseBackingFieldModel Create(IEncapsulateFieldCollectionsProvider collectionsProvider, IEnumerable requests); + EncapsulateFieldUseBackingFieldModel Create(IEncapsulateFieldCandidateSetsProvider contextCollections, IEnumerable fieldModels); } public class EncapsulateFieldUseBackingFieldModelFactory : IEncapsulateFieldUseBackingFieldModelFactory { - private readonly IEncapsulateFieldCollectionsProviderFactory _collectionProviderFactory; + private readonly IDeclarationFinderProvider _declarationFinderProvider; + private readonly IEncapsulateFieldCandidateFactory _candidatesFactory; + private readonly IEncapsulateFieldCandidateSetsProviderFactory _candidateSetsFactory; private readonly IEncapsulateFieldConflictFinderFactory _conflictFinderFactory; - public EncapsulateFieldUseBackingFieldModelFactory( - IEncapsulateFieldCollectionsProviderFactory encapsulateFieldCollectionsProviderFactory, + public EncapsulateFieldUseBackingFieldModelFactory(IDeclarationFinderProvider declarationFinderProvider, + IEncapsulateFieldCandidateFactory candidatesFactory, + IEncapsulateFieldCandidateSetsProviderFactory candidateSetsFactory, IEncapsulateFieldConflictFinderFactory encapsulateFieldConflictFinderFactory) { + _declarationFinderProvider = declarationFinderProvider; + _candidatesFactory = candidatesFactory; + _candidateSetsFactory = candidateSetsFactory; _conflictFinderFactory = encapsulateFieldConflictFinderFactory; - _collectionProviderFactory = encapsulateFieldCollectionsProviderFactory; } - public EncapsulateFieldUseBackingFieldModel Create(IEnumerable requests) + public EncapsulateFieldUseBackingFieldModel Create(IEnumerable fieldModels) { - if (!requests.Any()) + if (!fieldModels.Any()) { return new EncapsulateFieldUseBackingFieldModel(Enumerable.Empty()); } - var collectionsProvider = _collectionProviderFactory.Create(requests.First().Declaration.QualifiedModuleName); - return Create(collectionsProvider, requests); + var contextCollections = _candidateSetsFactory.Create(_declarationFinderProvider, _candidatesFactory, fieldModels.First().Declaration.QualifiedModuleName); + + return Create(contextCollections, fieldModels); } - public EncapsulateFieldUseBackingFieldModel Create(IEncapsulateFieldCollectionsProvider collectionsProvider, IEnumerable requests) + public EncapsulateFieldUseBackingFieldModel Create(IEncapsulateFieldCandidateSetsProvider contextCollections, IEnumerable fieldModels) { - var fieldCandidates = collectionsProvider.EncapsulateFieldCandidates.ToList(); + var fieldCandidates = contextCollections.EncapsulateFieldUseBackingFieldCandidates.ToList(); - foreach (var request in requests) + foreach (var fieldModel in fieldModels) { - var candidate = fieldCandidates.Single(c => c.Declaration.Equals(request.Declaration)); + var candidate = fieldCandidates.Single(c => c.Declaration.Equals(fieldModel.Declaration)); candidate.EncapsulateFlag = true; - candidate.IsReadOnly = request.IsReadOnly; - if (request.PropertyIdentifier != null) + candidate.IsReadOnly = fieldModel.IsReadOnly; + if (fieldModel.PropertyIdentifier != null) { - candidate.PropertyIdentifier = request.PropertyIdentifier; + candidate.PropertyIdentifier = fieldModel.PropertyIdentifier; } } - var conflictsFinder = _conflictFinderFactory.Create(collectionsProvider); + var conflictsFinder = _conflictFinderFactory.Create(_declarationFinderProvider, + contextCollections.EncapsulateFieldUseBackingFieldCandidates, + contextCollections.ObjectStateFieldCandidates); fieldCandidates.ForEach(c => c.ConflictFinder = conflictsFinder); diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModelFactory.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModelFactory.cs index ab5794d19b..f9456aa6b5 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModelFactory.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberModelFactory.cs @@ -1,4 +1,5 @@ using Rubberduck.Parsing.Symbols; +using Rubberduck.Parsing.VBA; using Rubberduck.Refactorings.EncapsulateField; using Rubberduck.Refactorings.EncapsulateFieldUseBackingUDTMember; using System; @@ -12,99 +13,107 @@ public interface IEncapsulateFieldUseBackingUDTMemberModelFactory /// /// Creates an EncapsulateFieldUseBackingUDTMemberModel used by the EncapsulateFieldUseBackingUDTMemberRefactoringAction. /// - /// Optional: UserDefinedType Field to host the Encapsulated Field(s) - EncapsulateFieldUseBackingUDTMemberModel Create(IEnumerable requests, Declaration userDefinedTypeTarget = null); + /// Optional: UserDefinedType Field to host the Encapsulated Field(s) + EncapsulateFieldUseBackingUDTMemberModel Create(IEnumerable fieldModels, Declaration objectStateField = null); /// /// Creates an EncapsulateFieldUseBackingUDTMemberModel based upon collection of - /// IEncapsulateFieldCandidate instances created by EncapsulateFieldCandidateCollectionFactory. + /// IEncapsulateFieldCandidate instances. /// This function is intended for exclusive use by the EncapsulateFieldModelFactory /// - EncapsulateFieldUseBackingUDTMemberModel Create(IEncapsulateFieldCollectionsProvider collectionsProvider, IEnumerable requests, Declaration userDefinedTypeTarget = null); + /// Optional: UserDefinedType Field to host the Encapsulated Field(s) + EncapsulateFieldUseBackingUDTMemberModel Create(IEncapsulateFieldCandidateSetsProvider contextCollections, IEnumerable fieldModels, Declaration objectStateField = null); } public class EncapsulateFieldUseBackingUDTMemberModelFactory : IEncapsulateFieldUseBackingUDTMemberModelFactory { - private readonly IEncapsulateFieldCollectionsProviderFactory _encapsulateFieldCollectionsProviderFactory; + private readonly IDeclarationFinderProvider _declarationFinderProvider; + private readonly IEncapsulateFieldCandidateFactory _candidatesFactory; + private readonly IEncapsulateFieldCandidateSetsProviderFactory _candidateSetsFactory; private readonly IEncapsulateFieldConflictFinderFactory _conflictFinderFactory; - public EncapsulateFieldUseBackingUDTMemberModelFactory( - IEncapsulateFieldCollectionsProviderFactory encapsulateFieldCollectionsProviderFactory, + public EncapsulateFieldUseBackingUDTMemberModelFactory(IDeclarationFinderProvider declarationFinderProvider, + IEncapsulateFieldCandidateFactory candidatesFactory, + IEncapsulateFieldCandidateSetsProviderFactory candidateSetsFactory, IEncapsulateFieldConflictFinderFactory encapsulateFieldConflictFinderFactory) { + _declarationFinderProvider = declarationFinderProvider; + _candidatesFactory = candidatesFactory; + _candidateSetsFactory = candidateSetsFactory; _conflictFinderFactory = encapsulateFieldConflictFinderFactory; - _encapsulateFieldCollectionsProviderFactory = encapsulateFieldCollectionsProviderFactory; } - public EncapsulateFieldUseBackingUDTMemberModel Create(IEnumerable requests, Declaration clientTarget) + public EncapsulateFieldUseBackingUDTMemberModel Create(IEnumerable fieldModels, Declaration objectStateField) { - if (!requests.Any()) + if (!fieldModels.Any()) { throw new ArgumentException(); } - var collectionsProvider = _encapsulateFieldCollectionsProviderFactory.Create(requests.First().Declaration.QualifiedModuleName); + var contextCollections = _candidateSetsFactory.Create(_declarationFinderProvider, _candidatesFactory, fieldModels.First().Declaration.QualifiedModuleName); - return Create(collectionsProvider, requests, clientTarget); + return Create(contextCollections, fieldModels, objectStateField); } - public EncapsulateFieldUseBackingUDTMemberModel Create(IEncapsulateFieldCollectionsProvider collectionsProvider, IEnumerable requests, Declaration clientTarget = null) + public EncapsulateFieldUseBackingUDTMemberModel Create(IEncapsulateFieldCandidateSetsProvider contextCollections, IEnumerable fieldModels, Declaration objectStateField = null) { - var asUDTMemberCandidates = collectionsProvider.EncapsulateAsUserDefinedTypeMemberCandidates.ToList(); + var fieldCandidates = contextCollections.EncapsulateFieldUseBackingUDTMemberCandidates.ToList(); - if (clientTarget != null - && (clientTarget.Accessibility != Accessibility.Private - || !asUDTMemberCandidates.Any(c => c.Declaration == clientTarget && c.WrappedCandidate is IUserDefinedTypeCandidate))) + if (objectStateField != null + && (objectStateField.Accessibility != Accessibility.Private + || !fieldCandidates.Any(c => c.Declaration == objectStateField && c.WrappedCandidate is IUserDefinedTypeCandidate))) { throw new ArgumentException("The object state Field must be a Private UserDefinedType"); } - var objectStateUDTs = collectionsProvider.ObjectStateUDTCandidates; + var objectStateFieldCandidates = contextCollections.ObjectStateFieldCandidates; - var defaultObjectStateUDT = objectStateUDTs.FirstOrDefault(os => !os.IsExistingDeclaration); + var defaultObjectStateUDT = objectStateFieldCandidates.FirstOrDefault(os => !os.IsExistingDeclaration); - var targetStateUDT = DetermineObjectStateUDTTarget(defaultObjectStateUDT, clientTarget, objectStateUDTs); + var targetStateUDT = DetermineObjectStateFieldTarget(defaultObjectStateUDT, objectStateField, objectStateFieldCandidates); - foreach (var request in requests) + foreach (var fieldModel in fieldModels) { - var candidate = asUDTMemberCandidates.Single(c => c.Declaration.Equals(request.Declaration)); + var candidate = fieldCandidates.Single(c => c.Declaration.Equals(fieldModel.Declaration)); candidate.EncapsulateFlag = true; - candidate.IsReadOnly = request.IsReadOnly; - if (request.PropertyIdentifier != null) + candidate.IsReadOnly = fieldModel.IsReadOnly; + if (fieldModel.PropertyIdentifier != null) { - candidate.PropertyIdentifier = request.PropertyIdentifier; + candidate.PropertyIdentifier = fieldModel.PropertyIdentifier; } } - var conflictsFinder = _conflictFinderFactory.Create(collectionsProvider); + var conflictsFinder = _conflictFinderFactory.Create(_declarationFinderProvider, + contextCollections.EncapsulateFieldUseBackingFieldCandidates, + contextCollections.ObjectStateFieldCandidates); - asUDTMemberCandidates.ForEach(c => c.ConflictFinder = conflictsFinder); + fieldCandidates.ForEach(c => c.ConflictFinder = conflictsFinder); - if (clientTarget == null && !targetStateUDT.IsExistingDeclaration) + if (objectStateField == null && !targetStateUDT.IsExistingDeclaration) { conflictsFinder.AssignNoConflictIdentifiers(targetStateUDT); } - asUDTMemberCandidates.ForEach(c => conflictsFinder.AssignNoConflictIdentifiers(c)); + fieldCandidates.ForEach(c => conflictsFinder.AssignNoConflictIdentifiers(c)); - return new EncapsulateFieldUseBackingUDTMemberModel(targetStateUDT, asUDTMemberCandidates, objectStateUDTs) + return new EncapsulateFieldUseBackingUDTMemberModel(targetStateUDT, fieldCandidates, objectStateFieldCandidates) { ConflictFinder = conflictsFinder }; } - IObjectStateUDT DetermineObjectStateUDTTarget(IObjectStateUDT defaultObjectStateUDT, Declaration clientTarget, IReadOnlyCollection objectStateUDTs) + IObjectStateUDT DetermineObjectStateFieldTarget(IObjectStateUDT defaultObjectStateField, Declaration objectStateFieldTarget, IReadOnlyCollection objectStateFieldCandidates) { - var targetStateUDT = defaultObjectStateUDT; + var targetStateUDT = defaultObjectStateField; - if (clientTarget != null) + if (objectStateFieldTarget != null) { - targetStateUDT = objectStateUDTs.Single(osc => clientTarget == osc.Declaration); + targetStateUDT = objectStateFieldCandidates.Single(osc => objectStateFieldTarget == osc.Declaration); } else { var preExistingDefaultUDTField = - objectStateUDTs.Where(osc => osc.TypeIdentifier == defaultObjectStateUDT.TypeIdentifier + objectStateFieldCandidates.Where(osc => osc.TypeIdentifier == defaultObjectStateField.TypeIdentifier && osc.IsExistingDeclaration); if (preExistingDefaultUDTField.Any() && preExistingDefaultUDTField.Count() == 1) @@ -118,4 +127,4 @@ IObjectStateUDT DetermineObjectStateUDTTarget(IObjectStateUDT defaultObjectState return targetStateUDT; } } -} +} \ No newline at end of file diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs index cbe900967f..5ca49ff474 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberRefactoringAction.cs @@ -17,7 +17,6 @@ public class EncapsulateFieldUseBackingUDTMemberRefactoringAction : CodeOnlyRefa private readonly ICodeOnlyRefactoringAction _replacePrivateUDTMemberReferencesRefactoringAction; private readonly ICodeOnlyRefactoringAction _replaceReferencesRefactoringAction; private readonly ICodeOnlyRefactoringAction _encapsulateFieldInsertNewCodeRefactoringAction; - private readonly IEncapsulateFieldCodeBuilder _encapsulateFieldCodeBuilder; private readonly INewContentAggregatorFactory _newContentAggregatorFactory; private readonly IReplacePrivateUDTMemberReferencesModelFactory _replaceUDTMemberReferencesModelFactory; @@ -25,15 +24,13 @@ public class EncapsulateFieldUseBackingUDTMemberRefactoringAction : CodeOnlyRefa IEncapsulateFieldRefactoringActionsProvider refactoringActionsProvider, IReplacePrivateUDTMemberReferencesModelFactory replaceUDTMemberReferencesModelFactory, IRewritingManager rewritingManager, - INewContentAggregatorFactory newContentAggregatorFactory, - IEncapsulateFieldCodeBuilderFactory encapsulateFieldCodeBuilderFactory) + INewContentAggregatorFactory newContentAggregatorFactory) : base(rewritingManager) { _modifyUDTRefactoringAction = refactoringActionsProvider.ModifyUserDefinedType; _replacePrivateUDTMemberReferencesRefactoringAction = refactoringActionsProvider.ReplaceUDTMemberReferences; _replaceReferencesRefactoringAction = refactoringActionsProvider.ReplaceReferences; _encapsulateFieldInsertNewCodeRefactoringAction = refactoringActionsProvider.EncapsulateFieldInsertNewCode; - _encapsulateFieldCodeBuilder = encapsulateFieldCodeBuilderFactory.Create(); _replaceUDTMemberReferencesModelFactory = replaceUDTMemberReferencesModelFactory; _newContentAggregatorFactory = newContentAggregatorFactory; } diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ArrayCandidate.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ArrayCandidate.cs deleted file mode 100644 index 3396bd7ce1..0000000000 --- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ArrayCandidate.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Rubberduck.Parsing.Grammar; -using Rubberduck.Parsing.Symbols; - -namespace Rubberduck.Refactorings.EncapsulateField -{ - public class ArrayCandidate : EncapsulateFieldCandidate - { - public ArrayCandidate(Declaration declaration) - :base(declaration) - { - PropertyAsTypeName = Tokens.Variant; - IsReadOnly = true; - } - } -} diff --git a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidateFactory.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidateFactory.cs index 3640f3c094..8af4875200 100644 --- a/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidateFactory.cs +++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/EncapsulateFieldCandidateFactory.cs @@ -2,13 +2,18 @@ using Rubberduck.Parsing.VBA; using Rubberduck.Refactorings.Common; using Rubberduck.Refactorings.EncapsulateField; +using Rubberduck.VBEditor; +using System; using System.Linq; namespace Rubberduck.Refactorings { public interface IEncapsulateFieldCandidateFactory { - IEncapsulateFieldCandidate Create(Declaration target); + IEncapsulateFieldCandidate CreateFieldCandidate(Declaration target); + IEncapsulateFieldAsUDTMemberCandidate CreateUDTMemberCandidate(IEncapsulateFieldCandidate fieldCandidate, IObjectStateUDT defaultObjectStateField); + IObjectStateUDT CreateDefaultObjectStateField(QualifiedModuleName qualifiedModuleName); + IObjectStateUDT CreateObjectStateField(IUserDefinedTypeCandidate userDefinedTypeField); } public class EncapsulateFieldCandidateFactory : IEncapsulateFieldCandidateFactory @@ -20,30 +25,42 @@ public EncapsulateFieldCandidateFactory(IDeclarationFinderProvider declarationFi _declarationFinderProvider = declarationFinderProvider; } - public IEncapsulateFieldCandidate Create(Declaration target) + public IEncapsulateFieldCandidate CreateFieldCandidate(Declaration target) { - if (target.IsUserDefinedType()) + if (!target.IsUserDefinedType()) { - var udtField = new UserDefinedTypeCandidate(target) as IUserDefinedTypeCandidate; + return new EncapsulateFieldCandidate(target); + } - var udtMembers = _declarationFinderProvider.DeclarationFinder - .UserDeclarations(DeclarationType.UserDefinedTypeMember) - .Where(utm => udtField.Declaration.AsTypeDeclaration == utm.ParentDeclaration); + var udtField = new UserDefinedTypeCandidate(target) as IUserDefinedTypeCandidate; - foreach (var udtMemberDeclaration in udtMembers) - { - var candidateUDTMember = new UserDefinedTypeMemberCandidate(Create(udtMemberDeclaration), udtField) as IUserDefinedTypeMemberCandidate; - udtField.AddMember(candidateUDTMember); - } + var udtMembers = _declarationFinderProvider.DeclarationFinder + .UserDeclarations(DeclarationType.UserDefinedTypeMember) + .Where(utm => udtField.Declaration.AsTypeDeclaration == utm.ParentDeclaration); - return udtField; + foreach (var udtMemberDeclaration in udtMembers) + { + var candidateUDTMember = new UserDefinedTypeMemberCandidate(CreateFieldCandidate(udtMemberDeclaration), udtField); + udtField.AddMember(candidateUDTMember); } - else if (target.IsArray) + + return udtField; + } + + public IEncapsulateFieldAsUDTMemberCandidate CreateUDTMemberCandidate(IEncapsulateFieldCandidate fieldCandidate, IObjectStateUDT defaultObjectStateField) + => new EncapsulateFieldAsUDTMemberCandidate(fieldCandidate, defaultObjectStateField); + + public IObjectStateUDT CreateDefaultObjectStateField(QualifiedModuleName qualifiedModuleName) + => new ObjectStateFieldCandidate(qualifiedModuleName); + + public IObjectStateUDT CreateObjectStateField(IUserDefinedTypeCandidate userDefinedTypeField) + { + if ((userDefinedTypeField.Declaration.AsTypeDeclaration?.Accessibility ?? Accessibility.Implicit) != Accessibility.Private) { - return new ArrayCandidate(target); + throw new ArgumentException(); } - return new EncapsulateFieldCandidate(target); + return new ObjectStateFieldCandidate(userDefinedTypeField); } } } diff --git a/Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT/ObjectStateUDT.cs b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ObjectStateFieldCandidate.cs similarity index 92% rename from Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT/ObjectStateUDT.cs rename to Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ObjectStateFieldCandidate.cs index e4245f104a..88f5bae33c 100644 --- a/Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT/ObjectStateUDT.cs +++ b/Rubberduck.Refactorings/EncapsulateField/FieldCandidates/ObjectStateFieldCandidate.cs @@ -28,12 +28,12 @@ public interface IObjectStateUDT : IEncapsulateFieldRefactoringElement /// Within the EncapsulateField refactoring, the ObjectStateUDT can be an existing /// UserDefinedType or a new UserDefinedType generated by the refactoring. /// - public class ObjectStateUDT : IObjectStateUDT + public class ObjectStateFieldCandidate : IObjectStateUDT { private static string _defaultNewFieldName = "this"; private readonly IUserDefinedTypeCandidate _wrappedUDTField; - public ObjectStateUDT(IUserDefinedTypeCandidate udtField) + public ObjectStateFieldCandidate(IUserDefinedTypeCandidate udtField) : this(udtField.IdentifierName, udtField.Declaration.AsTypeName) { if (!udtField.TypeDeclarationIsPrivate) @@ -45,13 +45,13 @@ public ObjectStateUDT(IUserDefinedTypeCandidate udtField) _wrappedUDTField = udtField; } - public ObjectStateUDT(QualifiedModuleName qualifiedModuleName) + public ObjectStateFieldCandidate(QualifiedModuleName qualifiedModuleName) :this(_defaultNewFieldName, $"T{qualifiedModuleName.ComponentName.CapitalizeFirstLetter()}") { QualifiedModuleName = qualifiedModuleName; } - private ObjectStateUDT(string fieldIdentifier, string typeIdentifier) + private ObjectStateFieldCandidate(string fieldIdentifier, string typeIdentifier) { FieldIdentifier = fieldIdentifier; TypeIdentifier = typeIdentifier; diff --git a/Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT/ObjectStateUserDefinedTypeFactory.cs b/Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT/ObjectStateUserDefinedTypeFactory.cs deleted file mode 100644 index 6595d21c68..0000000000 --- a/Rubberduck.Refactorings/EncapsulateField/ObjectStateUDT/ObjectStateUserDefinedTypeFactory.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Rubberduck.Parsing.Symbols; -using Rubberduck.Refactorings.EncapsulateField; -using Rubberduck.VBEditor; -using System; - -namespace Rubberduck.Refactorings -{ - public interface IObjectStateUserDefinedTypeFactory - { - IObjectStateUDT Create(QualifiedModuleName qualifiedModuleName); - IObjectStateUDT Create(IUserDefinedTypeCandidate userDefinedTypeField); - } - - public class ObjectStateUserDefinedTypeFactory : IObjectStateUserDefinedTypeFactory - { - public IObjectStateUDT Create(QualifiedModuleName qualifiedModuleName) - { - return new ObjectStateUDT(qualifiedModuleName); - } - - public IObjectStateUDT Create(IUserDefinedTypeCandidate userDefinedTypeField) - { - if ((userDefinedTypeField.Declaration.AsTypeDeclaration?.Accessibility ?? Accessibility.Implicit) != Accessibility.Private) - { - throw new ArgumentException(); - } - - return new ObjectStateUDT(userDefinedTypeField); - } - } -} diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs index a1bc160983..5a0b360711 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs @@ -12,6 +12,8 @@ using Rubberduck.SmartIndenter; using RubberduckTests.Settings; using Rubberduck.Refactorings.ModifyUserDefinedType; +using System.Collections.Generic; +using Rubberduck.VBEditor; namespace RubberduckTests.Refactoring.EncapsulateField { @@ -19,10 +21,12 @@ public class EncapsulateFieldTestComponentResolver { private static IDeclarationFinderProvider _declarationFinderProvider; private static IRewritingManager _rewritingManager; - public EncapsulateFieldTestComponentResolver(IDeclarationFinderProvider declarationFinderProvider, IRewritingManager rewritingManager) + private static QualifiedModuleName? _qmn; + public EncapsulateFieldTestComponentResolver(IDeclarationFinderProvider declarationFinderProvider, IRewritingManager rewritingManager, QualifiedModuleName? qmn = null) { _declarationFinderProvider = declarationFinderProvider; _rewritingManager = rewritingManager; + _qmn = qmn; } public T Resolve() where T : class @@ -47,10 +51,10 @@ public EncapsulateFieldTestComponentResolver(IDeclarationFinderProvider declarat case nameof(EncapsulateFieldInsertNewCodeRefactoringAction): return new EncapsulateFieldInsertNewCodeRefactoringAction( - _declarationFinderProvider, + _declarationFinderProvider, _rewritingManager, new PropertyAttributeSetsGenerator(), - ResolveImpl()) as T; + ResolveImpl()) as T; case nameof(ReplacePrivateUDTMemberReferencesRefactoringAction): return new ReplacePrivateUDTMemberReferencesRefactoringAction(_rewritingManager) as T; @@ -61,7 +65,7 @@ public EncapsulateFieldTestComponentResolver(IDeclarationFinderProvider declarat ResolveImpl(), ResolveImpl(), ResolveImpl(), - ResolveImpl() + ResolveImpl() ) as T; case nameof(EncapsulateFieldUseBackingFieldRefactoringAction): @@ -76,8 +80,7 @@ ResolveImpl() ResolveImpl(), ResolveImpl(), _rewritingManager, - ResolveImpl(), - ResolveImpl()) as T; + ResolveImpl()) as T; case nameof(IReplacePrivateUDTMemberReferencesModelFactory): return new ReplacePrivateUDTMemberReferencesModelFactory(_declarationFinderProvider) as T; @@ -106,44 +109,54 @@ ResolveImpl() ResolveImpl()) as T; case nameof(IEncapsulateFieldModelFactory): - return new EncapsulateFieldModelFactory( + return new EncapsulateFieldModelFactory(_declarationFinderProvider, + ResolveImpl(), ResolveImpl(), ResolveImpl(), - ResolveImpl(), - ResolveImpl < IEncapsulateFieldConflictFinderFactory>() + ResolveImpl(), + ResolveImpl< IEncapsulateFieldConflictFinderFactory>() ) as T; case nameof(IEncapsulateFieldUseBackingUDTMemberModelFactory): - return new EncapsulateFieldUseBackingUDTMemberModelFactory( - ResolveImpl(), + return new EncapsulateFieldUseBackingUDTMemberModelFactory(_declarationFinderProvider, + ResolveImpl(), + ResolveImpl(), ResolveImpl< IEncapsulateFieldConflictFinderFactory>()) as T; case nameof(IEncapsulateFieldUseBackingFieldModelFactory): - return new EncapsulateFieldUseBackingFieldModelFactory( - ResolveImpl(), + return new EncapsulateFieldUseBackingFieldModelFactory(_declarationFinderProvider, + ResolveImpl(), + ResolveImpl(), ResolveImpl< IEncapsulateFieldConflictFinderFactory>()) as T; - case nameof(IEncapsulateFieldCollectionsProviderFactory): - return new EncapsulateFieldCollectionsProviderFactory(_declarationFinderProvider, + case nameof(IEncapsulateFieldCandidateSetsProvider): + if (!_qmn.HasValue) + { + throw new ArgumentException($"QualifiedModuleName is not set"); + } + return new EncapsulateFieldCandidateSetsProvider( + _declarationFinderProvider, ResolveImpl(), - ResolveImpl()) as T; + _qmn.Value) as T; case nameof(IEncapsulateFieldCandidateFactory): return new EncapsulateFieldCandidateFactory(_declarationFinderProvider) as T; - case nameof(IObjectStateUserDefinedTypeFactory): - return new ObjectStateUserDefinedTypeFactory() as T; + case nameof(IEncapsulateFieldCandidateSetsProviderFactory): + return new TestEFCandidateSetsProviderFactory() as T; case nameof(IEncapsulateFieldConflictFinderFactory): - return new EncapsulateFieldConflictFinderFactory(_declarationFinderProvider) as T; + return new TestEFConflictFinderFactory() as T; case nameof(INewContentAggregatorFactory): - return new NewContentAggregatorFactory() as T; + return new TestNewContentAggregatorFactory() as T; + + case nameof(IEncapsulateFieldCodeBuilder): + return new EncapsulateFieldCodeBuilder(ResolveImpl()) as T; - case nameof(IEncapsulateFieldCodeBuilderFactory): - return new EncapsulateFieldCodeBuilderFactory(ResolveImpl()) as T; case nameof(ICodeBuilder): return new CodeBuilder(ResolveImpl()) as T; + case nameof(IIndenter): return new Indenter(null, CreateIndenterSettings) as T; } @@ -158,4 +171,28 @@ private static IndenterSettings CreateIndenterSettings() return s; } } + + internal class TestEFCandidateSetsProviderFactory : IEncapsulateFieldCandidateSetsProviderFactory + { + public IEncapsulateFieldCandidateSetsProvider Create(IDeclarationFinderProvider declarationFinderProvider, IEncapsulateFieldCandidateFactory factory, QualifiedModuleName qmn) + { + return new EncapsulateFieldCandidateSetsProvider(declarationFinderProvider, factory, qmn); + } + } + + internal class TestEFConflictFinderFactory : IEncapsulateFieldConflictFinderFactory + { + public IEncapsulateFieldConflictFinder Create(IDeclarationFinderProvider declarationFinderProvider, IEnumerable candidates, IEnumerable objectStateUDTs) + { + return new EncapsulateFieldConflictFinder(declarationFinderProvider, candidates, objectStateUDTs); + } + } + + internal class TestNewContentAggregatorFactory : INewContentAggregatorFactory + { + public INewContentAggregator Create() + { + return new NewContentAggregator(); + } + } } diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldRefactoringActionTests.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldRefactoringActionTests.cs index 4f09055f0c..ec199de264 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldRefactoringActionTests.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldRefactoringActionTests.cs @@ -34,8 +34,8 @@ EncapsulateFieldUseBackingFieldModel modelBuilder(RubberduckParserState state) var modelFactory = resolver.Resolve(); var field = state.DeclarationFinder.MatchName(target).Single(); - var encapsulateFieldRequest = new FieldEncapsulationModel(field as VariableDeclaration, isReadOnly, propertyIdentifier); - return modelFactory.Create( new List() { encapsulateFieldRequest }); + var fieldModel = new FieldEncapsulationModel(field as VariableDeclaration, isReadOnly, propertyIdentifier); + return modelFactory.Create( new List() { fieldModel }); } var refactoredCode = RefactoredCode(inputCode, modelBuilder); diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberTests.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberTests.cs index 983115b876..9c73b8e68d 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberTests.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberTests.cs @@ -35,8 +35,8 @@ EncapsulateFieldUseBackingUDTMemberModel modelBuilder(RubberduckParserState stat var modelFactory = resolver.Resolve(); var field = state.DeclarationFinder.MatchName(target).Single(); - var encapsulateFieldRequest = new FieldEncapsulationModel(field as VariableDeclaration, isReadOnly); - return modelFactory.Create(new List() { encapsulateFieldRequest }); + var fieldModel = new FieldEncapsulationModel(field as VariableDeclaration, isReadOnly); + return modelFactory.Create(new List() { fieldModel }); } var refactoredCode = RefactoredCode(inputCode, modelBuilder); @@ -91,9 +91,9 @@ EncapsulateFieldUseBackingUDTMemberModel modelBuilder(RubberduckParserState stat var firstValueField = state.DeclarationFinder.MatchName("thirdValue").Single(d => d.DeclarationType.HasFlag(DeclarationType.Variable)); var bazzField = state.DeclarationFinder.MatchName("bazz").Single(); - var encapsulateFieldRequestfirstValueField = new FieldEncapsulationModel(firstValueField as VariableDeclaration); - var encapsulateFieldRequestfirstbazzField = new FieldEncapsulationModel(bazzField as VariableDeclaration); - var inputList = new List() { encapsulateFieldRequestfirstValueField, encapsulateFieldRequestfirstbazzField }; + var fieldModelfirstValueField = new FieldEncapsulationModel(firstValueField as VariableDeclaration); + var fieldModelfirstbazzField = new FieldEncapsulationModel(bazzField as VariableDeclaration); + var inputList = new List() { fieldModelfirstValueField, fieldModelfirstbazzField }; return modelFactory.Create(inputList); } @@ -142,10 +142,10 @@ EncapsulateFieldUseBackingUDTMemberModel modelBuilder(RubberduckParserState stat var thirdValueField = state.DeclarationFinder.MatchName("thirdValue").Single(d => d.DeclarationType.HasFlag(DeclarationType.Variable)); var bazzField = state.DeclarationFinder.MatchName("bazz").Single(); - var encapsulateFieldRequestThirdValueField = new FieldEncapsulationModel(thirdValueField as VariableDeclaration); - var encapsulateFieldRequestBazzField = new FieldEncapsulationModel(bazzField as VariableDeclaration); + var fieldModelThirdValueField = new FieldEncapsulationModel(thirdValueField as VariableDeclaration); + var fieldModelBazzField = new FieldEncapsulationModel(bazzField as VariableDeclaration); - var inputList = new List() { encapsulateFieldRequestThirdValueField, encapsulateFieldRequestBazzField }; + var inputList = new List() { fieldModelThirdValueField, fieldModelBazzField }; var targetUDT = state.DeclarationFinder.MatchName("this").Single(d => d.DeclarationType.HasFlag(DeclarationType.Variable)); @@ -194,9 +194,9 @@ EncapsulateFieldUseBackingUDTMemberModel modelBuilder(RubberduckParserState stat var modelFactory = resolver.Resolve(); var mVehicleField = state.DeclarationFinder.UserDeclarations(DeclarationType.Variable).Single(d => d.IdentifierName.Equals("mVehicle")); - var encapsulateFieldRequestMVehicleField = new FieldEncapsulationModel(mVehicleField as VariableDeclaration, false, "Vehicle"); + var fieldModelMVehicleField = new FieldEncapsulationModel(mVehicleField as VariableDeclaration, false, "Vehicle"); - var inputList = new List() { encapsulateFieldRequestMVehicleField }; + var inputList = new List() { fieldModelMVehicleField }; return modelFactory.Create(inputList); } @@ -213,7 +213,6 @@ EncapsulateFieldUseBackingUDTMemberModel modelBuilder(RubberduckParserState stat StringAssert.Contains($" this.Vehicle.Wheels =", refactoredCode); } - [Test] [Category("Refactorings")] [Category("Encapsulate Field")] @@ -246,9 +245,9 @@ EncapsulateFieldUseBackingUDTMemberModel modelBuilder(RubberduckParserState stat var modelFactory = resolver.Resolve(); var mTestField = state.DeclarationFinder.UserDeclarations(DeclarationType.Variable).Single(d => d.IdentifierName.Equals("mTest")); - var fieldEncapsulationModelMTest = new FieldEncapsulationModel(mTestField as VariableDeclaration, false); + var fieldModelMTest = new FieldEncapsulationModel(mTestField as VariableDeclaration, false); - var inputList = new List() { fieldEncapsulationModelMTest }; + var inputList = new List() { fieldModelMTest }; return modelFactory.Create(inputList); } @@ -315,9 +314,9 @@ EncapsulateFieldUseBackingUDTMemberModel modelBuilder(RubberduckParserState stat var invalidTarget = state.DeclarationFinder.MatchName(objectStateTargetIdentifier).Single(d => d.DeclarationType.HasFlag(DeclarationType.Variable)); var resolver = new EncapsulateFieldTestComponentResolver(state, null); var modelFactory = resolver.Resolve(); - var request = new FieldEncapsulationModel(invalidTarget as VariableDeclaration); + var fieldModel = new FieldEncapsulationModel(invalidTarget as VariableDeclaration); - return modelFactory.Create(new List() { request }, invalidTarget); + return modelFactory.Create(new List() { fieldModel }, invalidTarget); } Assert.Throws(() => RefactoredCode(inputCode, modelBuilder)); diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldValidatorTests.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldValidatorTests.cs index 7a9de08672..66183f7579 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldValidatorTests.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldValidatorTests.cs @@ -632,15 +632,16 @@ End Type var mFirstTarget = state.DeclarationFinder.DeclarationsWithType(DeclarationType.Variable) .First(d => d.IdentifierName == fieldUT); - var resolver = new EncapsulateFieldTestComponentResolver(state, null); + var resolver = new EncapsulateFieldTestComponentResolver(state, null, mFirstTarget.QualifiedModuleName); - var collectionsProviderFactory = resolver.Resolve(); - var collectionsProvider = collectionsProviderFactory.Create(mTypeTarget.QualifiedModuleName); + var contextCollections = resolver.Resolve(); + //.RetrieveCandidateSets(mTypeTarget.QualifiedModuleName); - var encapsulateFieldCandidates = collectionsProvider.EncapsulateFieldCandidates; + var encapsulateFieldCandidates = contextCollections.EncapsulateFieldUseBackingFieldCandidates; var finderFactory = resolver.Resolve(); - var conflictFinder = finderFactory.Create(collectionsProvider); + + var conflictFinder = finderFactory.Create(state, contextCollections.EncapsulateFieldUseBackingFieldCandidates, contextCollections.ObjectStateFieldCandidates); foreach (var candidate in encapsulateFieldCandidates) { diff --git a/RubberduckTests/Refactoring/EncapsulateField/PropertyAttributeSetsGeneratorTests.cs b/RubberduckTests/Refactoring/EncapsulateField/PropertyAttributeSetsGeneratorTests.cs index 8e33774674..c7313dcfbc 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/PropertyAttributeSetsGeneratorTests.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/PropertyAttributeSetsGeneratorTests.cs @@ -47,12 +47,11 @@ End Type var resolver = new EncapsulateFieldTestComponentResolver(state, null); var encapsulateFieldCandidateFactory = resolver.Resolve(); - var objStateFactory = resolver.Resolve(); - var objStateCandidate = encapsulateFieldCandidateFactory.Create(objectStateUDTTarget); - var objStateUDT = objStateFactory.Create(objStateCandidate as IUserDefinedTypeCandidate); + var objStateCandidate = encapsulateFieldCandidateFactory.CreateFieldCandidate(objectStateUDTTarget); + var objStateUDT = encapsulateFieldCandidateFactory.CreateObjectStateField(objStateCandidate as IUserDefinedTypeCandidate); - var candidate = new EncapsulateFieldAsUDTMemberCandidate(encapsulateFieldCandidateFactory.Create(encapsulateTarget), objStateUDT) + var candidate = new EncapsulateFieldAsUDTMemberCandidate(encapsulateFieldCandidateFactory.CreateFieldCandidate(encapsulateTarget), objStateUDT) { PropertyIdentifier = "MyType" }; @@ -113,12 +112,11 @@ End Type var resolver = new EncapsulateFieldTestComponentResolver(state, null); var encapsulateFieldCandidateFactory = resolver.Resolve(); - var objStateFactory = resolver.Resolve(); - var objStateCandidate = encapsulateFieldCandidateFactory.Create(objectStateUDTTarget); - var objStateUDT = objStateFactory.Create(objStateCandidate as IUserDefinedTypeCandidate); + var objStateCandidate = encapsulateFieldCandidateFactory.CreateFieldCandidate(objectStateUDTTarget); + var objStateUDT = encapsulateFieldCandidateFactory.CreateObjectStateField(objStateCandidate as IUserDefinedTypeCandidate); - var candidate = new EncapsulateFieldAsUDTMemberCandidate(encapsulateFieldCandidateFactory.Create(encapsulateTarget), objStateUDT); + var candidate = new EncapsulateFieldAsUDTMemberCandidate(encapsulateFieldCandidateFactory.CreateFieldCandidate(encapsulateTarget), objStateUDT); var generator = new PropertyAttributeSetsGenerator(); var propAttributeSets = generator.GeneratePropertyAttributeSets(candidate); From 6cb105dc6a70d5c0fe5c0f60b85b848d79abccea Mon Sep 17 00:00:00 2001 From: Max Doerner Date: Mon, 12 Oct 2020 01:29:19 +0200 Subject: [PATCH 59/67] Do not issue neg line number results for On Error GoTo -1 unless there is a line number -1 This PR also fixes a declaration resolver bug that removed the minus sign from negative line labels. --- .../NegativeLineNumberInspection.cs | 26 ++++++++++++++- .../DeclarationSymbolsListener.cs | 4 +-- .../ThunderCode/ThunderCodeInspectionTests.cs | 33 +++++++++++++++++++ 3 files changed, 60 insertions(+), 3 deletions(-) diff --git a/Rubberduck.CodeAnalysis/Inspections/Concrete/ThunderCode/NegativeLineNumberInspection.cs b/Rubberduck.CodeAnalysis/Inspections/Concrete/ThunderCode/NegativeLineNumberInspection.cs index d0f7eac9a8..76c391197a 100644 --- a/Rubberduck.CodeAnalysis/Inspections/Concrete/ThunderCode/NegativeLineNumberInspection.cs +++ b/Rubberduck.CodeAnalysis/Inspections/Concrete/ThunderCode/NegativeLineNumberInspection.cs @@ -1,9 +1,12 @@ -using Antlr4.Runtime; +using System.Linq; +using Antlr4.Runtime; using Antlr4.Runtime.Tree; using Rubberduck.CodeAnalysis.Inspections.Abstract; using Rubberduck.Parsing; using Rubberduck.Parsing.Grammar; +using Rubberduck.Parsing.Symbols; using Rubberduck.Parsing.VBA; +using Rubberduck.Parsing.VBA.DeclarationCaching; using Rubberduck.Resources.Inspections; namespace Rubberduck.CodeAnalysis.Inspections.Concrete.ThunderCode @@ -31,6 +34,27 @@ protected override string ResultDescription(QualifiedContext return InspectionResults.NegativeLineNumberInspection.ThunderCodeFormat(); } + protected override bool IsResultContext(QualifiedContext context, DeclarationFinder finder) + { + return !IsOnErrorGotoMinusOne(context.Context) + || ProcedureHasMinusOneLabel(finder, context); + } + + private static bool IsOnErrorGotoMinusOne(ParserRuleContext context) + { + return context is VBAParser.OnErrorStmtContext onErrorStatement + && "-1".Equals(onErrorStatement.expression()?.GetText().Trim()); + } + + private static bool ProcedureHasMinusOneLabel(DeclarationFinder finder, QualifiedContext context) + { + return finder.Members(context.ModuleName, DeclarationType.LineLabel) + .Any(label => label.IdentifierName.Equals("-1") + && (label.ParentScopeDeclaration + .Context?.GetSelection() + .Contains(context.Context.GetSelection()) ?? false)); + } + private class NegativeLineNumberKeywordsListener : InspectionListenerBase { public override void EnterOnErrorStmt(VBAParser.OnErrorStmtContext context) diff --git a/Rubberduck.Parsing/VBA/DeclarationResolving/DeclarationSymbolsListener.cs b/Rubberduck.Parsing/VBA/DeclarationResolving/DeclarationSymbolsListener.cs index 02c4c87328..6e9d19ffe6 100644 --- a/Rubberduck.Parsing/VBA/DeclarationResolving/DeclarationSymbolsListener.cs +++ b/Rubberduck.Parsing/VBA/DeclarationResolving/DeclarationSymbolsListener.cs @@ -646,8 +646,8 @@ private void AddIdentifierStatementLabelDeclaration(VBAParser.IdentifierStatemen private void AddLineNumberLabelDeclaration(VBAParser.LineNumberLabelContext context) { - var statementText = context.numberLiteral().GetText(); - var statementSelection = context.numberLiteral().GetSelection(); + var statementText = context.GetText().Trim(); + var statementSelection = context.GetSelection(); AddDeclaration( CreateDeclaration( diff --git a/RubberduckTests/Inspections/ThunderCode/ThunderCodeInspectionTests.cs b/RubberduckTests/Inspections/ThunderCode/ThunderCodeInspectionTests.cs index 2c0c910f40..3fdc28b62f 100644 --- a/RubberduckTests/Inspections/ThunderCode/ThunderCodeInspectionTests.cs +++ b/RubberduckTests/Inspections/ThunderCode/ThunderCodeInspectionTests.cs @@ -224,6 +224,39 @@ GoTo 1 GoTo -5 1 -5: +End Sub")] + [TestCase(1, @"Public Sub Gogo() +On Error GoTo 1 +1 +-1: +End Sub")] + [TestCase(2, @"Public Sub Gogo() +On Error GoTo -1 +1 +-1: +End Sub")] + [TestCase(2, @"Public Sub Gogo() +On Error GoTo -1 +1: +-1 +End Sub")] + [TestCase(0, @"Public Sub Gogo() +On Error GoTo -1 +1 +End Sub")] + [TestCase(1, @"Public Sub Gogo() +On Error GoTo -2 +1 +End Sub")] + [TestCase(2, @"Public Sub Gogo() +On Error GoTo -5 +1 +-5: +End Sub")] + [TestCase(2, @"Public Sub Gogo() +On Error GoTo -5 +1: +-5 End Sub")] public void NegativeLineNumberLabel_ReturnResults(int expectedCount, string inputCode) { From 6c3e8528acbf92e68200abcfd111711f3d0bc428 Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Mon, 12 Oct 2020 11:25:08 -0700 Subject: [PATCH 60/67] Make NoActiveSeletion_Throws() test virtual Passing a null IDeclarationFinderProvider reference may cause some subclasses to fail or throw an unexpected Exception prior to generating the expected Exception. Support overriding when needed (e.g., EncapsulateFieldRefactoring tests) --- RubberduckTests/Refactoring/RefactoringTestBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RubberduckTests/Refactoring/RefactoringTestBase.cs b/RubberduckTests/Refactoring/RefactoringTestBase.cs index c2ee30d6cd..02d977f299 100644 --- a/RubberduckTests/Refactoring/RefactoringTestBase.cs +++ b/RubberduckTests/Refactoring/RefactoringTestBase.cs @@ -21,7 +21,7 @@ public abstract class RefactoringTestBase { [Test] [Category("Refactorings")] - public void NoActiveSelection_Throws() + public virtual void NoActiveSelection_Throws() { var rewritingManager = new Mock().Object; var refactoring = TestRefactoring(rewritingManager, null, initialSelection: null); From 11598dd929811a531781624a34e46f54ed6c0be2 Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Mon, 12 Oct 2020 11:27:27 -0700 Subject: [PATCH 61/67] Relocate to correct namespace Move from RubberduckTests.Refactoring.EncapsulateField to RubberduckTests --- .../EncapsulateField => }/IModuleRewriterExtensionTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename RubberduckTests/{Refactoring/EncapsulateField => }/IModuleRewriterExtensionTests.cs (98%) diff --git a/RubberduckTests/Refactoring/EncapsulateField/IModuleRewriterExtensionTests.cs b/RubberduckTests/IModuleRewriterExtensionTests.cs similarity index 98% rename from RubberduckTests/Refactoring/EncapsulateField/IModuleRewriterExtensionTests.cs rename to RubberduckTests/IModuleRewriterExtensionTests.cs index 736cb9266d..ee4103be5b 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/IModuleRewriterExtensionTests.cs +++ b/RubberduckTests/IModuleRewriterExtensionTests.cs @@ -8,7 +8,7 @@ using System.Collections.Generic; using System.Linq; -namespace RubberduckTests.Refactoring.EncapsulateField +namespace RubberduckTests { [TestFixture] public class IModuleRewriterExtensionTests From 91ed30e30387cae70d9f8584166e6fec93fa1e8f Mon Sep 17 00:00:00 2001 From: Max Doerner Date: Tue, 13 Oct 2020 14:48:01 +0200 Subject: [PATCH 62/67] Fix missing default member result for optional parentheses on properties The problem was that the empty argument list gets parsed as an argument list with a single missing argument and the IndexDefaultBinding did not account for it. Since the missing argument is actually a valid way to parse the empty argument list and the parser cannot know whether there is an optional parameter or not, I chose to catch this in the IndexDefaultBinding. --- .../Binding/Bindings/IndexDefaultBinding.cs | 7 ++++-- .../DefaultMemberRequiredInspectionTests.cs | 25 +++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/Rubberduck.Parsing/Binding/Bindings/IndexDefaultBinding.cs b/Rubberduck.Parsing/Binding/Bindings/IndexDefaultBinding.cs index e7d819492c..70d959abb0 100644 --- a/Rubberduck.Parsing/Binding/Bindings/IndexDefaultBinding.cs +++ b/Rubberduck.Parsing/Binding/Bindings/IndexDefaultBinding.cs @@ -356,8 +356,11 @@ declared type. private static bool ArgumentListIsCompatible(ICollection parameters, ArgumentList argumentList) { return (parameters.Count >= (argumentList?.Arguments.Count ?? 0) - || parameters.Any(parameter => parameter.IsParamArray)) - && parameters.Count(parameter => !parameter.IsOptional && !parameter.IsParamArray) <= (argumentList?.Arguments.Count ?? 0); + || parameters.Any(parameter => parameter.IsParamArray)) + && parameters.Count(parameter => !parameter.IsOptional && !parameter.IsParamArray) <= (argumentList?.Arguments.Count ?? 0) + || parameters.Count == 0 + && argumentList?.Arguments.Count == 1 + && argumentList.Arguments.Single().ArgumentType == ArgumentListArgumentType.Missing; } private IBoundExpression ResolveRecursiveDefaultMember(Declaration defaultMember, ExpressionClassification defaultMemberClassification, ArgumentList argumentList, ParserRuleContext expression, Declaration parent, int defaultMemberResolutionRecursionDepth, RecursiveDefaultMemberAccessExpression containedExpression) diff --git a/RubberduckTests/Inspections/DefaultMemberRequiredInspectionTests.cs b/RubberduckTests/Inspections/DefaultMemberRequiredInspectionTests.cs index 69205e9b13..062706917a 100644 --- a/RubberduckTests/Inspections/DefaultMemberRequiredInspectionTests.cs +++ b/RubberduckTests/Inspections/DefaultMemberRequiredInspectionTests.cs @@ -330,6 +330,31 @@ End Function Assert.AreEqual(expectedSelection, actualSelection); } + [Category("Inspections")] + [Test] + public void OptionalParenthesesAfterVariantReturningProperty_NoResult() + { + var classCode = @" +Public Property Get Foo() As Variant +End Property +"; + + var moduleCode = @" +Private Function Bar() As String + Dim cls As new Class1 + Bar = cls.Foo() +End Function +"; + + var vbe = MockVbeBuilder.BuildFromModules( + ("Class1", classCode, ComponentType.ClassModule), + ("Module1", moduleCode, ComponentType.StandardModule)); + + var inspectionResults = InspectionResults(vbe.Object); + + Assert.AreEqual(0, inspectionResults.Count()); + } + [Category("Inspections")] [Test] public void FailedIndexExpressionOnFunctionWithParameters_NoResult() From 53a707c7bad77ee1f7ed25031b4dc421ce50f392 Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Mon, 12 Oct 2020 11:33:48 -0700 Subject: [PATCH 63/67] Introduce CW support for tests Modified EncapsulateFieldRefactoring tests to use a WindsorContainer to manage ctor injection and object instantiations - causing some degree of modification to all EF related tests. --- .../EncapsulateFieldCommandTests.cs | 30 +- ...apsulateFieldInteractiveRefactoringTest.cs | 39 +++ .../EncapsulateFieldTestComponentResolver.cs | 198 -------------- .../EncapsulateFieldTestSupport.cs | 77 +++++- .../EncapsulateFieldTestsResolver.cs | 176 ++++++++++++ .../EncapsulateArrayFieldTests.cs | 20 +- .../EncapsulateFieldTests.cs | 107 ++++---- ...ldUseBackingFieldRefactoringActionTests.cs | 15 +- .../EncapsulateUDTFieldTests.cs | 257 ++++-------------- ...ncapsulateFieldUseBackingUDTMemberTests.cs | 30 +- .../EncapsulateUsingStateUDTTests.cs | 82 +++--- .../EncapsulateFieldValidatorTests.cs | 110 ++++---- .../EncapsulationIdentifiersTests.cs | 10 +- .../EncapsulateField/PreviewerTests.cs | 43 +-- .../PropertyAttributeSetsGeneratorTests.cs | 26 +- ...fyUserDefinedTypeRefactoringActionTests.cs | 8 +- ...ReplaceReferencesRefactoringActionTests.cs | 12 +- 17 files changed, 569 insertions(+), 671 deletions(-) create mode 100644 RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldInteractiveRefactoringTest.cs delete mode 100644 RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs create mode 100644 RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestsResolver.cs diff --git a/RubberduckTests/Commands/RefactorCommands/EncapsulateFieldCommandTests.cs b/RubberduckTests/Commands/RefactorCommands/EncapsulateFieldCommandTests.cs index 10d445d5a2..31df2f60e5 100644 --- a/RubberduckTests/Commands/RefactorCommands/EncapsulateFieldCommandTests.cs +++ b/RubberduckTests/Commands/RefactorCommands/EncapsulateFieldCommandTests.cs @@ -1,15 +1,9 @@ -using System; -using Moq; +using Castle.Windsor; using NUnit.Framework; -using Rubberduck.Interaction; using Rubberduck.Parsing.Rewriter; -using Rubberduck.Parsing.UIContext; using Rubberduck.Parsing.VBA; -using Rubberduck.Refactorings; -using Rubberduck.Refactorings.EncapsulateField; using Rubberduck.UI.Command; using Rubberduck.UI.Command.Refactorings; -using Rubberduck.UI.Command.Refactorings.Notifiers; using Rubberduck.VBEditor; using Rubberduck.VBEditor.SafeComWrappers.Abstract; using Rubberduck.VBEditor.Utility; @@ -60,25 +54,9 @@ Sub Foo() protected override CommandBase TestCommand(IVBE vbe, RubberduckParserState state, IRewritingManager rewritingManager, ISelectionService selectionService) { - var resolver = new EncapsulateFieldTestComponentResolver(state, rewritingManager); - - var msgBox = new Mock().Object; - var factory = new Mock().Object; - var selectedDeclarationProvider = new SelectedDeclarationProvider(selectionService, state); - var uiDispatcherMock = new Mock(); - uiDispatcherMock - .Setup(m => m.Invoke(It.IsAny())) - .Callback((Action action) => action.Invoke()); - var userInteraction = new RefactoringUserInteraction(factory, uiDispatcherMock.Object); - var refactoring = new EncapsulateFieldRefactoring(resolver.Resolve(), - resolver.Resolve(), - resolver.Resolve(), - userInteraction, - selectionService, - selectedDeclarationProvider); - var notifier = new EncapsulateFieldFailedNotifier(msgBox); - var selectedDeclarationService = new SelectedDeclarationProvider(selectionService, state); - return new RefactorEncapsulateFieldCommand(refactoring, notifier, state, selectionService, selectedDeclarationService); + var resolver = new EncapsulateFieldTestsResolver(state, rewritingManager, selectionService); + resolver.Install(new WindsorContainer(), null); + return resolver.Resolve(); } protected override IVBE SetupAllowingExecution() diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldInteractiveRefactoringTest.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldInteractiveRefactoringTest.cs new file mode 100644 index 0000000000..b9022afd59 --- /dev/null +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldInteractiveRefactoringTest.cs @@ -0,0 +1,39 @@ +using System; +using NUnit.Framework; +using Rubberduck.Parsing.Rewriter; +using Rubberduck.Parsing.VBA; +using Rubberduck.Refactorings; +using Rubberduck.Refactorings.EncapsulateField; +using Rubberduck.Refactorings.Exceptions; +using Rubberduck.VBEditor.Utility; +using RubberduckTests.Mocks; + +namespace RubberduckTests.Refactoring.EncapsulateField +{ + [TestFixture] + public abstract class EncapsulateFieldInteractiveRefactoringTest : InteractiveRefactoringTestBase + { + //RefactoringTestBase.NoActiveSelection_Throws passes a null + //IDeclarationFinderProvider parameter to 'TestRefactoring(...). + //The EncapsulateFieldRefactoring tests Resolver throws a different + //exception type without a valid interface reference and causes the + //base class version of the test to fail. + [Test] + [Category("Refactorings")] + public override void NoActiveSelection_Throws() + { + var testVbe = TestVbe(string.Empty, out _); + var (state, rewritingManager) = MockParser.CreateAndParseWithRewritingManager(testVbe); + using (state) + { + var refactoring = TestRefactoring(rewritingManager, state, initialSelection: null); + Assert.Throws(() => refactoring.Refactor()); + } + } + + protected override IRefactoring TestRefactoring(IRewritingManager rewritingManager, RubberduckParserState state, RefactoringUserInteraction userInteraction, ISelectionService selectionService) + { + throw new NotImplementedException(); + } + } +} diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs deleted file mode 100644 index 5a0b360711..0000000000 --- a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestComponentResolver.cs +++ /dev/null @@ -1,198 +0,0 @@ -using Rubberduck.Parsing.Rewriter; -using Rubberduck.Parsing.VBA; -using Rubberduck.Refactorings; -using Rubberduck.Refactorings.EncapsulateField; -using Rubberduck.Refactorings.ReplaceReferences; -using Rubberduck.Refactorings.ReplacePrivateUDTMemberReferences; -using Rubberduck.Refactorings.ReplaceDeclarationIdentifier; -using Rubberduck.Refactorings.EncapsulateFieldUseBackingUDTMember; -using Rubberduck.Refactorings.EncapsulateFieldUseBackingField; -using Rubberduck.Refactorings.EncapsulateFieldInsertNewCode; -using System; -using Rubberduck.SmartIndenter; -using RubberduckTests.Settings; -using Rubberduck.Refactorings.ModifyUserDefinedType; -using System.Collections.Generic; -using Rubberduck.VBEditor; - -namespace RubberduckTests.Refactoring.EncapsulateField -{ - public class EncapsulateFieldTestComponentResolver - { - private static IDeclarationFinderProvider _declarationFinderProvider; - private static IRewritingManager _rewritingManager; - private static QualifiedModuleName? _qmn; - public EncapsulateFieldTestComponentResolver(IDeclarationFinderProvider declarationFinderProvider, IRewritingManager rewritingManager, QualifiedModuleName? qmn = null) - { - _declarationFinderProvider = declarationFinderProvider; - _rewritingManager = rewritingManager; - _qmn = qmn; - } - - public T Resolve() where T : class - { - return ResolveImpl(); - } - - private static T ResolveImpl() where T : class - { - switch (typeof(T).Name) - { - case nameof(EncapsulateFieldRefactoringAction): - return new EncapsulateFieldRefactoringAction( - ResolveImpl(), - ResolveImpl()) as T; - - case nameof(ReplaceReferencesRefactoringAction): - return new ReplaceReferencesRefactoringAction(_rewritingManager) as T; - - case nameof(ReplaceDeclarationIdentifierRefactoringAction): - return new ReplaceDeclarationIdentifierRefactoringAction(_rewritingManager) as T; - - case nameof(EncapsulateFieldInsertNewCodeRefactoringAction): - return new EncapsulateFieldInsertNewCodeRefactoringAction( - _declarationFinderProvider, - _rewritingManager, - new PropertyAttributeSetsGenerator(), - ResolveImpl()) as T; - - case nameof(ReplacePrivateUDTMemberReferencesRefactoringAction): - return new ReplacePrivateUDTMemberReferencesRefactoringAction(_rewritingManager) as T; - - case nameof(IEncapsulateFieldRefactoringActionsProvider): - return new EncapsulateFieldRefactoringActionsProvider( - ResolveImpl(), - ResolveImpl(), - ResolveImpl(), - ResolveImpl(), - ResolveImpl() - ) as T; - - case nameof(EncapsulateFieldUseBackingFieldRefactoringAction): - return new EncapsulateFieldUseBackingFieldRefactoringAction( - ResolveImpl(), - ResolveImpl(), - _rewritingManager, - ResolveImpl()) as T; - - case nameof(EncapsulateFieldUseBackingUDTMemberRefactoringAction): - return new EncapsulateFieldUseBackingUDTMemberRefactoringAction( - ResolveImpl(), - ResolveImpl(), - _rewritingManager, - ResolveImpl()) as T; - - case nameof(IReplacePrivateUDTMemberReferencesModelFactory): - return new ReplacePrivateUDTMemberReferencesModelFactory(_declarationFinderProvider) as T; - - case nameof(ModifyUserDefinedTypeRefactoringAction): - return new ModifyUserDefinedTypeRefactoringAction( - _declarationFinderProvider, - _rewritingManager, - ResolveImpl()) as T; - - case nameof(EncapsulateFieldPreviewProvider): - return new EncapsulateFieldPreviewProvider( - ResolveImpl(), - ResolveImpl()) as T; - - case nameof(EncapsulateFieldUseBackingFieldPreviewProvider): - return new EncapsulateFieldUseBackingFieldPreviewProvider( - ResolveImpl(), - _rewritingManager, - ResolveImpl()) as T; - - case nameof(EncapsulateFieldUseBackingUDTMemberPreviewProvider): - return new EncapsulateFieldUseBackingUDTMemberPreviewProvider( - ResolveImpl(), - _rewritingManager, - ResolveImpl()) as T; - - case nameof(IEncapsulateFieldModelFactory): - return new EncapsulateFieldModelFactory(_declarationFinderProvider, - ResolveImpl(), - ResolveImpl(), - ResolveImpl(), - ResolveImpl(), - ResolveImpl< IEncapsulateFieldConflictFinderFactory>() - ) as T; - - case nameof(IEncapsulateFieldUseBackingUDTMemberModelFactory): - return new EncapsulateFieldUseBackingUDTMemberModelFactory(_declarationFinderProvider, - ResolveImpl(), - ResolveImpl(), - ResolveImpl< IEncapsulateFieldConflictFinderFactory>()) as T; - - case nameof(IEncapsulateFieldUseBackingFieldModelFactory): - return new EncapsulateFieldUseBackingFieldModelFactory(_declarationFinderProvider, - ResolveImpl(), - ResolveImpl(), - ResolveImpl< IEncapsulateFieldConflictFinderFactory>()) as T; - - case nameof(IEncapsulateFieldCandidateSetsProvider): - if (!_qmn.HasValue) - { - throw new ArgumentException($"QualifiedModuleName is not set"); - } - return new EncapsulateFieldCandidateSetsProvider( - _declarationFinderProvider, - ResolveImpl(), - _qmn.Value) as T; - - case nameof(IEncapsulateFieldCandidateFactory): - return new EncapsulateFieldCandidateFactory(_declarationFinderProvider) as T; - - case nameof(IEncapsulateFieldCandidateSetsProviderFactory): - return new TestEFCandidateSetsProviderFactory() as T; - - case nameof(IEncapsulateFieldConflictFinderFactory): - return new TestEFConflictFinderFactory() as T; - - case nameof(INewContentAggregatorFactory): - return new TestNewContentAggregatorFactory() as T; - - case nameof(IEncapsulateFieldCodeBuilder): - return new EncapsulateFieldCodeBuilder(ResolveImpl()) as T; - - case nameof(ICodeBuilder): - return new CodeBuilder(ResolveImpl()) as T; - - case nameof(IIndenter): - return new Indenter(null, CreateIndenterSettings) as T; - } - throw new ArgumentException($"Unable to resolve {typeof(T).Name}") ; - } - - private static IndenterSettings CreateIndenterSettings() - { - var s = IndenterSettingsTests.GetMockIndenterSettings(); - s.VerticallySpaceProcedures = true; - s.LinesBetweenProcedures = 1; - return s; - } - } - - internal class TestEFCandidateSetsProviderFactory : IEncapsulateFieldCandidateSetsProviderFactory - { - public IEncapsulateFieldCandidateSetsProvider Create(IDeclarationFinderProvider declarationFinderProvider, IEncapsulateFieldCandidateFactory factory, QualifiedModuleName qmn) - { - return new EncapsulateFieldCandidateSetsProvider(declarationFinderProvider, factory, qmn); - } - } - - internal class TestEFConflictFinderFactory : IEncapsulateFieldConflictFinderFactory - { - public IEncapsulateFieldConflictFinder Create(IDeclarationFinderProvider declarationFinderProvider, IEnumerable candidates, IEnumerable objectStateUDTs) - { - return new EncapsulateFieldConflictFinder(declarationFinderProvider, candidates, objectStateUDTs); - } - } - - internal class TestNewContentAggregatorFactory : INewContentAggregatorFactory - { - public INewContentAggregator Create() - { - return new NewContentAggregator(); - } - } -} diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestSupport.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestSupport.cs index bb44648fd9..c48948d393 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestSupport.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestSupport.cs @@ -1,9 +1,11 @@ +using Castle.Windsor; using Rubberduck.Parsing.Rewriter; using Rubberduck.Parsing.Symbols; using Rubberduck.Parsing.VBA; using Rubberduck.Refactorings; using Rubberduck.Refactorings.EncapsulateField; using Rubberduck.VBEditor; +using Rubberduck.VBEditor.SafeComWrappers; using Rubberduck.VBEditor.SafeComWrappers.Abstract; using Rubberduck.VBEditor.Utility; using RubberduckTests.Mocks; @@ -13,8 +15,38 @@ namespace RubberduckTests.Refactoring.EncapsulateField { - public class EncapsulateFieldTestSupport : InteractiveRefactoringTestBase + public class EncapsulateFieldTestSupport : EncapsulateFieldInteractiveRefactoringTest { + private EncapsulateFieldTestsResolver _testResolver; + + public void ResetResolver() + { + _testResolver = null; + } + + public T Resolve(IDeclarationFinderProvider declarationFinderProvider, IRewritingManager rewritingManager = null, ISelectionService selectionService = null) where T : class + { + SetupResolver(declarationFinderProvider, rewritingManager, selectionService); + return Resolve() as T; + } + + public T Resolve() where T : class + => _testResolver?.Resolve() as T ?? throw new InvalidOperationException("Test Resolver not initialized. Call 'SetupResolver(...)' or use one of the 'Resolve()' overloads"); + + public void SetupResolver(IDeclarationFinderProvider declarationFinderProvider, IRewritingManager rewritingManager = null, ISelectionService selectionService = null) + { + if (declarationFinderProvider is null) + { + throw new ArgumentNullException("declarationFinderProvider is null"); + } + + if (_testResolver is null) + { + _testResolver = new EncapsulateFieldTestsResolver(declarationFinderProvider, rewritingManager, selectionService); + _testResolver.Install(new WindsorContainer(), null); + } + } + public string RHSIdentifier => Rubberduck.Resources.Refactorings.Refactorings.CodeBuilder_DefaultPropertyRHSParam; public string StateUDTDefaultTypeName => $"T{MockVbeBuilder.TestModuleName}"; @@ -116,14 +148,41 @@ public string RefactoredCode(CodeString codeString, Func userInteraction, ISelectionService selectionService) { - var resolver = new EncapsulateFieldTestComponentResolver(state, rewritingManager); - var selectedDeclarationProvider = new SelectedDeclarationProvider(selectionService, state); - return new EncapsulateFieldRefactoring(resolver.Resolve(), - resolver.Resolve(), - resolver.Resolve(), + SetupResolver(state, rewritingManager, selectionService); + return new EncapsulateFieldRefactoring(Resolve(state, rewritingManager, selectionService), + Resolve(), + Resolve(), userInteraction, selectionService, - selectedDeclarationProvider); + Resolve()); + } + + public IDictionary RefactoredCode( + Func presenterAction, + TestCodeString codeString, + params (string, string, ComponentType)[] otherModules) + { + return RefactoredCode(presenterAction, + (MockVbeBuilder.TestModuleName, codeString, ComponentType.StandardModule), + otherModules); + } + + public IDictionary RefactoredCode( + Func presenterAction, + (string selectedModuleName, TestCodeString codeString, ComponentType componentType) moduleUnderTest, + params (string, string, ComponentType)[] otherModules) + { + var modules = otherModules.ToList(); + + modules.Add((moduleUnderTest.selectedModuleName, moduleUnderTest.codeString.Code, moduleUnderTest.componentType)); + + return RefactoredCode( + moduleUnderTest.selectedModuleName, + moduleUnderTest.codeString.CaretPosition.ToOneBased(), + presenterAction, + null, + false, + modules.ToArray()); } public IEncapsulateFieldCandidate RetrieveEncapsulateFieldCandidate(string inputCode, string fieldName) @@ -145,9 +204,7 @@ public IEncapsulateFieldCandidate RetrieveEncapsulateFieldCandidate(IVBE vbe, st { var match = state.DeclarationFinder.MatchName(fieldName).Where(m => m.DeclarationType.Equals(declarationType)).Single(); - var resolver = new EncapsulateFieldTestComponentResolver(state, null); - - var model = resolver.Resolve().Create(match); + var model = Resolve(state).Create(match); model.ConflictFinder.AssignNoConflictIdentifiers(model[match.IdentifierName]); diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestsResolver.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestsResolver.cs new file mode 100644 index 0000000000..ded4d4972e --- /dev/null +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldTestsResolver.cs @@ -0,0 +1,176 @@ +using Rubberduck.Parsing.Rewriter; +using Rubberduck.Parsing.VBA; +using Rubberduck.Refactorings; +using Rubberduck.Refactorings.EncapsulateField; +using Rubberduck.Refactorings.ReplaceReferences; +using Rubberduck.Refactorings.ReplacePrivateUDTMemberReferences; +using Rubberduck.Refactorings.ReplaceDeclarationIdentifier; +using Rubberduck.Refactorings.EncapsulateFieldUseBackingUDTMember; +using Rubberduck.Refactorings.EncapsulateFieldUseBackingField; +using Rubberduck.Refactorings.EncapsulateFieldInsertNewCode; +using Rubberduck.SmartIndenter; +using RubberduckTests.Settings; +using Rubberduck.Refactorings.ModifyUserDefinedType; +using Castle.Windsor; +using Castle.Facilities.TypedFactory; +using Castle.MicroKernel.Registration; +using Moq; +using System; +using Rubberduck.Parsing.UIContext; +using Rubberduck.VBEditor.Utility; +using Rubberduck.UI.Command.Refactorings; +using Rubberduck.Interaction; +using Rubberduck.UI.Command.Refactorings.Notifiers; +using Castle.MicroKernel.SubSystems.Configuration; + +namespace RubberduckTests.Refactoring.EncapsulateField +{ + public class EncapsulateFieldTestsResolver : IWindsorInstaller + { + private readonly IDeclarationFinderProvider _declarationFinderProvider; + private readonly IRewritingManager _rewritingManager; + private readonly ICodeBuilder _codeBuilder; + private readonly IIndenter _testIndenter; + private readonly IUiDispatcher _uiDispatcher; + private readonly IRefactoringPresenterFactory _presenterFactory; + private readonly ISelectionService _selectionService; + private readonly IMessageBox _messageBox; + + private IWindsorContainer _container; + + public EncapsulateFieldTestsResolver(IDeclarationFinderProvider declarationFinderProvider, IRewritingManager rewritingManager = null, ISelectionService selectionService = null) + { + _declarationFinderProvider = declarationFinderProvider; + + _rewritingManager = rewritingManager; + + _selectionService = selectionService; + + _testIndenter = new Indenter(null, () => + { + var s = IndenterSettingsTests.GetMockIndenterSettings(); + s.VerticallySpaceProcedures = true; + s.LinesBetweenProcedures = 1; + return s; + }); + + _codeBuilder = new CodeBuilder(_testIndenter); + + _presenterFactory = new Mock().Object; + + var uiDispatcherMock = new Mock(); + uiDispatcherMock + .Setup(m => m.Invoke(It.IsAny())) + .Callback((Action action) => action.Invoke()); + + _uiDispatcher = uiDispatcherMock.Object; + + _messageBox = new Mock().Object; + } + + public void Install(IWindsorContainer container, IConfigurationStore store) + => Install(container); + + public T Resolve() where T : class => _container.Resolve() as T; + + private void Install(IWindsorContainer container) + { + _container = container; + RegisterInstances(_container); + RegisterSingletonObjects(container); + RegisterInterfaceToImplementationPairsSingleton(container); + RegisterInterfaceToImplementationPairsTransient(container); + RegisterAutoFactories(container); + } + + private void RegisterInstances(IWindsorContainer container) + { + container.Kernel.Register(Component.For().Instance(_declarationFinderProvider)); + container.Kernel.Register(Component.For().Instance(_testIndenter)); + container.Kernel.Register(Component.For().Instance(_codeBuilder)); + if (_rewritingManager != null) + { + container.Kernel.Register(Component.For().Instance(_rewritingManager)); + } + if (_selectionService != null) + { + container.Kernel.Register(Component.For().Instance(_selectionService)); + } + container.Kernel.Register(Component.For().Instance(_uiDispatcher)); + container.Kernel.Register(Component.For().Instance(_presenterFactory)); + container.Kernel.Register(Component.For().Instance(_messageBox)); + } + + private static void RegisterSingletonObjects(IWindsorContainer container) + { + container.Kernel.Register(Component.For()); + container.Kernel.Register(Component.For()); + container.Kernel.Register(Component.For()); + container.Kernel.Register(Component.For()); + container.Kernel.Register(Component.For()); + container.Kernel.Register(Component.For()); + container.Kernel.Register(Component.For()); + container.Kernel.Register(Component.For()); + container.Kernel.Register(Component.For()); + container.Kernel.Register(Component.For()); + container.Kernel.Register(Component.For()); + container.Kernel.Register(Component.For()); + container.Kernel.Register(Component.For()); + container.Kernel.Register(Component.For()); + container.Kernel.Register(Component.For>()); + } + + private static void RegisterInterfaceToImplementationPairsSingleton(IWindsorContainer container) + { + container.Kernel.Register(Component.For() + .ImplementedBy()); + + container.Kernel.Register(Component.For() + .ImplementedBy()); + + container.Kernel.Register(Component.For() + .ImplementedBy()); + + container.Kernel.Register(Component.For() + .ImplementedBy()); + + container.Kernel.Register(Component.For() + .ImplementedBy()); + + container.Kernel.Register(Component.For() + .ImplementedBy()); + + container.Kernel.Register(Component.For() + .ImplementedBy()); + + container.Kernel.Register(Component.For() + .ImplementedBy()); + + container.Kernel.Register(Component.For() + .ImplementedBy()); + } + + private static void RegisterInterfaceToImplementationPairsTransient(IWindsorContainer container) + { + container.Kernel.Register(Component.For() + .ImplementedBy() + .LifestyleTransient()); + + container.Kernel.Register(Component.For() + .ImplementedBy() + .LifestyleTransient()); + + container.Kernel.Register(Component.For() + .ImplementedBy() + .LifestyleTransient()); + } + + private static void RegisterAutoFactories(IWindsorContainer container) + { + container.Kernel.AddFacility(); + container.Kernel.Register(Component.For().AsFactory().LifestyleSingleton()); + container.Kernel.Register(Component.For().AsFactory().LifestyleSingleton()); + container.Kernel.Register(Component.For().AsFactory().LifestyleSingleton()); + } + } +} diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateArrayFieldTests.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateArrayFieldTests.cs index 78d8f9b0f8..2b839da70a 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateArrayFieldTests.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateArrayFieldTests.cs @@ -13,10 +13,16 @@ namespace RubberduckTests.Refactoring.EncapsulateField { [TestFixture] - public class EncapsulateArrayFieldTests : InteractiveRefactoringTestBase + public class EncapsulateArrayFieldTests : EncapsulateFieldInteractiveRefactoringTest { private EncapsulateFieldTestSupport Support { get; } = new EncapsulateFieldTestSupport(); + [SetUp] + public void ExecutesBeforeAllTests() + { + Support.ResetResolver(); + } + [TestCase("Private", "mArray(5) As String", "mArray(5) As String")] [TestCase("Public", "mArray(5) As String", "mArray(5) As String")] [TestCase("Private", "mArray(5,2,3) As String", "mArray(5,2,3) As String")] @@ -31,7 +37,7 @@ public class EncapsulateArrayFieldTests : InteractiveRefactoringTestBase + public class EncapsulateFieldTests : EncapsulateFieldInteractiveRefactoringTest { private EncapsulateFieldTestSupport Support { get; } = new EncapsulateFieldTestSupport(); + [SetUp] + public void ExecutesBeforeAllTests() + { + Support.ResetResolver(); + } + [TestCase("fizz", true, "baz", true, "buzz", true)] [TestCase("fizz", false, "baz", true, "buzz", true)] [TestCase("fizz", false, "baz", false, "buzz", true)] @@ -31,7 +37,7 @@ public class EncapsulateFieldTests : InteractiveRefactoringTestBase presenterAction = model => null; @@ -523,9 +527,9 @@ public void EncapsulateField_ModelIsNull() [Category("Encapsulate Field")] public void StandardModuleSource_ExternalReferences(bool moduleResolve) { - var sourceModuleName = "SourceModule"; - var referenceExpression = moduleResolve ? $"{sourceModuleName}." : string.Empty; - var sourceModuleCode = + var testModuleName = MockVbeBuilder.TestModuleName; + var referenceExpression = moduleResolve ? $"{testModuleName}." : string.Empty; + var testModuleCode = $@" Public th|is As Long"; @@ -540,13 +544,13 @@ public void StandardModuleSource_ExternalReferences(bool moduleResolve) End Sub Public Sub Foo() - With {sourceModuleName} + With {testModuleName} .this = bar End With End Sub "; - string classModuleReferencingCode = + var classModuleReferencingCode = $@"Option Explicit Private Const bar As Long = 7 @@ -556,7 +560,7 @@ End Sub End Sub Public Sub Foo() - With {sourceModuleName} + With {testModuleName} .this = bar End With End Sub @@ -567,25 +571,18 @@ End Sub var presenterAction = Support.SetParameters(userInput); - var sourceCodeString = sourceModuleCode.ToCodeString(); - var actualModuleCode = RefactoredCode( - sourceModuleName, - sourceCodeString.CaretPosition.ToOneBased(), - presenterAction, - null, - false, + var actualModuleCode = Support.RefactoredCode(presenterAction, testModuleCode.ToCodeString(), ("StdModule", procedureModuleReferencingCode, ComponentType.StandardModule), - ("ClassModule", classModuleReferencingCode, ComponentType.ClassModule), - (sourceModuleName, sourceCodeString.Code, ComponentType.StandardModule)); + ("ClassModule", classModuleReferencingCode, ComponentType.ClassModule)); var referencingModuleCode = actualModuleCode["StdModule"]; - StringAssert.Contains($"{sourceModuleName}.MyProperty = ", referencingModuleCode); - StringAssert.DoesNotContain($"{sourceModuleName}.{sourceModuleName}.MyProperty = ", referencingModuleCode); + StringAssert.Contains($"{testModuleName}.MyProperty = ", referencingModuleCode); + StringAssert.DoesNotContain($"{testModuleName}.{testModuleName}.MyProperty = ", referencingModuleCode); StringAssert.Contains($" .MyProperty = bar", referencingModuleCode); var referencingClassCode = actualModuleCode["ClassModule"]; - StringAssert.Contains($"{sourceModuleName}.MyProperty = ", referencingClassCode); - StringAssert.DoesNotContain($"{sourceModuleName}.{sourceModuleName}.MyProperty = ", referencingClassCode); + StringAssert.Contains($"{testModuleName}.MyProperty = ", referencingClassCode); + StringAssert.DoesNotContain($"{testModuleName}.{testModuleName}.MyProperty = ", referencingClassCode); StringAssert.Contains($" .MyProperty = bar", referencingClassCode); } diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldRefactoringActionTests.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldRefactoringActionTests.cs index ec199de264..3344bbb2fd 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldRefactoringActionTests.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateFieldUseBackingFieldRefactoringActionTests.cs @@ -16,6 +16,12 @@ public class EncapsulateFieldUseBackingFieldRefactoringActionTests : Refactoring { private EncapsulateFieldTestSupport Support { get; } = new EncapsulateFieldTestSupport(); + [SetUp] + public void ExecutesBeforeAllTests() + { + Support.ResetResolver(); + } + [TestCase(false, "Name")] [TestCase(true, "Name")] [TestCase(false, null)] @@ -30,8 +36,7 @@ public void EncapsulatePublicField(bool isReadOnly, string propertyIdentifier) EncapsulateFieldUseBackingFieldModel modelBuilder(RubberduckParserState state) { - var resolver = new EncapsulateFieldTestComponentResolver(state, null); - var modelFactory = resolver.Resolve(); + var modelFactory = Support.Resolve(state); var field = state.DeclarationFinder.MatchName(target).Single(); var fieldModel = new FieldEncapsulationModel(field as VariableDeclaration, isReadOnly, propertyIdentifier); @@ -72,8 +77,7 @@ public void EmptyTargetSet() EncapsulateFieldUseBackingFieldModel modelBuilder(RubberduckParserState state) { - var resolver = new EncapsulateFieldTestComponentResolver(state, null); - var modelFactory = resolver.Resolve(); + var modelFactory = Support.Resolve(state); return modelFactory.Create(Enumerable.Empty()); } @@ -83,8 +87,7 @@ EncapsulateFieldUseBackingFieldModel modelBuilder(RubberduckParserState state) protected override IRefactoringAction TestBaseRefactoring(RubberduckParserState state, IRewritingManager rewritingManager) { - var resolver = new EncapsulateFieldTestComponentResolver(state, rewritingManager); - return resolver.Resolve(); + return Support.Resolve(state, rewritingManager); } } } diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateUDTFieldTests.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateUDTFieldTests.cs index a75dc8733f..38adbd4a6e 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateUDTFieldTests.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingField/EncapsulateUDTFieldTests.cs @@ -5,22 +5,28 @@ using Rubberduck.Refactorings.EncapsulateField; using Rubberduck.VBEditor.SafeComWrappers; using Rubberduck.VBEditor.Utility; -using System.Collections.Generic; +using RubberduckTests.Mocks; namespace RubberduckTests.Refactoring.EncapsulateField { [TestFixture] - public class EncapsulateUDTFieldTests : InteractiveRefactoringTestBase + public class EncapsulateUDTFieldTests : EncapsulateFieldInteractiveRefactoringTest { private EncapsulateFieldTestSupport Support { get; } = new EncapsulateFieldTestSupport(); + [SetUp] + public void ExecutesBeforeAllTests() + { + Support.ResetResolver(); + } + [TestCase("Public")] [TestCase("Private")] [Category("Refactorings")] [Category("Encapsulate Field")] public void UserDefinedType_UserAcceptsDefaults(string accessibility) { - string inputCode = + var inputCode = $@" Private Type TBar First As String @@ -51,7 +57,7 @@ End Type [Category("Encapsulate Field")] public void UserDefinedType_TwoFields(bool encapsulateThis, bool encapsulateThat) { - string inputCode = + var inputCode = $@" Private Type TBar First As String @@ -128,7 +134,7 @@ End Type [Category("Encapsulate Field")] public void ModifiesCorrectUDTMemberReference_MemberAccess() { - string inputCode = + var inputCode = $@" Private Type TBar First As String @@ -165,7 +171,7 @@ End Sub [Category("Encapsulate Field")] public void ModifiesCorrectUDTMemberReference_WithMemberAccess() { - string inputCode = + var inputCode = $@" Private Type TBar First As String @@ -217,8 +223,7 @@ End With [Category("Encapsulate Field")] public void ModifiesCorrectUDTMemberReference_WithMemberAccessExternal() { - string sourceModuleName = "SourceModule"; - string inputCode = + var inputCode = $@" Public Type TBar First As String @@ -229,10 +234,10 @@ End Type Private that As TBar "; - string module2 = + var module2 = $@" Public Sub Foo(arg1 As String, arg2 As Long) - With {sourceModuleName}.this + With {MockVbeBuilder.TestModuleName}.this .First = arg1 .Second = arg2 End With @@ -246,25 +251,18 @@ End Sub var presenterAction = Support.UserAcceptsDefaults(); - var codeString = inputCode.ToCodeString(); - - var actualModuleCode = RefactoredCode( - sourceModuleName, - codeString.CaretPosition.ToOneBased(), - presenterAction, - null, - false, - ("Module2", module2, ComponentType.StandardModule), - (sourceModuleName, codeString.Code, ComponentType.StandardModule)); + var actualModuleCode = Support.RefactoredCode(presenterAction, + (MockVbeBuilder.TestModuleName, inputCode.ToCodeString(), ComponentType.StandardModule), + ("Module2", module2, ComponentType.StandardModule)); var actualCode = actualModuleCode["Module2"]; - var sourceCode = actualModuleCode[sourceModuleName]; + var sourceCode = actualModuleCode[MockVbeBuilder.TestModuleName]; StringAssert.DoesNotContain($" First = arg1", actualCode); StringAssert.DoesNotContain($" Second = arg2", actualCode); StringAssert.Contains($" .First = arg1", actualCode); StringAssert.Contains($" .Second = arg2", actualCode); - StringAssert.Contains($"With {sourceModuleName}.This", actualCode); + StringAssert.Contains($"With {MockVbeBuilder.TestModuleName}.This", actualCode); } [TestCase("Public")] @@ -273,7 +271,7 @@ End Sub [Category("Encapsulate Field")] public void UserDefinedTypeMembersAndFields(string accessibility) { - string inputCode = + var inputCode = $@" Private Type TBar First As String @@ -321,7 +319,7 @@ Private mFizz [Category("Encapsulate Field")] public void UserDefinedTypeMember_ContainsObjects(string accessibility) { - string inputCode = + var inputCode = $@" Private Type TBar First As Class1 @@ -330,7 +328,7 @@ End Type {accessibility} th|is As TBar"; - string class1Code = + var class1Code = @"Option Explicit Public Sub Foo() @@ -342,17 +340,10 @@ End Sub var presenterAction = Support.SetParameters(userInput); - var codeString = inputCode.ToCodeString(); - var actualModuleCode = RefactoredCode( - "Module1", - codeString.CaretPosition.ToOneBased(), - presenterAction, - null, - false, - ("Class1", class1Code, ComponentType.ClassModule), - ("Module1", codeString.Code, ComponentType.StandardModule)); + var actualModuleCode = Support.RefactoredCode(presenterAction, inputCode.ToCodeString(), + ("Class1", class1Code, ComponentType.ClassModule)); - var actualCode = actualModuleCode["Module1"]; + var actualCode = actualModuleCode[MockVbeBuilder.TestModuleName]; StringAssert.Contains("Private this As TBar", actualCode); StringAssert.DoesNotContain($"this = {Support.RHSIdentifier}", actualCode); @@ -373,7 +364,7 @@ End Sub [Category("Encapsulate Field")] public void UserDefinedTypeMember_ContainsVariant(string accessibility) { - string inputCode = + var inputCode = $@" Private Type TBar First As String @@ -405,7 +396,7 @@ End Type [Category("Encapsulate Field")] public void UserDefinedTypeMember_ContainsArrays(string accessibility) { - string inputCode = + var inputCode = $@" Private Type TBar First(5) As String @@ -439,13 +430,13 @@ End Type [Category("Encapsulate Field")] public void UserDefinedTypeMembers_ExternallyDefinedType(string accessibility) { - string inputCode = + var inputCode = $@" Option Explicit {accessibility} th|is As TBar"; - string typeDefinition = + var typeDefinition = $@" Public Type TBar First As String @@ -458,14 +449,8 @@ End Type var presenterAction = Support.SetParameters(userInput); - var codeString = inputCode.ToCodeString(); - var actualModuleCode = RefactoredCode( - "Class1", - codeString.CaretPosition.ToOneBased(), - presenterAction, - null, - false, - ("Class1", codeString.Code, ComponentType.ClassModule), + var actualModuleCode = Support.RefactoredCode(presenterAction, + ("Class1", inputCode.ToCodeString(), ComponentType.ClassModule), ("Module1", typeDefinition, ComponentType.StandardModule)); Assert.AreEqual(typeDefinition, actualModuleCode["Module1"]); @@ -492,13 +477,13 @@ End Type [Category("Encapsulate Field")] public void UserDefinedTypeMembers_ObjectField(string accessibility) { - string inputCode = + var inputCode = $@" Option Explicit {accessibility} mThe|Class As Class1"; - string classContent = + var classContent = $@" Option Explicit @@ -508,17 +493,10 @@ End Sub var presenterAction = Support.UserAcceptsDefaults(); - var codeString = inputCode.ToCodeString(); - var actualModuleCode = RefactoredCode( - "Module1", - codeString.CaretPosition.ToOneBased(), - presenterAction, - null, - false, - ("Class1", classContent, ComponentType.ClassModule), - ("Module1", codeString.Code, ComponentType.StandardModule)); + var actualModuleCode = Support.RefactoredCode(presenterAction, inputCode.ToCodeString(), + ("Class1", classContent, ComponentType.ClassModule)); - var actualCode = actualModuleCode["Module1"]; + var actualCode = actualModuleCode[MockVbeBuilder.TestModuleName]; StringAssert.Contains($"Private mTheClass As Class1", actualCode); StringAssert.Contains($"Set mTheClass = {Support.RHSIdentifier}", actualCode); @@ -569,16 +547,9 @@ End Sub var presenterAction = Support.SetParameters(userInput); - var sourceCodeString = sourceModuleCode.ToCodeString(); - - var actualModuleCode = RefactoredCode( - sourceModuleName, - sourceCodeString.CaretPosition.ToOneBased(), - presenterAction, - null, - false, - ("StdModule", moduleReferencingCode, ComponentType.StandardModule), - (sourceModuleName, sourceCodeString.Code, ComponentType.StandardModule)); + var actualModuleCode = Support.RefactoredCode(presenterAction, + (sourceModuleName, sourceModuleCode.ToCodeString(), ComponentType.StandardModule), + ("StdModule", moduleReferencingCode, ComponentType.StandardModule)); var referencingModuleCode = actualModuleCode["StdModule"]; StringAssert.Contains($"{sourceModuleName}.MyType.First = ", referencingModuleCode); @@ -586,96 +557,6 @@ End Sub StringAssert.Contains($" .MyType.Second = ", referencingModuleCode); } - private IDictionary Scenario_StdModuleSource_StandardAndClassReferencingModules(string referenceQualifier, string typeDeclarationAccessibility, string sourceModuleName, UserInputDataObject userInput) - { - var sourceModuleCode = -$@" -{typeDeclarationAccessibility} Type TBar - First As String - Second As Long -End Type - -Public th|is As TBar"; - - var procedureModuleReferencingCode = -$@"Option Explicit - -'StdModule referencing the UDT - -Private Const foo As String = ""Foo"" - -Private Const bar As Long = 7 - -Public Sub Foo() - {referenceQualifier}.First = foo -End Sub - -Public Sub Bar() - {referenceQualifier}.Second = bar -End Sub - -Public Sub FooBar() - With {sourceModuleName} - .this.First = foo - .this.Second = bar - End With -End Sub -"; - - string classModuleReferencingCode = -$@"Option Explicit - -'ClassModule referencing the UDT - -Private Const foo As String = ""Foo"" - -Private Const bar As Long = 7 - -Public Sub Foo() - {referenceQualifier}.First = foo -End Sub - -Public Sub Bar() - {referenceQualifier}.Second = bar -End Sub - -Public Sub FooBar() - With {sourceModuleName} - .this.First = foo - .this.Second = bar - End With -End Sub -"; - - var presenterAction = Support.SetParameters(userInput); - - var sourceCodeString = sourceModuleCode.ToCodeString(); - - //Only Public Types are accessible to ClassModules - if (typeDeclarationAccessibility.Equals("Public")) - { - return RefactoredCode( - sourceModuleName, - sourceCodeString.CaretPosition.ToOneBased(), - presenterAction, - null, - false, - ("StdModule", procedureModuleReferencingCode, ComponentType.StandardModule), - ("ClassModule", classModuleReferencingCode, ComponentType.ClassModule), - (sourceModuleName, sourceCodeString.Code, ComponentType.StandardModule)); - } - var actualModuleCode = RefactoredCode( - sourceModuleName, - sourceCodeString.CaretPosition.ToOneBased(), - presenterAction, - null, - false, - ("StdModule", procedureModuleReferencingCode, ComponentType.StandardModule), - (sourceModuleName, sourceCodeString.Code, ComponentType.StandardModule)); - - return actualModuleCode; - } - [Test] [Category("Refactorings")] [Category("Encapsulate Field")] @@ -688,7 +569,7 @@ public void ClassModuleUDTFieldSelection_ExternalReferences_ClassModule() Public th|is As TBar"; - string classModuleReferencingCode = + var classModuleReferencingCode = $@"Option Explicit Private {sourceClassName} As {sourceModuleName} @@ -720,16 +601,9 @@ End Sub var presenterAction = Support.SetParameters(userInput); - var sourceCodeString = sourceModuleCode.ToCodeString(); - - var actualModuleCode = RefactoredCode( - sourceModuleName, - sourceCodeString.CaretPosition.ToOneBased(), - presenterAction, - null, - false, - ("ClassModule", classModuleReferencingCode, ComponentType.ClassModule), - (sourceModuleName, sourceCodeString.Code, ComponentType.ClassModule)); + var actualModuleCode = Support.RefactoredCode(presenterAction, + (sourceModuleName, sourceModuleCode.ToCodeString(), ComponentType.ClassModule), + ("ClassModule", classModuleReferencingCode, ComponentType.ClassModule)); var referencingClassCode = actualModuleCode["ClassModule"]; StringAssert.Contains($"{sourceClassName}.MyType.First = ", referencingClassCode); @@ -786,16 +660,9 @@ End Sub var presenterAction = Support.SetParameters(userInput); - var sourceCodeString = sourceModuleCode.ToCodeString(); - - var actualModuleCode = RefactoredCode( - classModuleName, - sourceCodeString.CaretPosition.ToOneBased(), - presenterAction, - null, - false, - ("StdModule", procedureModuleReferencingCode, ComponentType.StandardModule), - (classModuleName, sourceCodeString.Code, ComponentType.ClassModule)); + var actualModuleCode = Support.RefactoredCode(presenterAction, + (classModuleName, sourceModuleCode.ToCodeString(), ComponentType.ClassModule), + ("StdModule", procedureModuleReferencingCode, ComponentType.StandardModule)); var referencingModuleCode = actualModuleCode["StdModule"]; StringAssert.Contains($"{classInstanceName}.MyType.First = ", referencingModuleCode); @@ -808,7 +675,7 @@ End Sub [Category("Encapsulate Field")] public void UserDefinedTypeUserUpdatesToBeReadOnly() { - string inputCode = + var inputCode = $@" Private Type TBar First As String @@ -833,7 +700,7 @@ End Type [Category("Encapsulate Field")] public void MultipleUserDefinedTypesOfSameName() { - string inputCode = + var inputCode = $@" Option Explicit @@ -845,7 +712,7 @@ End Type Public mF|oo As TBar "; - string module2Content = + var module2Content = $@" Public Type TBar FirstVal As Long @@ -855,19 +722,9 @@ End Type var presenterAction = Support.UserAcceptsDefaults(); - var codeString = inputCode.ToCodeString(); - var actualModuleCode = RefactoredCode( - "Module1", - codeString.CaretPosition.ToOneBased(), - presenterAction, - null, - false, - ("Module2", module2Content, ComponentType.StandardModule), - ("Module1", codeString.Code, ComponentType.StandardModule)); - - var actualCode = actualModuleCode["Module1"]; - - StringAssert.Contains($"Public Property Let FirstValue(", actualCode); + var actualModuleCode = Support.RefactoredCode(presenterAction, inputCode.ToCodeString(), + ("Module2", module2Content, ComponentType.StandardModule)); + StringAssert.Contains($"Public Property Let FirstValue(", actualModuleCode[MockVbeBuilder.TestModuleName]); } [Test] @@ -875,7 +732,7 @@ End Type [Category("Encapsulate Field")] public void UDTMemberPropertyConflictsWithExistingFunction() { - string inputCode = + var inputCode = $@" Private Type TBar First As String @@ -901,7 +758,7 @@ End Type [Category("Encapsulate Field")] public void UDTMemberIsPrivateUDT() { - string inputCode = + var inputCode = $@" Private Type TFoo @@ -930,7 +787,7 @@ End Type [Category("Encapsulate Field")] public void UDTMemberIsPrivateUDT_RepeatedType() { - string inputCode = + var inputCode = $@" Private Type TFoo @@ -963,7 +820,7 @@ End Type [Category("Encapsulate Field")] public void UDTMemberIsPublicUDT() { - string inputCode = + var inputCode = $@" Public Type TFoo diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberTests.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberTests.cs index 9c73b8e68d..a61579b203 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberTests.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateFieldUseBackingUDTMemberTests.cs @@ -17,6 +17,12 @@ public class EncapsulateFieldUseBackingUDTMemberTests : RefactoringActionTestBas { private EncapsulateFieldTestSupport Support { get; } = new EncapsulateFieldTestSupport(); + [SetUp] + public void ExecutesBeforeAllTests() + { + Support.ResetResolver(); + } + [TestCase(false, "Name")] [TestCase(true, "Name")] [TestCase(false, null)] @@ -31,8 +37,7 @@ public void EncapsulatePublicField(bool isReadOnly, string propertyIdentifier) EncapsulateFieldUseBackingUDTMemberModel modelBuilder(RubberduckParserState state) { - var resolver = new EncapsulateFieldTestComponentResolver(state, null); - var modelFactory = resolver.Resolve(); + var modelFactory = Support.Resolve(state); var field = state.DeclarationFinder.MatchName(target).Single(); var fieldModel = new FieldEncapsulationModel(field as VariableDeclaration, isReadOnly); @@ -86,8 +91,7 @@ End Type EncapsulateFieldUseBackingUDTMemberModel modelBuilder(RubberduckParserState state) { - var resolver = new EncapsulateFieldTestComponentResolver(state, null); - var modelFactory = resolver.Resolve(); + var modelFactory = Support.Resolve(state); var firstValueField = state.DeclarationFinder.MatchName("thirdValue").Single(d => d.DeclarationType.HasFlag(DeclarationType.Variable)); var bazzField = state.DeclarationFinder.MatchName("bazz").Single(); @@ -137,8 +141,7 @@ End Type EncapsulateFieldUseBackingUDTMemberModel modelBuilder(RubberduckParserState state) { - var resolver = new EncapsulateFieldTestComponentResolver(state, null); - var modelFactory = resolver.Resolve(); + var modelFactory = Support.Resolve(state); var thirdValueField = state.DeclarationFinder.MatchName("thirdValue").Single(d => d.DeclarationType.HasFlag(DeclarationType.Variable)); var bazzField = state.DeclarationFinder.MatchName("bazz").Single(); @@ -190,8 +193,7 @@ End Type EncapsulateFieldUseBackingUDTMemberModel modelBuilder(RubberduckParserState state) { - var resolver = new EncapsulateFieldTestComponentResolver(state, null); - var modelFactory = resolver.Resolve(); + var modelFactory = Support.Resolve(state); var mVehicleField = state.DeclarationFinder.UserDeclarations(DeclarationType.Variable).Single(d => d.IdentifierName.Equals("mVehicle")); var fieldModelMVehicleField = new FieldEncapsulationModel(mVehicleField as VariableDeclaration, false, "Vehicle"); @@ -241,8 +243,7 @@ End Type EncapsulateFieldUseBackingUDTMemberModel modelBuilder(RubberduckParserState state) { - var resolver = new EncapsulateFieldTestComponentResolver(state, null); - var modelFactory = resolver.Resolve(); + var modelFactory = Support.Resolve(state); var mTestField = state.DeclarationFinder.UserDeclarations(DeclarationType.Variable).Single(d => d.IdentifierName.Equals("mTest")); var fieldModelMTest = new FieldEncapsulationModel(mTestField as VariableDeclaration, false); @@ -274,8 +275,7 @@ public void EmptyTargetSet_Throws() EncapsulateFieldUseBackingUDTMemberModel modelBuilder(RubberduckParserState state) { - var resolver = new EncapsulateFieldTestComponentResolver(state, null); - var modelFactory = resolver.Resolve(); + var modelFactory = Support.Resolve(state); return modelFactory.Create(Enumerable.Empty()); } @@ -312,8 +312,7 @@ End Type EncapsulateFieldUseBackingUDTMemberModel modelBuilder(RubberduckParserState state) { var invalidTarget = state.DeclarationFinder.MatchName(objectStateTargetIdentifier).Single(d => d.DeclarationType.HasFlag(DeclarationType.Variable)); - var resolver = new EncapsulateFieldTestComponentResolver(state, null); - var modelFactory = resolver.Resolve(); + var modelFactory = Support.Resolve(state); var fieldModel = new FieldEncapsulationModel(invalidTarget as VariableDeclaration); return modelFactory.Create(new List() { fieldModel }, invalidTarget); @@ -324,8 +323,7 @@ EncapsulateFieldUseBackingUDTMemberModel modelBuilder(RubberduckParserState stat protected override IRefactoringAction TestBaseRefactoring(RubberduckParserState state, IRewritingManager rewritingManager) { - var resolver = new EncapsulateFieldTestComponentResolver(state, rewritingManager); - return resolver.Resolve(); + return Support.Resolve(state, rewritingManager); } } } diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateUsingStateUDTTests.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateUsingStateUDTTests.cs index bcbda7b2c2..76cf0aa640 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateUsingStateUDTTests.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldUseBackingUDTMember/EncapsulateUsingStateUDTTests.cs @@ -13,16 +13,22 @@ namespace RubberduckTests.Refactoring.EncapsulateField { [TestFixture] - public class EncapsulateUsingStateUDTTests : InteractiveRefactoringTestBase + public class EncapsulateUsingStateUDTTests : EncapsulateFieldInteractiveRefactoringTest { private EncapsulateFieldTestSupport Support { get; } = new EncapsulateFieldTestSupport(); + [SetUp] + public void ExecutesBeforeAllTests() + { + Support.ResetResolver(); + } + [Test] [Category("Refactorings")] [Category("Encapsulate Field")] public void EncapsulatePrivateFieldAsUDT() { - const string inputCode = + var inputCode = @"|Private fizz As Integer"; var presenterAction = Support.SetParametersForSingleTarget("fizz", "Name", asUDT: true); @@ -38,7 +44,7 @@ public void EncapsulatePrivateFieldAsUDT() [Category("Encapsulate Field")] public void UserDefinedType_UserAcceptsDefaults_ConflictWithStateUDT(string accessibility) { - string inputCode = + var inputCode = $@" Private Type TBar First As String @@ -60,7 +66,7 @@ End Type [Category("Encapsulate Field")] public void UserDefinedTypeMembers_OnlyEncapsulateUDTMembers() { - string inputCode = + var inputCode = $@" Private Type TBar First As String @@ -90,7 +96,7 @@ End Type [Category("Encapsulate Field")] public void UserDefinedTypeMembers_UDTFieldReferences() { - string inputCode = + var inputCode = $@" Private Type TBar First As String @@ -119,7 +125,7 @@ End Type [Category("Encapsulate Field")] public void LoadsExistingUDT() { - string inputCode = + var inputCode = $@" Private Type TBar First As String @@ -160,7 +166,7 @@ End Type [Category("Encapsulate Field")] public void DoesNotChangeExistingUDTMembers() { - string inputCode = + var inputCode = $@" Private Type T{MockVbeBuilder.TestModuleName} Name As String @@ -197,7 +203,7 @@ End Property [Category("Encapsulate Field")] public void MultipleFields() { - string inputCode = + var inputCode = $@" Public fo|o As Long Public bar As String @@ -225,7 +231,7 @@ public void MultipleFields() [Category("Encapsulate Field")] public void UserDefinedType_MultipleFieldsWithUDT() { - string inputCode = + var inputCode = $@" Private Type TBar @@ -259,7 +265,7 @@ End Type [Category("Encapsulate Field")] public void UserDefinedType_MultipleFieldsOfSameUDT() { - string inputCode = + var inputCode = $@" Private Type TBar @@ -287,7 +293,7 @@ End Type [Category("Encapsulate Field")] public void UserDefinedType_PrivateEnumField() { - const string inputCode = + var inputCode = @" Private Enum NumberTypes Whole = -1 @@ -318,7 +324,7 @@ End Enum public void UserDefinedType_BoundedArrayField(string arrayIdentifier, string dimensions) { var selectedInput = arrayIdentifier.Replace("n", "n|"); - string inputCode = + var inputCode = $@" Public {selectedInput}({dimensions}) As String "; @@ -343,7 +349,7 @@ public void UserDefinedType_BoundedArrayField(string arrayIdentifier, string dim public void UserDefinedType_LocallyReferencedArray(string arrayIdentifier, string dimensions) { var selectedInput = arrayIdentifier.Replace("n", "n|"); - string inputCode = + var inputCode = $@" Public {selectedInput}({dimensions}) As String @@ -372,7 +378,7 @@ End Property public void UserDefinedTypeDefaultNameHasConflict() { var expectedIdentifier = "TTestModule1_1"; - string inputCode = + var inputCode = $@" Private Type TBar @@ -405,7 +411,7 @@ End Type [Category("Encapsulate Field")] public void ObjectStateUDTs(string udtFieldAccessibility, int expectedCount) { - string inputCode = + var inputCode = $@" Private Type TBar First As String @@ -436,7 +442,7 @@ Private mFizz [Category("Encapsulate Field")] public void UDTMemberIsPrivateUDT() { - string inputCode = + var inputCode = $@" Private Type TFoo @@ -469,7 +475,7 @@ End Type [Category("Encapsulate Field")] public void UDTMemberIsPublicUDT() { - string inputCode = + var inputCode = $@" Public Type TFoo @@ -500,13 +506,13 @@ End Type [Category("Encapsulate Field")] public void GivenReferencedPublicField_UpdatesReferenceToNewProperty() { - const string codeClass1 = + var codeClass1 = @"|Public fizz As Integer Sub Foo() fizz = 1 End Sub"; - const string codeClass2 = + var codeClass2 = @"Sub Foo() Dim theClass As Class1 theClass.fizz = 0 @@ -523,20 +529,14 @@ Sub Bar(ByVal v As Integer) var presenterAction = Support.SetParameters(userInput); - var class1CodeString = codeClass1.ToCodeString(); - var actualCode = RefactoredCode( - "Class1", - class1CodeString.CaretPosition.ToOneBased(), - presenterAction, - null, - false, - ("Class1", class1CodeString.Code, ComponentType.ClassModule), + var refactoredCode = Support.RefactoredCode(presenterAction, + ("Class1", codeClass1.ToCodeString(), ComponentType.ClassModule), ("Class2", codeClass2, ComponentType.ClassModule)); - StringAssert.Contains("Name = 1", actualCode["Class1"]); - StringAssert.Contains("theClass.Name = 0", actualCode["Class2"]); - StringAssert.Contains("Bar theClass.Name", actualCode["Class2"]); - StringAssert.DoesNotContain("fizz", actualCode["Class2"]); + StringAssert.Contains("Name = 1", refactoredCode["Class1"]); + StringAssert.Contains("theClass.Name = 0", refactoredCode["Class2"]); + StringAssert.Contains("Bar theClass.Name", refactoredCode["Class2"]); + StringAssert.DoesNotContain("fizz", refactoredCode["Class2"]); } [TestCase(false)] @@ -568,7 +568,7 @@ End With End Sub "; - string classModuleReferencingCode = + var classModuleReferencingCode = $@"Option Explicit Private Const bar As Long = 7 @@ -591,23 +591,17 @@ End Sub var presenterAction = Support.SetParameters(userInput); - var sourceCodeString = sourceModuleCode.ToCodeString(); - var actualModuleCode = RefactoredCode( - sourceModuleName, - sourceCodeString.CaretPosition.ToOneBased(), - presenterAction, - null, - false, + var refactoredCode = Support.RefactoredCode(presenterAction, + (sourceModuleName, sourceModuleCode.ToCodeString(), ComponentType.StandardModule), ("StdModule", procedureModuleReferencingCode, ComponentType.StandardModule), - ("ClassModule", classModuleReferencingCode, ComponentType.ClassModule), - (sourceModuleName, sourceCodeString.Code, ComponentType.StandardModule)); + ("ClassModule", classModuleReferencingCode, ComponentType.ClassModule)); - var referencingModuleCode = actualModuleCode["StdModule"]; + var referencingModuleCode = refactoredCode["StdModule"]; StringAssert.Contains($"{sourceModuleName}.MyProperty = ", referencingModuleCode); StringAssert.DoesNotContain($"{sourceModuleName}.{sourceModuleName}.MyProperty = ", referencingModuleCode); StringAssert.Contains($" .MyProperty = bar", referencingModuleCode); - var referencingClassCode = actualModuleCode["ClassModule"]; + var referencingClassCode = refactoredCode["ClassModule"]; StringAssert.Contains($"{sourceModuleName}.MyProperty = ", referencingClassCode); StringAssert.DoesNotContain($"{sourceModuleName}.{sourceModuleName}.MyProperty = ", referencingClassCode); StringAssert.Contains($" .MyProperty = bar", referencingClassCode); @@ -618,7 +612,7 @@ End Sub [Category("Encapsulate Field")] public void PrivateUDT_SelectedOtherThanObjectStateUDT() { - string inputCode = + var inputCode = $@" Private Type TTest diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldValidatorTests.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldValidatorTests.cs index 66183f7579..6a1d02a925 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldValidatorTests.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulateFieldValidatorTests.cs @@ -13,17 +13,23 @@ namespace RubberduckTests.Refactoring.EncapsulateField { [TestFixture] - public class EncapsulateFieldValidatorTests : InteractiveRefactoringTestBase + public class EncapsulateFieldValidatorTests : EncapsulateFieldInteractiveRefactoringTest { private EncapsulateFieldTestSupport Support { get; } = new EncapsulateFieldTestSupport(); + [SetUp] + public void ExecutesBeforeAllTests() + { + Support.ResetResolver(); + } + [TestCase("fizz", "_Fizz", false)] [TestCase("fizz", "FizzProp", true)] [Category("Refactorings")] [Category("Encapsulate Field")] public void VBAIdentifier_Property(string originalFieldName, string newPropertyName, bool expectedResult) { - string inputCode = + var inputCode = $@"Public {originalFieldName} As String"; var encapsulatedField = Support.RetrieveEncapsulateFieldCandidate(inputCode, originalFieldName); @@ -38,7 +44,7 @@ public void VBAIdentifier_Property(string originalFieldName, string newPropertyN [Category("Encapsulate Field")] public void EncapsulatePrivateField_ReadOnlyRequiresSet() { - const string inputCode = + var inputCode = @"|Private fizz As Collection"; const string expectedCode = @@ -58,7 +64,7 @@ End Property [Category("Encapsulate Field")] public void PropertyNameNotDuplicated() { - const string inputCode = + var inputCode = @"Public var|iable As Integer, variable1 As Long, variable2 As Integer"; var userInput = new UserInputDataObject() @@ -82,7 +88,7 @@ public void PropertyNameNotDuplicated() [Category("Encapsulate Field")] public void UDTMemberPropertyConflictsWithExistingFunction() { - string inputCode = + var inputCode = $@" Private Type TBar First As String @@ -105,7 +111,7 @@ End Type [Category("Encapsulate Field")] public void FieldNameDefaultsToNonConflictName() { - string inputCode = + var inputCode = $@"Public fizz As String Private fizzle As String @@ -129,7 +135,7 @@ End Property [Category("Encapsulate Field")] public void UserEntersConflictingName(string userModifiedPropertyName) { - string inputCode = + var inputCode = $@"Public fizz As String Private mName As String @@ -161,7 +167,7 @@ End Property [Category("Encapsulate Field")] public void UserModificationIsExistingPropertyNameConflicts(string fizz_modifiedPropertyName, string bazz_modifiedPropertyName, bool fizz_expectedResult, bool bazz_expectedResult) { - string inputCode = + var inputCode = $@"Public fizz As Integer Public bazz As Integer Public buzz As Integer @@ -193,7 +199,7 @@ public void UserModificationIsExistingPropertyNameConflicts(string fizz_modified [Category("Encapsulate Field")] public void EncapsulateMultipleUDTFields_DefaultsAreNotInConflict(string udtAccessibility, string fieldAccessibility) { - string inputCode = + var inputCode = $@" {udtAccessibility} Type TBar First As Long @@ -221,7 +227,7 @@ End Type [Category("Encapsulate Field")] public void PropertyNameConflictsWithModuleVariable() { - string inputCode = + var inputCode = $@" Public longValue As Long @@ -244,7 +250,7 @@ public void PropertyNameConflictsWithModuleVariable() public void EncapsulatePrivateField_EnumMemberConflict() { //5.2.3.4: An enum member name may not be the same as any variable name, or constant name that is defined within the same module - const string inputCode = + var inputCode = @" Public Enum NumberTypes @@ -267,7 +273,7 @@ End Enum [Category("Encapsulate Field")] public void EncapsulatePrivateField_UDTMemberConflict() { - const string inputCode = + var inputCode = @" Private Type TVehicle @@ -290,7 +296,7 @@ End Type public void DefaultPropertyNameConflictsResolved() { //Both fields default to "Test" as the property name - const string inputCode = + var inputCode = @"Private mTest As Integer Private strTest As String"; @@ -310,7 +316,7 @@ public void DefaultPropertyNameConflictsResolved() [Category("Encapsulate Field")] public void TargetNameUsedForLimitedScopeDeclarations(string localDeclaration, string parameter) { - string inputCode = + var inputCode = $@" Private te|st As Long @@ -334,7 +340,7 @@ End Function [Category("Encapsulate Field")] public void TargetReferenceScopeUsesPropertyName(string localDeclaration, string parameter) { - string inputCode = + var inputCode = $@" Private aName As String @@ -360,7 +366,7 @@ End Function [Category("Encapsulate Field")] public void TargetDefaultFieldIDConflict() { - string inputCode = + var inputCode = $@" Private tes|t As String Private test_1 As String @@ -403,10 +409,10 @@ public void ModuleAndProjectNamesAreValid(string userEnteredName) [Test] [Category("Refactorings")] [Category("Encapsulate Field")] - public void MultipleUserDefinedTypesOfSameNameOtherModule() + public void ExistingPublicUDTConflictWithDefaultObjectStateType() { - var moduleOneName = "ModuleOne"; - string inputCode = + string moduleOneName = "ModuleOne"; + var inputCode = $@" Option Explicit @@ -429,29 +435,21 @@ End Type var presenterAction = Support.SetParameters(userInput); - var codeString = inputCode.ToCodeString(); - var actualModuleCode = RefactoredCode( - moduleOneName, - codeString.CaretPosition.ToOneBased(), - presenterAction, - null, - false, - ("Module2", module2Content, ComponentType.StandardModule), - (moduleOneName, codeString.Code, ComponentType.StandardModule)); + var actualModuleCode = Support.RefactoredCode(presenterAction, + (moduleOneName, inputCode.ToCodeString(), ComponentType.StandardModule), + ("Module2", module2Content, ComponentType.StandardModule)); - var actualCode = actualModuleCode[moduleOneName]; - - StringAssert.Contains($"Private Type TModuleOne", actualCode); + StringAssert.Contains($"Private Type TModuleOne", actualModuleCode[moduleOneName]); } [TestCase("Public")] [TestCase("Private")] [Category("Refactorings")] [Category("Encapsulate Field")] - public void MultipleUserDefinedTypesOfSameNameSameModule(string accessibility) + public void ExistingUDTConflictWithDefaultObjectStateType(string accessibility) { var moduleOneName = "ModuleOne"; - string inputCode = + var inputCode = $@" Option Explicit @@ -471,18 +469,10 @@ End Type var presenterAction = Support.SetParameters(userInput); - var codeString = inputCode.ToCodeString(); - var actualModuleCode = RefactoredCode( - moduleOneName, - codeString.CaretPosition.ToOneBased(), - presenterAction, - null, - false, - (moduleOneName, codeString.Code, ComponentType.StandardModule)); - - var actualCode = actualModuleCode[moduleOneName]; + var actualModuleCode = Support.RefactoredCode(presenterAction, + (moduleOneName, inputCode.ToCodeString(), ComponentType.StandardModule)); - StringAssert.Contains($"Private Type TModuleOne_1", actualCode); + StringAssert.Contains($"Private Type TModuleOne_1", actualModuleCode[moduleOneName]); } [Test] @@ -514,7 +504,7 @@ public void UDTReservedMemberArrayIdentifier() [Category("Encapsulate Field")] public void UserEntersUDTMemberPropertyNameInConflictWithExistingField() { - const string inputCode = + var inputCode = @" Private Type TVehicle @@ -545,7 +535,7 @@ End Type [Category("Encapsulate Field")] public void UserClearsConflictingNameByEncapsulatingConflictingVariable() { - const string inputCode = + var inputCode = @" Private Type TVehicle @@ -580,7 +570,7 @@ End Type public void AddedUDTMemberConflictsWithExistingName() { var fieldUT = "mFirstValue"; - string inputCode = + var inputCode = $@" Private Type MyType @@ -609,7 +599,7 @@ End Type public void AddedFieldConflictsWithExistingUDTMemberName() { var fieldUT = "mFirstValue"; - string inputCode = + var inputCode = $@" Private Type MyType @@ -623,8 +613,7 @@ End Type "; var vbe = MockVbeBuilder.BuildFromSingleStandardModule(inputCode, out _).Object; - var state = MockParser.CreateAndParse(vbe); - using (state) + using (var state = MockParser.CreateAndParse(vbe)) { var mTypeTarget = state.DeclarationFinder.DeclarationsWithType(DeclarationType.Variable) .First(d => d.IdentifierName == "myType"); @@ -632,16 +621,16 @@ End Type var mFirstTarget = state.DeclarationFinder.DeclarationsWithType(DeclarationType.Variable) .First(d => d.IdentifierName == fieldUT); - var resolver = new EncapsulateFieldTestComponentResolver(state, null, mFirstTarget.QualifiedModuleName); + Support.SetupResolver(state); - var contextCollections = resolver.Resolve(); - //.RetrieveCandidateSets(mTypeTarget.QualifiedModuleName); + var candidateSetsProviderFactory = Support.Resolve(); + var candidateSets = candidateSetsProviderFactory.Create(state, Support.Resolve(), mFirstTarget.QualifiedModuleName); - var encapsulateFieldCandidates = contextCollections.EncapsulateFieldUseBackingFieldCandidates; + var encapsulateFieldCandidates = candidateSets.EncapsulateFieldUseBackingFieldCandidates; - var finderFactory = resolver.Resolve(); + var finderFactory = Support.Resolve(); - var conflictFinder = finderFactory.Create(state, contextCollections.EncapsulateFieldUseBackingFieldCandidates, contextCollections.ObjectStateFieldCandidates); + var conflictFinder = finderFactory.Create(state, candidateSets.EncapsulateFieldUseBackingFieldCandidates, candidateSets.ObjectStateFieldCandidates); foreach (var candidate in encapsulateFieldCandidates) { @@ -669,22 +658,19 @@ End Type public void ObjectStateUDTFieldConflictsWithAssignedProperty() { var fieldUT = "mFirstValue"; - string inputCode = + var inputCode = $@" Private {fieldUT} As Double "; var vbe = MockVbeBuilder.BuildFromSingleStandardModule(inputCode, out _).Object; - var state = MockParser.CreateAndParse(vbe); - using (state) + using (var state = MockParser.CreateAndParse(vbe)) { var mFirstTarget = state.DeclarationFinder.DeclarationsWithType(DeclarationType.Variable) .First(d => d.IdentifierName == fieldUT) as VariableDeclaration; - var resolver = new EncapsulateFieldTestComponentResolver(state, null); - - var modelFactory = resolver.Resolve(); + var modelFactory = Support.Resolve(state); var model = modelFactory.Create(mFirstTarget); var mFirstCandidate = model[mFirstTarget.IdentifierName]; diff --git a/RubberduckTests/Refactoring/EncapsulateField/EncapsulationIdentifiersTests.cs b/RubberduckTests/Refactoring/EncapsulateField/EncapsulationIdentifiersTests.cs index 16979e12c0..8c5757ca7d 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/EncapsulationIdentifiersTests.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/EncapsulationIdentifiersTests.cs @@ -8,12 +8,18 @@ public class EncapsulationIdentifiersTests { private EncapsulateFieldTestSupport Support { get; } = new EncapsulateFieldTestSupport(); + [SetUp] + public void ExecutesBeforeAllTests() + { + Support.ResetResolver(); + } + [Test] [Category("Refactorings")] [Category("Encapsulate Field")] public void FieldNameAttributeValidation_DefaultsToAvailableFieldName() { - string inputCode = + var inputCode = $@"Public fizz As String 'fizz1 is the intial default name for encapsulating 'fizz' @@ -36,7 +42,7 @@ End Property [Category("Encapsulate Field")] public void FieldNameValuesPerSequenceOfPropertyNameChanges() { - string inputCode = "Public fizz As String"; + var inputCode = "Public fizz As String"; var encapsulatedField = Support.RetrieveEncapsulateFieldCandidate(inputCode, "fizz"); StringAssert.AreEqualIgnoringCase("fizz_1", encapsulatedField.BackingIdentifier); diff --git a/RubberduckTests/Refactoring/EncapsulateField/PreviewerTests.cs b/RubberduckTests/Refactoring/EncapsulateField/PreviewerTests.cs index d8f7f5a3d3..b7bf02f9c0 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/PreviewerTests.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/PreviewerTests.cs @@ -1,6 +1,5 @@ using NUnit.Framework; using Rubberduck.Parsing.Rewriter; -using Rubberduck.Parsing.Symbols; using Rubberduck.Parsing.VBA; using Rubberduck.Refactorings; using Rubberduck.Refactorings.EncapsulateField; @@ -13,10 +12,16 @@ namespace RubberduckTests.Refactoring.EncapsulateField { [TestFixture] - public class EncapsulateFieldPreviewerTests : InteractiveRefactoringTestBase + public class EncapsulateFieldPreviewerTests : EncapsulateFieldInteractiveRefactoringTest { private EncapsulateFieldTestSupport Support { get; } = new EncapsulateFieldTestSupport(); + [SetUp] + public void ExecutesBeforeAllTests() + { + Support.ResetResolver(); + } + [TestCase(EncapsulateFieldStrategy.UseBackingFields)] [TestCase(EncapsulateFieldStrategy.ConvertFieldsToUDTMembers)] [Category("Refactorings")] @@ -24,30 +29,27 @@ public class EncapsulateFieldPreviewerTests : InteractiveRefactoringTestBase(); + var target = state.DeclarationFinder.MatchName("mTest").First(); + var modelfactory = Support.Resolve(); var model = modelfactory.Create(target); model.EncapsulateFieldStrategy = strategy; var field = model["mTest"]; field.PropertyIdentifier = "ATest"; - var previewProvider = resolver.Resolve(); + var previewProvider = Support.Resolve(); var firstPreview = previewProvider.Preview(model); StringAssert.Contains("Property Get ATest", firstPreview); @@ -65,7 +67,7 @@ public void Preview_EditPropertyIdentifier(EncapsulateFieldStrategy strategy) [Category(nameof(EncapsulateFieldPreviewProvider))] public void PreviewWrapMember_EditPropertyIdentifier() { - string inputCode = + var inputCode = $@"Option Explicit Private Type T{MockVbeBuilder.TestModuleName} @@ -83,17 +85,15 @@ End Type Private bType As B{MockVbeBuilder.TestModuleName} "; - var presenterAction = Support.SetParametersForSingleTarget("mTest", "ATest"); - var actualCode = RefactoredCode("mTest", DeclarationType.Variable, null, (MockVbeBuilder.TestModuleName, inputCode, ComponentType.StandardModule)); - var vbe = MockVbeBuilder.BuildFromSingleModule(inputCode, ComponentType.StandardModule, out _); (RubberduckParserState state, IRewritingManager rewritingManager) = MockParser.CreateAndParseWithRewritingManager(vbe.Object); using (state) { + Support.SetupResolver(state, rewritingManager, null); + var target = state.DeclarationFinder.MatchName("mTest").First(); - var resolver = new EncapsulateFieldTestComponentResolver(state, rewritingManager); - var modelfactory = resolver.Resolve(); + var modelfactory = Support.Resolve(); var model = modelfactory.Create(target); var field = model["mTest"]; @@ -103,7 +103,7 @@ End Type var test = model.ObjectStateUDTCandidates; Assert.AreEqual(3, test.Count()); - var previewProvider = resolver.Resolve(); + var previewProvider = Support.Resolve(); var firstPreview = previewProvider.Preview(model); StringAssert.Contains("Property Get ATest", firstPreview); @@ -122,7 +122,7 @@ End Type [Category(nameof(EncapsulateFieldPreviewProvider))] public void Preview_IncludeEndOfChangesMarker(EncapsulateFieldStrategy strategy) { - string inputCode = + var inputCode = $@"Option Explicit Public mTest As Long @@ -132,11 +132,12 @@ public void Preview_IncludeEndOfChangesMarker(EncapsulateFieldStrategy strategy) (RubberduckParserState state, IRewritingManager rewritingManager) = MockParser.CreateAndParseWithRewritingManager(vbe.Object); using (state) { + Support.SetupResolver(state, rewritingManager, null); + var target = state.DeclarationFinder.MatchName("mTest").First(); - var resolver = new EncapsulateFieldTestComponentResolver(state, rewritingManager); - var modelfactory = resolver.Resolve(); - var previewProvider = resolver.Resolve(); + var modelfactory = Support.Resolve(); + var previewProvider = Support.Resolve(); var model = modelfactory.Create(target); diff --git a/RubberduckTests/Refactoring/EncapsulateField/PropertyAttributeSetsGeneratorTests.cs b/RubberduckTests/Refactoring/EncapsulateField/PropertyAttributeSetsGeneratorTests.cs index c7313dcfbc..ad84e08cc1 100644 --- a/RubberduckTests/Refactoring/EncapsulateField/PropertyAttributeSetsGeneratorTests.cs +++ b/RubberduckTests/Refactoring/EncapsulateField/PropertyAttributeSetsGeneratorTests.cs @@ -1,10 +1,6 @@ using NUnit.Framework; using Rubberduck.Refactorings.EncapsulateField; -using System; -using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; using RubberduckTests.Mocks; using Rubberduck.Refactorings; @@ -13,6 +9,14 @@ namespace RubberduckTests.Refactoring.EncapsulateField [TestFixture] public class PropertyAttributeSetsGeneratorTests { + private EncapsulateFieldTestSupport Support { get; } = new EncapsulateFieldTestSupport(); + + [SetUp] + public void ExecutesBeforeAllTests() + { + Support.ResetResolver(); + } + [Test] [Category("Refactorings")] [Category("Encapsulate Field")] @@ -38,15 +42,12 @@ End Type var vbe = MockVbeBuilder.BuildFromSingleStandardModule(inputCode, out _).Object; - var (state, rewritingManager) = MockParser.CreateAndParseWithRewritingManager(vbe); - using (state) + using (var state = MockParser.CreateAndParse(vbe)) { var encapsulateTarget = state.AllUserDeclarations.Single(d => d.IdentifierName.Equals("mVehicle")); var objectStateUDTTarget = state.AllUserDeclarations.Single(d => d.IdentifierName.Equals("this")); - var resolver = new EncapsulateFieldTestComponentResolver(state, null); - - var encapsulateFieldCandidateFactory = resolver.Resolve(); + var encapsulateFieldCandidateFactory = Support.Resolve(state); var objStateCandidate = encapsulateFieldCandidateFactory.CreateFieldCandidate(objectStateUDTTarget); var objStateUDT = encapsulateFieldCandidateFactory.CreateObjectStateField(objStateCandidate as IUserDefinedTypeCandidate); @@ -103,15 +104,12 @@ End Type "; var vbe = MockVbeBuilder.BuildFromSingleStandardModule(inputCode, out _).Object; - var (state, rewritingManager) = MockParser.CreateAndParseWithRewritingManager(vbe); - using (state) + using (var state = MockParser.CreateAndParse(vbe)) { var encapsulateTarget = state.AllUserDeclarations.Single(d => d.IdentifierName.Equals("mTest")); var objectStateUDTTarget = state.AllUserDeclarations.Single(d => d.IdentifierName.Equals("this")); - var resolver = new EncapsulateFieldTestComponentResolver(state, null); - - var encapsulateFieldCandidateFactory = resolver.Resolve(); + var encapsulateFieldCandidateFactory = Support.Resolve(state); var objStateCandidate = encapsulateFieldCandidateFactory.CreateFieldCandidate(objectStateUDTTarget); var objStateUDT = encapsulateFieldCandidateFactory.CreateObjectStateField(objStateCandidate as IUserDefinedTypeCandidate); diff --git a/RubberduckTests/Refactoring/ModifyUserDefinedType/ModifyUserDefinedTypeRefactoringActionTests.cs b/RubberduckTests/Refactoring/ModifyUserDefinedType/ModifyUserDefinedTypeRefactoringActionTests.cs index c816711891..7d59205298 100644 --- a/RubberduckTests/Refactoring/ModifyUserDefinedType/ModifyUserDefinedTypeRefactoringActionTests.cs +++ b/RubberduckTests/Refactoring/ModifyUserDefinedType/ModifyUserDefinedTypeRefactoringActionTests.cs @@ -25,7 +25,7 @@ public class ModifyUserDefinedTypeRefactoringActionTests : RefactoringActionTest [Category(nameof(ModifyUserDefinedTypeRefactoringAction))] public void AddSingleMember(string prototypeDeclaration, DeclarationType declarationType) { - string inputCode = + var inputCode = $@" Option Explicit @@ -52,7 +52,7 @@ End Type [Category(nameof(ModifyUserDefinedTypeRefactoringAction))] public void MultipleNewMembers() { - string inputCode = + var inputCode = $@" Option Explicit @@ -85,7 +85,7 @@ End Type [Category(nameof(ModifyUserDefinedTypeRefactoringAction))] public void MultipleNewMembersRemoveMultiple() { - string inputCode = + var inputCode = $@" Option Explicit @@ -130,7 +130,7 @@ End Type [Category(nameof(ModifyUserDefinedTypeRefactoringAction))] public void RemoveOnlyMultiple() { - string inputCode = + var inputCode = $@" Option Explicit diff --git a/RubberduckTests/Refactoring/ReplaceReferences/ReplaceReferencesRefactoringActionTests.cs b/RubberduckTests/Refactoring/ReplaceReferences/ReplaceReferencesRefactoringActionTests.cs index c4dda9ea66..7e3f63e179 100644 --- a/RubberduckTests/Refactoring/ReplaceReferences/ReplaceReferencesRefactoringActionTests.cs +++ b/RubberduckTests/Refactoring/ReplaceReferences/ReplaceReferencesRefactoringActionTests.cs @@ -20,7 +20,7 @@ public class ReplaceReferencesRefactoringActionTests : RefactoringActionTestBase [Category(nameof(ReplaceReferencesRefactoringAction))] public void ValueField() { - string inputCode = + var inputCode = $@" Option Explicit @@ -102,7 +102,7 @@ End Sub public void ArrayReferences2() { var sourceModuleName = "SourceModule"; - string inputCode = + var inputCode = @"Private Sub Foo() ReDim arr(0 To 1) arr(1) = arr(0) @@ -127,7 +127,7 @@ ReDim bar(0 To 1) public void ArrayReferences() { var sourceModuleName = "SourceModule"; - string inputCode = + var inputCode = $@" Option Explicit @@ -207,7 +207,7 @@ End Sub [Category(nameof(ReplaceReferencesRefactoringAction))] public void UDTField_MemberAccess() { - string inputCode = + var inputCode = $@" Private Type TBar FirstVal As String @@ -256,7 +256,7 @@ End Function [Category(nameof(ReplaceReferencesRefactoringAction))] public void UDTField_MemberAccessMultipleInstances() { - string inputCode = + var inputCode = $@" Private Type TBar FirstVal As String @@ -314,7 +314,7 @@ End Function [Category(nameof(ReplaceReferencesRefactoringAction))] public void UDTField_WithMemberAccess() { - string inputCode = + var inputCode = $@" Private Type TBar FirstVal As String From 84c2173f350fb7a4bae3322912a43f7395f4b3ef Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Mon, 12 Oct 2020 17:37:53 -0700 Subject: [PATCH 64/67] Address PR comment --- .../UserDefinedTypeInstance.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Rubberduck.Refactorings/ReplacePrivateUDTMemberReferences/UserDefinedTypeInstance.cs b/Rubberduck.Refactorings/ReplacePrivateUDTMemberReferences/UserDefinedTypeInstance.cs index 2bf16aba0d..6b38782dca 100644 --- a/Rubberduck.Refactorings/ReplacePrivateUDTMemberReferences/UserDefinedTypeInstance.cs +++ b/Rubberduck.Refactorings/ReplacePrivateUDTMemberReferences/UserDefinedTypeInstance.cs @@ -49,9 +49,10 @@ private bool IsRelatedReference(IdentifierReference idRef, IEnumerable(); - while (accessExprContext != null && ++guard < 100) + while (accessExprContext != null && ++guard < maxGetAncestorAttempts) { var prCtxt = accessExprContext as ParserRuleContext; if (prCtxt == goalContext) @@ -61,7 +62,7 @@ private bool IsRelatedReference(IdentifierReference idRef, IEnumerable(); } - Debug.Assert(guard < 100); + Debug.Assert(guard < maxGetAncestorAttempts); return false; } } From 9db29d95b0ff63800686964e8ea9560bbec04025 Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Tue, 13 Oct 2020 05:26:17 -0700 Subject: [PATCH 65/67] Add NewLines Resource class --- .../EncapsulateFieldInsertNewCodeRefactoringAction.cs | 8 +++----- .../NewContentAggregator.cs | 10 +++++----- .../AddInterfaceImplementationsRefactoringAction.cs | 4 ++-- Rubberduck.Resources/NewLines.cs | 9 +++++++++ 4 files changed, 19 insertions(+), 12 deletions(-) create mode 100644 Rubberduck.Resources/NewLines.cs diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeRefactoringAction.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeRefactoringAction.cs index c40b5f3aa0..98fb44da45 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeRefactoringAction.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/EncapsulateFieldInsertNewCodeRefactoringAction.cs @@ -90,9 +90,7 @@ private void InsertBlocks(EncapsulateFieldInsertNewCodeModel model, IRewriteSess return; } - var doubleSpace = $"{Environment.NewLine}{Environment.NewLine}"; - - var allNewContent = string.Join(doubleSpace, new string[] { newDeclarationSectionBlock }); + var allNewContent = string.Join(NewLines.DOUBLE_SPACE, new string[] { newDeclarationSectionBlock }); var previewMarker = model.NewContentAggregator.RetrieveBlock(RubberduckUI.EncapsulateField_PreviewMarker); if (!string.IsNullOrEmpty(previewMarker)) @@ -109,10 +107,10 @@ private void InsertBlocks(EncapsulateFieldInsertNewCodeModel model, IRewriteSess if (codeSectionStartIndex.HasValue) { - rewriter.InsertBefore(codeSectionStartIndex.Value, $"{allNewContent}{doubleSpace}"); + rewriter.InsertBefore(codeSectionStartIndex.Value, $"{allNewContent}{NewLines.DOUBLE_SPACE}"); return; } - rewriter.InsertBefore(rewriter.TokenStream.Size - 1, $"{doubleSpace}{allNewContent}"); + rewriter.InsertBefore(rewriter.TokenStream.Size - 1, $"{NewLines.DOUBLE_SPACE}{allNewContent}"); } } } diff --git a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/NewContentAggregator.cs b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/NewContentAggregator.cs index fc649081ca..09924c6198 100644 --- a/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/NewContentAggregator.cs +++ b/Rubberduck.Refactorings/EncapsulateField/EncapsulateFieldInsertNewCode/NewContentAggregator.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Rubberduck.Resources; namespace Rubberduck.Refactorings.EncapsulateField { @@ -49,7 +50,6 @@ public interface INewContentAggregator /// public class NewContentAggregator : INewContentAggregator { - private readonly static string _doubleSpace = $"{Environment.NewLine}{Environment.NewLine}"; private readonly Dictionary> _newContent; private Dictionary> _unStructuredContent; @@ -90,12 +90,12 @@ public string RetrieveBlock(params NewContentType[] newContentTypes) var block = string.Empty; foreach (var newContentType in newContentTypes) { - var newContent = string.Join(_doubleSpace, _newContent[newContentType]); + var newContent = string.Join(NewLines.DOUBLE_SPACE, _newContent[newContentType]); if (!string.IsNullOrEmpty(newContent)) { block = string.IsNullOrEmpty(block) ? newContent - : $"{block}{_doubleSpace}{newContent}"; + : $"{block}{NewLines.DOUBLE_SPACE}{newContent}"; } } return LimitNewLines(block.Trim(), NewLineLimit); @@ -108,12 +108,12 @@ public string RetrieveBlock(params string[] contentIdentifiers) { if (_unStructuredContent.TryGetValue(identifier, out var adHocContent)) { - var newContent = string.Join(_doubleSpace, adHocContent); + var newContent = string.Join(NewLines.DOUBLE_SPACE, adHocContent); if (!string.IsNullOrEmpty(newContent)) { block = string.IsNullOrEmpty(block) ? newContent - : $"{block}{_doubleSpace}{newContent}"; + : $"{block}{NewLines.DOUBLE_SPACE}{newContent}"; } } } diff --git a/Rubberduck.Refactorings/ImplementInterface/AddInterfaceImplementations/AddInterfaceImplementationsRefactoringAction.cs b/Rubberduck.Refactorings/ImplementInterface/AddInterfaceImplementations/AddInterfaceImplementationsRefactoringAction.cs index e2f475521a..ac7de29e44 100644 --- a/Rubberduck.Refactorings/ImplementInterface/AddInterfaceImplementations/AddInterfaceImplementationsRefactoringAction.cs +++ b/Rubberduck.Refactorings/ImplementInterface/AddInterfaceImplementations/AddInterfaceImplementationsRefactoringAction.cs @@ -4,7 +4,7 @@ using Rubberduck.Parsing.Grammar; using Rubberduck.Parsing.Rewriter; using Rubberduck.Parsing.Symbols; - +using Rubberduck.Resources; namespace Rubberduck.Refactorings.AddInterfaceImplementations { @@ -68,7 +68,7 @@ private string GetInterfaceMember(Declaration member, string interfaceName) members.Add(propertySet); } - return string.Join($"{Environment.NewLine}{Environment.NewLine}", members); + return string.Join($"{NewLines.DOUBLE_SPACE}", members); } return string.Empty; diff --git a/Rubberduck.Resources/NewLines.cs b/Rubberduck.Resources/NewLines.cs new file mode 100644 index 0000000000..66a002d42b --- /dev/null +++ b/Rubberduck.Resources/NewLines.cs @@ -0,0 +1,9 @@ +using System; + +namespace Rubberduck.Resources +{ + public static class NewLines + { + public static readonly string DOUBLE_SPACE = $"{Environment.NewLine}{Environment.NewLine}"; + } +} From 3e4a0c5ae8c54fb5ba70751568de69aedcd059bc Mon Sep 17 00:00:00 2001 From: Brian Zenger Date: Fri, 16 Oct 2020 06:48:42 -0700 Subject: [PATCH 66/67] Address PR comments --- .../Concrete/ImplicitByRefModifierInspection.cs | 2 +- .../Concrete/MisleadingByRefParameterInspection.cs | 12 ++++-------- .../Inspections/InspectionInfo.Designer.cs | 2 +- Rubberduck.Resources/Inspections/InspectionInfo.resx | 2 +- .../Inspections/InspectionNames.Designer.cs | 2 +- .../Inspections/InspectionNames.resx | 2 +- .../Inspections/InspectionResults.Designer.cs | 2 +- .../Inspections/InspectionResults.resx | 4 ++-- 8 files changed, 12 insertions(+), 16 deletions(-) diff --git a/Rubberduck.CodeAnalysis/Inspections/Concrete/ImplicitByRefModifierInspection.cs b/Rubberduck.CodeAnalysis/Inspections/Concrete/ImplicitByRefModifierInspection.cs index 833d778a1f..72a01905bb 100644 --- a/Rubberduck.CodeAnalysis/Inspections/Concrete/ImplicitByRefModifierInspection.cs +++ b/Rubberduck.CodeAnalysis/Inspections/Concrete/ImplicitByRefModifierInspection.cs @@ -67,7 +67,7 @@ protected override bool IsResultDeclaration(Declaration declaration, Declaration && !finder.FindEventHandlers().Contains(enclosingMethod); } - private bool IsPropertyMutatorRHSParameter(ModuleBodyElementDeclaration enclosingMethod, ParameterDeclaration implicitByRefParameter) + private static bool IsPropertyMutatorRHSParameter(ModuleBodyElementDeclaration enclosingMethod, ParameterDeclaration implicitByRefParameter) { return (enclosingMethod.DeclarationType.HasFlag(DeclarationType.PropertyLet) || enclosingMethod.DeclarationType.HasFlag(DeclarationType.PropertySet)) diff --git a/Rubberduck.CodeAnalysis/Inspections/Concrete/MisleadingByRefParameterInspection.cs b/Rubberduck.CodeAnalysis/Inspections/Concrete/MisleadingByRefParameterInspection.cs index 57f65acd52..7c58237a9d 100644 --- a/Rubberduck.CodeAnalysis/Inspections/Concrete/MisleadingByRefParameterInspection.cs +++ b/Rubberduck.CodeAnalysis/Inspections/Concrete/MisleadingByRefParameterInspection.cs @@ -49,24 +49,20 @@ public MisleadingByRefParameterInspection(IDeclarationFinderProvider declaration protected override bool IsResultDeclaration(Declaration declaration, DeclarationFinder finder) { - if ((declaration is ParameterDeclaration parameter) + return declaration is ParameterDeclaration parameter + && !(parameter.AsTypeDeclaration?.DeclarationType.HasFlag(DeclarationType.UserDefinedType) ?? false) && parameter.ParentDeclaration is ModuleBodyElementDeclaration enclosingMethod && (enclosingMethod.DeclarationType.HasFlag(DeclarationType.PropertyLet) || enclosingMethod.DeclarationType.HasFlag(DeclarationType.PropertySet)) && enclosingMethod.Parameters.Last() == parameter - && !(parameter.AsTypeDeclaration?.DeclarationType.HasFlag(DeclarationType.UserDefinedType) ?? false)) - { - return parameter.IsByRef && !parameter.IsImplicitByRef; - } - - return false; + && parameter.IsByRef && !parameter.IsImplicitByRef; } protected override string ResultDescription(Declaration declaration) { return string.Format( InspectionResults.MisleadingByRefParameterInspection, - declaration.IdentifierName); + declaration.IdentifierName, declaration.ParentDeclaration.QualifiedName.MemberName); } } } diff --git a/Rubberduck.Resources/Inspections/InspectionInfo.Designer.cs b/Rubberduck.Resources/Inspections/InspectionInfo.Designer.cs index a6e9ba4553..45f128e1be 100644 --- a/Rubberduck.Resources/Inspections/InspectionInfo.Designer.cs +++ b/Rubberduck.Resources/Inspections/InspectionInfo.Designer.cs @@ -502,7 +502,7 @@ public class InspectionInfo { } /// - /// Looks up a localized string similar to The last parameter (the 'Value' parameter) of property mutators are always passed by value. This is true regardless of the presence or absence of a ByVal modifier. Exception: UserDefinedType parameters must always be passed by reference in all cases.. + /// Looks up a localized string similar to The last parameter (the 'Value' parameter) of property mutators are always passed ByVal. This is true regardless of the presence or absence of a ByRef or ByVal modifier. Exception: A UserDefinedType must always be passed ByRef even when it is the last parameter of a property mutator.. /// public static string MisleadingByRefParameterInspection { get { diff --git a/Rubberduck.Resources/Inspections/InspectionInfo.resx b/Rubberduck.Resources/Inspections/InspectionInfo.resx index f9a1b9b469..92f1fccf27 100644 --- a/Rubberduck.Resources/Inspections/InspectionInfo.resx +++ b/Rubberduck.Resources/Inspections/InspectionInfo.resx @@ -443,6 +443,6 @@ If the parameter can be null, ignore this inspection result; passing a null valu An annotation has more arguments than allowed; superfluous arguments are ignored. - The last parameter (the 'Value' parameter) of property mutators are always passed by value. This is true regardless of the presence or absence of a ByVal modifier. Exception: UserDefinedType parameters must always be passed by reference in all cases. + The last parameter (the 'Value' parameter) of property mutators are always passed ByVal. This is true regardless of the presence or absence of a ByRef or ByVal modifier. Exception: A UserDefinedType must always be passed ByRef even when it is the last parameter of a property mutator. \ No newline at end of file diff --git a/Rubberduck.Resources/Inspections/InspectionNames.Designer.cs b/Rubberduck.Resources/Inspections/InspectionNames.Designer.cs index 20a7ff299b..2e53ac723b 100644 --- a/Rubberduck.Resources/Inspections/InspectionNames.Designer.cs +++ b/Rubberduck.Resources/Inspections/InspectionNames.Designer.cs @@ -502,7 +502,7 @@ public class InspectionNames { } /// - /// Looks up a localized string similar to ByRef is a misleading modifier because the parameter will be passed ByVal.. + /// Looks up a localized string similar to Misleading ByRef parameter modifier. /// public static string MisleadingByRefParameterInspection { get { diff --git a/Rubberduck.Resources/Inspections/InspectionNames.resx b/Rubberduck.Resources/Inspections/InspectionNames.resx index 8fd97a1efe..b30dde4f45 100644 --- a/Rubberduck.Resources/Inspections/InspectionNames.resx +++ b/Rubberduck.Resources/Inspections/InspectionNames.resx @@ -443,6 +443,6 @@ Superfluous annotation arguments - ByRef is a misleading modifier because the parameter will be passed ByVal. + Misleading ByRef parameter modifier \ No newline at end of file diff --git a/Rubberduck.Resources/Inspections/InspectionResults.Designer.cs b/Rubberduck.Resources/Inspections/InspectionResults.Designer.cs index 8663085b24..37adbe331b 100644 --- a/Rubberduck.Resources/Inspections/InspectionResults.Designer.cs +++ b/Rubberduck.Resources/Inspections/InspectionResults.Designer.cs @@ -520,7 +520,7 @@ public class InspectionResults { } /// - /// Looks up a localized string similar to Misleading ByRef modifier used for Parameter '{0}'.. + /// Looks up a localized string similar to Misleading ByRef modifier used for parameter '{0}' ({1}).. /// public static string MisleadingByRefParameterInspection { get { diff --git a/Rubberduck.Resources/Inspections/InspectionResults.resx b/Rubberduck.Resources/Inspections/InspectionResults.resx index 34de378b6e..d09e3f88ee 100644 --- a/Rubberduck.Resources/Inspections/InspectionResults.resx +++ b/Rubberduck.Resources/Inspections/InspectionResults.resx @@ -514,7 +514,7 @@ In memoriam, 1972-2018 {0} annotation name - Misleading ByRef modifier used for Parameter '{0}'. - {0} Parameter name + Misleading ByRef modifier used for parameter '{0}' ({1}). + {0} Parameter, {1} Member \ No newline at end of file From 04510b518adc93582f77342455f1722f1acf82f8 Mon Sep 17 00:00:00 2001 From: Max Doerner Date: Fri, 23 Oct 2020 13:31:28 +0200 Subject: [PATCH 67/67] Stop adding super type names for documents that are already there This fixes a small memory leak in the reference resolver. Although the supertypes of a class declaration are a hash set, the super type names are a list. So, adding all supertype names derived from the typeLib API every parse would grow that list unnecessarily. --- .../VBA/ReferenceManagement/ReferenceResolveRunnerBase.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Rubberduck.Parsing/VBA/ReferenceManagement/ReferenceResolveRunnerBase.cs b/Rubberduck.Parsing/VBA/ReferenceManagement/ReferenceResolveRunnerBase.cs index 315f2a149a..6fa0c0ac44 100644 --- a/Rubberduck.Parsing/VBA/ReferenceManagement/ReferenceResolveRunnerBase.cs +++ b/Rubberduck.Parsing/VBA/ReferenceManagement/ReferenceResolveRunnerBase.cs @@ -191,6 +191,11 @@ private void AddSuperTypeNamesForDocumentModules(IReadOnlyCollection