Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions RetailCoder.VBE/Common/DeclarationExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,22 @@ public static IEnumerable<Declaration> FindEventHandlers(this IEnumerable<Declar
&& declaration.IdentifierName.StartsWith(control.IdentifierName + "_"));
}

public static IEnumerable<Declaration> FindUserEventHandlers(this IEnumerable<Declaration> declarations)
{
var declarationList = declarations.ToList();

var userEvents =
declarationList.Where(item => !item.IsBuiltIn && item.DeclarationType == DeclarationType.Event).ToList();

var handlers = new List<Declaration>();
foreach (var @event in userEvents)
{
handlers.AddRange(declarations.FindHandlersForEvent(@event).Select(s => s.Item2));
}

return handlers;
}

public static IEnumerable<Declaration> FindBuiltInEventHandlers(this IEnumerable<Declaration> declarations)
{
var declarationList = declarations.ToList();
Expand Down Expand Up @@ -451,6 +467,12 @@ public static IEnumerable<Declaration> FindInterfaceImplementationMembers(this I
.Where(m => m.IdentifierName.EndsWith(interfaceMember));
}

public static IEnumerable<Declaration> FindInterfaceImplementationMembers(this IEnumerable<Declaration> declarations, Declaration interfaceDeclaration)
{
return FindInterfaceImplementationMembers(declarations)
.Where(m => m.IdentifierName == interfaceDeclaration.ComponentName + "_" + interfaceDeclaration.IdentifierName);
}

public static Declaration FindInterfaceMember(this IEnumerable<Declaration> declarations, Declaration implementation)
{
var members = FindInterfaceMembers(declarations);
Expand Down
7 changes: 1 addition & 6 deletions RetailCoder.VBE/Common/RubberduckHooks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,17 @@
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Windows.Input;
using Microsoft.Vbe.Interop;
using Rubberduck.Common.Hotkeys;
using Rubberduck.Common.WinAPI;
using Rubberduck.Settings;
using Rubberduck.UI.Command;
using Rubberduck.UI.Command.Refactorings;
using NLog;
using Rubberduck.UI;

namespace Rubberduck.Common
{
public class RubberduckHooks : IRubberduckHooks
{
private readonly VBE _vbe;
private readonly IntPtr _mainWindowHandle;
private readonly IntPtr _oldWndPointer;
private readonly User32.WndProc _oldWndProc;
Expand All @@ -34,7 +30,6 @@ public class RubberduckHooks : IRubberduckHooks

public RubberduckHooks(VBE vbe, IGeneralConfigService config, IEnumerable<CommandBase> commands)
{
_vbe = vbe;
_mainWindowHandle = (IntPtr)vbe.MainWindow.HWnd;
_oldWndProc = WindowProc;
_newWndProc = WindowProc;
Expand Down Expand Up @@ -175,7 +170,7 @@ public void Detach()
private void hook_MessageReceived(object sender, HookEventArgs e)
{
var hotkey = sender as IHotkey;
if (hotkey != null)
if (hotkey != null && hotkey.Command.CanExecute(null))
{
hotkey.Command.Execute(null);
return;
Expand Down
99 changes: 67 additions & 32 deletions RetailCoder.VBE/Inspections/ParameterCanBeByValInspection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,39 +21,86 @@ public ParameterCanBeByValInspection(RubberduckParserState state)
public override IEnumerable<InspectionResultBase> GetInspectionResults()
{
var declarations = UserDeclarations.ToList();
var issues = new List<ParameterCanBeByValInspectionResult>();

IEnumerable<Declaration> interfaceMembers = null;
interfaceMembers = declarations.FindInterfaceMembers()
.Concat(declarations.FindInterfaceImplementationMembers())
.ToList();
var interfaceDeclarationMembers = declarations.FindInterfaceMembers().ToList();
var interfaceScopes = declarations.FindInterfaceImplementationMembers().Concat(interfaceDeclarationMembers).Select(s => s.Scope);

var formEventHandlerScopes = State.FindFormEventHandlers()
.Select(handler => handler.Scope);
issues.AddRange(GetResults(declarations, interfaceDeclarationMembers));

var eventScopes = declarations.Where(item =>
!item.IsBuiltIn && item.DeclarationType == DeclarationType.Event)
.Select(e => e.Scope).Concat(State.AllDeclarations.FindBuiltInEventHandlers().Select(e => e.Scope));
var eventMembers = declarations.Where(item => !item.IsBuiltIn && item.DeclarationType == DeclarationType.Event).ToList();
var formEventHandlerScopes = State.FindFormEventHandlers().Select(handler => handler.Scope);
var eventHandlerScopes = State.AllDeclarations.FindBuiltInEventHandlers().Concat(declarations.FindUserEventHandlers()).Select(e => e.Scope);
var eventScopes = eventMembers.Select(s => s.Scope)
.Concat(formEventHandlerScopes)
.Concat(eventHandlerScopes);

issues.AddRange(GetResults(declarations, eventMembers));

var declareScopes = declarations.Where(item =>
item.DeclarationType == DeclarationType.LibraryFunction
|| item.DeclarationType == DeclarationType.LibraryProcedure)
.Select(e => e.Scope);

var ignoredScopes = formEventHandlerScopes.Concat(eventScopes).Concat(declareScopes);

var issues = declarations.Where(declaration =>

issues.AddRange(declarations.Where(declaration =>
!declaration.IsArray
&& !ignoredScopes.Contains(declaration.ParentScope)
&& !declareScopes.Contains(declaration.ParentScope)
&& !eventScopes.Contains(declaration.ParentScope)
&& !interfaceScopes.Contains(declaration.ParentScope)
&& declaration.DeclarationType == DeclarationType.Parameter
&& !interfaceMembers.Select(m => m.Scope).Contains(declaration.ParentScope)
&& ((VBAParser.ArgContext)declaration.Context).BYVAL() == null
&& !IsUsedAsByRefParam(declarations, declaration)
&& !declaration.References.Any(reference => reference.IsAssignment))
.Select(issue => new ParameterCanBeByValInspectionResult(this, issue, issue.Context, issue.QualifiedName));
.Select(issue => new ParameterCanBeByValInspectionResult(this, State, issue, issue.Context, issue.QualifiedName)));

return issues;
}

private IEnumerable<ParameterCanBeByValInspectionResult> GetResults(List<Declaration> declarations, List<Declaration> declarationMembers)
{
foreach (var declaration in declarationMembers)
{
var declarationParameters =
declarations.Where(d => d.DeclarationType == DeclarationType.Parameter &&
d.ParentDeclaration == declaration)
.OrderBy(o => o.Selection.StartLine)
.ThenBy(t => t.Selection.StartColumn)
.ToList();

var parametersAreByRef = declarationParameters.Select(s => true).ToList();

var members = declarationMembers.Any(a => a.DeclarationType == DeclarationType.Event)
? declarations.FindHandlersForEvent(declaration).Select(s => s.Item2).ToList()
: declarations.FindInterfaceImplementationMembers(declaration).ToList();

foreach (var member in members)
{
var parameters =
declarations.Where(d => d.DeclarationType == DeclarationType.Parameter &&
d.ParentDeclaration == member)
.OrderBy(o => o.Selection.StartLine)
.ThenBy(t => t.Selection.StartColumn)
.ToList();

for (var i = 0; i < parameters.Count; i++)
{
parametersAreByRef[i] = parametersAreByRef[i] && !IsUsedAsByRefParam(declarations, parameters[i]) &&
((VBAParser.ArgContext)parameters[i].Context).BYVAL() == null &&
!parameters[i].References.Any(reference => reference.IsAssignment);
}
}

for (var i = 0; i < declarationParameters.Count; i++)
{
if (parametersAreByRef[i])
{
yield return new ParameterCanBeByValInspectionResult(this, State, declarationParameters[i],
declarationParameters[i].Context, declarationParameters[i].QualifiedName);
}
}
}
}

private static bool IsUsedAsByRefParam(IEnumerable<Declaration> declarations, Declaration parameter)
{
// find the procedure calls in the procedure of the parameter.
Expand All @@ -74,28 +121,16 @@ private static bool IsUsedAsByRefParam(IEnumerable<Declaration> declarations, De
.ThenBy(arg => arg.Selection.StartColumn)
.ToArray();

for (var i = 0; i < calledProcedureArgs.Count(); i++)
foreach (var declaration in calledProcedureArgs)
{
if (((VBAParser.ArgContext)calledProcedureArgs[i].Context).BYVAL() != null)
if (((VBAParser.ArgContext)declaration.Context).BYVAL() != null)
{
continue;
}

foreach (var reference in item)
if (declaration.References.Any(reference => reference.IsAssignment))
{
if (!(reference.Context is VBAParser.ArgContext))
{
continue;
}
var context = ((dynamic)reference.Context.Parent).argsCall() as VBAParser.ArgContext;
if (context == null)
{
continue;
}
if (parameter.IdentifierName == context.GetText())
{
return true;
}
return true;
}
}
}
Expand Down
76 changes: 67 additions & 9 deletions RetailCoder.VBE/Inspections/ParameterCanBeByValInspectionResult.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
using System.Collections.Generic;
using System.Linq;
using Antlr4.Runtime;
using Rubberduck.Common;
using Rubberduck.Parsing.Grammar;
using Rubberduck.Parsing.Symbols;
using Rubberduck.Parsing.VBA;
using Rubberduck.VBEditor;

namespace Rubberduck.Inspections
Expand All @@ -10,12 +13,12 @@ public class ParameterCanBeByValInspectionResult : InspectionResultBase
{
private readonly IEnumerable<CodeInspectionQuickFix> _quickFixes;

public ParameterCanBeByValInspectionResult(IInspection inspection, Declaration target, ParserRuleContext context, QualifiedMemberName qualifiedName)
public ParameterCanBeByValInspectionResult(IInspection inspection, RubberduckParserState state, Declaration target, ParserRuleContext context, QualifiedMemberName qualifiedName)
: base(inspection, qualifiedName.QualifiedModuleName, context, target)
{
_quickFixes = new CodeInspectionQuickFix[]
{
new PassParameterByValueQuickFix(Context, QualifiedSelection),
new PassParameterByValueQuickFix(state, Target, Context, QualifiedSelection),
new IgnoreOnceQuickFix(Context, QualifiedSelection, inspection.AnnotationName)
};
}
Expand All @@ -30,21 +33,76 @@ public override string Description

public class PassParameterByValueQuickFix : CodeInspectionQuickFix
{
public PassParameterByValueQuickFix(ParserRuleContext context, QualifiedSelection selection)
private readonly RubberduckParserState _state;
private readonly Declaration _target;

public PassParameterByValueQuickFix(RubberduckParserState state, Declaration target, ParserRuleContext context, QualifiedSelection selection)
: base(context, selection, InspectionsUI.PassParameterByValueQuickFix)
{
_state = state;
_target = target;
}

public override void Fix()
{
var selection = Selection.Selection;
var selectionLength = ((VBAParser.ArgContext) Context).BYREF() == null ? 0 : 6;
if (_target.ParentDeclaration.DeclarationType == DeclarationType.Event ||
_state.AllUserDeclarations.FindInterfaceMembers().Contains(_target.ParentDeclaration))
{
FixMethods();
}
else
{
FixMethod((VBAParser.ArgContext)Context, Selection);
}
}

private void FixMethods()
{
var declarationParameters =
_state.AllUserDeclarations.Where(declaration => declaration.DeclarationType == DeclarationType.Parameter &&
declaration.ParentDeclaration == _target.ParentDeclaration)
.OrderBy(o => o.Selection.StartLine)
.ThenBy(t => t.Selection.StartColumn)
.ToList();

var parameterIndex = declarationParameters.IndexOf(_target);
if (parameterIndex == -1)
{
return; // should only happen if the parse results are stale; prevents a crash in that case
}

var members = _target.ParentDeclaration.DeclarationType == DeclarationType.Event
? _state.AllUserDeclarations.FindHandlersForEvent(_target.ParentDeclaration)
.Select(s => s.Item2)
.ToList()
: _state.AllUserDeclarations.FindInterfaceImplementationMembers(_target.ParentDeclaration).ToList();

foreach (var member in members)
{
var parameters =
_state.AllUserDeclarations.Where(declaration => declaration.DeclarationType == DeclarationType.Parameter &&
declaration.ParentDeclaration == member)
.OrderBy(o => o.Selection.StartLine)
.ThenBy(t => t.Selection.StartColumn)
.ToList();

FixMethod((VBAParser.ArgContext)parameters[parameterIndex].Context,
parameters[parameterIndex].QualifiedSelection);
}

FixMethod((VBAParser.ArgContext)declarationParameters[parameterIndex].Context,
declarationParameters[parameterIndex].QualifiedSelection);
}

private void FixMethod(VBAParser.ArgContext context, QualifiedSelection qualifiedSelection)
{
var selectionLength = context.BYREF() == null ? 0 : 6;

var module = Selection.QualifiedName.Component.CodeModule;
var lines = module.Lines[selection.StartLine, 1];
var module = qualifiedSelection.QualifiedName.Component.CodeModule;
var lines = module.Lines[context.Start.Line, 1];

var result = lines.Remove(selection.StartColumn - 1, selectionLength).Insert(selection.StartColumn - 1, Tokens.ByVal + ' ');
module.ReplaceLine(selection.StartLine, result);
var result = lines.Remove(context.Start.Column, selectionLength).Insert(context.Start.Column, Tokens.ByVal + ' ');
module.ReplaceLine(context.Start.Line, result);
}
}
}
8 changes: 4 additions & 4 deletions Rubberduck.Parsing/Symbols/IdentifierReferenceResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ private void ResolveDefault(
"Default Context: Failed to resolve {0}. Binding as much as we can.",
expression.GetText()));
}
_boundExpressionVisitor.AddIdentifierReferences(boundExpression, _qualifiedModuleName, _currentScope, _currentParent, isAssignmentTarget, false);
_boundExpressionVisitor.AddIdentifierReferences(boundExpression, _qualifiedModuleName, _currentScope, _currentParent, isAssignmentTarget, hasExplicitLetStatement);
}

private void ResolveType(ParserRuleContext expression)
Expand Down Expand Up @@ -444,7 +444,7 @@ private void ResolveRecordRange(VBAParser.RecordRangeContext recordRange)
public void Resolve(VBAParser.LineInputStmtContext context)
{
ResolveDefault(context.markedFileNumber().expression());
ResolveDefault(context.variableName().expression());
ResolveDefault(context.variableName().expression(), isAssignmentTarget: true);
}

public void Resolve(VBAParser.WidthStmtContext context)
Expand Down Expand Up @@ -496,7 +496,7 @@ public void Resolve(VBAParser.InputStmtContext context)
ResolveDefault(context.markedFileNumber().expression());
foreach (var inputVariable in context.inputList().inputVariable())
{
ResolveDefault(inputVariable.expression());
ResolveDefault(inputVariable.expression(), isAssignmentTarget: true);
}
}

Expand All @@ -522,7 +522,7 @@ public void Resolve(VBAParser.GetStmtContext context)
}
if (context.variable() != null)
{
ResolveDefault(context.variable().expression());
ResolveDefault(context.variable().expression(), isAssignmentTarget: true);
}
}

Expand Down
6 changes: 3 additions & 3 deletions Rubberduck.Parsing/Symbols/ReferencedDeclarationsCollector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ public List<Declaration> GetDeclarationsForReference(Reference reference)
break;
case DeclarationType.ClassModule:
var module = new ClassModuleDeclaration(typeQualifiedMemberName, projectDeclaration, typeName, true, new List<IAnnotation>(), attributes);
var implements = GetImplementedInterfaceNames(typeAttributes, info);
var implements = GetImplementedInterfaceNames(typeAttributes, info, module);
foreach (var supertypeName in implements)
{
module.AddSupertype(supertypeName);
Expand Down Expand Up @@ -575,7 +575,7 @@ private ParameterDeclaration CreateParameterDeclaration(IReadOnlyList<string> me
return new ParameterDeclaration(new QualifiedMemberName(typeQualifiedModuleName, paramName), memberDeclaration, paramInfo.Name, null, null, isOptional, paramInfo.IsByRef, paramInfo.IsArray);
}

private IEnumerable<string> GetImplementedInterfaceNames(TYPEATTR typeAttr, ITypeInfo info)
private IEnumerable<string> GetImplementedInterfaceNames(TYPEATTR typeAttr, ITypeInfo info, Declaration module)
{
var output = new List<string>();
for (var implIndex = 0; implIndex < typeAttr.cImplTypes; implIndex++)
Expand Down Expand Up @@ -616,7 +616,7 @@ private IEnumerable<string> GetImplementedInterfaceNames(TYPEATTR typeAttr, ITyp
else
{
_comInformation.Add(typeAttributes.guid,
new ComInformation(typeAttributes, flags, implTypeInfo, implTypeName, new QualifiedModuleName(), null, 0));
new ComInformation(typeAttributes, flags, implTypeInfo, implTypeName, module.QualifiedName.QualifiedModuleName, module, 0));
}
}

Expand Down
2 changes: 1 addition & 1 deletion Rubberduck.SourceControl/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Rubberduck.SourceControl")]
[assembly: AssemblyDescription("Rubberduck Source Control 1.0")]
[assembly: AssemblyDescription("Rubberduck Source Control 2.0")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Rubberduck")]
[assembly: AssemblyProduct("Rubberduck.SourceControl")]
Expand Down
Loading