//A move looks like a deleted method and then an added method in // another place, this find the added methods private static IEnumerable <MethodDeclarationSyntax> GetInsertedMethods(IEnumerable <Diff> diffs) { return(diffs .Where(diff => TriviaCompare.IsSpanInNodeTrivia(diff.Ancestor.Span, diff.Ancestor.Node)) .Select(diff => diff.Changed.Node as MethodDeclarationSyntax) .Where(node => node != null).CacheEnumerable()); }
/// <summary> /// Given a Repo and a Tree find any possible FalsePositives /// </summary> /// <param name="repo">Repo that has the remote changes that need to be checked</param> /// <param name="local">The syntax tree that will be compared with syntax trees from repo</param> public static IEnumerable <DetectedFalsePositive> ForFalsePositive(Repo repo, SyntaxTree local) { var relativePath = GetRelativePath(repo.LocalRepoDirectory, local.FilePath); var pulls = GetPulls(repo, relativePath); foreach (var pull in pulls) { var conflicts = Diff3.Compare(pull.File.BaseTree, local, pull.File.HeadTree); var locs = GetInsertedMethods(conflicts.Local); var rems = GetInsertedMethods(conflicts.Remote); var methodConflicts = conflicts.Conflicts.Where(con => con.Ancestor.Node is MethodDeclarationSyntax); foreach (var c in methodConflicts) { var ancestor = (MethodDeclarationSyntax)c.Ancestor.Node; var conflict = GetInnerMethodConflicts(ancestor, c.Remote, c.Local, locs, InnerMethodConflict.Local.Moved); //If the local was not moved, the local could have still been changed if (conflict == null) { conflict = GetInnerMethodConflicts(ancestor, c.Local, c.Remote, rems, InnerMethodConflict.Local.Changed); if (conflict == null) { continue; } } if (!conflict.DiffResult.Conflicts.Any(con => TriviaCompare.IsSemanticChange(con.Local.Node, con.Remote.Node))) { yield return(new DetectedFalsePositive { Location = Location.Create(local, conflict.GetLocal().Identifier.Span), MethodName = ancestor.Identifier.ToString(), RemoteFile = pull.File, RemoteChange = pull.Change, ConflictType = conflict.LocalLocation == InnerMethodConflict.Local.Changed ? DetectedFalsePositive.ConflictTypes.LocalMethodChanged : DetectedFalsePositive.ConflictTypes.LocalMethodRemoved, }); } } } }
//Given the base class and a file that contains another version of the file private static IEnumerable <Diff> DiffClassVersion(ClassDeclarationSyntax b, RepoFile f) { var ancestorDecs = f.BaseTree .GetRoot() .DescendantNodes() .OfType <ClassDeclarationSyntax>(); var remoteDecs = f.HeadTree .GetRoot() .DescendantNodes() .OfType <ClassDeclarationSyntax>(); var merged = MergeClassDeclarationSyntaxes(ancestorDecs, remoteDecs) .FirstOrDefault(t => AreSameClass(t.Item1, b)); return(Diff.Compare(merged.Item1, merged.Item2) .Where(d => TriviaCompare.IsSemanticChange(d.Ancestor.Node, d.Changed.Node))); }
private static InnerMethodConflict GetInnerMethodConflicts(MethodDeclarationSyntax ancestor, SpanDetails changed, SpanDetails removed, IEnumerable <MethodDeclarationSyntax> insertedMethods, InnerMethodConflict.Local type) { if (!TriviaCompare.IsSpanInNodeTrivia(removed.Span, removed.Node)) { return(null); } var change = changed.Node as MethodDeclarationSyntax; if (change == null) { return(null); } var moved = GetMovedMethod(insertedMethods, change); if (moved == null) { return(null); } var diffRes = Diff3.Compare(ancestor, moved, change); return(new InnerMethodConflict(ancestor, change, moved, diffRes, type)); }