From ac4937048656a8bd22db60a9402be009f2f04bce Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Wed, 21 Mar 2012 15:03:58 +0100 Subject: [PATCH 1/2] Simplify ConditionalToNullCoalescingInspector using pattern matching. --- .../ConditionalToNullCoalescingInspector.cs | 105 +++++++----------- 1 file changed, 42 insertions(+), 63 deletions(-) diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/ConditionalToNullCoalescingInspector.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/ConditionalToNullCoalescingInspector.cs index 8cafe8a795..fde5e11e15 100644 --- a/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/ConditionalToNullCoalescingInspector.cs +++ b/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/ConditionalToNullCoalescingInspector.cs @@ -1,6 +1,6 @@ -// +// // ConditionalToNullCoalescingInspector.cs -// +// // Author: // Mike Krüger // @@ -25,18 +25,42 @@ // THE SOFTWARE. using System; using System.Collections.Generic; +using System.Linq; using ICSharpCode.NRefactory.PatternMatching; namespace ICSharpCode.NRefactory.CSharp.Refactoring { /// - /// Checks for obj != null ? obj : - /// Converts to: obj ?? + /// Checks for "a != null ? a : other" + /// Converts to: "a ?? other" /// public class ConditionalToNullCoalescingInspector : IInspector { - static ConditionalExpression[] Matches; - + static readonly Pattern pattern = new Choice { + // a != null ? a : other + new ConditionalExpression( + new Choice { + // a != null + new BinaryOperatorExpression(new AnyNode("a"), BinaryOperatorType.InEquality, new NullReferenceExpression()), + // null != a + new BinaryOperatorExpression(new NullReferenceExpression(), BinaryOperatorType.InEquality, new AnyNode("a")), + }, + new Backreference("a"), + new AnyNode("other") + ), + // a == null ? other : a + new ConditionalExpression( + new Choice { + // a == null + new BinaryOperatorExpression(new AnyNode("a"), BinaryOperatorType.Equality, new NullReferenceExpression()), + // null == a + new BinaryOperatorExpression(new NullReferenceExpression(), BinaryOperatorType.Equality, new AnyNode("a")), + }, + new AnyNode("other"), + new Backreference("a") + ), + }; + string title = "Convert to '??' expression"; public string Title { @@ -46,16 +70,6 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring set { title = value; } - } - - public ConditionalToNullCoalescingInspector () - { - Matches = new [] { - new ConditionalExpression (new BinaryOperatorExpression (new NullReferenceExpression (), BinaryOperatorType.Equality, new AnyNode ()), new AnyNode (), new AnyNode ()), - new ConditionalExpression (new BinaryOperatorExpression (new AnyNode (), BinaryOperatorType.Equality, new NullReferenceExpression ()), new AnyNode (), new AnyNode ()), - new ConditionalExpression (new BinaryOperatorExpression (new NullReferenceExpression (), BinaryOperatorType.InEquality, new AnyNode ()), new AnyNode (), new AnyNode ()), - new ConditionalExpression (new BinaryOperatorExpression (new AnyNode (), BinaryOperatorType.InEquality, new NullReferenceExpression ()), new AnyNode (), new AnyNode ()), - }; } public IEnumerable Run (BaseRefactoringContext context) @@ -76,56 +90,21 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring public override void VisitConditionalExpression (ConditionalExpression conditionalExpression) { - foreach (var match in Matches) { - if (match.IsMatch (conditionalExpression) && IsCandidate (conditionalExpression)) { - AddIssue (conditionalExpression, - inspector.Title, - delegate { - using (var script = ctx.StartScript ()) { - var expressions = SortExpressions (conditionalExpression); - var expr = new BinaryOperatorExpression (expressions.Item1.Clone (), BinaryOperatorType.NullCoalescing, expressions.Item2.Clone ()); - script.Replace (conditionalExpression, expr); - } - }); - } + Match m = pattern.Match(conditionalExpression); + if (m.Success) { + var a = m.Get("a").Single(); + var other = m.Get("other").Single(); + AddIssue (conditionalExpression, + inspector.Title, + delegate { + using (var script = ctx.StartScript ()) { + var expr = new BinaryOperatorExpression (a.Clone (), BinaryOperatorType.NullCoalescing, other.Clone ()); + script.Replace (conditionalExpression, expr); + } + }); } base.VisitConditionalExpression (conditionalExpression); } } - - static bool IsCandidate (ConditionalExpression node) - { - var condition = node.Condition as BinaryOperatorExpression; - var compareNode = condition.Left is NullReferenceExpression ? condition.Right : condition.Left; - - - if (compareNode.IsMatch (node.TrueExpression)) { - // a == null ? a : other - if (condition.Operator == BinaryOperatorType.Equality) - return false; - // a != null ? a : other - return compareNode.IsMatch (node.TrueExpression); - } else { - // a == null ? other : a - if (condition.Operator == BinaryOperatorType.Equality) - return compareNode.IsMatch (node.FalseExpression); - // a != null ? other : a - return false; - } - } - - static Tuple SortExpressions (ConditionalExpression cond) - { - var condition = cond.Condition as BinaryOperatorExpression; - var compareNode = condition.Left is NullReferenceExpression ? condition.Right : condition.Left; - - if (compareNode.IsMatch (cond.TrueExpression)) { - // a != null ? a : other - return new Tuple (cond.TrueExpression, cond.FalseExpression); - } - - // a == null ? other : a - return new Tuple (cond.FalseExpression, cond.TrueExpression); - } } } From efa4b0b576fc376745039db6f17974ac356b583a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Kr=C3=BCger?= Date: Wed, 21 Mar 2012 16:34:29 +0100 Subject: [PATCH 2/2] Added more inspectors. --- .../ICSharpCode.NRefactory.CSharp.csproj | 8 + .../Refactoring/BaseRefactoringContext.cs | 10 ++ .../ConditionalToNullCoalescingInspector.cs | 6 +- .../Inspector/GatherVisitorBase.cs | 7 +- .../NotImplementedExceptionInspector.cs | 77 ++++++++ .../Inspector/RedundantInternalInspector.cs | 84 +++++++++ .../RedundantNamespaceUsageInspector.cs | 94 ++++++++++ .../Inspector/RedundantPrivateInspector.cs | 142 +++++++++++++++ .../Inspector/RedundantThisInspector.cs | 130 ++++++++++++++ .../Inspector/RedundantUsingInspector.cs | 77 ++++++++ .../Inspector/StringIsNullOrEmptyInspector.cs | 165 ++++++++++++++++++ .../Inspector/UseVarKeywordInspector.cs | 121 +++++++++++++ 12 files changed, 916 insertions(+), 5 deletions(-) create mode 100644 ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/NotImplementedExceptionInspector.cs create mode 100644 ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/RedundantInternalInspector.cs create mode 100644 ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/RedundantNamespaceUsageInspector.cs create mode 100644 ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/RedundantPrivateInspector.cs create mode 100644 ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/RedundantThisInspector.cs create mode 100644 ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/RedundantUsingInspector.cs create mode 100644 ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/StringIsNullOrEmptyInspector.cs create mode 100644 ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/UseVarKeywordInspector.cs diff --git a/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj b/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj index f77f115a5a..daa0514310 100644 --- a/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj +++ b/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj @@ -327,6 +327,14 @@ + + + + + + + + diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/BaseRefactoringContext.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/BaseRefactoringContext.cs index 83c5c0c2ae..6cc9214561 100644 --- a/ICSharpCode.NRefactory.CSharp/Refactoring/BaseRefactoringContext.cs +++ b/ICSharpCode.NRefactory.CSharp/Refactoring/BaseRefactoringContext.cs @@ -70,6 +70,16 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring return resolver.Resolve (node, cancellationToken); } + public CSharpResolver GetResolverStateBefore(AstNode node) + { + return resolver.GetResolverStateBefore (node, cancellationToken); + } + + public CSharpResolver GetResolverStateAfter(AstNode node) + { + return resolver.GetResolverStateAfter (node, cancellationToken); + } + public IType ResolveType (AstType type) { return resolver.Resolve (type, cancellationToken).Type; diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/ConditionalToNullCoalescingInspector.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/ConditionalToNullCoalescingInspector.cs index fde5e11e15..6c71283515 100644 --- a/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/ConditionalToNullCoalescingInspector.cs +++ b/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/ConditionalToNullCoalescingInspector.cs @@ -88,15 +88,13 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring this.inspector = inspector; } - public override void VisitConditionalExpression (ConditionalExpression conditionalExpression) + public override void VisitConditionalExpression(ConditionalExpression conditionalExpression) { Match m = pattern.Match(conditionalExpression); if (m.Success) { var a = m.Get("a").Single(); var other = m.Get("other").Single(); - AddIssue (conditionalExpression, - inspector.Title, - delegate { + AddIssue(conditionalExpression, inspector.Title, delegate { using (var script = ctx.StartScript ()) { var expr = new BinaryOperatorExpression (a.Clone (), BinaryOperatorType.NullCoalescing, other.Clone ()); script.Replace (conditionalExpression, expr); diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/GatherVisitorBase.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/GatherVisitorBase.cs index 699ecf3ba9..d5f9bcbcef 100644 --- a/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/GatherVisitorBase.cs +++ b/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/GatherVisitorBase.cs @@ -47,10 +47,15 @@ namespace ICSharpCode.NRefactory.CSharp base.VisitChildren (node); } - protected void AddIssue (AstNode node, string title, System.Action fix) + protected void AddIssue (AstNode node, string title, System.Action fix = null) { FoundIssues.Add (new InspectionIssue (title, node.StartLocation, node.EndLocation, fix)); } + + protected void AddIssue(TextLocation start, TextLocation end, string title, System.Action fix = null) + { + FoundIssues.Add (new InspectionIssue (title, start, end, fix)); + } } diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/NotImplementedExceptionInspector.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/NotImplementedExceptionInspector.cs new file mode 100644 index 0000000000..2eea3f03da --- /dev/null +++ b/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/NotImplementedExceptionInspector.cs @@ -0,0 +1,77 @@ +// +// NotImplementedExceptionInspector.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2012 Xamarin +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using ICSharpCode.NRefactory.PatternMatching; +using System.Collections.Generic; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp.Refactoring +{ + /// + /// This inspector just shows that there is a not implemented exception. It doesn't offer a fix. + /// Should only be shown in overview bar, no underlining. + /// + public class NotImplementedExceptionInspector : IInspector + { + string title = "NotImplemented exception thrown"; + + public string Title { + get { + return title; + } + set { + title = value; + } + } + + public IEnumerable Run (BaseRefactoringContext context) + { + var visitor = new GatherVisitor (context, this); + context.RootNode.AcceptVisitor (visitor); + return visitor.FoundIssues; + } + + class GatherVisitor : GatherVisitorBase + { + readonly NotImplementedExceptionInspector inspector; + + public GatherVisitor (BaseRefactoringContext ctx, NotImplementedExceptionInspector inspector) : base (ctx) + { + this.inspector = inspector; + } + + public override void VisitThrowStatement(ThrowStatement throwStatement) + { + var result = ctx.Resolve (throwStatement.Expression); + if (result.Type.Equals (ctx.Compilation.FindType (typeof(System.NotImplementedException)))) + AddIssue (throwStatement, inspector.Title); + + base.VisitThrowStatement(throwStatement); + } + } + } +} + diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/RedundantInternalInspector.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/RedundantInternalInspector.cs new file mode 100644 index 0000000000..63b274f368 --- /dev/null +++ b/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/RedundantInternalInspector.cs @@ -0,0 +1,84 @@ +// +// RedundantInternalInspector.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2012 Xamarin +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using ICSharpCode.NRefactory.PatternMatching; +using System.Collections.Generic; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp.Refactoring +{ + /// + /// Finds redundant internal modifiers. + /// + public class RedundantInternalInspector : IInspector + { + string title = "Remove redundant 'internal' modifier"; + + public string Title { + get { + return title; + } + set { + title = value; + } + } + + public IEnumerable Run (BaseRefactoringContext context) + { + var visitor = new GatherVisitor (context, this); + context.RootNode.AcceptVisitor (visitor); + return visitor.FoundIssues; + } + + class GatherVisitor : GatherVisitorBase + { + readonly RedundantInternalInspector inspector; + + public GatherVisitor (BaseRefactoringContext ctx, RedundantInternalInspector inspector) : base (ctx) + { + this.inspector = inspector; + } + + public override void VisitTypeDeclaration(TypeDeclaration typeDeclaration) + { + if (!(typeDeclaration.Parent is TypeDeclaration)) { + foreach (var token in typeDeclaration.ModifierTokens) { + if (token.Modifier == Modifiers.Internal) { + AddIssue(token, inspector.Title, delegate { + using (var script = ctx.StartScript ()) { + script.Remove(token); + } + }); + } + } + } + base.VisitTypeDeclaration(typeDeclaration); + } + } + } +} + diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/RedundantNamespaceUsageInspector.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/RedundantNamespaceUsageInspector.cs new file mode 100644 index 0000000000..44593a21c5 --- /dev/null +++ b/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/RedundantNamespaceUsageInspector.cs @@ -0,0 +1,94 @@ +// +// RedundantNamespaceUsageInspector.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2012 Xamarin +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using ICSharpCode.NRefactory.PatternMatching; +using System.Collections.Generic; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.Semantics; + +namespace ICSharpCode.NRefactory.CSharp.Refactoring +{ + /// + /// Finds redundant namespace usages. + /// + public class RedundantNamespaceUsageInspector : IInspector + { + string title = "Remove redundant namespace usage"; + + public string Title { + get { + return title; + } + set { + title = value; + } + } + + public IEnumerable Run (BaseRefactoringContext context) + { + var visitor = new GatherVisitor (context, this); + context.RootNode.AcceptVisitor (visitor); + return visitor.FoundIssues; + } + + class GatherVisitor : GatherVisitorBase + { + readonly RedundantNamespaceUsageInspector inspector; + + public GatherVisitor (BaseRefactoringContext ctx, RedundantNamespaceUsageInspector inspector) : base (ctx) + { + this.inspector = inspector; + } + + public override void VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression) + { + base.VisitMemberReferenceExpression(memberReferenceExpression); + + var result = ctx.Resolve(memberReferenceExpression.Target); + if (!(result is NamespaceResolveResult)) { + return; + } + var wholeResult = ctx.Resolve(memberReferenceExpression); + if (!(wholeResult is TypeResolveResult)) { + return; + } + + var state = ctx.GetResolverStateBefore(memberReferenceExpression); + var lookupName = state.LookupSimpleNameOrTypeName(memberReferenceExpression.MemberName, new List (), SimpleNameLookupMode.Expression); + + if (lookupName != null && wholeResult.Type.Equals(lookupName.Type)) { + AddIssue(memberReferenceExpression.StartLocation, memberReferenceExpression.MemberNameToken.StartLocation, inspector.Title, delegate { + using (var script = ctx.StartScript ()) { + script.Replace(memberReferenceExpression, new IdentifierExpression (memberReferenceExpression.MemberName)); + } + } + ); + } + } + } + } +} diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/RedundantPrivateInspector.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/RedundantPrivateInspector.cs new file mode 100644 index 0000000000..cbec10452a --- /dev/null +++ b/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/RedundantPrivateInspector.cs @@ -0,0 +1,142 @@ +// +// RedundantPrivateInspector.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2012 Xamarin +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using ICSharpCode.NRefactory.PatternMatching; +using System.Collections.Generic; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp.Refactoring +{ + /// + /// Finds redundant internal modifiers. + /// + public class RedundantPrivateInspector : IInspector + { + string title = "Remove redundant 'private' modifier"; + + public string Title { + get { + return title; + } + set { + title = value; + } + } + + public IEnumerable Run (BaseRefactoringContext context) + { + var visitor = new GatherVisitor (context, this); + context.RootNode.AcceptVisitor (visitor); + return visitor.FoundIssues; + } + + class GatherVisitor : GatherVisitorBase + { + readonly RedundantPrivateInspector inspector; + + public GatherVisitor (BaseRefactoringContext ctx, RedundantPrivateInspector inspector) : base (ctx) + { + this.inspector = inspector; + } + + void CheckNode(EntityDeclaration node) + { + foreach (var token in node.ModifierTokens) { + if (token.Modifier == Modifiers.Private) { + AddIssue(token, inspector.Title, delegate { + using (var script = ctx.StartScript ()) { + script.Remove(token); + } + }); + } + } + } + + public override void VisitMethodDeclaration(MethodDeclaration methodDeclaration) + { + base.VisitMethodDeclaration(methodDeclaration); + CheckNode(methodDeclaration); + } + + public override void VisitFieldDeclaration(FieldDeclaration fieldDeclaration) + { + base.VisitFieldDeclaration(fieldDeclaration); + CheckNode(fieldDeclaration); + } + + public override void VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration) + { + base.VisitPropertyDeclaration(propertyDeclaration); + CheckNode(propertyDeclaration); + } + + public override void VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration) + { + base.VisitIndexerDeclaration(indexerDeclaration); + CheckNode(indexerDeclaration); + } + + public override void VisitEventDeclaration(EventDeclaration eventDeclaration) + { + base.VisitEventDeclaration(eventDeclaration); + CheckNode(eventDeclaration); + } + + public override void VisitCustomEventDeclaration(CustomEventDeclaration eventDeclaration) + { + base.VisitCustomEventDeclaration(eventDeclaration); + CheckNode(eventDeclaration); + } + + public override void VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration) + { + base.VisitConstructorDeclaration(constructorDeclaration); + CheckNode(constructorDeclaration); + } + + public override void VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration) + { + base.VisitOperatorDeclaration(operatorDeclaration); + CheckNode(operatorDeclaration); + } + + public override void VisitFixedFieldDeclaration(FixedFieldDeclaration fixedFieldDeclaration) + { + base.VisitFixedFieldDeclaration(fixedFieldDeclaration); + CheckNode(fixedFieldDeclaration); + } + + public override void VisitTypeDeclaration(TypeDeclaration typeDeclaration) + { + if (!(typeDeclaration.Parent is TypeDeclaration)) { + CheckNode(typeDeclaration); + } + base.VisitTypeDeclaration(typeDeclaration); + } + } + } +} \ No newline at end of file diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/RedundantThisInspector.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/RedundantThisInspector.cs new file mode 100644 index 0000000000..5bc13cac09 --- /dev/null +++ b/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/RedundantThisInspector.cs @@ -0,0 +1,130 @@ +// +// RedundantThisInspector.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2012 Xamarin +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using ICSharpCode.NRefactory.PatternMatching; +using System.Collections.Generic; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.CSharp.Resolver; +using System.Linq; + +namespace ICSharpCode.NRefactory.CSharp.Refactoring +{ + /// + /// Finds redundant namespace usages. + /// + public class RedundantThisInspector : IInspector + { + string title = "Remove redundant 'this.'"; + + public string Title { + get { + return title; + } + set { + title = value; + } + } + + public IEnumerable Run (BaseRefactoringContext context) + { + var visitor = new GatherVisitor (context, this); + context.RootNode.AcceptVisitor (visitor); + return visitor.FoundIssues; + } + + class GatherVisitor : GatherVisitorBase + { + readonly RedundantThisInspector inspector; + + public GatherVisitor (BaseRefactoringContext ctx, RedundantThisInspector inspector) : base (ctx) + { + this.inspector = inspector; + } + + static IMember GetMember(ResolveResult result) + { + if (result is MemberResolveResult) { + return ((MemberResolveResult)result).Member; + } + return null; + } + + static IEnumerable GetMembers (ResolveResult result) + { + if (result is MemberResolveResult) { + return new IMember[] { ((MemberResolveResult)result).Member }; + } else if (result is MethodGroupResolveResult) { + return ((MethodGroupResolveResult)result).Methods; + } + + return null; + } + + + public override void VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression) + { + base.VisitThisReferenceExpression(thisReferenceExpression); + var memberReference = thisReferenceExpression.Parent as MemberReferenceExpression; + if (memberReference == null) { + return; + } + + var state = ctx.GetResolverStateAfter(thisReferenceExpression); + var wholeResult = ctx.Resolve(thisReferenceExpression); + + var result = state.LookupSimpleNameOrTypeName(memberReference.MemberName, new List (), SimpleNameLookupMode.Expression); + if (result == null || wholeResult == null) { + return; + } + + IMember member = GetMember(wholeResult); + if (member == null) { + return; + } + + bool isRedundant; + if (result is MemberResolveResult) { + isRedundant = ((MemberResolveResult)result).Member.Region.Equals(member.Region); + } else if (result is MethodGroupResolveResult) { + isRedundant = ((MethodGroupResolveResult)result).Methods.Any(m => m.Region.Equals(member.Region)); + } else { + return; + } + + if (isRedundant) { + AddIssue(thisReferenceExpression.StartLocation, memberReference.MemberNameToken.StartLocation, inspector.Title, delegate { + using (var script = ctx.StartScript ()) { + script.Replace(memberReference, new IdentifierExpression (memberReference.MemberName)); + } + } + ); + } + } + } + } +} \ No newline at end of file diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/RedundantUsingInspector.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/RedundantUsingInspector.cs new file mode 100644 index 0000000000..f310ff6f61 --- /dev/null +++ b/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/RedundantUsingInspector.cs @@ -0,0 +1,77 @@ +// +// RedundantUsingInspector.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2012 Xamarin +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using ICSharpCode.NRefactory.PatternMatching; +using System.Collections.Generic; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.Semantics; + +namespace ICSharpCode.NRefactory.CSharp.Refactoring +{ + /// + /// Finds redundant using declarations. + /// + public class RedundantUsingInspector : IInspector + { + string title = "Remove redundant using"; + + public string Title { + get { + return title; + } + set { + title = value; + } + } + + public IEnumerable Run (BaseRefactoringContext context) + { + var visitor = new GatherVisitor (context, this); + context.RootNode.AcceptVisitor (visitor); + return visitor.FoundIssues; + } + + class GatherVisitor : GatherVisitorBase + { + readonly RedundantUsingInspector inspector; + + public GatherVisitor (BaseRefactoringContext ctx, RedundantUsingInspector inspector) : base (ctx) + { + this.inspector = inspector; + } + + public override void VisitUsingDeclaration(UsingDeclaration usingDeclaration) + { + base.VisitUsingDeclaration(usingDeclaration); + // TODO + // return cSharpResolver.usedScopes + // .OfType () + // .Any (u => u.ResolveNamespace (ctx).NamespaceName == ns) || additionalNamespaces.Contains (ns); + } + } + } +} diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/StringIsNullOrEmptyInspector.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/StringIsNullOrEmptyInspector.cs new file mode 100644 index 0000000000..5a2961ca1e --- /dev/null +++ b/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/StringIsNullOrEmptyInspector.cs @@ -0,0 +1,165 @@ +// +// StringIsNullOrEmptyInspector.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2012 Xamarin +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.Collections.Generic; +using System.Linq; +using ICSharpCode.NRefactory.PatternMatching; + +namespace ICSharpCode.NRefactory.CSharp.Refactoring +{ + /// + /// Checks for str == null && str == "" + /// Converts to: string.IsNullOrEmpty (str) + /// + public class StringIsNullOrEmptyInspector : IInspector + { + static readonly Pattern pattern = new Choice { + // str == null && str == "" + new BinaryOperatorExpression ( + new Choice { + // str == null + new BinaryOperatorExpression (new AnyNode ("str"), BinaryOperatorType.Equality, new NullReferenceExpression ()), + // null == str + new BinaryOperatorExpression (new NullReferenceExpression (), BinaryOperatorType.Equality, new AnyNode ("str")), + }, + BinaryOperatorType.ConditionalAnd, + new Choice { + // str == "" + new BinaryOperatorExpression (new Backreference ("str"), BinaryOperatorType.Equality, new PrimitiveExpression ("")), + // "" == str + new BinaryOperatorExpression (new PrimitiveExpression (""), BinaryOperatorType.Equality, new Backreference ("str")), + } + ), + // str == "" && str == null + new BinaryOperatorExpression ( + new Choice { + // str == "" + new BinaryOperatorExpression (new AnyNode ("str"), BinaryOperatorType.Equality, new PrimitiveExpression ("")), + // "" == str + new BinaryOperatorExpression (new PrimitiveExpression (""), BinaryOperatorType.Equality, new AnyNode ("str")), + }, + BinaryOperatorType.ConditionalAnd, + new Choice { + // str == null + new BinaryOperatorExpression (new Backreference ("str"), BinaryOperatorType.Equality, new NullReferenceExpression ()), + // null == str + new BinaryOperatorExpression (new NullReferenceExpression (), BinaryOperatorType.Equality, new Backreference ("str")), + } + ), + }; + + static readonly Pattern negPattern = new Choice { + // str != null && str != "" + new BinaryOperatorExpression ( + new Choice { + // str != null + new BinaryOperatorExpression (new AnyNode ("str"), BinaryOperatorType.InEquality, new NullReferenceExpression ()), + // null != str + new BinaryOperatorExpression (new NullReferenceExpression (), BinaryOperatorType.InEquality, new AnyNode ("str")), + }, + BinaryOperatorType.ConditionalAnd, + new Choice { + // str != "" + new BinaryOperatorExpression (new Backreference ("str"), BinaryOperatorType.InEquality, new PrimitiveExpression ("")), + // "" != str + new BinaryOperatorExpression (new PrimitiveExpression (""), BinaryOperatorType.InEquality, new Backreference ("str")), + } + ), + // str != "" && str != null + new BinaryOperatorExpression ( + new Choice { + // str != "" + new BinaryOperatorExpression (new AnyNode ("str"), BinaryOperatorType.InEquality, new PrimitiveExpression ("")), + // "" != str + new BinaryOperatorExpression (new PrimitiveExpression (""), BinaryOperatorType.InEquality, new AnyNode ("str")), + }, + BinaryOperatorType.ConditionalAnd, + new Choice { + // str != null + new BinaryOperatorExpression (new Backreference ("str"), BinaryOperatorType.InEquality, new NullReferenceExpression ()), + // null != str + new BinaryOperatorExpression (new NullReferenceExpression (), BinaryOperatorType.InEquality, new Backreference ("str")), + } + ), + }; + + string title = "Use string.IsNullOrEmpty"; + + public string Title { + get { + return title; + } + set { + title = value; + } + } + + public IEnumerable Run (BaseRefactoringContext context) + { + var visitor = new GatherVisitor (context, this); + context.RootNode.AcceptVisitor (visitor); + return visitor.FoundIssues; + } + + class GatherVisitor : GatherVisitorBase + { + readonly StringIsNullOrEmptyInspector inspector; + + public GatherVisitor (BaseRefactoringContext ctx, StringIsNullOrEmptyInspector inspector) : base (ctx) + { + this.inspector = inspector; + } + + public override void VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression) + { + base.VisitBinaryOperatorExpression(binaryOperatorExpression); + Match m = pattern.Match(binaryOperatorExpression); + bool isNegated = false; + if (!m.Success) { + m = negPattern.Match(binaryOperatorExpression); + isNegated = true; + } + if (m.Success) { + var str = m.Get("str").Single(); + AddIssue(binaryOperatorExpression, inspector.Title, delegate { + using (var script = ctx.StartScript ()) { + Expression expr = new InvocationExpression ( + new MemberReferenceExpression ( + new TypeReferenceExpression (new PrimitiveType ("string")), + "IsNullOrEmpty" + ), str.Clone()); + if (isNegated) + expr = new UnaryOperatorExpression (UnaryOperatorType.Not, expr); + script.Replace(binaryOperatorExpression, expr); + } + }); + return; + } + } + + } + } +} diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/UseVarKeywordInspector.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/UseVarKeywordInspector.cs new file mode 100644 index 0000000000..2d41a7086a --- /dev/null +++ b/ICSharpCode.NRefactory.CSharp/Refactoring/Inspector/UseVarKeywordInspector.cs @@ -0,0 +1,121 @@ +// +// UseVarKeywordInspector.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2012 Xamarin +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using ICSharpCode.NRefactory.PatternMatching; +using System.Collections.Generic; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.Semantics; +using System.Linq; + +namespace ICSharpCode.NRefactory.CSharp.Refactoring +{ + /// + /// Checks for places where the 'var' keyword can be used. Note that the action is actually done with a context + /// action. + /// + public class UseVarKeywordInspector : IInspector + { + string title = "Use 'var' keyword"; + + public string Title { + get { + return title; + } + set { + title = value; + } + } + + public IEnumerable Run (BaseRefactoringContext context) + { + var visitor = new GatherVisitor (context, this); + context.RootNode.AcceptVisitor (visitor); + return visitor.FoundIssues; + } + + class GatherVisitor : GatherVisitorBase + { + readonly UseVarKeywordInspector inspector; + + public GatherVisitor (BaseRefactoringContext ctx, UseVarKeywordInspector inspector) : base (ctx) + { + this.inspector = inspector; + } + + public override void VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement) + { + base.VisitVariableDeclarationStatement(variableDeclarationStatement); + if (variableDeclarationStatement.Type is PrimitiveType) { + return; + } + if (variableDeclarationStatement.Type is SimpleType && ((SimpleType)variableDeclarationStatement.Type).Identifier == "var") { + return; + } + + //only checks for cases where the type would be obvious - assignment of new, cast, etc. + //also check the type actually matches else the user might want to assign different subclasses later + foreach (var v in variableDeclarationStatement.Variables) { + if (v.Initializer.IsNull) { + return; + } + + var arrCreate = v.Initializer as ArrayCreateExpression; + if (arrCreate != null) { + var n = variableDeclarationStatement.Type as ComposedType; + //FIXME: check the specifier compatibility + if (n != null && n.ArraySpecifiers.Any() && n.BaseType.IsMatch(arrCreate.Type)) { + continue; + } + return; + } + var objCreate = v.Initializer as ObjectCreateExpression; + if (objCreate != null) { + if (objCreate.Type.IsMatch(variableDeclarationStatement.Type)) { + continue; + } + return; + } + var asCast = v.Initializer as AsExpression; + if (asCast != null) { + if (asCast.Type.IsMatch(variableDeclarationStatement.Type)) { + continue; + } + return; + } + var cast = v.Initializer as CastExpression; + if (cast != null) { + if (cast.Type.IsMatch(variableDeclarationStatement.Type)) { + continue; + } + return; + } + return; + } + AddIssue(variableDeclarationStatement.Type, inspector.Title); + } + } + } +}