diff --git a/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/IndexerDeclaration.cs b/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/IndexerDeclaration.cs index c175485ab5..02bc126aec 100644 --- a/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/IndexerDeclaration.cs +++ b/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/IndexerDeclaration.cs @@ -24,6 +24,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +using System; using ICSharpCode.NRefactory.TypeSystem; namespace ICSharpCode.NRefactory.CSharp @@ -47,6 +48,16 @@ namespace ICSharpCode.NRefactory.CSharp set { SetChildByRole (PrivateImplementationTypeRole, value); } } + public override string Name { + get { return "Item"; } + set { throw new NotSupportedException(); } + } + + public override Identifier NameToken { + get { return Identifier.Null; } + set { throw new NotSupportedException(); } + } + public CSharpTokenNode LBracketToken { get { return GetChildByRole (Roles.LBracket); } } @@ -95,7 +106,7 @@ namespace ICSharpCode.NRefactory.CSharp protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { IndexerDeclaration o = other as IndexerDeclaration; - return o != null && MatchString(this.Name, o.Name) + return o != null && this.MatchAttributesAndModifiers(o, match) && this.ReturnType.DoMatch(o.ReturnType, match) && this.PrivateImplementationType.DoMatch(o.PrivateImplementationType, match) && this.Parameters.DoMatch(o.Parameters, match) diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpAstResolver.cs b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpAstResolver.cs index 6c06be7e06..234e17f261 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpAstResolver.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpAstResolver.cs @@ -42,16 +42,23 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver /// Use this overload if you are resolving within a complete C# file. /// /// The current compilation. + /// The compilation unit corresponding to the specified parsed file. /// - /// Result of the for the file being passed. This is used for setting up the context on the resolver. The parsed file must be registered in the compilation. + /// Optional: Result of the for the file being resolved. + /// + /// This is used for setting up the context on the resolver. The parsed file must be registered in the compilation. + /// + /// + /// When a parsedFile is specified, the resolver will use the member's StartLocation/EndLocation to identify + /// member declarations in the AST with members in the type system. + /// When no parsedFile is specified (null value for this parameter), the resolver will instead compare the + /// member's signature in the AST with the signature in the type system. + /// /// - /// The compilation unit corresponding to the specified parsed file. - public CSharpAstResolver(ICompilation compilation, CompilationUnit compilationUnit, CSharpParsedFile parsedFile) + public CSharpAstResolver(ICompilation compilation, CompilationUnit compilationUnit, CSharpParsedFile parsedFile = null) { if (compilation == null) throw new ArgumentNullException("compilation"); - if (parsedFile == null) - throw new ArgumentNullException("parsedFile"); if (compilationUnit == null) throw new ArgumentNullException("compilationUnit"); this.initialResolverState = new CSharpResolver(compilation); @@ -66,9 +73,18 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver /// /// The resolver state at the root node (to be more precise: outside the root node). /// The root node of the resolved tree. - /// The parsed file for the nodes being resolved. This parameter is used only - /// when the root node is on the type level; it is not necessary when an expression is passed. - /// This parameter may be null. + /// + /// Optional: Result of the for the file being resolved. + /// + /// This is used for setting up the context on the resolver. The parsed file must be registered in the compilation. + /// + /// + /// When a parsedFile is specified, the resolver will use the member's StartLocation/EndLocation to identify + /// member declarations in the AST with members in the type system. + /// When no parsedFile is specified (null value for this parameter), the resolver will instead compare the + /// member's signature in the AST with the signature in the type system. + /// + /// public CSharpAstResolver(CSharpResolver resolver, AstNode rootNode, CSharpParsedFile parsedFile = null) { if (resolver == null) diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs b/ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs index eed5b3e8ac..0e395b51cd 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs @@ -515,8 +515,13 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver { CSharpResolver previousResolver = resolver; try { - if (parsedFile != null) + if (parsedFile != null) { resolver = resolver.WithCurrentUsingScope(parsedFile.RootUsingScope.Resolve(resolver.Compilation)); + } else { + var cv = new TypeSystemConvertVisitor(unit.FileName ?? string.Empty); + ApplyVisitorToUsings(cv, unit.Children); + PushUsingScope(cv.ParsedFile.RootUsingScope); + } ScanChildren(unit); return voidResult; } finally { @@ -524,12 +529,50 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } } + void ApplyVisitorToUsings(TypeSystemConvertVisitor visitor, IEnumerable children) + { + foreach (var child in children) { + if (child is ExternAliasDeclaration || child is UsingDeclaration || child is UsingAliasDeclaration) { + child.AcceptVisitor(visitor); + } + } + } + + void PushUsingScope(UsingScope usingScope) + { + usingScope.Freeze(); + resolver = resolver.WithCurrentUsingScope(new ResolvedUsingScope(resolver.CurrentTypeResolveContext, usingScope)); + } + ResolveResult IAstVisitor.VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration) { CSharpResolver previousResolver = resolver; try { if (parsedFile != null) { resolver = resolver.WithCurrentUsingScope(parsedFile.GetUsingScope(namespaceDeclaration.StartLocation).Resolve(resolver.Compilation)); + } else { + string fileName = namespaceDeclaration.GetRegion().FileName ?? string.Empty; + // Fetch parent using scope + // Create root using scope if necessary + if (resolver.CurrentUsingScope == null) + PushUsingScope(new UsingScope()); + + // Create child using scope + DomRegion region = namespaceDeclaration.GetRegion(); + var identifiers = namespaceDeclaration.Identifiers.ToList(); + // For all but the last identifier: + UsingScope usingScope; + for (int i = 0; i < identifiers.Count - 1; i++) { + usingScope = new UsingScope(resolver.CurrentUsingScope.UnresolvedUsingScope, identifiers[i].Name); + usingScope.Region = region; + PushUsingScope(usingScope); + } + // Last using scope: + usingScope = new UsingScope(resolver.CurrentUsingScope.UnresolvedUsingScope, identifiers.Last().Name); + usingScope.Region = region; + var cv = new TypeSystemConvertVisitor(new CSharpParsedFile(region.FileName ?? string.Empty), usingScope); + ApplyVisitorToUsings(cv, namespaceDeclaration.Children); + PushUsingScope(usingScope); } ScanChildren(namespaceDeclaration); // merge undecided lambdas before leaving the using scope so that @@ -591,26 +634,37 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver #region Track CurrentMember ResolveResult IAstVisitor.VisitFieldDeclaration(FieldDeclaration fieldDeclaration) { - return VisitFieldOrEventDeclaration(fieldDeclaration); + return VisitFieldOrEventDeclaration(fieldDeclaration, EntityType.Field); } ResolveResult IAstVisitor.VisitFixedFieldDeclaration(FixedFieldDeclaration fixedFieldDeclaration) { - return VisitFieldOrEventDeclaration(fixedFieldDeclaration); + return VisitFieldOrEventDeclaration(fixedFieldDeclaration, EntityType.Field); } ResolveResult IAstVisitor.VisitEventDeclaration(EventDeclaration eventDeclaration) { - return VisitFieldOrEventDeclaration(eventDeclaration); + return VisitFieldOrEventDeclaration(eventDeclaration, EntityType.Event); } - ResolveResult VisitFieldOrEventDeclaration(EntityDeclaration fieldOrEventDeclaration) + ResolveResult VisitFieldOrEventDeclaration(EntityDeclaration fieldOrEventDeclaration, EntityType entityType) { //int initializerCount = fieldOrEventDeclaration.GetChildrenByRole(Roles.Variable).Count; CSharpResolver oldResolver = resolver; for (AstNode node = fieldOrEventDeclaration.FirstChild; node != null; node = node.NextSibling) { if (node.Role == Roles.Variable) { - resolver = resolver.WithCurrentMember(GetMemberFromLocation(node.StartLocation)); + IMember member = null; + if (parsedFile != null) { + member = GetMemberFromLocation(node.StartLocation); + } else if (resolver.CurrentTypeDefinition != null) { + string name = ((VariableInitializer)node).Name; + if (entityType == EntityType.Event) { + member = resolver.CurrentTypeDefinition.GetEvents(e => e.Name == name && !e.IsExplicitInterfaceImplementation, GetMemberOptions.IgnoreInheritedMembers).FirstOrDefault(); + } else { + member = resolver.CurrentTypeDefinition.GetFields(e => e.Name == name, GetMemberOptions.IgnoreInheritedMembers).FirstOrDefault(); + } + } + resolver = resolver.WithCurrentMember(member); Scan(node); @@ -717,16 +771,57 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } } - ResolveResult VisitMethodMember(EntityDeclaration member) + ResolveResult VisitMethodMember(EntityDeclaration memberDeclaration) { CSharpResolver oldResolver = resolver; try { - resolver = resolver.WithCurrentMember(GetMemberFromLocation(member.StartLocation)); - - ScanChildren(member); + IMember member = null; + if (parsedFile != null) { + member = GetMemberFromLocation(memberDeclaration.StartLocation); + } else if (resolver.CurrentTypeDefinition != null) { + // Re-discover the method: + EntityType entityType = memberDeclaration.EntityType; + var typeParameters = memberDeclaration.GetChildrenByRole(Roles.TypeParameter).ToArray(); + var parameterTypes = TypeSystemConvertVisitor.GetParameterTypes(memberDeclaration.GetChildrenByRole(Roles.Parameter)); + if (entityType == EntityType.Constructor) { + bool isStatic = memberDeclaration.HasModifier(Modifiers.Static); + member = resolver.CurrentTypeDefinition.Methods.FirstOrDefault( + m => m.EntityType == entityType && m.IsStatic == isStatic + && m.Parameters.Count == parameterTypes.Count + && IsMatchingMethod(m, typeParameters, parameterTypes)); + } else { + string name = (entityType == EntityType.Destructor ? "Finalize" : memberDeclaration.Name); + AstType explicitInterfaceAstType = memberDeclaration.GetChildByRole(EntityDeclaration.PrivateImplementationTypeRole); + bool isExplicitInterfaceImplementation = false; + IType explicitInterfaceType = null; + if (!explicitInterfaceAstType.IsNull) { + isExplicitInterfaceImplementation = true; + explicitInterfaceType = explicitInterfaceAstType.ToTypeReference().Resolve(resolver.CurrentTypeResolveContext); + } + foreach (var method in resolver.CurrentTypeDefinition.GetMethods( + m => m.EntityType == entityType && m.Name == name + && m.TypeParameters.Count == typeParameters.Length && m.Parameters.Count == parameterTypes.Count + && m.IsExplicitInterfaceImplementation == isExplicitInterfaceImplementation, + GetMemberOptions.IgnoreInheritedMembers + )) { + if (isExplicitInterfaceImplementation) { + if (method.ImplementedInterfaceMembers.Count != 1) + continue; + if (!explicitInterfaceType.Equals(method.ImplementedInterfaceMembers[0].DeclaringType)) + continue; + } + if (IsMatchingMethod(method, typeParameters, parameterTypes)) { + member = method; + break; + } + } + } + } + resolver = resolver.WithCurrentMember(member); + ScanChildren(memberDeclaration); - if (resolver.CurrentMember != null) - return new MemberResolveResult(null, resolver.CurrentMember, false); + if (member != null) + return new MemberResolveResult(null, member, false); else return errorResult; } finally { @@ -734,6 +829,20 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } } + bool IsMatchingMethod(IMethod method, TypeParameterDeclaration[] typeParameters, IList parameterTypes) + { + for (int i = 0; i < typeParameters.Length; i++) { + if (method.TypeParameters[i].Name != typeParameters[i].Name) + return false; + } + var resolvedParameterTypes = parameterTypes.Resolve(resolver.CurrentTypeResolveContext.WithCurrentMember(method)); + for (int i = 0; i < parameterTypes.Count; i++) { + if (!method.Parameters[i].Type.Equals(resolvedParameterTypes[i])) + return false; + } + return true; + } + ResolveResult IAstVisitor.VisitMethodDeclaration(MethodDeclaration methodDeclaration) { return VisitMethodMember(methodDeclaration); @@ -759,20 +868,51 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver { CSharpResolver oldResolver = resolver; try { - resolver = resolver.WithCurrentMember(GetMemberFromLocation(propertyOrIndexerDeclaration.StartLocation)); + IMember member = null; + if (parsedFile != null) { + member = GetMemberFromLocation(propertyOrIndexerDeclaration.StartLocation); + } else if (resolver.CurrentTypeDefinition != null) { + // Re-discover the property: + string name = propertyOrIndexerDeclaration.Name; + var parameterTypeReferences = TypeSystemConvertVisitor.GetParameterTypes(propertyOrIndexerDeclaration.GetChildrenByRole(Roles.Parameter)); + var parameterTypes = parameterTypeReferences.Resolve(resolver.CurrentTypeResolveContext); + AstType explicitInterfaceAstType = propertyOrIndexerDeclaration.GetChildByRole(EntityDeclaration.PrivateImplementationTypeRole); + bool isExplicitInterfaceImplementation = false; + IType explicitInterfaceType = null; + if (!explicitInterfaceAstType.IsNull) { + isExplicitInterfaceImplementation = true; + explicitInterfaceType = explicitInterfaceAstType.ToTypeReference().Resolve(resolver.CurrentTypeResolveContext); + } + foreach (IProperty property in resolver.CurrentTypeDefinition.GetProperties( + p => p.Name == name && p.Parameters.Count == parameterTypes.Count && p.IsExplicitInterfaceImplementation == isExplicitInterfaceImplementation, + GetMemberOptions.IgnoreInheritedMembers + )) { + if (isExplicitInterfaceImplementation) { + if (property.ImplementedInterfaceMembers.Count != 1) + continue; + if (!explicitInterfaceType.Equals(property.ImplementedInterfaceMembers[0].DeclaringType)) + continue; + } + if (Enumerable.SequenceEqual(parameterTypes, property.Parameters.Select(p => p.Type))) { + member = property; + break; + } + } + } + resolver = resolver.WithCurrentMember(member); for (AstNode node = propertyOrIndexerDeclaration.FirstChild; node != null; node = node.NextSibling) { - if (node.Role == PropertyDeclaration.SetterRole && resolver.CurrentMember != null) { + if (node.Role == PropertyDeclaration.SetterRole && member != null) { resolver = resolver.PushBlock(); - resolver = resolver.AddVariable(new DefaultParameter(resolver.CurrentMember.ReturnType, "value")); + resolver = resolver.AddVariable(new DefaultParameter(member.ReturnType, "value")); Scan(node); resolver = resolver.PopBlock(); } else { Scan(node); } } - if (resolver.CurrentMember != null) - return new MemberResolveResult(null, resolver.CurrentMember, false); + if (member != null) + return new MemberResolveResult(null, member, false); else return errorResult; } finally { @@ -794,18 +934,38 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver { CSharpResolver oldResolver = resolver; try { - resolver = resolver.WithCurrentMember(GetMemberFromLocation(eventDeclaration.StartLocation)); + IMember member = null; + if (parsedFile != null) { + member = GetMemberFromLocation(eventDeclaration.StartLocation); + } else if (resolver.CurrentTypeDefinition != null) { + string name = eventDeclaration.Name; + AstType explicitInterfaceAstType = eventDeclaration.PrivateImplementationType; + if (explicitInterfaceAstType.IsNull) { + member = resolver.CurrentTypeDefinition.GetEvents( + e => e.Name == name && !e.IsExplicitInterfaceImplementation, + GetMemberOptions.IgnoreInheritedMembers).FirstOrDefault(); + } else { + IType explicitInterfaceType = explicitInterfaceAstType.ToTypeReference().Resolve(resolver.CurrentTypeResolveContext); + member = resolver.CurrentTypeDefinition.GetEvents( + e => e.Name == name && e.IsExplicitInterfaceImplementation, + GetMemberOptions.IgnoreInheritedMembers + ).FirstOrDefault( + e => e.ImplementedInterfaceMembers.Count == 1 && explicitInterfaceType.Equals(e.ImplementedInterfaceMembers[0].DeclaringType) + ); + } + } + resolver = resolver.WithCurrentMember(member); - if (resolver.CurrentMember != null) { + if (member != null) { resolver = resolver.PushBlock(); - resolver = resolver.AddVariable(new DefaultParameter(resolver.CurrentMember.ReturnType, "value")); + resolver = resolver.AddVariable(new DefaultParameter(member.ReturnType, "value")); ScanChildren(eventDeclaration); } else { ScanChildren(eventDeclaration); } - if (resolver.CurrentMember != null) - return new MemberResolveResult(null, resolver.CurrentMember, false); + if (member != null) + return new MemberResolveResult(null, member, false); else return errorResult; } finally { @@ -876,16 +1036,23 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver try { // Scan enum member attributes before setting resolver.CurrentMember, so that // enum values used as attribute arguments have the correct type. - // (which an enum member, all other enum members are treated as having their underlying type) + // (within an enum member, all other enum members are treated as having their underlying type) foreach (var attributeSection in enumMemberDeclaration.Attributes) Scan(attributeSection); - resolver = resolver.WithCurrentMember(GetMemberFromLocation(enumMemberDeclaration.StartLocation)); + IMember member = null; + if (parsedFile != null) { + member = GetMemberFromLocation(enumMemberDeclaration.StartLocation); + } else if (resolver.CurrentTypeDefinition != null) { + string name = enumMemberDeclaration.Name; + member = resolver.CurrentTypeDefinition.GetFields(f => f.Name == name, GetMemberOptions.IgnoreInheritedMembers).FirstOrDefault(); + } + resolver = resolver.WithCurrentMember(member); if (resolverEnabled && resolver.CurrentTypeDefinition != null) { ResolveAndProcessConversion(enumMemberDeclaration.Initializer, resolver.CurrentTypeDefinition.EnumUnderlyingType); - if (resolverEnabled && resolver.CurrentMember != null) - return new MemberResolveResult(null, resolver.CurrentMember, false); + if (resolverEnabled && member != null) + return new MemberResolveResult(null, member, false); else return errorResult; } else { @@ -1747,7 +1914,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver DomRegion MakeRegion(AstNode node) { - return new DomRegion(parsedFile != null ? parsedFile.FileName : null, node.StartLocation, node.EndLocation); + if (parsedFile != null) + return new DomRegion(parsedFile.FileName, node.StartLocation, node.EndLocation); + else + return node.GetRegion(); } sealed class ExplicitlyTypedLambda : LambdaBase diff --git a/ICSharpCode.NRefactory.CSharp/TypeSystem/TypeSystemConvertVisitor.cs b/ICSharpCode.NRefactory.CSharp/TypeSystem/TypeSystemConvertVisitor.cs index 934e990e79..55c80e9c9a 100644 --- a/ICSharpCode.NRefactory.CSharp/TypeSystem/TypeSystemConvertVisitor.cs +++ b/ICSharpCode.NRefactory.CSharp/TypeSystem/TypeSystemConvertVisitor.cs @@ -1139,6 +1139,18 @@ namespace ICSharpCode.NRefactory.CSharp.TypeSystem outputList.Add(p); } } + + internal static IList GetParameterTypes(IEnumerable parameters) + { + List result = new List(); + foreach (ParameterDeclaration pd in parameters) { + ITypeReference type = pd.Type.ToTypeReference(); + if (pd.ParameterModifier == ParameterModifier.Ref || pd.ParameterModifier == ParameterModifier.Out) + type = new ByReferenceTypeReference(type); + result.Add(type); + } + return result; + } #endregion #region XML Documentation diff --git a/ICSharpCode.NRefactory.ConsistencyCheck/Program.cs b/ICSharpCode.NRefactory.ConsistencyCheck/Program.cs index 869b98a48a..b93c60f960 100644 --- a/ICSharpCode.NRefactory.ConsistencyCheck/Program.cs +++ b/ICSharpCode.NRefactory.ConsistencyCheck/Program.cs @@ -57,10 +57,10 @@ namespace ICSharpCode.NRefactory.ConsistencyCheck solution.AllFiles.Count(), solution.Projects.Count); - using (new Timer("ID String test... ")) - IDStringConsistencyCheck.Run(solution); + //using (new Timer("ID String test... ")) IDStringConsistencyCheck.Run(solution); //RunTestOnAllFiles("Roundtripping test", RoundtripTest.RunTest); RunTestOnAllFiles("Resolver test", ResolverTest.RunTest); + RunTestOnAllFiles("Resolver test (no parsed file)", ResolverTest.RunTestWithoutParsedFile); RunTestOnAllFiles("Resolver test (randomized order)", RandomizedOrderResolverTest.RunTest); new FindReferencesConsistencyCheck(solution).Run(); diff --git a/ICSharpCode.NRefactory.ConsistencyCheck/RandomizedOrderResolverTest.cs b/ICSharpCode.NRefactory.ConsistencyCheck/RandomizedOrderResolverTest.cs index 4c1ad1f799..d6f9390bfc 100644 --- a/ICSharpCode.NRefactory.ConsistencyCheck/RandomizedOrderResolverTest.cs +++ b/ICSharpCode.NRefactory.ConsistencyCheck/RandomizedOrderResolverTest.cs @@ -147,7 +147,7 @@ namespace ICSharpCode.NRefactory.ConsistencyCheck return false; } - bool IsEqualResolveResult(ResolveResult rr1, ResolveResult rr2) + internal static bool IsEqualResolveResult(ResolveResult rr1, ResolveResult rr2) { if (rr1 == rr2) return true; @@ -169,7 +169,7 @@ namespace ICSharpCode.NRefactory.ConsistencyCheck return eq; } - bool Compare(object val1, object val2, Type type) + static bool Compare(object val1, object val2, Type type) { if (val1 == val2) return true; @@ -205,7 +205,7 @@ namespace ICSharpCode.NRefactory.ConsistencyCheck } } - bool IsEqualResolverState(CSharpResolver r1, CSharpResolver r2) + internal static bool IsEqualResolverState(CSharpResolver r1, CSharpResolver r2) { if (r1.CheckForOverflow != r2.CheckForOverflow) return false; @@ -226,7 +226,7 @@ namespace ICSharpCode.NRefactory.ConsistencyCheck return r1.LocalVariables.Zip(r2.LocalVariables, IsEqualVariable).All(_ => _); } - bool IsEqualVariable(IVariable v1, IVariable v2) + internal static bool IsEqualVariable(IVariable v1, IVariable v2) { return object.Equals(v1.ConstantValue, v2.ConstantValue) && v1.IsConst == v2.IsConst diff --git a/ICSharpCode.NRefactory.ConsistencyCheck/ResolverTest.cs b/ICSharpCode.NRefactory.ConsistencyCheck/ResolverTest.cs index a20e26135d..59f156eb8c 100644 --- a/ICSharpCode.NRefactory.ConsistencyCheck/ResolverTest.cs +++ b/ICSharpCode.NRefactory.ConsistencyCheck/ResolverTest.cs @@ -29,7 +29,7 @@ using ICSharpCode.NRefactory.TypeSystem; namespace ICSharpCode.NRefactory.ConsistencyCheck { /// - /// Description of ResolverTest. + /// Validates that no compile errors are found in valid code. /// public class ResolverTest { @@ -38,10 +38,10 @@ namespace ICSharpCode.NRefactory.ConsistencyCheck CSharpAstResolver resolver = new CSharpAstResolver(file.Project.Compilation, file.CompilationUnit, file.ParsedFile); var navigator = new ValidatingResolveAllNavigator(file.FileName); resolver.ApplyNavigator(navigator, CancellationToken.None); - navigator.Validate(file.CompilationUnit); + navigator.Validate(resolver, file.CompilationUnit); } - sealed class ValidatingResolveAllNavigator : IResolveVisitorNavigator + class ValidatingResolveAllNavigator : IResolveVisitorNavigator { string fileName; bool allowErrors; @@ -54,7 +54,7 @@ namespace ICSharpCode.NRefactory.ConsistencyCheck this.allowErrors = (fileName.Contains(".xaml") || File.Exists(Path.ChangeExtension(fileName, ".xaml")) || fileName.EndsWith("AvalonDockLayout.cs") || fileName.EndsWith("ResourcesFileTreeNode.cs") || fileName.EndsWith("ChangeMarkerMargin.cs")); } - HashSet resolvedNodes = new HashSet(); + Dictionary resolvedNodes = new Dictionary(); HashSet nodesWithConversions = new HashSet(); public ResolveVisitorNavigationMode Scan(AstNode node) @@ -62,10 +62,11 @@ namespace ICSharpCode.NRefactory.ConsistencyCheck return ResolveVisitorNavigationMode.Resolve; } - public void Resolved(AstNode node, ResolveResult result) + public virtual void Resolved(AstNode node, ResolveResult result) { - if (!resolvedNodes.Add(node)) + if (resolvedNodes.ContainsKey(node)) throw new InvalidOperationException("Duplicate Resolved() call"); + resolvedNodes.Add(node, result); if (CSharpAstResolver.IsUnresolvableNode(node)) throw new InvalidOperationException("Resolved unresolvable node"); @@ -74,7 +75,7 @@ namespace ICSharpCode.NRefactory.ConsistencyCheck } } - public void ProcessConversion(Expression expression, ResolveResult result, Conversion conversion, IType targetType) + public virtual void ProcessConversion(Expression expression, ResolveResult result, Conversion conversion, IType targetType) { if (!nodesWithConversions.Add(expression)) throw new InvalidOperationException("Duplicate ProcessConversion() call"); @@ -83,13 +84,47 @@ namespace ICSharpCode.NRefactory.ConsistencyCheck } } - public void Validate(CompilationUnit cu) + public virtual void Validate(CSharpAstResolver resolver, CompilationUnit cu) { - foreach (AstNode node in cu.DescendantsAndSelf.Except(resolvedNodes)) { + foreach (AstNode node in cu.DescendantsAndSelf.Except(resolvedNodes.Keys)) { if (!CSharpAstResolver.IsUnresolvableNode(node)) { Console.WriteLine("Forgot to resolve " + node); } } + foreach (var pair in resolvedNodes) { + if (resolver.Resolve(pair.Key) != pair.Value) + throw new InvalidOperationException("Inconsistent result"); + } + } + } + + public static void RunTestWithoutParsedFile(CSharpFile file) + { + CSharpAstResolver originalResolver = new CSharpAstResolver(file.Project.Compilation, file.CompilationUnit, file.ParsedFile); + originalResolver.ApplyNavigator(new ValidatingResolveAllNavigator(file.FileName), CancellationToken.None); + CSharpAstResolver resolver = new CSharpAstResolver(file.Project.Compilation, file.CompilationUnit); + var navigator = new ComparingResolveAllNavigator(originalResolver); + resolver.ApplyNavigator(navigator, CancellationToken.None); + navigator.Validate(resolver, file.CompilationUnit); + } + + sealed class ComparingResolveAllNavigator : ValidatingResolveAllNavigator + { + readonly CSharpAstResolver originalResolver; + + public ComparingResolveAllNavigator(CSharpAstResolver originalResolver) + : base(originalResolver.ParsedFile.FileName) + { + this.originalResolver = originalResolver; + } + + public override void Resolved(AstNode node, ResolveResult result) + { + base.Resolved(node, result); + ResolveResult originalResult = originalResolver.Resolve(node); + if (!RandomizedOrderResolverTest.IsEqualResolveResult(originalResult, result)) { + Console.WriteLine("Compiler error at " + node.GetRegion().FileName + ":" + node.StartLocation + ": Should be " + originalResult + " but was " + result); + } } } } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Parser/TypeMembers/IndexerDeclarationTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Parser/TypeMembers/IndexerDeclarationTests.cs index 4cabc1d2bf..330a0fe2f0 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Parser/TypeMembers/IndexerDeclarationTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Parser/TypeMembers/IndexerDeclarationTests.cs @@ -35,6 +35,7 @@ namespace ICSharpCode.NRefactory.CSharp.Parser.TypeMembers Assert.AreEqual(Modifiers.Public, id.Modifiers); Assert.AreEqual(Modifiers.None, id.Getter.Modifiers); Assert.AreEqual(Modifiers.Protected, id.Setter.Modifiers); + Assert.AreEqual("Item", id.Name); } [Test] @@ -59,7 +60,6 @@ namespace ICSharpCode.NRefactory.CSharp.Parser.TypeMembers Identifier = "MyInterface", TypeArguments = { new PrimitiveType("string") } }, - Name = "this", Parameters = { new ParameterDeclaration(new PrimitiveType("int"), "a"), new ParameterDeclaration(new PrimitiveType("string"), "b")