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 8cafe8a795..6c71283515 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)
@@ -74,58 +88,21 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
this.inspector = inspector;
}
- public override void VisitConditionalExpression (ConditionalExpression conditionalExpression)
+ 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);
- }
}
}
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);
+ }
+ }
+ }
+}