private SyntaxNode ComputeReplacement(SyntaxNode old1, SyntaxNode old2) { // old1 is the original node in the tree // old2 is the (potentially) modified version of old1 // that is, old1 is in dict_old_to_new but old2 wont be // if any of its chilrden have been modified if (dict_old_to_new.Keys.Contains(old1)) // this check is redundant? { switch (old2.CSharpKind()) { case SyntaxKind.GetAccessorDeclaration: case SyntaxKind.SetAccessorDeclaration: case SyntaxKind.ConstructorDeclaration: case SyntaxKind.MethodDeclaration: case SyntaxKind.AddAccessorDeclaration: // who knew this was a thing? maybe this will work case SyntaxKind.RemoveAccessorDeclaration: // who knew this was a thing? maybe this will work //Console.WriteLine("Replacing {0} with {1}", old2, dict_old_to_new[old2]); return(dict_old_to_new[old2]); default: RBLogger.Error("Unhandled syntax node kind {0}", old2.CSharpKind()); break; } } return(old2); // don't replace }
private bool CheckRequiredArguments(ref string why) { #region CodeContracts Contract.Ensures(!Contract.Result <bool>() || this.ClousotXML != null); Contract.Ensures(!Contract.Result <bool>() || this.Project != null); Contract.Ensures(!Contract.Result <bool>() || this.Solution != null); Contract.Ensures(!Contract.Result <bool>() || this.Output != OutputOption.unintialized); // I have to think about this: //Contract.Ensures(Contract.Result<bool>() ^ !((this.Output == OutputOption.git && this.GitRoot == null) ^ this.Output == OutputOption.inplace)); #endregion CodeContracts // I'm not positive this is correct: //Contract.Ensures(Contract.Result<bool>() == false || (this.Output != OutputOption.git && this.GitRoot != null) || this.Output != OutputOption.inplace); // right now, only support all options provided var ok = this.ClousotXML != null && this.Project != null && this.Solution != null && this.Output != OutputOption.unintialized; if (!ok) { why = "You need to specify all of: Clousot XML, Project, Solution, output"; RBLogger.Error(why); } var ok2 = (this.Output == OutputOption.git && this.GitRoot != null) ^ this.Output == OutputOption.inplace; if (!ok2) { why = "You need to either: (1) give -output git -gitroot <some_path> (2) give -output inplace but not both"; RBLogger.Error(why); } return(ok & ok2); }
internal static void PrintUsage(string error = null) { if (error != null) { RBLogger.Error("Error in parsing the command line: {0}", error); } RBLogger.Error("USAGE: $ ReviewBot.exe <cccheckoutput.xml> -project <projectfile> -solution <solutionfile> -output [inplace|git -gitroot <gitdirectory>]"); }
public static ReplacementDictionary PrecomputeReplacementNodes(SyntaxDictionary annotationsByNode, Compilation compilation) { #region CodeContracts Contract.Requires(annotationsByNode != null); Contract.Requires(compilation != null); Contract.Ensures(Contract.Result <ReplacementDictionary>() != null); #endregion CodeContracts Output.WriteLine("Precomputing the replacement nodes"); //GetContractRequiresSymbols(comp); var newdict = new ReplacementDictionary(); foreach (var oldkvp in annotationsByNode) { var oldsubdict = oldkvp.Value; var oldfile = oldkvp.Key; var newsubdict = new Dictionary <SyntaxNode, SyntaxNode>(); if (!oldsubdict.Any()) { continue; /* TODO is this right? we have a node with no annotations? */ } SemanticModel = compilation.GetSemanticModel(oldsubdict.First().Key.SyntaxTree); Compilation = compilation; foreach (var oldnode in oldsubdict.Keys) { switch (oldnode.CSharpKind()) { case SyntaxKind.MethodDeclaration: case SyntaxKind.ConstructorDeclaration: case SyntaxKind.SetAccessorDeclaration: case SyntaxKind.GetAccessorDeclaration: case SyntaxKind.AddAccessorDeclaration: // who knew this was a thing? maybe this will work case SyntaxKind.RemoveAccessorDeclaration: // who knew this was a thing? maybe this will work //var oldmethod = oldnode as BaseMethodDeclarationSyntax; //var newmethod = PrecomputeNewMethod(oldmethod, oldsubdict[oldnode]); //if (oldnode.GetText().ToString().Contains("ObjectInvariant")) { int x; } var newmethod = PrecomputeNewMethod(oldnode, oldsubdict[oldnode]); newsubdict.Add(oldnode, newmethod); continue; case SyntaxKind.FieldDeclaration: continue; // we don't need to do anything to read only fields at this point default: RBLogger.Error("Unhandled SyntaxNode kind {0}", oldnode.CSharpKind()); // Debug.Assert(false); // unhandled annotation type continue; } } newdict.Add(oldfile, newsubdict); } return(newdict); }
private static void ReplaceStatement(StatementSyntax original, StatementSyntax replacement, IEnumerable <StatementSyntax> requires, IEnumerable <StatementSyntax> ensures, IEnumerable <StatementSyntax> assumes, IEnumerable <StatementSyntax> other, out IEnumerable <StatementSyntax> newrequires, out IEnumerable <StatementSyntax> newensures, out IEnumerable <StatementSyntax> newassumes, out IEnumerable <StatementSyntax> newother) { newassumes = assumes; newrequires = requires; newensures = ensures; newother = other; if (IsEnsures(original)) { newensures = ensures.Select(x => x == original ? replacement : x); } else if (IsRequires(original)) { newrequires = requires.Select(x => x == original ? replacement : x); } else if (IsAssumes(original)) { newassumes = assumes.Select(x => x == original ? replacement : x); } else if (other.Contains(original)) { newother = other.Select(x => x == original ? replacement : x); } else { RBLogger.Error("The original statement should have been an ensure, require, or assume"); Debug.Assert(false); } }
private static SyntaxNode PrecomputeNewMethod(SyntaxNode oldmethod, List <BaseAnnotation> anns, bool useRegion = true) { SyntaxList <StatementSyntax> oldstmts; if (oldmethod is BaseMethodDeclarationSyntax) { var casted = oldmethod as BaseMethodDeclarationSyntax; oldstmts = casted.Body.Statements; } else if (oldmethod is AccessorDeclarationSyntax) { var casted = oldmethod as AccessorDeclarationSyntax; oldstmts = casted.Body.Statements; } else { RBLogger.Error("Unhandled syntax node kind {0}", oldmethod.CSharpKind()); } var newstmtlist = SyntaxFactory.Block(); var newObjectInvariants = anns.Where(x => x.Kind == ClousotSuggestion.Kind.ObjectInvariant).Select(x => x.statement_syntax as StatementSyntax); var oldObjectInvariants = oldstmts.Where(IsInvariant); var new_requires = anns.Where(x => x.Kind == ClousotSuggestion.Kind.Requires).Select(x => x.statement_syntax as StatementSyntax); var old_requires = oldstmts.Where(IsRequires); var new_ensures = anns.Where(x => x.Kind == ClousotSuggestion.Kind.Ensures || x.Kind == ClousotSuggestion.Kind.EnsuresNecessary) .Select(x => x.statement_syntax as StatementSyntax); var old_ensures = oldstmts.Where(IsEnsures); var new_assumes = anns.Where(x => x.Kind == ClousotSuggestion.Kind.AssumeOnEntry).Select(x => x.statement_syntax as StatementSyntax); //var old_eveything_else = oldstmts.Where(x => !IsEnsures(x) && !IsRequires(x) && !IsAssumes(x)); //var old_eveything_else = oldstmts.Where(x => !IsEnsures(x) && !IsRequires(x)); //var old_assumes = oldstmts.Where(IsAssumes); var old_assumes_list = new List <StatementSyntax>(); // we only want to consider the old assumes to be the ones at the start of the method // assumes can be anywhere, but its incorrect to move ones that use declared variables foreach (var stmt in oldstmts) { if (IsEnsures(stmt)) { continue; } if (IsRequires(stmt)) { continue; } if (IsAssumes(stmt)) { old_assumes_list.Add(stmt); continue; } break; } var old_assumes = old_assumes_list.AsEnumerable(); var old_eveything_else = oldstmts.Except(old_ensures.Concat(old_requires).Concat(old_assumes)); var objectInvariants = newObjectInvariants.Union(oldObjectInvariants, new ContractEqualityComparer()); //if (objectInvariants.Any()) { Debugger.Break(); } SyntaxTrivia regionStart, regionEnd; regionEnd = regionStart = SyntaxFactory.Comment(""); // a dummy initial value if (useRegion && !objectInvariants.Any()) { if (TryFindContractsRegion(old_requires, old_ensures, old_assumes, out regionStart, out regionEnd)) { var first = oldstmts.First(x => x.GetLeadingTrivia().Contains(regionStart)); //var last = oldstmts.First(x => x.GetTrailingTrivia().Contains(regionEnd)); //var last = oldstmts.First(x => x.GetLeadingTrivia().Contains(regionEnd)); //Console.WriteLine(first.Parent.DescendantTrivia().Contains(regionEnd)); // it seems like the #endregion can be essentially anywhere var statements = oldstmts.Where(x => x.GetLeadingTrivia().Contains(regionEnd) || x.GetTrailingTrivia().Contains(regionEnd)); StatementSyntax last, lastModified; last = lastModified = null; if (oldstmts.Any(x => x.GetLeadingTrivia().Contains(regionEnd))) { last = oldstmts.First(x => x.GetLeadingTrivia().Contains(regionEnd)); var lastTrivia = last.GetLeadingTrivia().Where(x => x != regionEnd); lastModified = last.WithLeadingTrivia(lastTrivia); } else if (oldstmts.Any(x => x.GetTrailingTrivia().Contains(regionEnd))) { last = oldstmts.First(x => x.GetTrailingTrivia().Contains(regionEnd)); var lastTrivia = last.GetTrailingTrivia().Where(x => x != regionEnd); lastModified = last.WithTrailingTrivia(lastTrivia); } else if (first.Parent.DescendantTrivia().Contains(regionEnd)) { oldmethod = first.Parent.Parent.ReplaceTrivia(regionEnd, SyntaxFactory.Comment("")); } var firstTrivia = first.GetLeadingTrivia().Where(x => x != regionStart); var firstModified = first.WithLeadingTrivia(firstTrivia); ReplaceStatement(first, firstModified, old_requires, old_ensures, old_assumes, old_eveything_else, out old_requires, out old_ensures, out old_assumes, out old_eveything_else); if (last != null) { ReplaceStatement(last, lastModified, old_requires, old_ensures, old_assumes, old_eveything_else, out old_requires, out old_ensures, out old_assumes, out old_eveything_else); } } else { var addNewLine = !old_requires.Any() && !old_requires.Any(); GetNewRegionTrivia("CodeContracts", addNewLine, out regionStart, out regionEnd); } } var requires = new_requires.Union(old_requires, new ContractEqualityComparer()); var ensures = new_ensures.Union(old_ensures, new ContractEqualityComparer()); var assumes = new_assumes.Union(old_assumes, new ContractEqualityComparer()); RBLogger.ErrorIf(requires.Count() < new_requires.Count(), "Union deleted some items!?"); RBLogger.ErrorIf(ensures.Count() < new_ensures.Count(), "Union deleted some items!?"); RBLogger.ErrorIf(assumes.Count() < new_assumes.Count(), "Union deleted some items!?"); // Scott: there is some weird case where we get duplicate ensures // I haven't tracked it down // this is for debugging: if (ensures.Count() < new_ensures.Count()) { RBLogger.Info("new ensures:"); RBLogger.Indent(); foreach (var ensure in new_ensures) { RBLogger.Info(ensure); } RBLogger.Unindent(); } //foreach(var r in requires) //{ // Console.WriteLine(r); //} //Console.WriteLine(requires.Count()); // the if x.Any()'s are unnecesary (IMHO) but there is a roslyn bug for // adding an empty StatementSyntax[] to an empty BlockSyntax if (requires.Any()) { newstmtlist = newstmtlist.AddStatements(requires.ToArray()); } if (ensures.Any()) { newstmtlist = newstmtlist.AddStatements(ensures.ToArray()); } if (assumes.Any()) { newstmtlist = newstmtlist.AddStatements(assumes.ToArray()); } if (old_eveything_else.Any()) { newstmtlist = newstmtlist.AddStatements(old_eveything_else.ToArray()); } if (objectInvariants.Any()) { newstmtlist = newstmtlist.AddStatements(objectInvariants.ToArray()); // object invariant methods should only have these, so order doesn't matter } if (useRegion && !objectInvariants.Any()) { var first = newstmtlist.Statements.First(); var oldTrivia = first.GetLeadingTrivia(); var newTrivia = SyntaxFactory.TriviaList(regionStart).Concat(oldTrivia); var firstModified = first.WithLeadingTrivia(newTrivia); newstmtlist = newstmtlist.ReplaceNode(first, firstModified); var index = requires.Count() + ensures.Count() + assumes.Count(); var last = newstmtlist.Statements[index - 1]; oldTrivia = last.GetTrailingTrivia(); newTrivia = oldTrivia.Concat(SyntaxFactory.TriviaList(new[] { regionEnd })); var lastModified = last.WithTrailingTrivia(newTrivia); newstmtlist = newstmtlist.ReplaceNode(last, lastModified); } SyntaxNode newmethod = null; if (oldmethod is MethodDeclarationSyntax) { var casted = oldmethod as MethodDeclarationSyntax; //newmethod = casted.WithBody(newstmtlist); // the awful line below is partly so awful to preserve the trivia around the method newmethod = casted.WithBody(casted.Body.WithStatements(newstmtlist.Statements)); } if (oldmethod is ConstructorDeclarationSyntax) { var casted = oldmethod as ConstructorDeclarationSyntax; //newmethod = casted.WithBody(newstmtlist); newmethod = casted.WithBody(casted.Body.WithStatements(newstmtlist.Statements)); } if (oldmethod is AccessorDeclarationSyntax) { var casted = oldmethod as AccessorDeclarationSyntax; //newmethod = casted.WithBody(newstmtlist); newmethod = casted.WithBody(casted.Body.WithStatements(newstmtlist.Statements)); } //var newmethod = oldmethod is MethodDeclarationSyntax ? (BaseMethodDeclarationSyntax)((MethodDeclarationSyntax)oldmethod).WithBody(newstmtlist) : // (BaseMethodDeclarationSyntax)((ConstructorDeclarationSyntax)oldmethod).WithBody(newstmtlist); //Console.WriteLine("Annotated Method: {0}", newmethod); return(newmethod); }
/// <summary> /// Parse the args into an Option object /// </summary> /// <param name="args">the args param to Main</param> /// <param name="options">the parsed arguements, valid if return is ture</param> /// <param name="why">an error message if parsing fails and the return is false</param> /// <returns>true if parsing succeeded, false otherwise</returns> internal static bool TryParseOptions(string[] args, out Options options, out string why) { #region CodeContracts Contract.Requires(Contract.ForAll(args, arg => arg != null)); Contract.Ensures(!Contract.Result <bool>() || Contract.ValueAtReturn(out options) != null); Contract.Ensures(!Contract.Result <bool>() || Contract.ValueAtReturn(out options).Project != null); Contract.Ensures(!Contract.Result <bool>() || Contract.ValueAtReturn(out options).Solution != null); Contract.Ensures(!Contract.Result <bool>() || Contract.ValueAtReturn(out options).ClousotXML != null); Contract.Ensures(!Contract.Result <bool>() || Contract.ValueAtReturn(out options).Output != OutputOption.unintialized); Contract.Ensures(Contract.Result <bool>() || Contract.ValueAtReturn(out why) != null); // there should be a contract for the output option, but it's complicated //Contract.Ensures( // !Contract.Result<bool>() // || ((Contract.ValueAtReturn(out options).Output == OutputOption.git && Contract.ValueAtReturn(out options).GitRoot != null) // ^ (Contract.ValueAtReturn(out options).Output == OutputOption.inplace)) //); #endregion CodeContracts options = new Options(); why = null; for (var i = 0; i < args.Length; i++) { var arg = args[i]; if (IsOption(arg, out arg)) { switch (arg) { case "project": if (i == args.Length - 1) { why = "The last argument can't be a keyword"; return(false); } options.Project = args[++i]; break; case "source": if (i == args.Length - 1) { why = "The last argument can't be a keyword"; return(false); } options.SourceFile = args[++i]; break; case "break": System.Diagnostics.Debugger.Launch(); break; case "solution": if (i == args.Length - 1) { why = "The last argument can't be a keyword"; return(false); } options.Solution = args[++i]; break; case "output": if (i == args.Length - 1) { why = "The last argument can't be a keyword"; return(false); } OutputOption oo; if (Enum.TryParse(args[++i], true, out oo)) { options.Output = oo; } else { why = "Unrecognized output option: " + args[i]; return(false); } break; case "gitroot": if (i == args.Length - 1) { why = "The last argument can't be a keyword"; return(false); } options.GitRoot = args[++i]; if (!Directory.Exists(options.GitRoot)) { why = "git root directory must exist"; return(false); } break; default: options = null; why = "Unrecognized option " + arg; RBLogger.Error("Invalid option {0}", arg); return(false); } } else { if (options.ClousotXML != null) { why = "Cannot express two (or more) .xml files"; return(false); } else { options.ClousotXML = args[0]; } } } return(options.CheckRequiredArguments(ref why)); }