diff --git a/RetailCoder.VBE/Inspections/ObjectVariableNotSetInspection.cs b/RetailCoder.VBE/Inspections/ObjectVariableNotSetInspection.cs index fc3db24454..d56f91bc18 100644 --- a/RetailCoder.VBE/Inspections/ObjectVariableNotSetInspection.cs +++ b/RetailCoder.VBE/Inspections/ObjectVariableNotSetInspection.cs @@ -47,7 +47,7 @@ public SetObjectVariableQuickFix(IdentifierReference reference) public override void Fix() { var codeModule = Selection.QualifiedName.Component.CodeModule; - var codeLine = codeModule.get_Lines(Selection.Selection.StartLine, 1); + var codeLine = codeModule.Lines[Selection.Selection.StartLine, 1]; var letStatementLeftSide = Context.GetText(); var setStatementLeftSide = Tokens.Set + ' ' + letStatementLeftSide; @@ -86,19 +86,26 @@ public ObjectVariableNotSetInspection(RubberduckParserState state) public override IEnumerable GetInspectionResults() { - return State.AllUserDeclarations - .Where(item => !ValueTypes.Contains(item.AsTypeName) - && !item.IsSelfAssigned - && (item.DeclarationType == DeclarationType.Variable - || item.DeclarationType == DeclarationType.Parameter)) + var interestingDeclarations = + State.AllUserDeclarations.Where(item => + !item.IsSelfAssigned && + !ValueTypes.Contains(item.AsTypeName) && + (item.AsTypeDeclaration == null || + item.AsTypeDeclaration.DeclarationType != DeclarationType.Enumeration && + item.AsTypeDeclaration.DeclarationType != DeclarationType.UserDefinedType) && + (item.DeclarationType == DeclarationType.Variable || + item.DeclarationType == DeclarationType.Parameter)); + + var interestingReferences = interestingDeclarations .SelectMany(declaration => declaration.References.Where(reference => { - var k = reference.Context.parent.GetType(); var setStmtContext = ParserRuleContextHelper.GetParent(reference.Context); - return setStmtContext != null && setStmtContext.LET() == null; - })) - .Select(reference => new ObjectVariableNotSetInspectionResult(this, reference)); + return reference.IsAssignment && setStmtContext != null && setStmtContext.LET() == null; + })); + + + return interestingReferences.Select(reference => new ObjectVariableNotSetInspectionResult(this, reference)); } } } diff --git a/RetailCoder.VBE/Inspections/ObsoleteTypeHintInspection.cs b/RetailCoder.VBE/Inspections/ObsoleteTypeHintInspection.cs index e59466226f..99bbeb4349 100644 --- a/RetailCoder.VBE/Inspections/ObsoleteTypeHintInspection.cs +++ b/RetailCoder.VBE/Inspections/ObsoleteTypeHintInspection.cs @@ -22,12 +22,20 @@ public override IEnumerable GetInspectionResults() var declarations = from item in results where item.HasTypeHint() - // bug: this inspection result only has one value. Why are we passing two in? - select new ObsoleteTypeHintInspectionResult(this, string.Format(InspectionsUI.ObsoleteTypeHintInspectionResultFormat, InspectionsUI.Inspections_Declaration, item.DeclarationType.ToString().ToLower(), item.IdentifierName), new QualifiedContext(item.QualifiedName, item.Context), item); - // todo: localize this InspectionResultFormat properly + select + new ObsoleteTypeHintInspectionResult(this, + string.Format(InspectionsUI.ObsoleteTypeHintInspectionResultFormat, + InspectionsUI.Inspections_Declaration, item.DeclarationType.ToString().ToLower(), + item.IdentifierName), new QualifiedContext(item.QualifiedName, item.Context), item); + var references = from item in results.SelectMany(d => d.References) where item.HasTypeHint() - select new ObsoleteTypeHintInspectionResult(this, string.Format(InspectionsUI.ObsoleteTypeHintInspectionResultFormat, InspectionsUI.Inspections_Usage, item.Declaration.DeclarationType.ToString().ToLower(), item.IdentifierName), new QualifiedContext(item.QualifiedModuleName, item.Context), item.Declaration); + select + new ObsoleteTypeHintInspectionResult(this, + string.Format(InspectionsUI.ObsoleteTypeHintInspectionResultFormat, + InspectionsUI.Inspections_Usage, item.Declaration.DeclarationType.ToString().ToLower(), + item.IdentifierName), new QualifiedContext(item.QualifiedModuleName, item.Context), + item.Declaration); return declarations.Union(references); } diff --git a/RetailCoder.VBE/Inspections/UntypedFunctionUsageInspectionResult.cs b/RetailCoder.VBE/Inspections/UntypedFunctionUsageInspectionResult.cs index 1120a68d52..102c094ba5 100644 --- a/RetailCoder.VBE/Inspections/UntypedFunctionUsageInspectionResult.cs +++ b/RetailCoder.VBE/Inspections/UntypedFunctionUsageInspectionResult.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using Antlr4.Runtime; using Rubberduck.Parsing.Grammar; @@ -72,6 +73,8 @@ public override void Fix() private static string GetNewSignature(ParserRuleContext context) { + Debug.Assert(context != null); + return context.children.Aggregate(string.Empty, (current, member) => { var isIdentifierNode = member is VBAParser.IdentifierContext; diff --git a/RetailCoder.VBE/Navigation/CodeExplorer/CodeExplorerItemViewModel.cs b/RetailCoder.VBE/Navigation/CodeExplorer/CodeExplorerItemViewModel.cs index d7e7b474e1..4d2d2a3ffd 100644 --- a/RetailCoder.VBE/Navigation/CodeExplorer/CodeExplorerItemViewModel.cs +++ b/RetailCoder.VBE/Navigation/CodeExplorer/CodeExplorerItemViewModel.cs @@ -28,6 +28,22 @@ public override int Compare(CodeExplorerItemViewModel x, CodeExplorerItemViewMod public class CompareByType : Comparer { + private static readonly Dictionary SortOrder = new Dictionary + { + {DeclarationType.LibraryFunction, 0}, + {DeclarationType.LibraryProcedure, 1}, + {DeclarationType.UserDefinedType, 2}, + {DeclarationType.Enumeration, 3}, + {DeclarationType.Event, 4}, + {DeclarationType.Constant, 5}, + {DeclarationType.Variable, 6}, + {DeclarationType.PropertyGet, 7}, + {DeclarationType.PropertyLet, 8}, + {DeclarationType.PropertySet, 9}, + {DeclarationType.Function, 10}, + {DeclarationType.Procedure, 11} + }; + public override int Compare(CodeExplorerItemViewModel x, CodeExplorerItemViewModel y) { if (x == y) @@ -54,6 +70,14 @@ public override int Compare(CodeExplorerItemViewModel x, CodeExplorerItemViewMod // keep separate types separate if (xNode.Declaration.DeclarationType != yNode.Declaration.DeclarationType) { + int xValue, yValue; + + if (SortOrder.TryGetValue(xNode.Declaration.DeclarationType, out xValue) && + SortOrder.TryGetValue(yNode.Declaration.DeclarationType, out yValue)) + { + return xValue < yValue ? -1 : 1; + } + return xNode.Declaration.DeclarationType < yNode.Declaration.DeclarationType ? -1 : 1; } diff --git a/RetailCoder.VBE/UI/Command/IndentCurrentProcedureCommand.cs b/RetailCoder.VBE/UI/Command/IndentCurrentProcedureCommand.cs index d054f5abb7..ca31332d36 100644 --- a/RetailCoder.VBE/UI/Command/IndentCurrentProcedureCommand.cs +++ b/RetailCoder.VBE/UI/Command/IndentCurrentProcedureCommand.cs @@ -1,4 +1,6 @@ using System.Runtime.InteropServices; +using Microsoft.Vbe.Interop; +using Rubberduck.Parsing.VBA; using Rubberduck.Settings; using Rubberduck.SmartIndenter; @@ -7,13 +9,22 @@ namespace Rubberduck.UI.Command [ComVisible(false)] public class IndentCurrentProcedureCommand : CommandBase { + private readonly VBE _vbe; + private readonly RubberduckParserState _state; private readonly IIndenter _indenter; - public IndentCurrentProcedureCommand(IIndenter indenter) + public IndentCurrentProcedureCommand(VBE vbe, RubberduckParserState state, IIndenter indenter) { + _vbe = vbe; + _state = state; _indenter = indenter; } + public override bool CanExecute(object parameter) + { + return _state.FindSelectedDeclaration(_vbe.ActiveCodePane, true) != null; + } + public override void Execute(object parameter) { _indenter.IndentCurrentProcedure(); diff --git a/RetailCoder.VBE/UI/Command/RunAllTestsCommand.cs b/RetailCoder.VBE/UI/Command/RunAllTestsCommand.cs index 918fc30c9d..0e997f5267 100644 --- a/RetailCoder.VBE/UI/Command/RunAllTestsCommand.cs +++ b/RetailCoder.VBE/UI/Command/RunAllTestsCommand.cs @@ -59,7 +59,7 @@ protected virtual void OnRunCompleted(TestRunEventArgs e) } } - public struct TestRunEventArgs + public class TestRunEventArgs { public long Duration { get; private set; } diff --git a/Rubberduck.Parsing/Symbols/ReferencedDeclarationsCollector.cs b/Rubberduck.Parsing/Symbols/ReferencedDeclarationsCollector.cs index 243c64084b..92853c5189 100644 --- a/Rubberduck.Parsing/Symbols/ReferencedDeclarationsCollector.cs +++ b/Rubberduck.Parsing/Symbols/ReferencedDeclarationsCollector.cs @@ -174,6 +174,7 @@ public IEnumerable GetDeclarationsForReference(Reference reference) info.GetTypeAttr(out typeAttributesPointer); var typeAttributes = (TYPEATTR)Marshal.PtrToStructure(typeAttributesPointer, typeof(TYPEATTR)); + info.ReleaseTypeAttr(typeAttributesPointer); var attributes = new Attributes(); if (typeAttributes.wTypeFlags.HasFlag(TYPEFLAGS.TYPEFLAG_FPREDECLID)) @@ -243,6 +244,7 @@ private Declaration CreateMemberDeclaration(out FUNCDESC memberDescriptor, TYPEK IntPtr memberDescriptorPointer; info.GetFuncDesc(memberIndex, out memberDescriptorPointer); memberDescriptor = (FUNCDESC)Marshal.PtrToStructure(memberDescriptorPointer, typeof(FUNCDESC)); + info.ReleaseFuncDesc(memberDescriptorPointer); if (memberDescriptor.callconv != CALLCONV.CC_STDCALL) { @@ -390,6 +392,7 @@ private Declaration CreateFieldDeclaration(ITypeInfo info, int fieldIndex, Decla info.GetVarDesc(fieldIndex, out ppVarDesc); var varDesc = (VARDESC)Marshal.PtrToStructure(ppVarDesc, typeof(VARDESC)); + info.ReleaseVarDesc(ppVarDesc); var names = new string[255]; int namesArrayLength; diff --git a/Rubberduck.SmartIndenter/Indenter.cs b/Rubberduck.SmartIndenter/Indenter.cs index 3d8f2d129f..c14cd95625 100644 --- a/Rubberduck.SmartIndenter/Indenter.cs +++ b/Rubberduck.SmartIndenter/Indenter.cs @@ -48,6 +48,11 @@ private void OnReportProgress(string moduleName, int progress, int max) public void IndentCurrentProcedure() { var pane = _vbe.ActiveCodePane; + + if (pane == null) + { + return; + } var module = pane.CodeModule; var selection = GetSelection(pane); @@ -69,6 +74,10 @@ public void IndentCurrentProcedure() public void IndentCurrentModule() { var pane = _vbe.ActiveCodePane; + if (pane == null) + { + return; + } Indent(pane.CodeModule.Parent); } @@ -288,12 +297,12 @@ public void Indent(string[] codeLines, string moduleName, bool reportProgress = break; case ": ": - // a multi-statement line separator => tidy up and continue - if (!currentLine.Substring(0, scan + 1).EndsWith("Then:")) + //a multi-statement line separator => tidy up and continue + if (!currentLine.Substring(0, scan + 1).EndsWith(" Then:")) { - currentLine = currentLine.Substring(0, scan + 1) + currentLine.Substring(scan + 2); + currentLine = currentLine.Substring(0, scan + 2) + currentLine.Substring(scan + 2); // check the indenting for the line segment - CheckLine(settings, currentLine, ref noIndent, out ins, out outs, ref atProcedureStart, ref atFirstProcLine, ref isInsideIfBlock); + CheckLine(settings, currentLine.Substring(start - 1), ref noIndent, out ins, out outs, ref atProcedureStart, ref atFirstProcLine, ref isInsideIfBlock); if (atProcedureStart) { atFirstDim = true; @@ -455,11 +464,7 @@ public void Indent(string[] codeLines, string moduleName, bool reportProgress = (settings.EndOfLineCommentStyle == EndOfLineCommentStyle.Absolute || settings.EndOfLineCommentStyle == EndOfLineCommentStyle.AlignInColumn)) { - gap -= lineNumber.Length - indents*settings.IndentSpaces - 1; - } - if (gap < 2) - { - gap = settings.IndentSpaces; + gap -= lineNumber.Length - indents * settings.IndentSpaces - 1; } commentStart = currentLine.Length + gap; @@ -467,15 +472,15 @@ public void Indent(string[] codeLines, string moduleName, bool reportProgress = } // work out where the text of the comment starts, to align the next line - if (currentLine.Substring(commentStart, 4) == "Rem ") + if (commentStart < currentLine.Length - 4 && currentLine.Substring(commentStart, 4) == "Rem ") { commentStart += 3; } - if (currentLine.Substring(commentStart, 1) == "'") + if (commentStart < currentLine.Length && currentLine.Substring(commentStart, 1) == "'") { commentStart += 1; } - while (currentLine.Substring(commentStart, 1) != " ") + while (commentStart < currentLine.Length && currentLine.Substring(commentStart, 1) != " ") { commentStart += 1; } @@ -528,7 +533,9 @@ public void Indent(string[] codeLines, string moduleName, bool reportProgress = atProcedureStart = false; } - CheckLine(settings, currentLine.Substring(start - 1, scan - 1), ref noIndent, out ins, out outs, ref atProcedureStart, ref atFirstProcLine, ref isInsideIfBlock); + CheckLine(settings, currentLine.Substring(start - 1, Math.Min(scan - 1, currentLine.Length - start)), + ref noIndent, out ins, out outs, ref atProcedureStart, ref atFirstProcLine, + ref isInsideIfBlock); if (atProcedureStart) { atFirstDim = true; diff --git a/RubberduckTests/CodeExplorer/CodeExplorerTests.cs b/RubberduckTests/CodeExplorer/CodeExplorerTests.cs index d0b4b2339e..4539f0dd77 100644 --- a/RubberduckTests/CodeExplorer/CodeExplorerTests.cs +++ b/RubberduckTests/CodeExplorer/CodeExplorerTests.cs @@ -1038,13 +1038,192 @@ public void CompareByType_ReturnsZeroForErrorNodes() } [TestMethod] - public void CompareByType_ReturnsFieldBelowSub() + public void CompareByType_ReturnsEventAboveConst() { var inputCode = -@"Public Foo As Boolean +@"Public Event Foo(ByVal arg1 As Integer, ByVal arg2 As String) +Public Const Bar = 0"; -Sub Bar() -End Sub"; + var builder = new MockVbeBuilder(); + VBComponent component; + var vbe = builder.BuildFromSingleStandardModule(inputCode, out component); + var mockHost = new Mock(); + mockHost.SetupAllProperties(); + + var state = new RubberduckParserState(); + var commands = new List(); + + var vm = new CodeExplorerViewModel(new FolderHelper(state, GetDelimiterConfigLoader()), state, commands); + + var parser = MockParser.Create(vbe.Object, state); + parser.Parse(); + + var eventNode = vm.Projects.First().Items.First().Items.First().Items.Single(s => s.Name == "Foo"); + var constNode = vm.Projects.First().Items.First().Items.First().Items.Single(s => s.Name == "Bar = 0"); + + Assert.AreEqual(-1, new CompareByType().Compare(eventNode, constNode)); + } + + [TestMethod] + public void CompareByType_ReturnsConstAboveField() + { + var inputCode = +@"Public Const Foo = 0 +Public Bar As Boolean"; + + var builder = new MockVbeBuilder(); + VBComponent component; + var vbe = builder.BuildFromSingleStandardModule(inputCode, out component); + var mockHost = new Mock(); + mockHost.SetupAllProperties(); + + var state = new RubberduckParserState(); + var commands = new List(); + + var vm = new CodeExplorerViewModel(new FolderHelper(state, GetDelimiterConfigLoader()), state, commands); + + var parser = MockParser.Create(vbe.Object, state); + parser.Parse(); + + var constNode = vm.Projects.First().Items.First().Items.First().Items.Single(s => s.Name == "Foo = 0"); + var fieldNode = vm.Projects.First().Items.First().Items.First().Items.Single(s => s.Name == "Bar"); + + Assert.AreEqual(-1, new CompareByType().Compare(constNode, fieldNode)); + } + + [TestMethod] + public void CompareByType_ReturnsFieldAbovePropertyGet() + { + var inputCode = +@"Private Bar As Boolean + +Public Property Get Foo() As Variant +End Property +"; + + var builder = new MockVbeBuilder(); + VBComponent component; + var vbe = builder.BuildFromSingleStandardModule(inputCode, out component); + var mockHost = new Mock(); + mockHost.SetupAllProperties(); + + var state = new RubberduckParserState(); + var commands = new List(); + + var vm = new CodeExplorerViewModel(new FolderHelper(state, GetDelimiterConfigLoader()), state, commands); + + var parser = MockParser.Create(vbe.Object, state); + parser.Parse(); + + var fieldNode = vm.Projects.First().Items.First().Items.First().Items.Single(s => s.Name == "Bar"); + var propertyGetNode = vm.Projects.First().Items.First().Items.First().Items.Single(s => s.Name == "Foo (Get)"); + + Assert.AreEqual(-1, new CompareByType().Compare(fieldNode, propertyGetNode)); + } + + [TestMethod] + public void CompareByType_ReturnsPropertyGetAbovePropertyLet() + { + var inputCode = +@"Public Property Get Foo() As Variant +End Property + +Public Property Let Foo(ByVal Value As Variant) +End Property +"; + + var builder = new MockVbeBuilder(); + VBComponent component; + var vbe = builder.BuildFromSingleStandardModule(inputCode, out component); + var mockHost = new Mock(); + mockHost.SetupAllProperties(); + + var state = new RubberduckParserState(); + var commands = new List(); + + var vm = new CodeExplorerViewModel(new FolderHelper(state, GetDelimiterConfigLoader()), state, commands); + + var parser = MockParser.Create(vbe.Object, state); + parser.Parse(); + + var propertyGetNode = vm.Projects.First().Items.First().Items.First().Items.Single(s => s.Name == "Foo (Get)"); + var propertyLetNode = vm.Projects.First().Items.First().Items.First().Items.Single(s => s.Name == "Foo (Let)"); + + Assert.AreEqual(-1, new CompareByType().Compare(propertyGetNode, propertyLetNode)); + } + + [TestMethod] + public void CompareByType_ReturnsPropertyLetAbovePropertySet() + { + var inputCode = +@"Public Property Let Foo(ByVal Value As Variant) +End Property + +Public Property Set Foo(ByVal Value As Variant) +End Property +"; + + var builder = new MockVbeBuilder(); + VBComponent component; + var vbe = builder.BuildFromSingleStandardModule(inputCode, out component); + var mockHost = new Mock(); + mockHost.SetupAllProperties(); + + var state = new RubberduckParserState(); + var commands = new List(); + + var vm = new CodeExplorerViewModel(new FolderHelper(state, GetDelimiterConfigLoader()), state, commands); + + var parser = MockParser.Create(vbe.Object, state); + parser.Parse(); + + var propertyLetNode = vm.Projects.First().Items.First().Items.First().Items.Single(s => s.Name == "Foo (Let)"); + var propertySetNode = vm.Projects.First().Items.First().Items.First().Items.Single(s => s.Name == "Foo (Set)"); + + Assert.AreEqual(-1, new CompareByType().Compare(propertyLetNode, propertySetNode)); + } + + [TestMethod] + public void CompareByType_ReturnsPropertySetAboveFunction() + { + var inputCode = +@"Public Property Set Foo(ByVal Value As Variant) +End Property + +Public Function Bar() As Boolean +End Function +"; + + var builder = new MockVbeBuilder(); + VBComponent component; + var vbe = builder.BuildFromSingleStandardModule(inputCode, out component); + var mockHost = new Mock(); + mockHost.SetupAllProperties(); + + var state = new RubberduckParserState(); + var commands = new List(); + + var vm = new CodeExplorerViewModel(new FolderHelper(state, GetDelimiterConfigLoader()), state, commands); + + var parser = MockParser.Create(vbe.Object, state); + parser.Parse(); + + var propertySetNode = vm.Projects.First().Items.First().Items.First().Items.Single(s => s.Name == "Foo (Set)"); + var functionNode = vm.Projects.First().Items.First().Items.First().Items.Single(s => s.Name == "Bar"); + + Assert.AreEqual(-1, new CompareByType().Compare(propertySetNode, functionNode)); + } + + [TestMethod] + public void CompareByType_ReturnsFunctionAboveSub() + { + var inputCode = +@"Public Function Foo() As Boolean +End Function + +Public Sub Bar() +End Sub +"; var builder = new MockVbeBuilder(); VBComponent component; @@ -1060,10 +1239,10 @@ Sub Bar() var parser = MockParser.Create(vbe.Object, state); parser.Parse(); + var functionNode = vm.Projects.First().Items.First().Items.First().Items.Single(s => s.Name == "Foo"); var subNode = vm.Projects.First().Items.First().Items.First().Items.Single(s => s.Name == "Bar"); - var fieldNode = vm.Projects.First().Items.First().Items.First().Items.Single(s => s.Name == "Foo"); - Assert.AreEqual(-1, new CompareByType().Compare(subNode, fieldNode)); + Assert.AreEqual(-1, new CompareByType().Compare(functionNode, subNode)); } [TestMethod]