public IEnumerable<CodeIssue> GetIssues(IDocument document, CommonSyntaxNode node, CancellationToken cancellationToken) { var statement = (IfStatementSyntax)node; var model = document.GetSemanticModel(); var newStatement = statement.DropEmptyBranchesIfApplicable(Assumptions.All, model); if (newStatement == statement) return null; var r = new ReadyCodeAction("Omit useless code", document, statement, () => newStatement); return r.CodeIssues1(CodeIssue.Severity.Warning, statement.IfKeyword.Span, "Useless branch"); }
public IEnumerable<CodeIssue> GetIssues(IDocument document, CommonSyntaxNode node, CancellationToken cancellationToken) { var ifStatement = node as IfStatementSyntax; if (ifStatement == null || ifStatement.Else != null) return null; var trueBlock = ifStatement.Statement as BlockSyntax; if (trueBlock == null) return null; if (trueBlock.Statements.Count != 1) return null; if (!trueBlock.IsGuaranteedToJumpOut()) return null; var r = new ReadyCodeAction("Remove unnecessary braces", document, trueBlock, () => trueBlock.Statements.Single()); return new[] { new CodeIssue(CodeIssue.Severity.Warning, trueBlock.OpenBraceToken.Span, "Unnecessary braces", new[] { r }) }; }
public IEnumerable<CodeIssue> GetIssues(IDocument document, CommonSyntaxNode node, CancellationToken cancellationToken) { var model = document.TryGetSemanticModel(); if (model == null) return null; if (model.GetDiagnostics().Any(e => e.Info.Severity == DiagnosticSeverity.Error)) return null; if (document.Project.Documents.Any(e => e.GetSyntaxTree() == null || e.GetSemanticModel() == null)) return null; var fieldNode = (FieldDeclarationSyntax)node; if (fieldNode.IsReadOnly()) return null; var classDecl = fieldNode.Ancestors().OfType<ClassDeclarationSyntax>().FirstOrDefault(); if (classDecl == null) return null; var constructors = classDecl.Members.OfType<ConstructorDeclarationSyntax>().ToRet(); var modsWithReadOnly = fieldNode.Modifiers.Append(SyntaxKind.ReadOnlyKeyword.AsToken()).AsTokenList(); var scopes = fieldNode.IsPrivate() ? new[] {Tuple.Create((CommonSyntaxNode)classDecl, model)} : document.Project.Documents.Select(e => Tuple.Create(e.GetSyntaxTree().GetRoot(), e.GetSemanticModel())); var unmutatedVars = fieldNode.Declaration.Variables.Where(v => { var field = model.GetDeclaredSymbol(v); if (scopes.Any(c => c.Item1.DescendantNodes(e => !constructors.Contains(e)).OfType<ExpressionSyntax>().Any(e => SurfaceWritesTo(e, c.Item2, field)))) return false; return true; }).ToArray(); if (unmutatedVars.Length == 0) return null; if (unmutatedVars.Length == fieldNode.Declaration.Variables.Count) { var r = new ReadyCodeAction("Make readonly", document, fieldNode, () => fieldNode.WithModifiers(modsWithReadOnly)); var desc = unmutatedVars.Length == 1 ? "Mutable field is never modified." : "Mutable fields are never modified."; return r.CodeIssues1(CodeIssue.Severity.Warning, fieldNode.Declaration.Type.Span, desc); } return unmutatedVars.Select(v => { var singleReadOnly = fieldNode.WithModifiers(modsWithReadOnly) .WithDeclaration(fieldNode.Declaration.WithVariables(v.SepList1())); var rest = fieldNode .WithLeadingTrivia() .WithTrailingTrivia(Syntax.Whitespace(Environment.NewLine)) .WithDeclaration(fieldNode.Declaration.WithVariables(fieldNode.Declaration.Variables.Without(v))); var newClassDecl = classDecl.With(members: classDecl.Members.WithItemReplacedByMany(fieldNode, new[] { singleReadOnly, rest })); var action = new ReadyCodeAction("Split readonly", document, classDecl, () => newClassDecl); return new CodeIssue(CodeIssue.Severity.Warning, v.Identifier.Span, "Mutable field is never modified.", new[] { action }); }).ToArray(); }
public IEnumerable<CodeIssue> GetIssues(IDocument document, CommonSyntaxNode node, CancellationToken cancellationToken) { var forLoop = (ForEachStatementSyntax)node; var model = document.TryGetSemanticModel(); if (model == null) return null; // list created just before loop starts? var initStatement = forLoop.TryGetPrevStatement(); var lhs = initStatement.TryGetLHSExpOfAssignmentOrInit() as IdentifierNameSyntax; if (lhs == null) return null; var target = lhs.Identifier; var rhs = initStatement.TryGetRHSOfAssignmentOrInit() as ObjectCreationExpressionSyntax; if (rhs == null) return null; var cr = model.GetTypeInfo(rhs).Type; if (cr.AllInterfaces.All(e => e.Name != "IList")) return null; // loop adds things directly into the list? var adderStatement = forLoop.Statement.Statements().SingleOrDefaultAllowMany() as ExpressionStatementSyntax; if (adderStatement == null) return null; var adderInvoke = adderStatement.Expression as InvocationExpressionSyntax; if (adderInvoke == null) return null; var adderAccess = adderInvoke.Expression as MemberAccessExpressionSyntax; if (adderAccess == null) return null; var adderTarget = adderAccess.Expression as IdentifierNameSyntax; if (adderTarget == null) return null; if (adderTarget.PlainName != target.ValueText) return null; if (adderAccess.Name.PlainName != "Add") return null; if (adderInvoke.ArgumentList.Arguments.Count != 1) return null; var adderExp = adderInvoke.ArgumentList.Arguments.Single().Expression as SimpleNameSyntax; if (adderExp == null) return null; if (adderExp.PlainName != forLoop.Identifier.ValueText) return null; var linqed = forLoop.Expression.Accessing("ToList").Invoking(); var replacedInit = initStatement.TryWithNewRightHandSideOfAssignmentOrSingleInit(linqed); var action = new ReadyCodeAction( "Select into list", document, new[] { initStatement, forLoop }, (e, a) => e == initStatement ? replacedInit : a.Dropped()); return action.CodeIssues1( CodeIssue.Severity.Warning, forLoop.ForEachKeyword.Span, "Initializing a list with a 'for each' loop."); }
public IEnumerable<CodeIssue> GetIssues(IDocument document, CommonSyntaxNode node, CancellationToken cancellationToken) { var assume = Assumptions.All; var ifNode = (IfStatementSyntax)node; var model = document.GetSemanticModel(); // try to get single statement branches created by the if statement's existence var branches = ifNode.TryGetImplicitBranchSingleStatements(model, assume); if (branches == null) return null; // check for same LHS/ret in both branches but not in condition if (!branches.True.HasMatchingLHSOrRet(branches.False, model, assume)) return null; var lhs = branches.True.TryGetLHSOfAssignmentOrInit(model); if (lhs != null) { var dataFlow = model.AnalyzeExpressionDataFlow(ifNode.Condition); if (dataFlow.ReadInside.Contains(lhs)) return null; if (dataFlow.WrittenInside.Contains(lhs)) return null; } // determine how to replace the if statement var expTrue = branches.True.TryGetRHSOfAssignmentOrInitOrReturn(); var expFalse = branches.False.TryGetRHSOfAssignmentOrInitOrReturn(); var expConditional = ifNode.Condition.Conditional(expTrue, expFalse); var replacement = branches.Base.TryUpdateRHSForAssignmentOrInitOrReturn(expConditional); // return as code issue / action var action = new ReadyCodeAction( "Fold into expression", document, new[] { ifNode, branches.True, branches.False, branches.ReplacePoint }, (e, a) => e == branches.ReplacePoint ? replacement : a.Dropped()); return action.CodeIssues1( CodeIssue.Severity.Warning, ifNode.IfKeyword.Span, "'If' statement folds into an expression"); }