diff --git a/ICSharpCode.NRefactory.CSharp/Analysis/DefiniteAssignmentAnalysis.cs b/ICSharpCode.NRefactory.CSharp/Analysis/DefiniteAssignmentAnalysis.cs index fdcc4d717c..fa3ca39144 100644 --- a/ICSharpCode.NRefactory.CSharp/Analysis/DefiniteAssignmentAnalysis.cs +++ b/ICSharpCode.NRefactory.CSharp/Analysis/DefiniteAssignmentAnalysis.cs @@ -721,7 +721,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis } DefiniteAssignmentStatus afterTrue = conditionalExpression.TrueExpression.AcceptVisitor(this, beforeTrue); - DefiniteAssignmentStatus afterFalse = conditionalExpression.TrueExpression.AcceptVisitor(this, beforeFalse); + DefiniteAssignmentStatus afterFalse = conditionalExpression.FalseExpression.AcceptVisitor(this, beforeFalse); return MergeStatus(CleanSpecialValues(afterTrue), CleanSpecialValues(afterFalse)); } } diff --git a/ICSharpCode.NRefactory.CSharp/Ast/CompilationUnit.cs b/ICSharpCode.NRefactory.CSharp/Ast/CompilationUnit.cs index 2897d7fab2..a48b924974 100644 --- a/ICSharpCode.NRefactory.CSharp/Ast/CompilationUnit.cs +++ b/ICSharpCode.NRefactory.CSharp/Ast/CompilationUnit.cs @@ -61,13 +61,30 @@ namespace ICSharpCode.NRefactory.CSharp public AstNodeCollection Members { get { return GetChildrenByRole(MemberRole); } } - + + IList conditionalSymbols = null; + List errors = new List (); public List Errors { get { return errors; } } - + + + /// + /// Gets the conditional symbols used to parse the source file. Note that this list contains + /// the conditional symbols at the start of the first token in the file - including the ones defined + /// in the source file. + /// + public IList ConditionalSymbols { + get { + return conditionalSymbols ?? EmptyList.Instance; + } + internal set { + conditionalSymbols = value; + } + } + /// /// Gets the expression that was on top of the parse stack. /// This is the only way to get an expression that isn't part of a statment. diff --git a/ICSharpCode.NRefactory.CSharp/Ast/Expressions/PointerReferenceExpression.cs b/ICSharpCode.NRefactory.CSharp/Ast/Expressions/PointerReferenceExpression.cs index a7152fd1db..35c4a72035 100644 --- a/ICSharpCode.NRefactory.CSharp/Ast/Expressions/PointerReferenceExpression.cs +++ b/ICSharpCode.NRefactory.CSharp/Ast/Expressions/PointerReferenceExpression.cs @@ -43,13 +43,22 @@ namespace ICSharpCode.NRefactory.CSharp public CSharpTokenNode ArrowToken { get { return GetChildByRole (ArrowRole); } } - + public string MemberName { get { return GetChildByRole (Roles.Identifier).Name; } set { - SetChildByRole(Roles.Identifier, Identifier.Create (value)); + SetChildByRole(Roles.Identifier, Identifier.Create (value)); + } + } + + public Identifier MemberNameToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole (Roles.Identifier, value); } } @@ -61,7 +70,7 @@ namespace ICSharpCode.NRefactory.CSharp { visitor.VisitPointerReferenceExpression (this); } - + public override T AcceptVisitor (IAstVisitor visitor) { return visitor.VisitPointerReferenceExpression (this); diff --git a/ICSharpCode.NRefactory.CSharp/CSharpProjectContent.cs b/ICSharpCode.NRefactory.CSharp/CSharpProjectContent.cs index 44c6e22969..a890c1ea41 100644 --- a/ICSharpCode.NRefactory.CSharp/CSharpProjectContent.cs +++ b/ICSharpCode.NRefactory.CSharp/CSharpProjectContent.cs @@ -31,6 +31,7 @@ namespace ICSharpCode.NRefactory.CSharp public class CSharpProjectContent : IProjectContent { string assemblyName; + string location; Dictionary parsedFiles; List assemblyReferences; CompilerSettings compilerSettings; @@ -47,6 +48,7 @@ namespace ICSharpCode.NRefactory.CSharp protected CSharpProjectContent(CSharpProjectContent pc) { this.assemblyName = pc.assemblyName; + this.location = pc.location; this.parsedFiles = new Dictionary(pc.parsedFiles, Platform.FileNameComparer); this.assemblyReferences = new List(pc.assemblyReferences); this.compilerSettings = pc.compilerSettings; @@ -63,7 +65,11 @@ namespace ICSharpCode.NRefactory.CSharp public string AssemblyName { get { return assemblyName; } } - + + public string Location { + get { return location; } + } + public CompilerSettings CompilerSettings { get { return compilerSettings; } } @@ -123,6 +129,13 @@ namespace ICSharpCode.NRefactory.CSharp pc.assemblyName = newAssemblyName; return pc; } + + public IProjectContent SetLocation(string location) + { + CSharpProjectContent pc = Clone(); + pc.location = location; + return pc; + } public IProjectContent SetCompilerSettings(object compilerSettings) { diff --git a/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs b/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs index 29afc6df02..e03dc273ae 100644 --- a/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs +++ b/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs @@ -712,7 +712,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion } } idx++; - foreach (var list in mgr.GetExtensionMethods ()) { + foreach (var list in mgr.GetEligibleExtensionMethods (true)) { foreach (var method in list) { if (idx < method.Parameters.Count && method.Parameters [idx].Type.Kind == TypeKind.Delegate) { AutoSelect = false; @@ -2268,11 +2268,6 @@ namespace ICSharpCode.NRefactory.CSharp.Completion continue; result.AddMember(field); } - foreach (var m in type.GetMethods ()) { - if (m.Name == "TryParse") { - result.AddMember(m); - } - } return result.Result; } @@ -2296,11 +2291,6 @@ namespace ICSharpCode.NRefactory.CSharp.Completion foreach (var field in trr.Type.GetFields ()) { result.AddMember(field); } - foreach (var m in trr.Type.GetMethods ()) { - if (m.Name == "TryParse" && m.IsStatic) { - result.AddMember(m); - } - } return result.Result; } } diff --git a/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngineBase.cs b/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngineBase.cs index a2ec8ec116..3ffa14a9c5 100644 --- a/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngineBase.cs +++ b/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngineBase.cs @@ -801,6 +801,8 @@ namespace ICSharpCode.NRefactory.CSharp.Completion if (root == null) { return null; } + if (root is Accessor) + root = root.Parent; var csResolver = CompletionContextProvider.GetResolver (GetState(), root); var result = csResolver.Resolve(resolveNode); var state = csResolver.GetResolverStateBefore(resolveNode); diff --git a/ICSharpCode.NRefactory.CSharp/Completion/CSharpParameterCompletionEngine.cs b/ICSharpCode.NRefactory.CSharp/Completion/CSharpParameterCompletionEngine.cs index 229016533c..6dc68605e6 100644 --- a/ICSharpCode.NRefactory.CSharp/Completion/CSharpParameterCompletionEngine.cs +++ b/ICSharpCode.NRefactory.CSharp/Completion/CSharpParameterCompletionEngine.cs @@ -121,7 +121,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion yield return method; } - foreach (var extMethods in resolveResult.GetExtensionMethods ()) { + foreach (var extMethods in resolveResult.GetEligibleExtensionMethods (true)) { foreach (var method in extMethods) { yield return method; } diff --git a/ICSharpCode.NRefactory.CSharp/Completion/CompletionDataWrapper.cs b/ICSharpCode.NRefactory.CSharp/Completion/CompletionDataWrapper.cs index d244b97771..68f20c593a 100644 --- a/ICSharpCode.NRefactory.CSharp/Completion/CompletionDataWrapper.cs +++ b/ICSharpCode.NRefactory.CSharp/Completion/CompletionDataWrapper.cs @@ -36,7 +36,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion { CSharpCompletionEngine completion; List result = new List (); - + public List Result { get { return result; @@ -48,24 +48,24 @@ namespace ICSharpCode.NRefactory.CSharp.Completion return completion.factory; } } - + public CompletionDataWrapper (CSharpCompletionEngine completion) { this.completion = completion; } - + public void Add (ICompletionData data) { result.Add (data); } - + public void AddCustom (string displayText, string description = null, string completionText = null) { result.Add (Factory.CreateLiteralCompletionData (displayText, description, completionText)); } - + HashSet usedNamespaces = new HashSet (); - + public void AddNamespace (string name) { if (string.IsNullOrEmpty (name) || usedNamespaces.Contains (name)) @@ -73,13 +73,15 @@ namespace ICSharpCode.NRefactory.CSharp.Completion usedNamespaces.Add (name); result.Add (Factory.CreateNamespaceCompletionData (name)); } - + HashSet usedTypes = new HashSet (); - + public ICompletionData AddType(IType type, string shortType) { if (type == null || string.IsNullOrEmpty(shortType) || usedTypes.Contains(shortType)) return null; + if (type.Name == "Void" && type.Namespace == "System") + return null; usedTypes.Add(shortType); var iCompletionData = Factory.CreateTypeCompletionData(type, shortType); result.Add(iCompletionData); @@ -90,14 +92,16 @@ namespace ICSharpCode.NRefactory.CSharp.Completion { if (type == null || string.IsNullOrEmpty(shortType) || usedTypes.Contains(shortType)) return null; + if (type.Name == "Void" && type.Namespace == "System") + return null; usedTypes.Add(shortType); var iCompletionData = Factory.CreateTypeCompletionData(type, shortType); result.Add(iCompletionData); return iCompletionData; } - + Dictionary> data = new Dictionary> (); - + public ICompletionData AddVariable(IVariable variable) { if (data.ContainsKey(variable.Name)) @@ -107,14 +111,14 @@ namespace ICSharpCode.NRefactory.CSharp.Completion result.Add(cd); return cd; } - + public ICompletionData AddNamedParameterVariable(IVariable variable) { var name = variable.Name + ":"; if (data.ContainsKey(name)) return null; data [name] = new List(); - + var cd = Factory.CreateVariableCompletionData(variable); cd.CompletionText += ":"; cd.DisplayText += ":"; @@ -129,12 +133,12 @@ namespace ICSharpCode.NRefactory.CSharp.Completion data [variable.Name] = new List (); result.Add (Factory.CreateVariableCompletionData (variable)); } - + public ICompletionData AddMember (IUnresolvedMember member) { var newData = Factory.CreateEntityCompletionData (member); -// newData.HideExtensionParameter = HideExtensionParameter; + // newData.HideExtensionParameter = HideExtensionParameter; string memberKey = newData.DisplayText; if (memberKey == null) return null; @@ -143,7 +147,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion } List existingData; data.TryGetValue (memberKey, out existingData); - + if (existingData != null) { var a = member as IEntity; foreach (var d in existingData) { @@ -171,7 +175,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion { var newData = Factory.CreateEntityCompletionData (member); -// newData.HideExtensionParameter = HideExtensionParameter; + // newData.HideExtensionParameter = HideExtensionParameter; string memberKey = newData.DisplayText; if (memberKey == null) return null; @@ -180,7 +184,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion } List existingData; data.TryGetValue (memberKey, out existingData); - + if (existingData != null) { var a = member as IEntity; foreach (var d in existingData) { @@ -203,7 +207,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion } return newData; } - + internal CompletionCategory GetCompletionCategory (IType type) { if (type == null) @@ -212,7 +216,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion completionCategories [type] = new TypeCompletionCategory (type); return completionCategories [type]; } - + Dictionary completionCategories = new Dictionary (); class TypeCompletionCategory : CompletionCategory { @@ -231,10 +235,10 @@ namespace ICSharpCode.NRefactory.CSharp.Completion var compareCategory = other as TypeCompletionCategory; if (compareCategory == null) return -1; - + if (Type.ReflectionName == compareCategory.Type.ReflectionName) return 0; - + if (Type.GetAllBaseTypes ().Any (t => t.ReflectionName == compareCategory.Type.ReflectionName)) return -1; return 1; @@ -254,8 +258,8 @@ namespace ICSharpCode.NRefactory.CSharp.Completion Result.Add(Factory.CreateEntityCompletionData( field, typeString + "." + field.Name - ) - ); + ) + ); } } } diff --git a/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj b/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj index 8cf58882ed..4ee3cda294 100644 --- a/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj +++ b/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj @@ -14,7 +14,7 @@ false 10.0.0 2.0 - true + True ..\ICSharpCode.NRefactory.snk False File @@ -41,11 +41,11 @@ TRACE;FULL_AST - none + PdbOnly full - true + True @@ -402,6 +402,7 @@ + diff --git a/ICSharpCode.NRefactory.CSharp/Parser/CSharpParser.cs b/ICSharpCode.NRefactory.CSharp/Parser/CSharpParser.cs index 5c2e3b9db8..78bf756126 100644 --- a/ICSharpCode.NRefactory.CSharp/Parser/CSharpParser.cs +++ b/ICSharpCode.NRefactory.CSharp/Parser/CSharpParser.cs @@ -3472,6 +3472,73 @@ namespace ICSharpCode.NRefactory.CSharp return result; } #endregion + + #region XmlDoc + public DocumentationReference ConvertXmlDoc(DocumentationBuilder doc) + { + DocumentationReference result = new DocumentationReference(); + if (doc.ParsedName != null) { + if (doc.ParsedName.Name == "") { + result.EntityType = EntityType.Indexer; + } else { + result.MemberName = doc.ParsedName.Name; + } + if (doc.ParsedName.Left != null) { + result.DeclaringType = ConvertToType(doc.ParsedName.Left); + } else if (doc.ParsedBuiltinType != null) { + result.DeclaringType = ConvertToType(doc.ParsedBuiltinType); + } + if (doc.ParsedName.TypeParameters != null) { + for (int i = 0; i < doc.ParsedName.TypeParameters.Count; i++) { + result.TypeArguments.Add(ConvertToType(doc.ParsedName.TypeParameters[i])); + } + } + } else if (doc.ParsedBuiltinType != null) { + result.EntityType = EntityType.TypeDefinition; + result.DeclaringType = ConvertToType(doc.ParsedBuiltinType); + } + if (doc.ParsedParameters != null) { + result.HasParameterList = true; + result.Parameters.AddRange(doc.ParsedParameters.Select(ConvertXmlDocParameter)); + } + if (doc.ParsedOperator != null) { + result.EntityType = EntityType.Operator; + result.OperatorType = (OperatorType)doc.ParsedOperator; + if (result.OperatorType == OperatorType.Implicit || result.OperatorType == OperatorType.Explicit) { + var returnTypeParam = result.Parameters.LastOrNullObject(); + returnTypeParam.Remove(); // detach from parameter list + var returnType = returnTypeParam.Type; + returnType.Remove(); + result.ConversionOperatorReturnType = returnType; + } + if (result.Parameters.Count == 0) { + // reset HasParameterList if necessary + result.HasParameterList = false; + } + } + return result; + } + + ParameterDeclaration ConvertXmlDocParameter(DocumentationParameter p) + { + ParameterDeclaration result = new ParameterDeclaration(); + switch (p.Modifier) { + case Parameter.Modifier.OUT: + result.ParameterModifier = ParameterModifier.Out; + break; + case Parameter.Modifier.REF: + result.ParameterModifier = ParameterModifier.Ref; + break; + case Parameter.Modifier.PARAMS: + result.ParameterModifier = ParameterModifier.Params; + break; + } + if (p.Type != null) { + result.Type = ConvertToType(p.Type); + } + return result; + } + #endregion } public CSharpParser () @@ -3672,7 +3739,9 @@ namespace ICSharpCode.NRefactory.CSharp if (top.LastYYValue is Mono.CSharp.Expression) { conversionVisitor.Unit.TopExpression = ((Mono.CSharp.Expression)top.LastYYValue).Accept(conversionVisitor) as AstNode; } + conversionVisitor.Unit.FileName = fileName; + conversionVisitor.Unit.ConditionalSymbols = top.Conditionals.ToArray (); return conversionVisitor.Unit; } @@ -3713,11 +3782,12 @@ namespace ICSharpCode.NRefactory.CSharp Location.Initialize (new List (new [] { file })); var module = new ModuleContainer (ctx); var parser = Driver.Parse (reader, file, module, lineModifier); - + var top = new CompilerCompilationUnit () { ModuleCompiled = module, LocationsBag = parser.LocationsBag, - SpecialsBag = parser.Lexer.sbag + SpecialsBag = parser.Lexer.sbag, + Conditionals = parser.Lexer.SourceFile.Conditionals }; var unit = Parse (top, fileName, lineModifier); unit.Errors.AddRange (errorReportPrinter.Errors); @@ -3733,8 +3803,13 @@ namespace ICSharpCode.NRefactory.CSharp if (cu == null) return Enumerable.Empty (); var td = cu.Children.FirstOrDefault () as TypeDeclaration; - if (td != null) - return td.Members; + if (td != null) { + var members = td.Members.ToArray(); + // detach members from parent + foreach (var m in members) + m.Remove(); + return members; + } return Enumerable.Empty (); } @@ -3743,8 +3818,13 @@ namespace ICSharpCode.NRefactory.CSharp string code = "void M() { " + Environment.NewLine + reader.ReadToEnd () + "}"; var members = ParseTypeMembers (new StringReader (code), lineModifier - 1); var method = members.FirstOrDefault () as MethodDeclaration; - if (method != null && method.Body != null) - return method.Body.Statements; + if (method != null && method.Body != null) { + var statements = method.Body.Statements.ToArray(); + // detach statements from parent + foreach (var st in statements) + st.Remove(); + return statements; + } return Enumerable.Empty (); } @@ -3753,8 +3833,11 @@ namespace ICSharpCode.NRefactory.CSharp string code = reader.ReadToEnd () + " a;"; var members = ParseTypeMembers (new StringReader (code)); var field = members.FirstOrDefault () as FieldDeclaration; - if (field != null) - return field.ReturnType; + if (field != null) { + AstType type = field.ReturnType; + type.Remove(); + return type; + } return AstType.Null; } @@ -3763,8 +3846,11 @@ namespace ICSharpCode.NRefactory.CSharp var es = ParseStatements (new StringReader ("tmp = " + Environment.NewLine + reader.ReadToEnd () + ";"), -1).FirstOrDefault () as ExpressionStatement; if (es != null) { AssignmentExpression ae = es.Expression as AssignmentExpression; - if (ae != null) - return ae.Right; + if (ae != null) { + Expression expr = ae.Right; + expr.Remove(); + return expr; + } } return Expression.Null; } @@ -3780,12 +3866,39 @@ namespace ICSharpCode.NRefactory.CSharp public DocumentationReference ParseDocumentationReference (string cref) { + // see Mono.CSharp.DocumentationBuilder.HandleXrefCommon if (cref == null) throw new ArgumentNullException ("cref"); + + // Additional symbols for < and > are allowed for easier XML typing cref = cref.Replace ('{', '<').Replace ('}', '>'); - // TODO: add support for parsing cref attributes - // (documentation_parsing production, see DocumentationBuilder.HandleXrefCommon) - throw new NotImplementedException (); + + lock (parseLock) { + errorReportPrinter = new ErrorReportPrinter(""); + var ctx = new CompilerContext(compilerSettings.ToMono(), errorReportPrinter); + ctx.Settings.TabSize = 1; + var stream = new MemoryStream(Encoding.Unicode.GetBytes(cref)); + var reader = new SeekableStreamReader(stream, Encoding.Unicode); + var file = new SourceFile("", "", 0); + Location.Initialize(new List (new [] { file })); + var module = new ModuleContainer(ctx); + module.DocumentationBuilder = new DocumentationBuilder(); + var source_file = new CompilationSourceFile (module); + var report = new Report (ctx, errorReportPrinter); + var parser = new Mono.CSharp.CSharpParser (reader, source_file, report); + parser.Lexer.putback_char = Tokenizer.DocumentationXref; + parser.Lexer.parsing_generic_declaration_doc = true; + parser.parse (); + if (report.Errors > 0) { +// Report.Warning (1584, 1, mc.Location, "XML comment on `{0}' has syntactically incorrect cref attribute `{1}'", +// mc.GetSignatureForError (), cref); + } + + ConversionVisitor conversionVisitor = new ConversionVisitor (false, parser.LocationsBag); + DocumentationReference docRef = conversionVisitor.ConvertXmlDoc(module.DocumentationBuilder); + CompilerCallableEntryPoint.Reset(); + return docRef; + } } } } diff --git a/ICSharpCode.NRefactory.CSharp/Parser/mcs/cs-tokenizer.cs b/ICSharpCode.NRefactory.CSharp/Parser/mcs/cs-tokenizer.cs index 6ca0a80b90..600569edc5 100644 --- a/ICSharpCode.NRefactory.CSharp/Parser/mcs/cs-tokenizer.cs +++ b/ICSharpCode.NRefactory.CSharp/Parser/mcs/cs-tokenizer.cs @@ -174,6 +174,13 @@ namespace Mono.CSharp readonly SeekableStreamReader reader; readonly CompilationSourceFile source_file; + + public CompilationSourceFile SourceFile { + get { + return source_file; + } + } + readonly CompilerContext context; SourceFile current_source; diff --git a/ICSharpCode.NRefactory.CSharp/Parser/mcs/doc.cs b/ICSharpCode.NRefactory.CSharp/Parser/mcs/doc.cs index e988178c43..683e4c292b 100644 --- a/ICSharpCode.NRefactory.CSharp/Parser/mcs/doc.cs +++ b/ICSharpCode.NRefactory.CSharp/Parser/mcs/doc.cs @@ -56,6 +56,11 @@ namespace Mono.CSharp XmlDocumentation = new XmlDocument (); XmlDocumentation.PreserveWhitespace = false; } + + internal DocumentationBuilder() + { + // for NRefactory CSharpParser.ParseDocumentationReference + } Report Report { get { diff --git a/ICSharpCode.NRefactory.CSharp/Parser/mcs/driver.cs b/ICSharpCode.NRefactory.CSharp/Parser/mcs/driver.cs index e86263da6c..0baa4833bc 100644 --- a/ICSharpCode.NRefactory.CSharp/Parser/mcs/driver.cs +++ b/ICSharpCode.NRefactory.CSharp/Parser/mcs/driver.cs @@ -369,6 +369,7 @@ namespace Mono.CSharp public ModuleContainer ModuleCompiled { get; set; } public LocationsBag LocationsBag { get; set; } public SpecialsBag SpecialsBag { get; set; } + public IEnumerable Conditionals { get; set; } public object LastYYValue { get; set; } } diff --git a/ICSharpCode.NRefactory.CSharp/Parser/mcs/namespace.cs b/ICSharpCode.NRefactory.CSharp/Parser/mcs/namespace.cs index a33a3a14ae..315aed9808 100644 --- a/ICSharpCode.NRefactory.CSharp/Parser/mcs/namespace.cs +++ b/ICSharpCode.NRefactory.CSharp/Parser/mcs/namespace.cs @@ -591,6 +591,14 @@ namespace Mono.CSharp { } } + public IEnumerable Conditionals { + get { + if (conditionals == null) + return Enumerable.Empty (); + return conditionals.Where (kv => kv.Value).Select (kv => kv.Key); + } + } + public string FileName { get { return file.Name; diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ExtractMethod/ExtractMethodAction.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ExtractMethod/ExtractMethodAction.cs index 12a47472c2..4d3d15c2d3 100644 --- a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ExtractMethod/ExtractMethodAction.cs +++ b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ExtractMethod/ExtractMethodAction.cs @@ -80,11 +80,18 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring.ExtractMethod if (!StaticVisitor.UsesNotStaticMember(context, expression)) method.Modifiers |= Modifiers.Static; var task = script.InsertWithCursor(context.TranslateString("Extract method"), Script.InsertPosition.Before, method); - task.ContinueWith (delegate { + + Action replaceStatements = delegate { var target = new IdentifierExpression(methodName); script.Replace(expression, new InvocationExpression(target)); script.Link(target, method.NameToken); - }, TaskScheduler.FromCurrentSynchronizationContext ()); + }; + + if (task.IsCompleted) { + replaceStatements (null); + } else { + task.ContinueWith (replaceStatements, TaskScheduler.FromCurrentSynchronizationContext ()); + } }); } @@ -113,84 +120,87 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring.ExtractMethod var usedVariables = VariableLookupVisitor.Analyze(context, statements); - var extractedCodeAnalysis = new DefiniteAssignmentAnalysis( - (Statement)statements [0].Parent, - context.Resolver, - context.CancellationToken); + var inExtractedRegion = new VariableUsageAnalyzation (context, usedVariables); var lastStatement = statements [statements.Count - 1]; - extractedCodeAnalysis.SetAnalyzedRange(statements [0], lastStatement); - var statusAfterMethod = new List>(); - foreach (var variable in usedVariables) { - extractedCodeAnalysis.Analyze( - variable.Name, - DefiniteAssignmentStatus.PotentiallyAssigned, - context.CancellationToken); - statusAfterMethod.Add(Tuple.Create(variable, extractedCodeAnalysis.GetStatusAfter(lastStatement))); - } var stmt = statements [0].GetParent(); while (stmt.GetParent () != null) { stmt = stmt.GetParent(); } - var wholeCodeAnalysis = new DefiniteAssignmentAnalysis(stmt, context.Resolver, context.CancellationToken); - var statusBeforeMethod = new Dictionary(); - foreach (var variable in usedVariables) { - wholeCodeAnalysis.Analyze(variable.Name, DefiniteAssignmentStatus.PotentiallyAssigned, context.CancellationToken); - statusBeforeMethod [variable] = extractedCodeAnalysis.GetStatusBefore(statements [0]); - } + inExtractedRegion.SetAnalyzedRange(statements [0], lastStatement); + stmt.AcceptVisitor (inExtractedRegion); - var afterCodeAnalysis = new DefiniteAssignmentAnalysis(stmt, context.Resolver, context.CancellationToken); - var statusAtEnd = new Dictionary(); - afterCodeAnalysis.SetAnalyzedRange(lastStatement, stmt.Statements.Last(), false, true); + var beforeExtractedRegion = new VariableUsageAnalyzation (context, usedVariables); + beforeExtractedRegion.SetAnalyzedRange(statements [0].Parent, statements [0], true, false); + stmt.AcceptVisitor (beforeExtractedRegion); + var afterExtractedRegion = new VariableUsageAnalyzation (context, usedVariables); + afterExtractedRegion.SetAnalyzedRange(lastStatement, stmt.Statements.Last(), false, true); + stmt.AcceptVisitor (afterExtractedRegion); + usedVariables.Sort ((l, r) => l.Region.Begin.CompareTo (r.Region.Begin)); + + IVariable generatedReturnVariable = null; foreach (var variable in usedVariables) { - afterCodeAnalysis.Analyze(variable.Name, DefiniteAssignmentStatus.PotentiallyAssigned, context.CancellationToken); - statusBeforeMethod [variable] = extractedCodeAnalysis.GetStatusBefore(statements [0]); + if ((variable is IParameter) || beforeExtractedRegion.Has (variable) || !afterExtractedRegion.Has (variable)) + continue; + generatedReturnVariable = variable; + method.ReturnType = context.CreateShortType (variable.Type); + method.Body.Add (new ReturnStatement (new IdentifierExpression (variable.Name))); + break; } - var beforeVisitor = new VariableLookupVisitor(context); - beforeVisitor.SetAnalyzedRange(stmt, statements [0], true, false); - stmt.AcceptVisitor(beforeVisitor); - var afterVisitor = new VariableLookupVisitor(context); - afterVisitor.SetAnalyzedRange(lastStatement, stmt, false, true); - stmt.AcceptVisitor(afterVisitor); - - foreach (var status in statusAfterMethod) { - if (!beforeVisitor.UsedVariables.Contains(status.Item1) && !afterVisitor.UsedVariables.Contains(status.Item1)) + + foreach (var variable in usedVariables) { + if (!(variable is IParameter) && !beforeExtractedRegion.Has (variable) && !afterExtractedRegion.Has (variable)) + continue; + if (variable == generatedReturnVariable) continue; - Expression argumentExpression = new IdentifierExpression(status.Item1.Name); + Expression argumentExpression = new IdentifierExpression(variable.Name); - ParameterModifier mod; - switch (status.Item2) { - case DefiniteAssignmentStatus.AssignedAfterTrueExpression: - case DefiniteAssignmentStatus.AssignedAfterFalseExpression: - case DefiniteAssignmentStatus.PotentiallyAssigned: - mod = ParameterModifier.Ref; - argumentExpression = new DirectionExpression(FieldDirection.Ref, argumentExpression); - break; - case DefiniteAssignmentStatus.DefinitelyAssigned: - if (statusBeforeMethod [status.Item1] != DefiniteAssignmentStatus.PotentiallyAssigned) - goto case DefiniteAssignmentStatus.PotentiallyAssigned; + ParameterModifier mod = ParameterModifier.None; + if (inExtractedRegion.GetStatus (variable) == VariableState.Changed) { + if (beforeExtractedRegion.GetStatus (variable) == VariableState.None) { mod = ParameterModifier.Out; argumentExpression = new DirectionExpression(FieldDirection.Out, argumentExpression); - break; - // case DefiniteAssignmentStatus.Unassigned: - default: - mod = ParameterModifier.None; - break; + } else { + mod = ParameterModifier.Ref; + argumentExpression = new DirectionExpression(FieldDirection.Ref, argumentExpression); + } } - method.Parameters.Add( - new ParameterDeclaration(context.CreateShortType(status.Item1.Type), status.Item1.Name, mod)); + + method.Parameters.Add(new ParameterDeclaration(context.CreateShortType(variable.Type), variable.Name, mod)); invocation.Arguments.Add(argumentExpression); } var task = script.InsertWithCursor(context.TranslateString("Extract method"), Script.InsertPosition.Before, method); - task.ContinueWith (delegate { + Action replaceStatements = delegate { foreach (var node in statements.Skip (1)) { script.Remove(node); } - script.Replace(statements [0], new ExpressionStatement(invocation)); + foreach (var variable in usedVariables) { + if ((variable is IParameter) || beforeExtractedRegion.Has (variable) || !afterExtractedRegion.Has (variable)) + continue; + if (variable == generatedReturnVariable) + continue; + script.InsertBefore (statements [0], new VariableDeclarationStatement (context.CreateShortType(variable.Type), variable.Name)); + } + AstNode invocationStatement; + + if (generatedReturnVariable != null) { + invocationStatement = new VariableDeclarationStatement (new SimpleType ("var"), generatedReturnVariable.Name, invocation); + } else { + invocationStatement = new ExpressionStatement(invocation); + } + script.Replace(statements [0], invocationStatement); + + script.Link(target, method.NameToken); - }, TaskScheduler.FromCurrentSynchronizationContext ()); + }; + + if (task.IsCompleted) { + replaceStatements (null); + } else { + task.ContinueWith (replaceStatements, TaskScheduler.FromCurrentSynchronizationContext ()); + } }); } } diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ExtractMethod/VariableLookupVisitor.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ExtractMethod/VariableLookupVisitor.cs index 3613ebb2a7..0a8240bb7f 100644 --- a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ExtractMethod/VariableLookupVisitor.cs +++ b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ExtractMethod/VariableLookupVisitor.cs @@ -33,18 +33,22 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring.ExtractMethod class VariableLookupVisitor : DepthFirstAstVisitor { readonly RefactoringContext context; - + public List UsedVariables = new List (); - + TextLocation startLocation = TextLocation.Empty; TextLocation endLocation = TextLocation.Empty; - - + public VariableLookupVisitor (RefactoringContext context) { this.context = context; } + public bool Has (IVariable item1) + { + return UsedVariables.Contains (item1); + } + public void SetAnalyzedRange(AstNode start, AstNode end, bool startInclusive = true, bool endInclusive = true) { if (start == null) @@ -54,17 +58,18 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring.ExtractMethod startLocation = startInclusive ? start.StartLocation : start.EndLocation; endLocation = endInclusive ? end.EndLocation : end.StartLocation; } - + public override void VisitIdentifierExpression(IdentifierExpression identifierExpression) { if (startLocation.IsEmpty || startLocation <= identifierExpression.StartLocation && identifierExpression.EndLocation <= endLocation) { var result = context.Resolve(identifierExpression); var local = result as LocalResolveResult; - if (local != null && !UsedVariables.Contains(local.Variable)) + if (local != null && !UsedVariables.Contains(local.Variable)) { UsedVariables.Add(local.Variable); + } } } - + public override void VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement) { base.VisitVariableDeclarationStatement(variableDeclarationStatement); @@ -78,14 +83,14 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring.ExtractMethod } } - + public static List Analyze(RefactoringContext context, Expression expression) { var visitor = new VariableLookupVisitor(context); expression.AcceptVisitor(visitor); return visitor.UsedVariables; } - + public static List Analyze(RefactoringContext context, List statements) { var visitor = new VariableLookupVisitor(context); diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ExtractMethod/VariableUsageAnalyzation.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ExtractMethod/VariableUsageAnalyzation.cs new file mode 100644 index 0000000000..b759245440 --- /dev/null +++ b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ExtractMethod/VariableUsageAnalyzation.cs @@ -0,0 +1,146 @@ +// +// VariableUsageAnalyzation.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2012 Xamarin Inc. (http://xamarin.com) +// +// 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 ICSharpCode.NRefactory.TypeSystem; +using System.Linq; + +namespace ICSharpCode.NRefactory.CSharp.Refactoring.ExtractMethod +{ + + public enum VariableState { + None, + Used, + Changed + } + + + public class VariableUsageAnalyzation : DepthFirstAstVisitor + { + readonly RefactoringContext context; + readonly List usedVariables; + + Dictionary states = new Dictionary (); + + TextLocation startLocation = TextLocation.Empty; + TextLocation endLocation = TextLocation.Empty; + + public VariableUsageAnalyzation (RefactoringContext context, List usedVariables) + { + this.context = context; + this.usedVariables = usedVariables; + } + + public bool Has(IVariable variable) + { + return states.ContainsKey (variable); + } + + public void SetAnalyzedRange(AstNode start, AstNode end, bool startInclusive = true, bool endInclusive = true) + { + if (start == null) + throw new ArgumentNullException("start"); + if (end == null) + throw new ArgumentNullException("end"); + startLocation = startInclusive ? start.StartLocation : start.EndLocation; + endLocation = endInclusive ? end.EndLocation : end.StartLocation; + states.Clear (); + } + + public VariableState GetStatus (IVariable variable) + { + VariableState state; + if (!states.TryGetValue (variable, out state)) + return VariableState.None; + return state; + } + + void SetState (string identifier, VariableState state) + { + var variable = usedVariables.FirstOrDefault (v => v.Name == identifier); + if (variable == null) + return; + VariableState oldState; + if (states.TryGetValue (variable, out oldState)) { + if (oldState < state) + states [variable] = state; + } else { + states [variable] = state; + } + } + + public override void VisitIdentifierExpression(IdentifierExpression identifierExpression) + { + if (startLocation.IsEmpty || startLocation <= identifierExpression.StartLocation && identifierExpression.EndLocation <= endLocation) { + SetState (identifierExpression.Identifier, VariableState.Used); + } + } + + public override void VisitAssignmentExpression(AssignmentExpression assignmentExpression) + { + if (startLocation.IsEmpty || startLocation <= assignmentExpression.StartLocation && assignmentExpression.EndLocation <= endLocation) { + var left = assignmentExpression.Left as IdentifierExpression; + if (left != null) + SetState(left.Identifier, VariableState.Changed); + } + base.VisitAssignmentExpression (assignmentExpression); + } + + public override void VisitDirectionExpression(DirectionExpression directionExpression) + { + if (startLocation.IsEmpty || startLocation <= directionExpression.StartLocation && directionExpression.EndLocation <= endLocation) { + var expr = directionExpression.Expression as IdentifierExpression; + if (expr != null) + SetState(expr.Identifier, VariableState.Changed); + } + base.VisitDirectionExpression (directionExpression); + } + + public override void VisitVariableInitializer(VariableInitializer variableInitializer) + { + if (startLocation.IsEmpty || startLocation <= variableInitializer.StartLocation && variableInitializer.EndLocation <= endLocation) { + SetState(variableInitializer.Name, variableInitializer.Initializer.IsNull ? VariableState.None : VariableState.Changed); + } + + base.VisitVariableInitializer(variableInitializer); + } + + public override void VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression) + { + if (startLocation.IsEmpty || startLocation <= unaryOperatorExpression.StartLocation && unaryOperatorExpression.EndLocation <= endLocation) { + if (unaryOperatorExpression.Operator == UnaryOperatorType.Increment || unaryOperatorExpression.Operator == UnaryOperatorType.Decrement || + unaryOperatorExpression.Operator == UnaryOperatorType.PostIncrement || unaryOperatorExpression.Operator == UnaryOperatorType.PostDecrement) { + var expr = unaryOperatorExpression.Expression as IdentifierExpression; + if (expr != null) + SetState(expr.Identifier, VariableState.Changed); + } + } + base.VisitUnaryOperatorExpression (unaryOperatorExpression); + } + + } +} + diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ImplementAbstractMembersAction.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ImplementAbstractMembersAction.cs index 8de75ab63e..97a4b9b00e 100644 --- a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ImplementAbstractMembersAction.cs +++ b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ImplementAbstractMembersAction.cs @@ -31,7 +31,7 @@ using System.Linq; namespace ICSharpCode.NRefactory.CSharp.Refactoring { -// [ContextAction("Implement abstract members", Description = "Implements abstract members from an abstract class.")] + [ContextAction("Implement abstract members", Description = "Implements abstract members from an abstract class.")] public class ImplementAbstractMembersAction : ICodeActionProvider { public IEnumerable GetActions(RefactoringContext context) @@ -47,15 +47,70 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring if (resolveResult.Type.Kind != TypeKind.Class || resolveResult.Type.GetDefinition() == null || !resolveResult.Type.GetDefinition().IsAbstract) yield break; - yield break; - /* + var toImplement = CollectMembersToImplement(state.CurrentTypeDefinition, resolveResult.Type); + if (toImplement.Count == 0) + yield break; + yield return new CodeAction(context.TranslateString("Implement abstract members"), script => { script.InsertWithCursor( context.TranslateString("Implement abstract members"), state.CurrentTypeDefinition, - ImplementInterfaceAction.GenerateImplementation (context, toImplement) + ImplementInterfaceAction.GenerateImplementation (context, toImplement.Select (m => Tuple.Create (m, false))).Select (entity => { + var decl = entity as EntityDeclaration; + if (decl != null) + decl.Modifiers |= Modifiers.Override; + return entity; + }) ); - });*/ + }); + } + + public static List CollectMembersToImplement(ITypeDefinition implementingType, IType abstractType) + { + var def = abstractType.GetDefinition(); + var toImplement = new List(); + bool alreadyImplemented; + + // Stub out non-implemented events defined by @iface + foreach (var ev in abstractType.GetEvents (e => !e.IsSynthetic && e.IsAbstract)) { + alreadyImplemented = implementingType.GetAllBaseTypeDefinitions().Any( + x => x.Kind != TypeKind.Interface && x.Events.Any (y => y.Name == ev.Name) + ); + + if (!alreadyImplemented) + toImplement.Add(ev); + } + + // Stub out non-implemented methods defined by @iface + foreach (var method in abstractType.GetMethods (d => !d.IsSynthetic && d.IsAbstract)) { + alreadyImplemented = false; + + foreach (var cmet in implementingType.GetMethods ()) { + if (!cmet.IsAbstract && ImplementInterfaceAction.CompareMethods(method, cmet)) { + alreadyImplemented = true; + } + } + if (!alreadyImplemented) + toImplement.Add(method); + } + + // Stub out non-implemented properties defined by @iface + foreach (var prop in abstractType.GetProperties (p => !p.IsSynthetic && p.IsAbstract)) { + alreadyImplemented = false; + foreach (var t in implementingType.GetAllBaseTypeDefinitions ()) { + if (t.Kind == TypeKind.Interface) + continue; + foreach (IProperty cprop in t.Properties) { + if (!cprop.IsAbstract && cprop.Name == prop.Name) { + alreadyImplemented = true; + } + } + } + if (!alreadyImplemented) + toImplement.Add(prop); + } + return toImplement; } + } } diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ImplementInterfaceAction.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ImplementInterfaceAction.cs index 750b80b31e..6c107a618b 100644 --- a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ImplementInterfaceAction.cs +++ b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ImplementInterfaceAction.cs @@ -23,7 +23,6 @@ // 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.TypeSystem; using System.Threading; @@ -32,7 +31,7 @@ using System.Linq; namespace ICSharpCode.NRefactory.CSharp.Refactoring { -// [ContextAction("Implement interface", Description = "Creates an interface implementation.")] + [ContextAction("Implement interface", Description = "Creates an interface implementation.")] public class ImplementInterfaceAction : ICodeActionProvider { public IEnumerable GetActions(RefactoringContext context) @@ -40,40 +39,48 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring var type = context.GetNode(); if (type == null || type.Role != Roles.BaseType) yield break; - + var state = context.GetResolverStateBefore(type); if (state.CurrentTypeDefinition == null) yield break; - + var resolveResult = context.Resolve(type); if (resolveResult.Type.Kind != TypeKind.Interface) yield break; + var toImplement = CollectMembersToImplement(state.CurrentTypeDefinition, resolveResult.Type, false); if (toImplement.Count == 0) yield break; + yield return new CodeAction(context.TranslateString("Implement interface"), script => { script.InsertWithCursor( - context.TranslateString ("Implement Interface"), + context.TranslateString("Implement Interface"), state.CurrentTypeDefinition, - GenerateImplementation (context, toImplement) + GenerateImplementation(context, toImplement) ); }); } - - public static IEnumerable GenerateImplementation(RefactoringContext context, List> toImplement) + + public static IEnumerable GenerateImplementation(RefactoringContext context, IEnumerable> toImplement) { var nodes = new Dictionary>(); - + foreach (var member in toImplement) { if (!nodes.ContainsKey(member.Item1.DeclaringType)) nodes [member.Item1.DeclaringType] = new List(); nodes [member.Item1.DeclaringType].Add(GenerateMemberImplementation(context, member)); } - + foreach (var kv in nodes) { - yield return new PreProcessorDirective( - PreProcessorDirectiveType.Region, - string.Format("{0} implementation", kv.Key.Name)); + if (kv.Key.Kind == TypeKind.Interface) { + yield return new PreProcessorDirective( + PreProcessorDirectiveType.Region, + string.Format("{0} implementation", kv.Key.Name)); + } else { + yield return new PreProcessorDirective( + PreProcessorDirectiveType.Region, + string.Format("implemented abstract members of {0}", kv.Key.Name)); + } foreach (var member in kv.Value) yield return member; yield return new PreProcessorDirective( @@ -81,23 +88,131 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring ); } } - + static AstNode GenerateMemberImplementation(RefactoringContext context, Tuple member) { switch (member.Item1.EntityType) { case EntityType.Property: - return null; + return GenerateProperty(context, (IProperty)member.Item1, member.Item2); case EntityType.Indexer: - return null; + return GenerateIndexer(context, (IProperty)member.Item1, member.Item2); case EntityType.Event: - return null; + return GenerateEvent(context, (IEvent)member.Item1, member.Item2); case EntityType.Method: return GenerateMethod(context, (IMethod)member.Item1, member.Item2); default: throw new ArgumentOutOfRangeException(); } } - + + static AstNode GenerateEvent(RefactoringContext context, IEvent evt, bool explicitImplementation) + { + if (!explicitImplementation) { + return new EventDeclaration() { + Modifiers = Modifiers.Public, + Name = evt.Name, + ReturnType = context.CreateShortType (evt.ReturnType) + }; + } + return new CustomEventDeclaration() { + Name = evt.Name, + ReturnType = context.CreateShortType (evt.ReturnType), + PrivateImplementationType = context.CreateShortType(evt.DeclaringType), + AddAccessor = new Accessor { + Body = new BlockStatement() { + new ThrowStatement(new ObjectCreateExpression(context.CreateShortType("System", "NotImplementedException"))) + } + }, + RemoveAccessor = new Accessor { + Body = new BlockStatement() { + new ThrowStatement(new ObjectCreateExpression(context.CreateShortType("System", "NotImplementedException"))) + } + } + }; + } + + static AstNode GenerateProperty(RefactoringContext context, IProperty property, bool explicitImplementation) + { + var result = new PropertyDeclaration() { + Name = property.Name, + ReturnType = context.CreateShortType (property.ReturnType) + }; + + if (!explicitImplementation) { + result.Modifiers = Modifiers.Public; + } else { + result.PrivateImplementationType = context.CreateShortType(property.DeclaringType); + } + + if (property.CanGet) { + if (property.DeclaringType.Kind != TypeKind.Interface) { + result.Getter = new Accessor() { + Body = new BlockStatement () { + new ThrowStatement(new ObjectCreateExpression(context.CreateShortType("System", "NotImplementedException"))) + } + }; + } else { + result.Getter = new Accessor(); + } + } + if (property.CanSet) { + if (property.DeclaringType.Kind != TypeKind.Interface) { + result.Setter = new Accessor() { + Body = new BlockStatement () { + new ThrowStatement(new ObjectCreateExpression(context.CreateShortType("System", "NotImplementedException"))) + } + }; + } else { + result.Setter = new Accessor(); + } + } + + return result; + } + + static AstNode GenerateIndexer(RefactoringContext context, IProperty indexer, bool explicitImplementation) + { + var result = new IndexerDeclaration() { + ReturnType = context.CreateShortType (indexer.ReturnType) + }; + + if (!explicitImplementation) { + result.Modifiers = Modifiers.Public; + } else { + result.PrivateImplementationType = context.CreateShortType(indexer.DeclaringType); + } + + foreach (var p in indexer.Parameters) { + ParameterModifier modifier; + if (p.IsOut) { + modifier = ParameterModifier.Out; + } else if (p.IsRef) { + modifier = ParameterModifier.Ref; + } else if (p.IsParams) { + modifier = ParameterModifier.Params; + } else { + modifier = ParameterModifier.None; + } + result.Parameters.Add(new ParameterDeclaration(context.CreateShortType(p.Type), p.Name, modifier)); + } + + if (indexer.CanGet) { + result.Getter = new Accessor() { + Body = new BlockStatement () { + new ThrowStatement(new ObjectCreateExpression(context.CreateShortType("System", "NotImplementedException"))) + } + }; + } + if (indexer.CanSet) { + result.Setter = new Accessor() { + Body = new BlockStatement () { + new ThrowStatement(new ObjectCreateExpression(context.CreateShortType("System", "NotImplementedException"))) + } + }; + } + return result; + } + static AstNode GenerateMethod(RefactoringContext context, IMethod method, bool explicitImplementation) { var result = new MethodDeclaration() { @@ -107,17 +222,40 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring new ThrowStatement(new ObjectCreateExpression(context.CreateShortType("System", "NotImplementedException"))) } }; - + if (!explicitImplementation) { result.Modifiers = Modifiers.Public; } else { result.PrivateImplementationType = context.CreateShortType(method.DeclaringType); } - + foreach (var typeParam in method.TypeParameters) { result.TypeParameters.Add(new TypeParameterDeclaration(typeParam.Name)); + + var constraint = new Constraint() { + TypeParameter = new SimpleType(typeParam.Name) + }; + + if (typeParam.HasDefaultConstructorConstraint) { + constraint.BaseTypes.Add(new PrimitiveType("new")); + } else if (typeParam.HasReferenceTypeConstraint) { + constraint.BaseTypes.Add(new PrimitiveType("class")); + } else if (typeParam.HasValueTypeConstraint) { + constraint.BaseTypes.Add(new PrimitiveType("struct")); + } + + foreach (var type in typeParam.DirectBaseTypes) { + if (type.FullName == "System.Object") + continue; + if (type.FullName == "System.ValueType") + continue; + constraint.BaseTypes.Add(context.CreateShortType(type)); + } + if (constraint.BaseTypes.Count == 0) + continue; + result.Constraints.Add(constraint); } - + foreach (var p in method.Parameters) { ParameterModifier modifier; if (p.IsOut) { @@ -131,71 +269,77 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring } result.Parameters.Add(new ParameterDeclaration(context.CreateShortType(p.Type), p.Name, modifier)); } - + return result; } - - + public static List> CollectMembersToImplement(ITypeDefinition implementingType, IType interfaceType, bool explicitly) { var def = interfaceType.GetDefinition(); List> toImplement = new List>(); bool alreadyImplemented; + // Stub out non-implemented events defined by @iface - foreach (var ev in interfaceType.GetEvents (e => !e.IsSynthetic && e.DeclaringTypeDefinition.ReflectionName == def.ReflectionName)) { - bool needsExplicitly = explicitly; - alreadyImplemented = implementingType.GetAllBaseTypeDefinitions().Any( - x => x.Kind != TypeKind.Interface && x.Events.Any (y => y.Name == ev.Name) - ); - - if (!alreadyImplemented) - toImplement.Add(new Tuple(ev, needsExplicitly)); - } - + foreach (var evGroup in interfaceType.GetEvents (e => !e.IsSynthetic && e.DeclaringTypeDefinition.ReflectionName == def.ReflectionName).GroupBy (m => m.DeclaringType).Reverse ()) + foreach (var ev in evGroup) { + bool needsExplicitly = explicitly; + alreadyImplemented = implementingType.GetAllBaseTypeDefinitions().Any( + x => x.Kind != TypeKind.Interface && x.Events.Any(y => y.Name == ev.Name) + ); + + if (!alreadyImplemented) + toImplement.Add(new Tuple(ev, needsExplicitly)); + } + // Stub out non-implemented methods defined by @iface - foreach (var method in interfaceType.GetMethods (d => !d.IsSynthetic && d.DeclaringTypeDefinition.ReflectionName == def.ReflectionName)) { - bool needsExplicitly = explicitly; - alreadyImplemented = false; + foreach (var methodGroup in interfaceType.GetMethods (d => !d.IsSynthetic).GroupBy (m => m.DeclaringType).Reverse ()) + foreach (var method in methodGroup) { - foreach (var cmet in implementingType.GetMethods ()) { - if (CompareMethods(method, cmet)) { - if (!needsExplicitly && !cmet.ReturnType.Equals(method.ReturnType)) - needsExplicitly = true; - else - alreadyImplemented |= !needsExplicitly /*|| cmet.InterfaceImplementations.Any (impl => impl.InterfaceType.Equals (interfaceType))*/; + bool needsExplicitly = explicitly; + alreadyImplemented = false; + + foreach (var cmet in implementingType.GetMethods ()) { + if (CompareMethods(method, cmet)) { + if (!needsExplicitly && !cmet.ReturnType.Equals(method.ReturnType)) + needsExplicitly = true; + else + alreadyImplemented |= !needsExplicitly /*|| cmet.InterfaceImplementations.Any (impl => impl.InterfaceType.Equals (interfaceType))*/; + } } + if (toImplement.Where(t => t.Item1 is IMethod).Any(t => CompareMethods(method, (IMethod)t.Item1))) + needsExplicitly = true; + if (!alreadyImplemented) + toImplement.Add(new Tuple(method, needsExplicitly)); } - if (!alreadyImplemented) - toImplement.Add(new Tuple(method, needsExplicitly)); - } - + // Stub out non-implemented properties defined by @iface - foreach (var prop in interfaceType.GetProperties (p => !p.IsSynthetic && p.DeclaringTypeDefinition.ReflectionName == def.ReflectionName)) { - bool needsExplicitly = explicitly; - alreadyImplemented = false; - foreach (var t in implementingType.GetAllBaseTypeDefinitions ()) { - if (t.Kind == TypeKind.Interface) - continue; - foreach (IProperty cprop in t.Properties) { - if (cprop.Name == prop.Name) { - if (!needsExplicitly && !cprop.ReturnType.Equals(prop.ReturnType)) - needsExplicitly = true; - else - alreadyImplemented |= !needsExplicitly/* || cprop.InterfaceImplementations.Any (impl => impl.InterfaceType.Resolve (ctx).Equals (interfaceType))*/; + foreach (var propGroup in interfaceType.GetProperties (p => !p.IsSynthetic && p.DeclaringTypeDefinition.ReflectionName == def.ReflectionName).GroupBy (m => m.DeclaringType).Reverse ()) + foreach (var prop in propGroup) { + bool needsExplicitly = explicitly; + alreadyImplemented = false; + foreach (var t in implementingType.GetAllBaseTypeDefinitions ()) { + if (t.Kind == TypeKind.Interface) + continue; + foreach (IProperty cprop in t.Properties) { + if (cprop.Name == prop.Name) { + if (!needsExplicitly && !cprop.ReturnType.Equals(prop.ReturnType)) + needsExplicitly = true; + else + alreadyImplemented |= !needsExplicitly/* || cprop.InterfaceImplementations.Any (impl => impl.InterfaceType.Resolve (ctx).Equals (interfaceType))*/; + } } } + if (!alreadyImplemented) + toImplement.Add(new Tuple(prop, needsExplicitly)); } - if (!alreadyImplemented) - toImplement.Add(new Tuple(prop, needsExplicitly)); - } return toImplement; } - - static bool CompareMethods (IMethod interfaceMethod, IMethod typeMethod) + + internal static bool CompareMethods(IMethod interfaceMethod, IMethod typeMethod) { if (typeMethod.IsExplicitInterfaceImplementation) - return typeMethod.ImplementedInterfaceMembers.Any (m => m.Equals (interfaceMethod)); - return SignatureComparer.Ordinal.Equals (interfaceMethod, typeMethod); + return typeMethod.ImplementedInterfaceMembers.Any(m => m.Equals(interfaceMethod)); + return SignatureComparer.Ordinal.Equals(interfaceMethod, typeMethod); } } } diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ImplementInterfaceExplicitAction.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ImplementInterfaceExplicitAction.cs index 524a4be297..d5206d5ed5 100644 --- a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ImplementInterfaceExplicitAction.cs +++ b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ImplementInterfaceExplicitAction.cs @@ -32,7 +32,7 @@ using System.Linq; namespace ICSharpCode.NRefactory.CSharp.Refactoring { -// [ContextAction("Implement interface explicit", Description = "Creates an interface implementation.")] + [ContextAction("Implement interface explicit", Description = "Creates an interface implementation.")] public class ImplementInterfaceExplicitAction : ICodeActionProvider { public IEnumerable GetActions(RefactoringContext context) @@ -60,7 +60,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring script.InsertWithCursor( context.TranslateString("Implement Interface"), state.CurrentTypeDefinition, - ImplementInterfaceAction.GenerateImplementation (context, toImplement) + ImplementInterfaceAction.GenerateImplementation (context, toImplement.Select (t => Tuple.Create (t.Item1, true))) ); }); } diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/Script.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/Script.cs index f657682c64..574fafe817 100644 --- a/ICSharpCode.NRefactory.CSharp/Refactoring/Script.cs +++ b/ICSharpCode.NRefactory.CSharp/Refactoring/Script.cs @@ -178,7 +178,11 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring { // Default implementation: do nothing // Derived classes are supposed to enter the text editor's linked state. - return null; + + // Immediately signal the task as completed: + var tcs = new TaskCompletionSource(); + tcs.SetResult(null); + return tcs.Task; } public void Replace (AstNode node, AstNode replaceWith) diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpConversions.cs b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpConversions.cs index 14071753bf..d45ba6c7ca 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpConversions.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpConversions.cs @@ -84,6 +84,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver public bool Equals(TypePair other) { + if (this.FromType == null || this.ToType == null || other.FromType == null || other.ToType == null) + return false; return this.FromType.Equals(other.FromType) && this.ToType.Equals(other.ToType); } @@ -110,6 +112,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } c = ImplicitConversion(resolveResult.Type, toType); if (c.IsValid) return c; + if (resolveResult.Type.Kind == TypeKind.Dynamic) + return Conversion.ImplicitDynamicConversion; c = AnonymousFunctionConversion(resolveResult, toType); if (c.IsValid) return c; c = MethodGroupConversion(resolveResult, toType); @@ -157,8 +161,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return Conversion.ImplicitReferenceConversion; if (IsBoxingConversion(fromType, toType)) return Conversion.BoxingConversion; - if (fromType.Kind == TypeKind.Dynamic) - return Conversion.ImplicitDynamicConversion; if (ImplicitTypeParameterConversion(fromType, toType)) { // Implicit type parameter conversions that aren't also // reference conversions are considered to be boxing conversions @@ -469,7 +471,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver bool IdentityOrVarianceConversion(IType s, IType t, int subtypeCheckNestingDepth) { ITypeDefinition def = s.GetDefinition(); - if (def != null && def.Equals(t.GetDefinition())) { + if (def != null) { + if (!def.Equals(t.GetDefinition())) + return false; ParameterizedType ps = s as ParameterizedType; ParameterizedType pt = t as ParameterizedType; if (ps != null && pt != null) { @@ -497,8 +501,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return false; // only of of them is parameterized, or counts don't match? -> not valid conversion } return true; + } else { + // not type definitions? we still need to check for equal types (e.g. s and t might be type parameters) + return s.Equals(t); } - return false; } #endregion diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs index 20255c6e05..20d34852fb 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs @@ -1618,6 +1618,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver throw new NotSupportedException("Invalid value for NameLookupMode"); } if (result is UnknownMemberResolveResult) { + // We intentionally use all extension methods here, not just the eligible ones. + // Proper eligibility checking is only possible for the full invocation + // (after we know the remaining arguments). + // The eligibility check in GetExtensionMethods is only intended for code completion. var extensionMethods = GetExtensionMethods(identifier, typeArguments); if (extensionMethods.Count > 0) { return new MethodGroupResolveResult(target, identifier, EmptyList.Instance, typeArguments) { @@ -1755,7 +1759,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver if (method.TypeParameters.Count != typeArguments.Count) continue; SpecializedMethod sm = new SpecializedMethod(method, new TypeParameterSubstitution(null, typeArguments)); - if (IsEligibleExtensionMethod(compilation, conversions, targetType, method, false, out inferredTypes)) + if (IsEligibleExtensionMethod(compilation, conversions, targetType, sm, false, out inferredTypes)) outputGroup.Add(sm); } else { if (IsEligibleExtensionMethod(compilation, conversions, targetType, method, true, out inferredTypes)) { @@ -1896,15 +1900,37 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver // C# 4.0 spec: §7.6.5 if (target.Type.Kind == TypeKind.Dynamic) { - return new DynamicInvocationResolveResult(target, arguments.Select((a, i) => new DynamicInvocationArgument(argumentNames != null ? argumentNames[i] : null, a)).ToList().AsReadOnly()); + return new DynamicInvocationResolveResult(target, DynamicInvocationType.Invocation, arguments.Select((a, i) => new DynamicInvocationArgument(argumentNames != null ? argumentNames[i] : null, a)).ToList().AsReadOnly()); } MethodGroupResolveResult mgrr = target as MethodGroupResolveResult; if (mgrr != null) { + if (arguments.Any(a => a.Type.Kind == TypeKind.Dynamic)) { + // If we have dynamic arguments, we need to represent the invocation as a dynamic invocation if there is more than one applicable method. + var or2 = CreateOverloadResolution(arguments, argumentNames, mgrr.TypeArguments.ToArray()); + var applicableMethods = mgrr.MethodsGroupedByDeclaringType.SelectMany(m => m, (x, m) => new { x.DeclaringType, Method = m }).Where(x => OverloadResolution.IsApplicable(or2.AddCandidate(x.Method))).ToList(); + + if (applicableMethods.Count > 1) { + ResolveResult actualTarget; + if (applicableMethods.All(x => x.Method.IsStatic) && !(mgrr.TargetResult is TypeResolveResult)) + actualTarget = new TypeResolveResult(mgrr.TargetType); + else + actualTarget = mgrr.TargetResult; + + var l = new List(); + foreach (var m in applicableMethods) { + if (l.Count == 0 || l[l.Count - 1].DeclaringType != m.DeclaringType) + l.Add(new MethodListWithDeclaringType(m.DeclaringType)); + l[l.Count - 1].Add(m.Method); + } + return new DynamicInvocationResolveResult(new MethodGroupResolveResult(actualTarget, mgrr.MethodName, l, mgrr.TypeArguments), DynamicInvocationType.Invocation, arguments.Select((a, i) => new DynamicInvocationArgument(argumentNames != null ? argumentNames[i] : null, a)).ToList().AsReadOnly()); + } + } + OverloadResolution or = mgrr.PerformOverloadResolution(compilation, arguments, argumentNames, checkForOverflow: checkForOverflow, conversions: conversions); if (or.BestCandidate != null) { if (or.BestCandidate.IsStatic && !or.IsExtensionMethodInvocation && !(mgrr.TargetResult is TypeResolveResult)) - return or.CreateResolveResult(new TypeResolveResult(mgrr.TargetResult.Type)); + return or.CreateResolveResult(new TypeResolveResult(mgrr.TargetType)); else return or.CreateResolveResult(mgrr.TargetResult); } else { @@ -2039,10 +2065,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver { switch (target.Type.Kind) { case TypeKind.Dynamic: - for (int i = 0; i < arguments.Length; i++) { - arguments[i] = Convert(arguments[i], SpecialType.Dynamic); - } - return new ArrayAccessResolveResult(SpecialType.Dynamic, target, arguments); + return new DynamicInvocationResolveResult(target, DynamicInvocationType.Indexing, arguments.Select((a, i) => new DynamicInvocationArgument(argumentNames != null ? argumentNames[i] : null, a)).ToList().AsReadOnly()); case TypeKind.Array: case TypeKind.Pointer: @@ -2052,9 +2075,21 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } // §7.6.6.2 Indexer access - OverloadResolution or = CreateOverloadResolution(arguments, argumentNames); + MemberLookup lookup = CreateMemberLookup(); var indexers = lookup.LookupIndexers(target.Type); + + if (arguments.Any(a => a.Type.Kind == TypeKind.Dynamic)) { + // If we have dynamic arguments, we need to represent the invocation as a dynamic invocation if there is more than one applicable indexer. + var or2 = CreateOverloadResolution(arguments, argumentNames, null); + var applicableIndexers = indexers.SelectMany(x => x).Where(m => OverloadResolution.IsApplicable(or2.AddCandidate(m))).ToList(); + + if (applicableIndexers.Count > 1) { + return new DynamicInvocationResolveResult(target, DynamicInvocationType.Indexing, arguments.Select((a, i) => new DynamicInvocationArgument(argumentNames != null ? argumentNames[i] : null, a)).ToList().AsReadOnly()); + } + } + + OverloadResolution or = CreateOverloadResolution(arguments, argumentNames); or.AddMethodLists(indexers); if (or.BestCandidate != null) { return or.CreateResolveResult(target); @@ -2105,16 +2140,35 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver public ResolveResult ResolveObjectCreation(IType type, ResolveResult[] arguments, string[] argumentNames = null, bool allowProtectedAccess = false, IList initializerStatements = null) { if (type.Kind == TypeKind.Delegate && arguments.Length == 1) { - return Convert(arguments[0], type); + ResolveResult input = arguments[0]; + IMethod invoke = input.Type.GetDelegateInvokeMethod(); + if (invoke != null) { + input = new MethodGroupResolveResult( + input, invoke.Name, + methods: new[] { new MethodListWithDeclaringType(invoke.DeclaringType) { invoke } }, + typeArguments: EmptyList.Instance + ); + } + return Convert(input, type); } OverloadResolution or = CreateOverloadResolution(arguments, argumentNames); MemberLookup lookup = CreateMemberLookup(); + var allApplicable = (arguments.Any(a => a.Type.Kind == TypeKind.Dynamic) ? new List() : null); foreach (IMethod ctor in type.GetConstructors()) { - if (lookup.IsAccessible(ctor, allowProtectedAccess)) - or.AddCandidate(ctor); + if (lookup.IsAccessible(ctor, allowProtectedAccess)) { + var orErrors = or.AddCandidate(ctor); + if (allApplicable != null && OverloadResolution.IsApplicable(orErrors)) + allApplicable.Add(ctor); + } else or.AddCandidate(ctor, OverloadResolutionErrors.Inaccessible); } + + if (allApplicable != null && allApplicable.Count > 1) { + // If we have dynamic arguments, we need to represent the invocation as a dynamic invocation if there is more than one applicable constructor. + return new DynamicInvocationResolveResult(new MethodGroupResolveResult(null, allApplicable[0].Name, new[] { new MethodListWithDeclaringType(type, allApplicable) }, null), DynamicInvocationType.ObjectCreation, arguments.Select((a, i) => new DynamicInvocationArgument(argumentNames != null ? argumentNames[i] : null, a)).ToList().AsReadOnly(), initializerStatements); + } + if (or.BestCandidate != null) { return or.CreateResolveResult(null, initializerStatements); } else { diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/DynamicInvocationResolveResult.cs b/ICSharpCode.NRefactory.CSharp/Resolver/DynamicInvocationResolveResult.cs index 6139be02c0..b720d68b74 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/DynamicInvocationResolveResult.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/DynamicInvocationResolveResult.cs @@ -45,24 +45,57 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } } + public enum DynamicInvocationType { + /// + /// The invocation is a normal invocation ( 'a(b)' ). + /// + Invocation, + + /// + /// The invocation is an indexing ( 'a[b]' ). + /// + Indexing, + + /// + /// The invocation is an object creation ( 'new a(b)' ). Also used when invoking a base constructor ( ' : base(a) ' ) and chaining constructors ( ' : this(a) '). + /// + ObjectCreation, + } + /// /// Represents the result of an invocation of a member of a dynamic object. /// public class DynamicInvocationResolveResult : ResolveResult { /// - /// Target of the invocation (a dynamic object). + /// Target of the invocation. Can be a dynamic expression or a . /// public readonly ResolveResult Target; + /// + /// Type of the invocation. + /// + public readonly DynamicInvocationType InvocationType; + /// /// Arguments for the call. /// public readonly IList Arguments; - public DynamicInvocationResolveResult(ResolveResult target, IList arguments) : base(SpecialType.Dynamic) { - this.Target = target; - this.Arguments = arguments ?? EmptyList.Instance; + /// + /// Gets the list of initializer statements that are appplied to the result of this invocation. + /// This is used to represent object and collection initializers. + /// With the initializer statements, the is used + /// to refer to the result of this invocation. + /// Initializer statements can only exist if the is . + /// + public readonly IList InitializerStatements; + + public DynamicInvocationResolveResult(ResolveResult target, DynamicInvocationType invocationType, IList arguments, IList initializerStatements = null) : base(SpecialType.Dynamic) { + this.Target = target; + this.InvocationType = invocationType; + this.Arguments = arguments ?? EmptyList.Instance; + this.InitializerStatements = initializerStatements ?? EmptyList.Instance; } public override string ToString() diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/MethodGroupResolveResult.cs b/ICSharpCode.NRefactory.CSharp/Resolver/MethodGroupResolveResult.cs index 037119dd21..39571f2918 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/MethodGroupResolveResult.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/MethodGroupResolveResult.cs @@ -29,10 +29,32 @@ using ICSharpCode.NRefactory.TypeSystem.Implementation; namespace ICSharpCode.NRefactory.CSharp.Resolver { + /// + /// A method list that belongs to a declaring type. + /// public class MethodListWithDeclaringType : List { readonly IType declaringType; + /// + /// The declaring type. + /// + /// + /// Not all methods in this list necessarily have this as their declaring type. + /// For example, this program: + /// + /// class Base { + /// public virtual void M() {} + /// } + /// class Derived : Base { + /// public override void M() {} + /// public void M(int i) {} + /// } + /// + /// results in two lists: + /// new MethodListWithDeclaringType(Base) { Derived.M() }, + /// new MethodListWithDeclaringType(Derived) { Derived.M(int) } + /// public IType DeclaringType { get { return declaringType; } } @@ -64,8 +86,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver public MethodGroupResolveResult(ResolveResult targetResult, string methodName, IList methods, IList typeArguments) : base(SpecialType.UnknownType) { - if (targetResult == null) - throw new ArgumentNullException("targetResult"); if (methods == null) throw new ArgumentNullException("methods"); this.targetResult = targetResult; @@ -85,7 +105,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver /// Gets the type of the reference to the target object. /// public IType TargetType { - get { return targetResult.Type; } + get { return targetResult != null ? targetResult.Type : SpecialType.UnknownType; } } /// @@ -132,6 +152,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver /// Gets all candidate extension methods. /// Note: this includes candidates that are not eligible due to an inapplicable /// this argument. + /// The candidates will only be specialized if the type arguments were provided explicitly. /// /// /// The results are stored in nested lists because they are grouped by using scope. @@ -155,6 +176,24 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return extensionMethods ?? Enumerable.Empty>(); } + /// + /// Gets the eligible extension methods. + /// + /// + /// Specifies whether to produce a + /// when type arguments could be inferred from . + /// This setting is only used for inferred types and has no effect if the type parameters are + /// specified explicitly. + /// + /// + /// The results are stored in nested lists because they are grouped by using scope. + /// That is, for "using SomeExtensions; namespace X { using MoreExtensions; ... }", + /// the return value will be + /// new List { + /// new List { all extensions from MoreExtensions }, + /// new List { all extensions from SomeExtensions } + /// } + /// public IEnumerable> GetEligibleExtensionMethods(bool substituteInferredTypes) { var result = new List>(); diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs b/ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs index f160fcbc60..bf8331d7d8 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs @@ -938,6 +938,23 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver ScanChildren(parameterDeclaration); if (resolverEnabled) { string name = parameterDeclaration.Name; + + if (parameterDeclaration.Parent is DocumentationReference) { + // create a dummy parameter + IType type = ResolveType(parameterDeclaration.Type); + switch (parameterDeclaration.ParameterModifier) { + case ParameterModifier.Ref: + case ParameterModifier.Out: + type = new ByReferenceType(type); + break; + } + return new LocalResolveResult(new DefaultParameter( + type, name, + isRef: parameterDeclaration.ParameterModifier == ParameterModifier.Ref, + isOut: parameterDeclaration.ParameterModifier == ParameterModifier.Out, + isParams: parameterDeclaration.ParameterModifier == ParameterModifier.Params)); + } + // Look in lambda parameters: foreach (IParameter p in resolver.LocalVariables.OfType()) { if (p.Name == name) @@ -1479,12 +1496,20 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver ResolveResult rr = resolver.ResolveObjectCreation(type, arguments, argumentNames, false, initializerStatements); if (arguments.Length == 1 && rr.Type.Kind == TypeKind.Delegate) { - // process conversion in case it's a delegate creation - ProcessConversionResult(objectCreateExpression.Arguments.Single(), rr as ConversionResolveResult); - // wrap the result so that the delegate creation is not handled as a reference - // to the target method - otherwise FindReferencedEntities would produce two results for - // the same delegate creation. - return WrapResult(rr); + // Apply conversion to argument if it directly wraps the argument + // (but not when creating a delegate from a delegate, as then there would be a MGRR for .Invoke in between) + // This is necessary for lambda type inference. + var crr = rr as ConversionResolveResult; + if (crr != null && crr.Input == arguments[0]) { + ProcessConversionResult(objectCreateExpression.Arguments.Single(), crr); + + // wrap the result so that the delegate creation is not handled as a reference + // to the target method - otherwise FindReferencedEntities would produce two results for + // the same delegate creation. + return WrapResult(rr); + } else { + return rr; + } } else { // process conversions in all other cases ProcessConversionsInInvocation(null, objectCreateExpression.Arguments, rr as CSharpInvocationResolveResult); @@ -3847,7 +3872,102 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver #region Documentation Reference ResolveResult IAstVisitor.VisitDocumentationReference(DocumentationReference documentationReference) { - throw new NotImplementedException(); + // Resolve child nodes: + ITypeDefinition declaringTypeDef; + if (documentationReference.DeclaringType.IsNull) + declaringTypeDef = resolver.CurrentTypeDefinition; + else + declaringTypeDef = ResolveType(documentationReference.DeclaringType).GetDefinition(); + IType[] typeArguments = documentationReference.TypeArguments.Select(ResolveType).ToArray(); + IType conversionOperatorReturnType = ResolveType(documentationReference.ConversionOperatorReturnType); + IParameter[] parameters = documentationReference.Parameters.Select(ResolveXmlDocParameter).ToArray(); + + if (documentationReference.EntityType == EntityType.TypeDefinition) { + if (declaringTypeDef != null) + return new TypeResolveResult(declaringTypeDef); + else + return errorResult; + } + + if (documentationReference.EntityType == EntityType.None) { + // might be a type, member or ctor + string memberName = documentationReference.MemberName; + ResolveResult rr; + if (documentationReference.DeclaringType.IsNull) { + rr = resolver.LookupSimpleNameOrTypeName(memberName, typeArguments, NameLookupMode.Expression); + } else { + var target = Resolve(documentationReference.DeclaringType); + rr = resolver.ResolveMemberAccess(target, memberName, typeArguments); + } + // reduce to definition: + if (rr.IsError) { + return rr; + } else if (rr is TypeResolveResult) { + var typeDef = rr.Type.GetDefinition(); + if (typeDef == null) + return errorResult; + if (documentationReference.HasParameterList) { + var ctors = typeDef.GetConstructors(options: GetMemberOptions.IgnoreInheritedMembers | GetMemberOptions.ReturnMemberDefinitions); + return FindByParameters(ctors, parameters); + } else { + return new TypeResolveResult(typeDef); + } + } else if (rr is MemberResolveResult) { + var mrr = (MemberResolveResult)rr; + return new MemberResolveResult(null, mrr.Member.MemberDefinition); + } else if (rr is MethodGroupResolveResult) { + var mgrr = (MethodGroupResolveResult)rr; + var methods = mgrr.MethodsGroupedByDeclaringType.Reverse() + .SelectMany(ml => ml.Select(m => (IParameterizedMember)m.MemberDefinition)); + return FindByParameters(methods, parameters); + } + return rr; + } + + // Indexer or operator + if (declaringTypeDef == null) + return errorResult; + if (documentationReference.EntityType == EntityType.Indexer) { + var indexers = declaringTypeDef.Properties.Where(p => p.IsIndexer && !p.IsExplicitInterfaceImplementation); + return FindByParameters(indexers, parameters); + } else if (documentationReference.EntityType == EntityType.Operator) { + var opType = documentationReference.OperatorType; + string memberName = OperatorDeclaration.GetName(opType); + var methods = declaringTypeDef.Methods.Where(m => m.IsOperator && m.Name == memberName); + if (opType == OperatorType.Implicit || opType == OperatorType.Explicit) { + // conversion operator + foreach (var method in methods) { + if (ParameterListComparer.Instance.Equals(method.Parameters, parameters)) { + if (method.ReturnType.Equals(conversionOperatorReturnType)) + return new MemberResolveResult(null, method); + } + } + return new MemberResolveResult(null, methods.FirstOrDefault()); + } else { + // not a conversion operator + return FindByParameters(methods, parameters); + } + } else { + throw new NotSupportedException(); // unknown entity type + } + } + + IParameter ResolveXmlDocParameter(ParameterDeclaration p) + { + var lrr = Resolve(p) as LocalResolveResult; + if (lrr != null && lrr.IsParameter) + return (IParameter)lrr.Variable; + else + return new DefaultParameter(SpecialType.UnknownType, string.Empty); + } + + ResolveResult FindByParameters(IEnumerable methods, IList parameters) + { + foreach (var method in methods) { + if (ParameterListComparer.Instance.Equals(method.Parameters, parameters)) + return new MemberResolveResult(null, method); + } + return new MemberResolveResult(null, methods.FirstOrDefault()); } #endregion } diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/TypeInference.cs b/ICSharpCode.NRefactory.CSharp/Resolver/TypeInference.cs index d176e9b7c6..e082f98d8e 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/TypeInference.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/TypeInference.cs @@ -510,7 +510,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } var or = mgrr.PerformOverloadResolution(compilation, args, - allowExtensionMethods: false, allowExpandingParams: false); if (or.FoundApplicableCandidate && or.BestCandidateAmbiguousWith == null) { IType returnType = or.GetBestCandidateWithSubstitutedTypeArguments().ReturnType; diff --git a/ICSharpCode.NRefactory.CSharp/TypeSystem/CSharpAssembly.cs b/ICSharpCode.NRefactory.CSharp/TypeSystem/CSharpAssembly.cs index 28ee59a3f0..fe72e9b576 100644 --- a/ICSharpCode.NRefactory.CSharp/TypeSystem/CSharpAssembly.cs +++ b/ICSharpCode.NRefactory.CSharp/TypeSystem/CSharpAssembly.cs @@ -290,6 +290,10 @@ namespace ICSharpCode.NRefactory.CSharp.TypeSystem get { return assembly.Compilation; } } + IEnumerable INamespace.ContributingAssemblies { + get { return new [] { assembly }; } + } + INamespace INamespace.GetChildNamespace(string name) { var nameComparer = assembly.compilation.NameComparer; diff --git a/ICSharpCode.NRefactory.CSharp/TypeSystem/CSharpDocumentationComment.cs b/ICSharpCode.NRefactory.CSharp/TypeSystem/CSharpDocumentationComment.cs index 5ca58078cb..d831eea509 100644 --- a/ICSharpCode.NRefactory.CSharp/TypeSystem/CSharpDocumentationComment.cs +++ b/ICSharpCode.NRefactory.CSharp/TypeSystem/CSharpDocumentationComment.cs @@ -17,8 +17,10 @@ // DEALINGS IN THE SOFTWARE. using System; +using ICSharpCode.NRefactory.CSharp.Resolver; using ICSharpCode.NRefactory.Documentation; using ICSharpCode.NRefactory.Editor; +using ICSharpCode.NRefactory.Semantics; using ICSharpCode.NRefactory.TypeSystem; namespace ICSharpCode.NRefactory.CSharp.TypeSystem @@ -38,6 +40,23 @@ namespace ICSharpCode.NRefactory.CSharp.TypeSystem // resolve ID string return base.ResolveCref(cref); } + var documentationReference = new CSharpParser().ParseDocumentationReference(cref); + var csharpContext = context as CSharpTypeResolveContext; + CSharpResolver resolver; + if (csharpContext != null) { + resolver = new CSharpResolver(csharpContext); + } else { + resolver = new CSharpResolver(context.Compilation); + } + var astResolver = new CSharpAstResolver(resolver, documentationReference); + var rr = astResolver.Resolve(documentationReference); + + MemberResolveResult mrr = rr as MemberResolveResult; + if (mrr != null) + return mrr.Member; + TypeResolveResult trr = rr as TypeResolveResult; + if (trr != null) + return trr.Type.GetDefinition(); return null; } } diff --git a/ICSharpCode.NRefactory.CSharp/TypeSystem/ResolvedUsingScope.cs b/ICSharpCode.NRefactory.CSharp/TypeSystem/ResolvedUsingScope.cs index f98d5a96ec..063a095192 100644 --- a/ICSharpCode.NRefactory.CSharp/TypeSystem/ResolvedUsingScope.cs +++ b/ICSharpCode.NRefactory.CSharp/TypeSystem/ResolvedUsingScope.cs @@ -172,6 +172,10 @@ namespace ICSharpCode.NRefactory.CSharp.TypeSystem get { return EmptyList.Instance; } } + IEnumerable INamespace.ContributingAssemblies { + get { return EmptyList.Instance; } + } + ICompilation IResolved.Compilation { get { return parentNamespace.Compilation; } } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Analysis/DefiniteAssignmentTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Analysis/DefiniteAssignmentTests.cs index 8d077fdb13..19af6d7310 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Analysis/DefiniteAssignmentTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Analysis/DefiniteAssignmentTests.cs @@ -17,9 +17,9 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.IO; using System.Linq; using System.Threading; - using ICSharpCode.NRefactory.CSharp.Resolver; using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.NRefactory.TypeSystem.Implementation; @@ -289,5 +289,41 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusBefore(case2.Statements.First())); Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusAfter(@switch)); } + + [Test] + public void ConditionalExpression1() + { + string code = "int a; int b = X ? (a = 1) : 0;"; + var block = new BlockStatement(); + block.Statements.AddRange(new CSharpParser().ParseStatements(new StringReader(code))); + + DefiniteAssignmentAnalysis da = CreateDefiniteAssignmentAnalysis(block); + da.Analyze("a"); + Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusAfter(block)); + } + + [Test] + public void ConditionalExpression2() + { + string code = "int a; int b = X ? (a = 1) : (a = 2);"; + var block = new BlockStatement(); + block.Statements.AddRange(new CSharpParser().ParseStatements(new StringReader(code))); + + DefiniteAssignmentAnalysis da = CreateDefiniteAssignmentAnalysis(block); + da.Analyze("a"); + Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusAfter(block)); + } + + [Test] + public void ConditionalExpression3() + { + string code = "int a; int b = true ? (a = 1) : 0;"; + var block = new BlockStatement(); + block.Statements.AddRange(new CSharpParser().ParseStatements(new StringReader(code))); + + DefiniteAssignmentAnalysis da = CreateDefiniteAssignmentAnalysis(block); + da.Analyze("a"); + Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusAfter(block)); + } } } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ExtractMethodTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ExtractMethodTests.cs index 1a85e2b188..0dba8a94b1 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ExtractMethodTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ExtractMethodTests.cs @@ -28,10 +28,59 @@ using ICSharpCode.NRefactory.CSharp.Refactoring.ExtractMethod; namespace ICSharpCode.NRefactory.CSharp.CodeActions { - [Ignore("FIXME!!")] [TestFixture] public class ExtractMethodTests : ContextActionTestBase { + [Test()] + public void SimpleArgument() + { + Test(@"class TestClass +{ + void TestMethod () + { + int i = 5; + <-Console.WriteLine (i);-> + } +} +", @"class TestClass +{ + static void NewMethod (int i) + { + Console.WriteLine (i); + } + void TestMethod () + { + int i = 5; + NewMethod (i); + } +} +"); + } + [Test()] + public void NoArgument() + { + Test(@"class TestClass +{ + void TestMethod () + { + int i = 5; + <-Console.WriteLine (""Hello World"");-> + } +} +", @"class TestClass +{ + static void NewMethod () + { + Console.WriteLine (""Hello World""); + } + void TestMethod () + { + int i = 5; + NewMethod (); + } +} +"); + } [Test()] public void ExtractMethodResultStatementTest() @@ -91,7 +140,6 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions "); } - [Ignore("FIXME!!")] [Test()] public void ExtractMethodStaticResultStatementTest() { @@ -146,7 +194,6 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions "); } - [Ignore("FIXME!!")] [Test()] public void ExtractMethodMultiVariableTest() { @@ -164,7 +211,7 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions ", @"class TestClass { int member; - void NewMethod (ref int j, int i, out int k) + void NewMethod (int i, ref int j, out int k) { j = i + j; k = j + member; @@ -185,7 +232,8 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions [Test()] public void TestBug607990() { - Test(@"class TestClass + Test(@"using System; +class TestClass { void TestMethod () { @@ -193,7 +241,8 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions obj1.ToString();-> } } -", @"class TestClass +", @"using System; +class TestClass { static void NewMethod () { @@ -212,7 +261,6 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions /// /// Bug 616193 - Extract method passes param with does not exists any more in main method /// - [Ignore("FIXME!!")] [Test()] public void TestBug616193() { @@ -248,7 +296,6 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions /// /// Bug 616199 - Extract method forgets to return a local var which is used in main method /// - [Ignore("FIXME!!")] [Test()] public void TestBug616199() { @@ -269,7 +316,7 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions } void TestMethod () { - string z = NewMethod (); + var z = NewMethod (); string ret = ""test1"" + z; } } @@ -331,8 +378,7 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions "); } - - [Ignore("FIXME!!")] + [Ignore("Fix me!")] [Test()] public void ExtractMethodMultiVariableWithLocalReturnVariableTest() { @@ -352,7 +398,7 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions ", @"class TestClass { int member; - void NewMethod (ref int j, int i, out int k, out int test) + void NewMethod (int i, ref int j, out int k, out int test) { j = i + j; k = j + member; diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ImplementAbstractMembersTest.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ImplementAbstractMembersTest.cs index adc2a57e2a..9bf6d38732 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ImplementAbstractMembersTest.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ImplementAbstractMembersTest.cs @@ -29,12 +29,11 @@ using ICSharpCode.NRefactory.CSharp.Refactoring; namespace ICSharpCode.NRefactory.CSharp.CodeActions { - [Ignore("TODO")] [TestFixture] public class ImplementAbstractMembersTest : ContextActionTestBase { [Test()] - public void TestSimpleInterface() + public void TestSimpleBaseType() { Test(@"abstract class Simple { public abstract void FooBar (string foo, int bar); @@ -50,12 +49,13 @@ class Foo : $Simple class Foo : Simple { #region implemented abstract members of Simple - public override void FooBar(string foo, int bar) + public override void FooBar (string foo, int bar) { - throw new System.NotImplementedException(); + throw new System.NotImplementedException (); } #endregion -}"); +} +"); } } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ImplementInterfaceExplicitTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ImplementInterfaceExplicitTests.cs index 6ec76d8e9a..1c0fd677cc 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ImplementInterfaceExplicitTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ImplementInterfaceExplicitTests.cs @@ -29,7 +29,6 @@ using ICSharpCode.NRefactory.CSharp.Refactoring; namespace ICSharpCode.NRefactory.CSharp.CodeActions { - [Ignore("TODO")] [TestFixture] public class ImplementInterfaceExplicitTests : ContextActionTestBase { @@ -44,9 +43,9 @@ class Foo : $IDisposable class Foo : IDisposable { #region IDisposable implementation - void IDisposable.Dispose() + void IDisposable.Dispose () { - throw new NotImplementedException(); + throw new NotImplementedException (); } #endregion } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ImplementInterfaceTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ImplementInterfaceTests.cs index 4a0f9fd770..d0e18a42b4 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ImplementInterfaceTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ImplementInterfaceTests.cs @@ -56,7 +56,6 @@ class Foo : IDisposable /// /// Bug 663842 - Interface implementation does not include constraints /// - [Ignore("TODO")] [Test()] public void TestBug663842() { @@ -82,21 +81,21 @@ interface ITest { class Foo : ITest { #region ITest implementation - public void MyMethod1 (T t) where T : new () + public void MyMethod1 (T t) where T : new() { - throw new System.NotImplementedException (); + throw new NotImplementedException (); } public void MyMethod2 (T t) where T : class { - throw new System.NotImplementedException (); + throw new NotImplementedException (); } public void MyMethod3 (T t) where T : struct { - throw new System.NotImplementedException (); + throw new NotImplementedException (); } public void MyMethod4 (T t) where T : IDisposable, IServiceProvider { - throw new System.NotImplementedException (); + throw new NotImplementedException (); } #endregion } @@ -206,7 +205,6 @@ class Foo : ITest /// /// Bug 3365 - MD cannot implement IEnumerable interface correctly - MD cannot implement IEnumerable interface correctly /// - [Ignore("TODO")] [Test()] public void TestBug3365() { @@ -238,20 +236,68 @@ public interface ITest : IA, IEnumerable class Foo : ITest { - #region ITest implementation - public bool GetEnumerator () + #region IEnumerable implementation + public IEnumerator GetEnumerator () { - throw new System.NotImplementedException (); + throw new NotImplementedException (); } #endregion - #region IEnumerable implementation - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator () + #region IA implementation + bool IA.GetEnumerator () { - throw new System.NotImplementedException (); + throw new NotImplementedException (); } #endregion }"); } + + + /// + /// Bug 4818 - Implement implicit does not handle 'params' types + /// + [Test()] + public void TestBug4818() + { + Test(@"using System; +interface ITest { + void OnScenesAdded (params ITest[] scenes); +} + +class Foo : $ITest +{ +} +", @"using System; +interface ITest { + void OnScenesAdded (params ITest[] scenes); +} + +class Foo : ITest +{ + #region ITest implementation + public void OnScenesAdded (params ITest[] scenes) + { + throw new NotImplementedException (); + } + #endregion +} +"); + + TestWrongContext(@"using System; +interface ITest { + void OnScenesAdded (params ITest[] scenes); +} + +class Foo : $ITest +{ + #region ITest implementation + public void OnScenesAdded (params ITest[] scenes) + { + throw new NotImplementedException (); + } + #endregion +} +"); + } } } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/TestRefactoringContext.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/TestRefactoringContext.cs index 2d6be54716..e7b3ae5256 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/TestRefactoringContext.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/TestRefactoringContext.cs @@ -105,9 +105,9 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions foreach (var node in nodes) { InsertBefore(entity, node); } - var t = new Task (() => {}); - t.RunSynchronously (); - return t; + var tcs = new TaskCompletionSource (); + tcs.SetResult (null); + return tcs.Task; } public override Task InsertWithCursor (string operation, ITypeDefinition parentType, IEnumerable nodes) @@ -121,9 +121,9 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions InsertText (startOffset, output.Text); output.RegisterTrackedSegments (this, startOffset); } - var t = new Task (() => {}); - t.RunSynchronously (); - return t; + var tcs = new TaskCompletionSource (); + tcs.SetResult (null); + return tcs.Task; } void Rename (AstNode node, string newName) diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/CodeCompletionBugTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/CodeCompletionBugTests.cs index b2c5464ff5..2c07ee42f7 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/CodeCompletionBugTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/CodeCompletionBugTests.cs @@ -4572,7 +4572,7 @@ class Test } } "); - Assert.AreEqual (4, provider.Count); // 2xTryParse + 2 fields + Assert.AreEqual (2, provider.Count); // 2 fields Assert.IsNotNull (provider.Find ("Value1"), "field 'Value1' not found."); Assert.IsNotNull (provider.Find ("Value2"), "field 'Value2' not found."); } @@ -5241,7 +5241,7 @@ public class TestFoo public void TestBug4961() { CombinedProviderTest( -@"using System; + @"using System; using System.Collections.Generic; namespace EnumerationProblem @@ -5323,7 +5323,47 @@ $mc->$ }); } + /// + /// Bug 6146 - No intellisense on value keyword in property set method + /// + [Test()] + public void TestBug6146() + { + CombinedProviderTest( + @"using System; +public class FooBar +{ + public FooBar Foo { + set { + $value.$ + } + } +} +", provider => { + Assert.IsNotNull(provider.Find("Foo")); + }); + } + + + [Test()] + public void TestBug6146Case2() + { + CombinedProviderTest( + @"using System; +public class FooBar +{ + public FooBar Foo { + set { + $value.Foo.F$ + } + } +} + +", provider => { + Assert.IsNotNull(provider.Find("Foo")); + }); + } } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/ParameterCompletionTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/ParameterCompletionTests.cs index aef48a5e15..e414b6bd2b 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/ParameterCompletionTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/ParameterCompletionTests.cs @@ -592,7 +592,7 @@ class TestClass } }"); Assert.IsNotNull (provider, "provider was not created."); - Assert.AreEqual (6, provider.Count); + Assert.IsTrue (provider.Count > 0); } [Test()] diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Parser/Expression/PrimitiveExpressionTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Parser/Expression/PrimitiveExpressionTests.cs index 41a5a68fa4..74579ddce9 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Parser/Expression/PrimitiveExpressionTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Parser/Expression/PrimitiveExpressionTests.cs @@ -18,6 +18,7 @@ using System; using System.Linq; +using System.Text; using NUnit.Framework; namespace ICSharpCode.NRefactory.CSharp.Parser.Expression @@ -234,5 +235,19 @@ namespace ICSharpCode.NRefactory.CSharp.Parser.Expression Assert.AreEqual(new TextLocation(1, 2), pe.EndLocation); Assert.AreEqual("0", pe.LiteralValue); } + + [Test] + [Ignore("Mono parser crash")] + public void LargeVerbatimString() + { + StringBuilder b = new StringBuilder(); + for (int i = 0; i < 10000; i++) { + b.Append(i.ToString()); + b.Append("\r\n"); + } + string literal = b.ToString(); + var pe = ParseUtilCSharp.ParseExpression("@\"" + literal + "\""); + Assert.AreEqual(literal, pe.Value); + } } } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Parser/GeneralScope/AttributeSectionTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Parser/GeneralScope/AttributeSectionTests.cs index cdf8e1cd09..5cced86459 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Parser/GeneralScope/AttributeSectionTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Parser/GeneralScope/AttributeSectionTests.cs @@ -205,5 +205,19 @@ public class Form1 { typeof(NamespaceDeclaration) }, cu.Children.Select(c => c.GetType()).ToArray()); } + + [Ignore("Fixme!")] + [Test] + public void AssemblyAttributeBeforeClass() + { + var cu = new CSharpParser().Parse(new StringReader("using System; [assembly: Attr] class X {}"), "code.cs"); + Assert.AreEqual( + new Type[] { + typeof(UsingDeclaration), + typeof(AttributeSection), + typeof(TypeDeclaration) + }, cu.Children.Select(c => c.GetType()).ToArray()); + Assert.That(((TypeDeclaration)cu.LastChild).Attributes, Is.Empty); + } } } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Parser/GeneralScope/PreprocessorDirectiveTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Parser/GeneralScope/PreprocessorDirectiveTests.cs index 21074279f7..28b522fc6c 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Parser/GeneralScope/PreprocessorDirectiveTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Parser/GeneralScope/PreprocessorDirectiveTests.cs @@ -132,5 +132,118 @@ namespace ICSharpCode.NRefactory.CSharp.Parser.GeneralScope Assert.AreEqual(PreProcessorDirectiveType.Pragma, ppd.Type); Assert.AreEqual("warning disable 809", ppd.Argument); } + + const string elifProgram = @" +#if AAA +class A { } +#elif BBB +class B { } +#endif"; + + [Test] + [Ignore("parser bug (missing comment node)")] + public void ElifBothFalse() + { + CSharpParser parser = new CSharpParser(); + var cu = parser.Parse(elifProgram, "elif.cs"); + Assert.IsFalse(parser.HasErrors); + + Assert.AreEqual(new Role[] { + Roles.PreProcessorDirective, + Roles.Comment, + Roles.PreProcessorDirective, + Roles.Comment, + Roles.PreProcessorDirective + }, cu.Children.Select(c => c.Role).ToArray()); + var aaa = cu.GetChildrenByRole(Roles.PreProcessorDirective).ElementAt(0); + Assert.IsFalse(aaa.Take); + Assert.AreEqual(PreProcessorDirectiveType.If, aaa.Type); + Assert.AreEqual("AAA", aaa.Argument); + + var bbb = cu.GetChildrenByRole(Roles.PreProcessorDirective).ElementAt(1); + Assert.IsFalse(bbb.Take); + Assert.AreEqual(PreProcessorDirectiveType.Elif, bbb.Type); + Assert.AreEqual("BBB", bbb.Argument); + } + + [Test] + [Ignore("parser bug (bbb.Take is true, should be false)")] + public void ElifBothTrue() + { + CSharpParser parser = new CSharpParser(); + parser.CompilerSettings.ConditionalSymbols.Add("AAA"); + var cu = parser.Parse(elifProgram, "elif.cs"); + Assert.IsFalse(parser.HasErrors); + + Assert.AreEqual(new Role[] { + Roles.PreProcessorDirective, + NamespaceDeclaration.MemberRole, + Roles.PreProcessorDirective, + Roles.Comment, + Roles.PreProcessorDirective + }, cu.Children.Select(c => c.Role).ToArray()); + var aaa = cu.GetChildrenByRole(Roles.PreProcessorDirective).ElementAt(0); + Assert.IsTrue(aaa.Take); + Assert.AreEqual(PreProcessorDirectiveType.If, aaa.Type); + Assert.AreEqual("AAA", aaa.Argument); + + var bbb = cu.GetChildrenByRole(Roles.PreProcessorDirective).ElementAt(1); + Assert.IsFalse(bbb.Take); + Assert.AreEqual(PreProcessorDirectiveType.Elif, bbb.Type); + Assert.AreEqual("BBB", bbb.Argument); + } + + [Test] + [Ignore("parser bug (bbb.Take is true, should be false)")] + public void ElifFirstTaken() + { + CSharpParser parser = new CSharpParser(); + parser.CompilerSettings.ConditionalSymbols.Add("AAA"); + var cu = parser.Parse(elifProgram, "elif.cs"); + Assert.IsFalse(parser.HasErrors); + + Assert.AreEqual(new Role[] { + Roles.PreProcessorDirective, + NamespaceDeclaration.MemberRole, + Roles.PreProcessorDirective, + Roles.Comment, + Roles.PreProcessorDirective + }, cu.Children.Select(c => c.Role).ToArray()); + var aaa = cu.GetChildrenByRole(Roles.PreProcessorDirective).ElementAt(0); + Assert.IsTrue(aaa.Take); + Assert.AreEqual(PreProcessorDirectiveType.If, aaa.Type); + Assert.AreEqual("AAA", aaa.Argument); + + var bbb = cu.GetChildrenByRole(Roles.PreProcessorDirective).ElementAt(1); + Assert.IsFalse(bbb.Take); + Assert.AreEqual(PreProcessorDirectiveType.Elif, bbb.Type); + Assert.AreEqual("BBB", bbb.Argument); + } + + [Test] + public void ElifSecondTaken() + { + CSharpParser parser = new CSharpParser(); + parser.CompilerSettings.ConditionalSymbols.Add("BBB"); + var cu = parser.Parse(elifProgram, "elif.cs"); + Assert.IsFalse(parser.HasErrors); + + Assert.AreEqual(new Role[] { + Roles.PreProcessorDirective, + Roles.Comment, + Roles.PreProcessorDirective, + NamespaceDeclaration.MemberRole, + Roles.PreProcessorDirective + }, cu.Children.Select(c => c.Role).ToArray()); + var aaa = cu.GetChildrenByRole(Roles.PreProcessorDirective).ElementAt(0); + Assert.IsFalse(aaa.Take); + Assert.AreEqual(PreProcessorDirectiveType.If, aaa.Type); + Assert.AreEqual("AAA", aaa.Argument); + + var bbb = cu.GetChildrenByRole(Roles.PreProcessorDirective).ElementAt(1); + Assert.IsTrue(bbb.Take); + Assert.AreEqual(PreProcessorDirectiveType.Elif, bbb.Type); + Assert.AreEqual("BBB", bbb.Argument); + } } } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Parser/TypeMembers/EventDeclarationTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Parser/TypeMembers/EventDeclarationTests.cs index 6ef545b313..d4ead13bff 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Parser/TypeMembers/EventDeclarationTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Parser/TypeMembers/EventDeclarationTests.cs @@ -70,9 +70,9 @@ namespace ICSharpCode.NRefactory.CSharp.Parser.TypeMembers MemberName = "EventHandler" }, Name = "MyEvent", - AddAccessor = new Accessor { Body = new BlockStatement() }, - RemoveAccessor = new Accessor { Body = new BlockStatement() } - }); + AddAccessor = new Accessor { Body = new BlockStatement() }, + RemoveAccessor = new Accessor { Body = new BlockStatement() } + }); } [Test] diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs index e5275bca18..6bfc82c647 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs @@ -73,8 +73,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver [Test] public void DynamicIdentityConversions() { - Assert.AreEqual(C.IdentityConversion, ImplicitConversion(typeof(object), typeof(ReflectionHelper.Dynamic))); - Assert.AreEqual(C.IdentityConversion, ImplicitConversion(typeof(ReflectionHelper.Dynamic), typeof(object))); + Assert.AreEqual(C.IdentityConversion, ImplicitConversion(typeof(object), typeof(dynamic))); + Assert.AreEqual(C.IdentityConversion, ImplicitConversion(typeof(dynamic), typeof(object))); } [Test] @@ -155,12 +155,24 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } [Test] - public void SimpleDynamicConversions() + public void ConversionToDynamic() { Assert.AreEqual(C.ImplicitReferenceConversion, ImplicitConversion(typeof(string), typeof(dynamic))); - Assert.AreEqual(C.ImplicitDynamicConversion, ImplicitConversion(typeof(dynamic), typeof(string))); Assert.AreEqual(C.BoxingConversion, ImplicitConversion(typeof(int), typeof(dynamic))); - Assert.AreEqual(C.ImplicitDynamicConversion, ImplicitConversion(typeof(dynamic), typeof(int))); + } + + [Test] + public void ConversionFromDynamic() + { + // There is no conversion from the type 'dynamic' to other types (except object). + // Such conversions only exists from dynamic expression. + // This is an important distinction for type inference (see TypeInferenceTests.IEnumerableCovarianceWithDynamic) + Assert.AreEqual(C.None, ImplicitConversion(typeof(dynamic), typeof(string))); + Assert.AreEqual(C.None, ImplicitConversion(typeof(dynamic), typeof(int))); + + var dynamicRR = new ResolveResult(SpecialType.Dynamic); + Assert.AreEqual(C.ImplicitDynamicConversion, conversions.ImplicitConversion(dynamicRR, compilation.FindType(typeof(string)))); + Assert.AreEqual(C.ImplicitDynamicConversion, conversions.ImplicitConversion(dynamicRR, compilation.FindType(typeof(int)))); } [Test] @@ -512,7 +524,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } [Test] - public void ExplicitUserDefinedConversion() { + public void ExplicitUserDefinedConversion() + { var rr = Resolve(@" class C1 {} class C2 { @@ -529,5 +542,29 @@ class C { Assert.IsTrue(rr.Conversion.IsUserDefined); Assert.AreEqual("op_Explicit", rr.Conversion.Method.Name); } + + [Test] + public void ImplicitTypeParameterConversion() + { + string program = @"using System; +class Test { + public void M(T t) where T : U { + U u = $t$; + } +}"; + Assert.AreEqual(C.BoxingConversion, GetConversion(program)); + } + + [Test] + public void InvalidImplicitTypeParameterConversion() + { + string program = @"using System; +class Test { + public void M(T t) where U : T { + U u = $t$; + } +}"; + Assert.AreEqual(C.None, GetConversion(program)); + } } } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/DynamicTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/DynamicTests.cs index 088064320c..1c02f6c114 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/DynamicTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/DynamicTests.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Linq.Expressions; using System.Text; using ICSharpCode.NRefactory.Semantics; using ICSharpCode.NRefactory.TypeSystem; @@ -37,6 +38,7 @@ class TestClass { }"; var rr = Resolve(program); Assert.That(rr.Type.Kind, Is.EqualTo(TypeKind.Dynamic)); + Assert.That(rr.InvocationType, Is.EqualTo(DynamicInvocationType.Invocation)); Assert.That(rr.Target, Is.InstanceOf()); var dynamicMember = (DynamicMemberResolveResult)rr.Target; Assert.That(dynamicMember.Target is LocalResolveResult && ((LocalResolveResult)dynamicMember.Target).Variable.Name == "obj"); @@ -61,6 +63,7 @@ class TestClass { }"; var rr = Resolve(program); Assert.That(rr.Type.Kind, Is.EqualTo(TypeKind.Dynamic)); + Assert.That(rr.InvocationType, Is.EqualTo(DynamicInvocationType.Invocation)); Assert.That(rr.Target, Is.InstanceOf()); var dynamicMember = (DynamicMemberResolveResult)rr.Target; Assert.That(dynamicMember.Target is LocalResolveResult && ((LocalResolveResult)dynamicMember.Target).Variable.Name == "obj"); @@ -86,12 +89,14 @@ class TestClass { }"; var rr = Resolve(program); Assert.That(rr.Type.Kind, Is.EqualTo(TypeKind.Dynamic)); + Assert.That(rr.InvocationType, Is.EqualTo(DynamicInvocationType.Invocation)); Assert.That(rr.Target, Is.InstanceOf()); var innerInvocation = (DynamicInvocationResolveResult)rr.Target; Assert.That(innerInvocation.Target, Is.InstanceOf()); var dynamicMember = (DynamicMemberResolveResult)innerInvocation.Target; Assert.That(dynamicMember.Target is LocalResolveResult && ((LocalResolveResult)dynamicMember.Target).Variable.Name == "obj"); Assert.That(dynamicMember.Member, Is.EqualTo("SomeMethod")); + Assert.That(rr.InvocationType, Is.EqualTo(DynamicInvocationType.Invocation)); Assert.That(innerInvocation.Arguments.Count, Is.EqualTo(1)); Assert.That(innerInvocation.Arguments[0].Name, Is.Null); Assert.That(innerInvocation.Arguments[0].Value is LocalResolveResult && ((LocalResolveResult)innerInvocation.Arguments[0].Value).Variable.Name == "a"); @@ -99,5 +104,612 @@ class TestClass { Assert.That(rr.Arguments[0].Name, Is.Null); Assert.That(rr.Arguments[0].Value is LocalResolveResult && ((LocalResolveResult)rr.Arguments[0].Value).Variable.Name == "b"); } + + [Test] + public void InvocationWithDynamicArgumentWithOneApplicableMethod() { + string program = @"using System; +class TestClass { + public void SomeMethod(int a) {} + public void SomeMethod(int a, string b) {} + + void F() { + dynamic obj = null; + var x = $this.SomeMethod(obj)$; + } +}"; + var rr = Resolve(program); + Assert.That(rr.Member.Name, Is.EqualTo("SomeMethod")); + Assert.That(((IParameterizedMember)rr.Member).Parameters.Count, Is.EqualTo(1)); + Assert.That(rr.Arguments.Count, Is.EqualTo(1)); + var cr = rr.Arguments[0] as ConversionResolveResult; + Assert.That(cr, Is.Not.Null); + Assert.That(cr.Conversion.IsImplicit, Is.True); + Assert.That(cr.Conversion.IsDynamicConversion, Is.True); + Assert.That(cr.Input is LocalResolveResult && ((LocalResolveResult)cr.Input).Variable.Name == "obj"); + } + + [Test] + public void InvocationWithDynamicArgumentWhenBothAnOwnAndABaseMethodAreApplicable() { + string program = @"using System; +class TestBase { + public void SomeMethod(int a) {} +} + +class TestClass : TestBase { + public void SomeMethod(string a) {} + public void SomeMethod(string a, int b) {} + + void F() { + dynamic obj = null; + var x = $this.SomeMethod(obj)$; + } +}"; + var rr = Resolve(program); + Assert.That(rr.InvocationType, Is.EqualTo(DynamicInvocationType.Invocation)); + + var mg = rr.Target as MethodGroupResolveResult; + Assert.That(mg, Is.Not.Null, "Expected a MethodGroup"); + Assert.That(mg.TargetResult, Is.InstanceOf()); + Assert.That(mg.MethodName, Is.EqualTo("SomeMethod")); + Assert.That(mg.Methods.Count(), Is.EqualTo(2)); + Assert.That(mg.Methods.Any(m => m.Parameters.Count == 1 && m.DeclaringType.Name == "TestBase" && m.Name == "SomeMethod" && m.Parameters[0].Type.Name == "Int32")); + Assert.That(mg.Methods.Any(m => m.Parameters.Count == 1 && m.DeclaringType.Name == "TestClass" && m.Name == "SomeMethod" && m.Parameters[0].Type.Name == "String")); + + Assert.That(rr.Arguments.Count, Is.EqualTo(1)); + Assert.That(rr.Arguments[0].Value is LocalResolveResult && ((LocalResolveResult)rr.Arguments[0].Value).Variable.Name == "obj"); + } + + [Test, Ignore("Fails")] + public void InvocationWithDynamicArgumentWhenABaseMethodIsShadowed() { + string program = @"using System; +class TestBase { + public void SomeMethod(int a) {} +} + +class TestClass : TestBase { + public void SomeMethod(int a) {} + public void SomeMethod(string a, int b) {} + + void F() { + dynamic obj = null; + var x = $this.SomeMethod(obj)$; + } +}"; + var rr = Resolve(program); + Assert.That(rr.Member.Name, Is.EqualTo("SomeMethod")); + Assert.That(rr.Member.DeclaringType.Name, Is.EqualTo("TestClass")); + Assert.That(((IParameterizedMember)rr.Member).Parameters.Count, Is.EqualTo(1)); + Assert.That(rr.Arguments.Count, Is.EqualTo(1)); + var cr = rr.Arguments[0] as ConversionResolveResult; + Assert.That(cr, Is.Not.Null); + Assert.That(cr.Conversion.IsImplicit, Is.True); + Assert.That(cr.Conversion.IsDynamicConversion, Is.True); + Assert.That(cr.Input is LocalResolveResult && ((LocalResolveResult)cr.Input).Variable.Name == "obj"); + } + + [Test] + public void InvocationWithDynamicArgumentWithTwoApplicableMethods() { + string program = @"using System; +class TestClass { + public void SomeMethod(int a) {} + public void SomeMethod(string a) {} + public void SomeMethod(int a, string b) {} + + void F() { + dynamic obj = null; + var x = $SomeMethod(obj)$; + } +}"; + var rr = Resolve(program); + Assert.That(rr.InvocationType, Is.EqualTo(DynamicInvocationType.Invocation)); + + var mg = rr.Target as MethodGroupResolveResult; + Assert.That(mg, Is.Not.Null, "Expected a MethodGroup"); + Assert.That(mg.TargetResult, Is.InstanceOf()); + Assert.That(mg.MethodName, Is.EqualTo("SomeMethod")); + Assert.That(mg.Methods.All(m => m.Parameters.Count == 1)); + Assert.That(mg.Methods.Select(m => m.Parameters[0].Type.Name), Is.EquivalentTo(new[] { "Int32", "String" })); + Assert.That(mg.Methods.All(m => m.Name == "SomeMethod" && m.DeclaringType.Name == "TestClass")); + + Assert.That(rr.Arguments.Count, Is.EqualTo(1)); + Assert.That(rr.Arguments[0].Value is LocalResolveResult && ((LocalResolveResult)rr.Arguments[0].Value).Variable.Name == "obj"); + } + + [Test] + public void InvocationWithDynamicArgumentWithTwoApplicableStaticMethods() { + string program = @"using System; +class TestClass { + public static void SomeMethod(int a) {} + public static void SomeMethod(string a) {} + public static void SomeMethod(int a, string b) {} + + void F() { + dynamic obj = null; + var x = $SomeMethod(obj)$; + } +}"; + var rr = Resolve(program); + Assert.That(rr.InvocationType, Is.EqualTo(DynamicInvocationType.Invocation)); + + var mg = rr.Target as MethodGroupResolveResult; + Assert.That(mg, Is.Not.Null, "Expected a MethodGroup"); + Assert.That(mg.TargetResult, Is.InstanceOf()); + Assert.That(mg.MethodName, Is.EqualTo("SomeMethod")); + Assert.That(mg.Methods.All(m => m.Parameters.Count == 1)); + Assert.That(mg.Methods.Select(m => m.Parameters[0].Type.Name), Is.EquivalentTo(new[] { "Int32", "String" })); + Assert.That(mg.Methods.All(m => m.Name == "SomeMethod" && m.DeclaringType.Name == "TestClass")); + + Assert.That(rr.Arguments.Count, Is.EqualTo(1)); + Assert.That(rr.Arguments[0].Value is LocalResolveResult && ((LocalResolveResult)rr.Arguments[0].Value).Variable.Name == "obj"); + } + + [Test] + public void InvocationWithDynamicArgumentWithApplicableStaticAndNonStaticMethodsFavorTheNonStaticOne() { + string program = @"using System; +class TestClass { + public static void SomeMethod(int a) {} + public void SomeMethod(string a) {} + public static void SomeMethod(int a, string b) {} + + void F() { + dynamic obj = null; + var x = $SomeMethod(obj)$; + } +}"; + var rr = Resolve(program); + Assert.That(rr.InvocationType, Is.EqualTo(DynamicInvocationType.Invocation)); + + var mg = rr.Target as MethodGroupResolveResult; + Assert.That(mg, Is.Not.Null, "Expected a MethodGroup"); + Assert.That(mg.TargetResult, Is.InstanceOf()); + Assert.That(mg.MethodName, Is.EqualTo("SomeMethod")); + Assert.That(mg.Methods.All(m => m.Parameters.Count == 1)); + Assert.That(mg.Methods.Select(m => m.Parameters[0].Type.Name), Is.EquivalentTo(new[] { "Int32", "String" })); + Assert.That(mg.Methods.All(m => m.Name == "SomeMethod" && m.DeclaringType.Name == "TestClass")); + + Assert.That(rr.Arguments.Count, Is.EqualTo(1)); + Assert.That(rr.Arguments[0].Value is LocalResolveResult && ((LocalResolveResult)rr.Arguments[0].Value).Variable.Name == "obj"); + } + + [Test] + public void InvocationWithDynamicArgumentWhenTheOnlyApplicableMethodIsAnExtensionMethod() { + string program = @"using System; +static class OtherClass { + public void SomeMethod(this TestClass x, int a) {} + public void SomeMethod(this TestClass x, string a) {} + public void SomeMethod(this TestClass x, int a, string b) {} +} +class TestClass { + void F() { + dynamic obj = null; + var x = $this.SomeMethod(obj)$; + } +}"; + var rr = Resolve(program); + Assert.That(rr.IsError, Is.True); + } + + [Test] + public void InvocationWithDynamicArgumentWithTwoApplicableMethodsAndNamedArguments() { + string program = @"using System; +class TestClass { + public void SomeMethod(int a, int i) {} + public void SomeMethod(string a, int i) {} + public void SomeMethod(int a, string b, int i) {} + + void F() { + dynamic obj = null; + int idx = 0; + var x = $this.SomeMethod(a: obj, i: idx)$; + } +}"; + var rr = Resolve(program); + Assert.That(rr.InvocationType, Is.EqualTo(DynamicInvocationType.Invocation)); + + var mg = rr.Target as MethodGroupResolveResult; + Assert.That(mg, Is.Not.Null, "Expected a MethodGroup"); + Assert.That(mg.TargetResult, Is.InstanceOf()); + Assert.That(mg.MethodName, Is.EqualTo("SomeMethod")); + Assert.That(mg.Methods.All(m => m.Parameters.Count == 2) && mg.Methods.All(m => m.Parameters[1].Type.Name == "Int32")); + Assert.That(mg.Methods.Select(m => m.Parameters[0].Type.Name), Is.EquivalentTo(new[] { "Int32", "String" })); + Assert.That(mg.Methods.All(m => m.Name == "SomeMethod" && m.DeclaringType.Name == "TestClass")); + + Assert.That(rr.Arguments.Count, Is.EqualTo(2)); + Assert.That(rr.Arguments[0].Name, Is.EqualTo("a")); + Assert.That(rr.Arguments[0].Value is LocalResolveResult && ((LocalResolveResult)rr.Arguments[0].Value).Variable.Name == "obj"); + Assert.That(rr.Arguments[1].Name, Is.EqualTo("i")); + Assert.That(rr.Arguments[1].Value is LocalResolveResult && ((LocalResolveResult)rr.Arguments[1].Value).Variable.Name == "idx"); + } + + [Test] + public void IndexingDynamicObjectWithUnnamedArguments() { + string program = @"using System; +class TestClass { + void F() { + dynamic obj = null; + int a = 0, b = 0; + object o = $obj[a]$; + } +}"; + var rr = Resolve(program); + Assert.That(rr.Type.Kind, Is.EqualTo(TypeKind.Dynamic)); + Assert.That(rr.InvocationType, Is.EqualTo(DynamicInvocationType.Indexing)); + Assert.That(rr.Target is LocalResolveResult && ((LocalResolveResult)rr.Target).Variable.Name == "obj"); + Assert.That(rr.Arguments.Count, Is.EqualTo(1)); + Assert.That(rr.Arguments[0].Name, Is.Null); + Assert.That(rr.Arguments[0].Value is LocalResolveResult && ((LocalResolveResult)rr.Arguments[0].Value).Variable.Name == "a"); + } + + [Test] + public void IndexingDynamicObjectWithNamedArguments() { + string program = @"using System; +class TestClass { + void F() { + dynamic obj = null; + int a = 0, b = 0; + $obj[arg1: a, arg2: b]$ = 1; + } +}"; + var rr = Resolve(program); + Assert.That(rr.Type.Kind, Is.EqualTo(TypeKind.Dynamic)); + Assert.That(rr.InvocationType, Is.EqualTo(DynamicInvocationType.Indexing)); + Assert.That(rr.Target is LocalResolveResult && ((LocalResolveResult)rr.Target).Variable.Name == "obj"); + Assert.That(rr.Arguments.Count, Is.EqualTo(2)); + Assert.That(rr.Arguments[0].Name, Is.EqualTo("arg1")); + Assert.That(rr.Arguments[0].Value is LocalResolveResult && ((LocalResolveResult)rr.Arguments[0].Value).Variable.Name == "a"); + Assert.That(rr.Arguments[1].Name, Is.EqualTo("arg2")); + Assert.That(rr.Arguments[1].Value is LocalResolveResult && ((LocalResolveResult)rr.Arguments[1].Value).Variable.Name == "b"); + } + + [Test] + public void IndexingWithDynamicArgumentWithOneApplicableIndexer() { + string program = @"using System; +class TestClass { + public int this[int a] { get { return 0; } } + public int this[int a, string b] { get { return 0; } } + + void F() { + dynamic obj = null; + var x = $this[obj]$; + } +}"; + var rr = Resolve(program); + Assert.That(rr.Member.Name, Is.EqualTo("Item")); + Assert.That(((IParameterizedMember)rr.Member).Parameters.Count, Is.EqualTo(1)); + Assert.That(rr.Arguments.Count, Is.EqualTo(1)); + var cr = rr.Arguments[0] as ConversionResolveResult; + Assert.That(cr, Is.Not.Null); + Assert.That(cr.Conversion.IsImplicit, Is.True); + Assert.That(cr.Conversion.IsDynamicConversion, Is.True); + Assert.That(cr.Input is LocalResolveResult && ((LocalResolveResult)cr.Input).Variable.Name == "obj"); + } + + [Test] + public void IndexingWithDynamicArgumentWithTwoApplicableIndexersAndUnnamedArguments() { + string program = @"using System; +class TestClass { + public int this[int a] { get { return 0; } } + public int this[string a] { get { return 0; } } + void F() { + dynamic obj = null; + var x = $this[obj]$; + } +}"; + var rr = Resolve(program); + Assert.That(rr.Type.Kind, Is.EqualTo(TypeKind.Dynamic)); + Assert.That(rr.InvocationType, Is.EqualTo(DynamicInvocationType.Indexing)); + Assert.That(rr.Target, Is.InstanceOf()); + Assert.That(rr.Arguments.Count, Is.EqualTo(1)); + Assert.That(rr.Arguments[0].Name, Is.Null); + Assert.That(rr.Arguments[0].Value is LocalResolveResult && ((LocalResolveResult)rr.Arguments[0].Value).Variable.Name == "obj"); + } + + [Test] + public void IndexingWithDynamicArgumentWithAnApplicableBaseIndexer() { + string program = @"using System; +class TestBase { + public int this[int a] { get { return 0; } } +} + +class TestClass : TestBase { + public int this[string a] { get { return 0; } } + public int this[string a, int b] { get { return 0; } } + void F() { + dynamic obj = null; + var x = $this[obj]$; + } +}"; + var rr = Resolve(program); + Assert.That(rr.Type.Kind, Is.EqualTo(TypeKind.Dynamic)); + Assert.That(rr.InvocationType, Is.EqualTo(DynamicInvocationType.Indexing)); + Assert.That(rr.Target, Is.InstanceOf()); + Assert.That(rr.Arguments.Count, Is.EqualTo(1)); + Assert.That(rr.Arguments[0].Name, Is.Null); + Assert.That(rr.Arguments[0].Value is LocalResolveResult && ((LocalResolveResult)rr.Arguments[0].Value).Variable.Name == "obj"); + } + + [Test, Ignore("Fails")] + public void IndexingWithDynamicArgumentWithTheOnlyApplicableIndexerShadowingABaseIndexer() { + string program = @"using System; +class TestBase { + public int this[int a] { get { return 0; } } +} + +class TestClass : TestBase { + public new int this[int a] { get { return 0; } } + public int this[int a, string b] { get { return 0; } } + + void F() { + dynamic obj = null; + var x = $this[obj]$; + } +}"; + var rr = Resolve(program); + Assert.That(rr.Member.Name, Is.EqualTo("Item")); + Assert.That(((IParameterizedMember)rr.Member).Parameters.Count, Is.EqualTo(1)); + Assert.That(rr.Arguments.Count, Is.EqualTo(1)); + var cr = rr.Arguments[0] as ConversionResolveResult; + Assert.That(cr, Is.Not.Null); + Assert.That(cr.Conversion.IsImplicit, Is.True); + Assert.That(cr.Conversion.IsDynamicConversion, Is.True); + Assert.That(cr.Input is LocalResolveResult && ((LocalResolveResult)cr.Input).Variable.Name == "obj"); + } + + [Test] + public void IndexingWithDynamicArgumentWithTwoApplicableIndexersAndNamedArguments() { + string program = @"using System; +class TestClass { + public int this[int a, int i] { get { return 0; } } + public int this[string a, int i] { get { return 0; } } + void F() { + dynamic obj = null; + int idx = 0; + var x = $this[a: obj, i: idx]$; + } +}"; + var rr = Resolve(program); + Assert.That(rr.Type.Kind, Is.EqualTo(TypeKind.Dynamic)); + Assert.That(rr.InvocationType, Is.EqualTo(DynamicInvocationType.Indexing)); + Assert.That(rr.Target, Is.InstanceOf()); + Assert.That(rr.Arguments.Count, Is.EqualTo(2)); + Assert.That(rr.Arguments[0].Name, Is.EqualTo("a")); + Assert.That(rr.Arguments[0].Value is LocalResolveResult && ((LocalResolveResult)rr.Arguments[0].Value).Variable.Name == "obj"); + Assert.That(rr.Arguments[1].Name, Is.EqualTo("i")); + Assert.That(rr.Arguments[1].Value is LocalResolveResult && ((LocalResolveResult)rr.Arguments[1].Value).Variable.Name == "idx"); + } + + [Test] + public void ConstructingObjectWithDynamicArgumentWithOneApplicableConstructor() { + string program = @"using System; +class TestClass { + public TestClass(int a) {} + public void TestClass(int a, string b) {} + + void F() { + dynamic obj = null; + var x = $new TestClass(obj)$; + } +}"; + var rr = Resolve(program); + Assert.That(rr.Member.Name, Is.EqualTo(".ctor")); + Assert.That(rr.TargetResult, Is.Null); + Assert.That(((IParameterizedMember)rr.Member).Parameters.Count, Is.EqualTo(1)); + Assert.That(rr.Arguments.Count, Is.EqualTo(1)); + var cr = rr.Arguments[0] as ConversionResolveResult; + Assert.That(cr, Is.Not.Null); + Assert.That(cr.Input is LocalResolveResult && ((LocalResolveResult)cr.Input).Variable.Name == "obj"); + } + + [Test] + public void ConstructingObjectWithDynamicArgumentWithTwoApplicableConstructors() { + string program = @"using System; +class TestClass { + public TestClass(int a, int b) {} + public TestClass(string a, int b) {} + public void TestClass(int a, string b) {} + + void F() { + dynamic obj = null; + int i = 0; + var x = $new TestClass(obj, i)$; + } +}"; + var rr = Resolve(program); + Assert.That(rr.InvocationType, Is.EqualTo(DynamicInvocationType.ObjectCreation)); + + var mg = rr.Target as MethodGroupResolveResult; + Assert.That(mg, Is.Not.Null, "Expected a MethodGroup"); + Assert.That(mg.TargetResult, Is.Null); + Assert.That(mg.MethodName, Is.EqualTo(".ctor")); + Assert.That(mg.Methods.All(m => m.Parameters.Count == 2 && m.Parameters[1].Type.Name == "Int32")); + Assert.That(mg.Methods.Select(m => m.Parameters[0].Type.Name), Is.EquivalentTo(new[] { "Int32", "String" })); + Assert.That(mg.Methods.All(m => m.Name == ".ctor" && m.DeclaringType.Name == "TestClass")); + + Assert.That(rr.Arguments.Count, Is.EqualTo(2)); + Assert.That(rr.Arguments[0].Value is LocalResolveResult && ((LocalResolveResult)rr.Arguments[0].Value).Variable.Name == "obj"); + Assert.That(rr.Arguments[1].Value is LocalResolveResult && ((LocalResolveResult)rr.Arguments[1].Value).Variable.Name == "i"); + } + + [Test] + public void ConstructingObjectWithDynamicArgumentWithTwoApplicableConstructorsAndNamedArguments() { + string program = @"using System; +class TestClass { + public TestClass(int arg1, int arg2) {} + public TestClass(string arg1, int arg2) {} + public void TestClass(int a) {} + + void F() { + dynamic obj = null; + int i = 0; + var x = $new TestClass(arg1: obj, arg2: i)$; + } +}"; + var rr = Resolve(program); + Assert.That(rr.InvocationType, Is.EqualTo(DynamicInvocationType.ObjectCreation)); + + var mg = rr.Target as MethodGroupResolveResult; + Assert.That(mg, Is.Not.Null, "Expected a MethodGroup"); + Assert.That(mg.TargetResult, Is.Null); + Assert.That(mg.MethodName, Is.EqualTo(".ctor")); + Assert.That(mg.Methods.All(m => m.Parameters.Count == 2 && m.Parameters[1].Type.Name == "Int32")); + Assert.That(mg.Methods.Select(m => m.Parameters[0].Type.Name), Is.EquivalentTo(new[] { "Int32", "String" })); + Assert.That(mg.Methods.All(m => m.Name == ".ctor" && m.DeclaringType.Name == "TestClass")); + + Assert.That(rr.Arguments.Count, Is.EqualTo(2)); + Assert.That(rr.Arguments[0].Name, Is.EqualTo("arg1")); + Assert.That(rr.Arguments[0].Value is LocalResolveResult && ((LocalResolveResult)rr.Arguments[0].Value).Variable.Name == "obj"); + Assert.That(rr.Arguments[1].Name, Is.EqualTo("arg2")); + Assert.That(rr.Arguments[1].Value is LocalResolveResult && ((LocalResolveResult)rr.Arguments[1].Value).Variable.Name == "i"); + } + + [Test] + public void ConstructingObjectWithDynamicArgumentWithTwoApplicableConstructorsAndInitializerStatements() { + string program = @"using System; +class TestClass { + public TestClass(int a, int b) {} + public TestClass(string a, int b) {} + + public int A { get; set; } + + void F() { + dynamic obj = null; + int i = 0; + int j = 0; + var x = $new TestClass(obj, i) { A = j }$; + } +}"; + var rr = Resolve(program); + Assert.That(rr.InvocationType, Is.EqualTo(DynamicInvocationType.ObjectCreation)); + + Assert.That(rr.InitializerStatements.Count, Is.EqualTo(1)); + var or = rr.InitializerStatements[0] as OperatorResolveResult; + Assert.That(or, Is.Not.Null); + Assert.That(or.OperatorType, Is.EqualTo(ExpressionType.Assign)); + var mrr = or.Operands[0] as MemberResolveResult; + Assert.That(mrr, Is.Not.Null); + Assert.That(mrr.TargetResult, Is.InstanceOf()); + Assert.That(mrr.Member.Name, Is.EqualTo("A")); + Assert.That(or.Operands[1], Is.InstanceOf()); + Assert.That(((LocalResolveResult)or.Operands[1]).Variable.Name, Is.EqualTo("j")); + } + + [Test] + public void InitializingBaseWithDynamicArgumentAndOneApplicableConstructor() { + string program = @"using System; +class TestBase { + public TestBase(int a, int b) {} + public TestBase(string a) {} +} + +class TestClass : TestBase { + private static dynamic d; + private static int i; + + public TestClass() : $base(d, i)$ {} +}"; + + var rr = Resolve(program); + + Assert.That(rr.Member.Name, Is.EqualTo(".ctor")); + Assert.That(rr.TargetResult, Is.Null); + Assert.That(((IParameterizedMember)rr.Member).Parameters.Count, Is.EqualTo(2)); + Assert.That(rr.Arguments.Count, Is.EqualTo(2)); + Assert.That(rr.Member.DeclaringType.Name, Is.EqualTo("TestBase")); + var cr = rr.Arguments[0] as ConversionResolveResult; + Assert.That(cr, Is.Not.Null); + Assert.That(cr.Input is MemberResolveResult && ((MemberResolveResult)cr.Input).Member.Name == "d"); + Assert.That(rr.Arguments[1] is MemberResolveResult && ((MemberResolveResult)rr.Arguments[1]).Member.Name == "i"); + } + + [Test] + public void InitializingBaseWithDynamicArgumentAndTwoApplicableConstructors() { + string program = @"using System; +class TestBase { + public TestBase(int a, int b) {} + public TestBase(string a, int b) {} + public TestBase(string a) {} +} + +class TestClass : TestBase { + private static dynamic d; + private static int i; + + public TestClass() : $base(d, i)$ {} +}"; + + var rr = Resolve(program); + Assert.That(rr.InvocationType, Is.EqualTo(DynamicInvocationType.ObjectCreation)); + + var mg = rr.Target as MethodGroupResolveResult; + Assert.That(mg, Is.Not.Null, "Expected a MethodGroup"); + Assert.That(mg.TargetResult, Is.Null); + Assert.That(mg.MethodName, Is.EqualTo(".ctor")); + Assert.That(mg.Methods.All(m => m.Parameters.Count == 2 && m.Parameters[1].Type.Name == "Int32")); + Assert.That(mg.Methods.Select(m => m.Parameters[0].Type.Name), Is.EquivalentTo(new[] { "Int32", "String" })); + Assert.That(mg.Methods.All(m => m.Name == ".ctor" && m.DeclaringType.Name == "TestBase")); + + Assert.That(rr.Arguments.Count, Is.EqualTo(2)); + Assert.That(rr.Arguments[0].Value is MemberResolveResult && ((MemberResolveResult)rr.Arguments[0].Value).Member.Name == "d"); + Assert.That(rr.Arguments[1].Value is MemberResolveResult && ((MemberResolveResult)rr.Arguments[1].Value).Member.Name == "i"); + } + + [Test] + public void ConstructorChainingWithDynamicArgumentAndOneApplicableConstructor() { + string program = @"using System; +class TestClass { + private static dynamic d; + private static int i; + + public TestClass(int a, int b) {} + public TestClass(string a) {} + + public TestClass() : $this(d, i)$ {} +}"; + + var rr = Resolve(program); + + Assert.That(rr.Member.Name, Is.EqualTo(".ctor")); + Assert.That(rr.TargetResult, Is.Null); + Assert.That(((IParameterizedMember)rr.Member).Parameters.Count, Is.EqualTo(2)); + Assert.That(rr.Arguments.Count, Is.EqualTo(2)); + Assert.That(rr.Member.DeclaringType.Name, Is.EqualTo("TestClass")); + var cr = rr.Arguments[0] as ConversionResolveResult; + Assert.That(cr, Is.Not.Null); + Assert.That(cr.Input is MemberResolveResult && ((MemberResolveResult)cr.Input).Member.Name == "d"); + Assert.That(rr.Arguments[1] is MemberResolveResult && ((MemberResolveResult)rr.Arguments[1]).Member.Name == "i"); + } + + [Test] + public void ConstructorChainingWithDynamicArgumentAndTwoApplicableConstructors() { + string program = @"using System; +class TestBase { +} + +class TestClass { + private static dynamic d; + private static int i; + + public TestClass(int a, int b) {} + public TestClass(string a, int b) {} + public TestClass(string a) {} + + public TestClass() : $this(d, i)$ {} +}"; + + var rr = Resolve(program); + Assert.That(rr.InvocationType, Is.EqualTo(DynamicInvocationType.ObjectCreation)); + + var mg = rr.Target as MethodGroupResolveResult; + Assert.That(mg, Is.Not.Null, "Expected a MethodGroup"); + Assert.That(mg.TargetResult, Is.Null); + Assert.That(mg.MethodName, Is.EqualTo(".ctor")); + Assert.That(mg.Methods.All(m => m.Parameters.Count == 2 && m.Parameters[1].Type.Name == "Int32")); + Assert.That(mg.Methods.Select(m => m.Parameters[0].Type.Name), Is.EquivalentTo(new[] { "Int32", "String" })); + Assert.That(mg.Methods.All(m => m.Name == ".ctor" && m.DeclaringType.Name == "TestClass")); + + Assert.That(rr.Arguments.Count, Is.EqualTo(2)); + Assert.That(rr.Arguments[0].Value is MemberResolveResult && ((MemberResolveResult)rr.Arguments[0].Value).Member.Name == "d"); + Assert.That(rr.Arguments[1].Value is MemberResolveResult && ((MemberResolveResult)rr.Arguments[1].Value).Member.Name == "i"); + } } } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExtensionMethodTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExtensionMethodTests.cs index 098deaa421..cb759789c7 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExtensionMethodTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExtensionMethodTests.cs @@ -17,8 +17,10 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.Linq; using ICSharpCode.NRefactory.Semantics; using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem.Implementation; using NUnit.Framework; namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -118,5 +120,120 @@ public static class XC { Assert.AreEqual(1, inferredTypes.Length); Assert.AreEqual("System.String", inferredTypes[0].ReflectionName); } + + + [Test] + public void InferTypeFromOverwrittenMethodArguments() + { + string program = @"using System.Collections.Generic; + using System.Linq; + + public class A { } + + public class B : A { } + + class Program + { + static void Main(string[] args) + { + IEnumerable list = new List(); + var arr = $list.ToArray()$; + } + } +"; + var rr = Resolve(program); + Assert.AreEqual("A[]", rr.Type.ReflectionName); + Assert.AreEqual("System.Linq.Enumerable.ToArray", rr.Member.FullName); + Assert.AreEqual("A", ((SpecializedMethod)rr.Member).TypeArguments.Single().ReflectionName); + } + + [Test] + public void TypeInferenceBasedOnTargetTypeAndArgumentType() + { + string program = @"using System.Collections.Generic; + using System.Linq; + + public class A { } + public class B : A { } + + static class Program + { + static void Main(A a, B b) + { + var x = $b.Choose(a)$; + } + + public static T Choose(this T a, T b) { } + } +"; + var rr = Resolve(program); + Assert.AreEqual("A", rr.Type.ReflectionName); + } + + [Test] + public void PartiallySpecializedMethod() + { + string program = @"using System.Collections.Generic; + using System.Linq; + + public class A { } + public class B : A { } + + static class Program + { + static void Main(A a, B b) + { + var x = $b.Choose$(a); + } + + public static T Choose(this T a, T b) { } + } +"; + var rr = Resolve(program); + Assert.IsFalse(rr.Methods.Any()); + // We deliberately do not specialize the method unless partial specialization is requested explicitly. + // This is because the actual type (when considering the whole invocation, not just the method group) + // is actually A. + Assert.AreEqual("``0", rr.GetExtensionMethods().Single().Single().ReturnType.ReflectionName); + Assert.AreEqual("``0", rr.GetEligibleExtensionMethods(false).Single().Single().ReturnType.ReflectionName); + Assert.AreEqual("B", rr.GetEligibleExtensionMethods(true).Single().Single().ReturnType.ReflectionName); + } + + [Test] + public void CreateDelegateFromExtensionMethod() + { + string program = @"using System; + static class Program + { + static void Main() { + Func f = $"""".id$; + } + static string id(this string x) { return x; } + } +"; + Conversion c = GetConversion(program); + Assert.IsTrue(c.IsValid); + Assert.IsTrue(c.IsMethodGroupConversion); + Assert.AreEqual("Program.id", c.Method.FullName); + } + + [Test] + public void InferDelegateTypeFromExtensionMethod() + { + string program = @"using System; + static class Program + { + static void Main() { + $call("""".id)$; + } + + static string id(this string x) { return x; } + static T call(Func f) { } + } +"; + var rr = Resolve(program); + Assert.IsFalse(rr.IsError); + Assert.AreEqual("System.String", rr.Type.FullName); + } } } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ObjectCreationTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ObjectCreationTests.cs index 721a510115..609c66ec33 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ObjectCreationTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ObjectCreationTests.cs @@ -275,5 +275,46 @@ class Test where T : new() { Assert.AreEqual(TypeKind.TypeParameter, rr.Type.Kind); Assert.AreEqual(TypeKind.TypeParameter, rr.Member.DeclaringType.Kind); } + + [Test] + public void CreateDelegateFromMethodGroup() + { + string program = @"using System; +delegate void D(int i); +class C { + void M(int y) { + D d = $new D(M)$; + } +}"; + var rr = Resolve(program); + Assert.IsFalse(rr.IsError); + Assert.IsTrue(rr.Conversion.IsIdentityConversion); + var rr2 = (ConversionResolveResult)rr.Input; + Assert.IsFalse(rr2.IsError); + Assert.IsTrue(rr2.Conversion.IsMethodGroupConversion); + + Assert.AreEqual("C.M", rr2.Conversion.Method.FullName); + var mgrr = (MethodGroupResolveResult)rr2.Input; + Assert.IsInstanceOf(mgrr.TargetResult); + } + + [Test] + public void CreateDelegateFromDelegate() + { + string program = @"using System; +delegate void D1(int i); +delegate void D2(int i); +class C { + void M(D1 d1) { + D2 d2 = $new D2(d1)$; + } +}"; + var rr = Resolve(program); + Assert.IsFalse(rr.IsError); + Assert.IsTrue(rr.Conversion.IsMethodGroupConversion); + Assert.AreEqual("D1.Invoke", rr.Conversion.Method.FullName); + var mgrr = (MethodGroupResolveResult)rr.Input; + Assert.IsInstanceOf(mgrr.TargetResult); + } } } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/TypeInferenceTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/TypeInferenceTests.cs index 287cfc9bd5..4addf8548e 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/TypeInferenceTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/TypeInferenceTests.cs @@ -148,6 +148,30 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver out success)); Assert.IsTrue(success); } + + [Test] + public void IEnumerableCovarianceWithDynamic() + { + ITypeParameter tp = new DefaultTypeParameter(compilation, EntityType.Method, 0, "T"); + var ienumerableOfT = new ParameterizedType(compilation.FindType(typeof(IEnumerable<>)).GetDefinition(), new[] { tp }); + var ienumerableOfString = compilation.FindType(typeof(IEnumerable)); + var ienumerableOfDynamic = compilation.FindType(typeof(IEnumerable)); + + // static T M(IEnumerable x, IEnumerable y) {} + // M(IEnumerable, IEnumerable); -> should infer T=dynamic, no ambiguity + // See http://blogs.msdn.com/b/cburrows/archive/2010/04/01/errata-dynamic-conversions-and-overload-resolution.aspx + // for details. + + bool success; + Assert.AreEqual( + new [] { SpecialType.Dynamic }, + ti.InferTypeArguments( + new [] { tp }, + new [] { new ResolveResult(ienumerableOfDynamic), new ResolveResult(ienumerableOfString) }, + new [] { ienumerableOfT, ienumerableOfT }, + out success)); + Assert.IsTrue(success); + } #endregion #region Inference with Method Groups diff --git a/ICSharpCode.NRefactory.Tests/Documentation/CSharpCrefLookupTests.cs b/ICSharpCode.NRefactory.Tests/Documentation/CSharpCrefLookupTests.cs index 97f855453c..26de3ca22d 100644 --- a/ICSharpCode.NRefactory.Tests/Documentation/CSharpCrefLookupTests.cs +++ b/ICSharpCode.NRefactory.Tests/Documentation/CSharpCrefLookupTests.cs @@ -26,7 +26,6 @@ using NUnit.Framework; namespace ICSharpCode.NRefactory.Documentation { [TestFixture] - [Ignore("Cref parsing not yet implemented")] public class CSharpCrefLookupTests { IEntity Lookup(string cref) @@ -58,7 +57,7 @@ class Impl : IGeneric[,], T> { var pc = new CSharpProjectContent().AddAssemblyReferences(new[] { CecilLoaderTests.Mscorlib }); var cu = new CSharpParser().Parse(new StringReader(program), "program.cs"); var compilation = pc.UpdateProjectContent(null, cu.ToTypeSystem()).CreateCompilation(); - var typeDefinition = compilation.MainAssembly.TopLevelTypeDefinitions.Single(); + var typeDefinition = compilation.MainAssembly.TopLevelTypeDefinitions.First(); IEntity entity = typeDefinition.Documentation.ResolveCref(cref); Assert.IsNotNull(entity, "ResolveCref() returned null."); return entity; @@ -109,7 +108,7 @@ class Impl : IGeneric[,], T> { [Test] public void M() { - Assert.AreEqual("M:Test.M(System.String[0:,0:])", + Assert.AreEqual("M:Test.M(System.Int32)", IdStringProvider.GetIdString(Lookup("M"))); } @@ -141,19 +140,25 @@ class Impl : IGeneric[,], T> { [Test] public void MethodInGenericInterface() { - Assert.AreEqual("M:XmlDocTest.IGeneric`2.Test``1(``0[0:,0:]@)", + Assert.AreEqual("M:IGeneric`2.Test``1(``0[0:,0:]@)", IdStringProvider.GetIdString(Lookup("IGeneric{X, Y}.Test"))); - Assert.AreEqual("M:XmlDocTest.IGeneric`2.Test``1(``0[0:,0:]@)", + Assert.AreEqual("M:IGeneric`2.Test``1(``0[0:,0:]@)", IdStringProvider.GetIdString(Lookup("IGeneric{X, Y}.Test{Z}"))); - Assert.AreEqual("M:XmlDocTest.IGeneric`2.Test``1(``0[0:,0:]@)", + Assert.AreEqual("M:IGeneric`2.Test``1(``0[0:,0:]@)", IdStringProvider.GetIdString(Lookup("IGeneric{X, Y}.Test{Z}(ref Z[,])"))); } [Test] - public void Indexer() + [Ignore("Fails due to mcs parser bug (see CSharpCrefParserTests.This)")] + public void IndexerWithoutDeclaringType() { Assert.AreEqual("P:Test.Item(System.Int32)", IdStringProvider.GetIdString(Lookup("this"))); + } + + [Test] + public void IndexerWithDeclaringType() + { Assert.AreEqual("P:Test.Item(System.Int32)", IdStringProvider.GetIdString(Lookup("Test.this"))); Assert.AreEqual("P:Test.Item(System.Int32)", @@ -161,6 +166,7 @@ class Impl : IGeneric[,], T> { } [Test] + [Ignore("mcs bug, see CSharpCrefParserTests.OperatorPlusWithDeclaringType")] public void OperatorPlus() { Assert.AreEqual("M:Test.op_Addition(Test,System.Int32)", @@ -172,6 +178,7 @@ class Impl : IGeneric[,], T> { } [Test] + [Ignore("mcs bug, see CSharpCrefParserTests.OperatorPlusWithDeclaringType")] public void ImplicitOperator() { Assert.AreEqual("M:Test.op_Implicit(Test)~System.Int32", diff --git a/ICSharpCode.NRefactory.Tests/Documentation/CSharpCrefParserTests.cs b/ICSharpCode.NRefactory.Tests/Documentation/CSharpCrefParserTests.cs index 505c7b83c9..d0dc33c5b6 100644 --- a/ICSharpCode.NRefactory.Tests/Documentation/CSharpCrefParserTests.cs +++ b/ICSharpCode.NRefactory.Tests/Documentation/CSharpCrefParserTests.cs @@ -25,7 +25,6 @@ using NUnit.Framework; namespace ICSharpCode.NRefactory.Documentation { [TestFixture] - [Ignore("Cref parsing not yet implemented")] public class CSharpCrefParserTests { [Test] @@ -39,6 +38,7 @@ namespace ICSharpCode.NRefactory.Documentation } [Test] + [Ignore("mcs bug")] public void This() { ParseUtilCSharp.AssertDocumentationReference( @@ -49,6 +49,7 @@ namespace ICSharpCode.NRefactory.Documentation } [Test] + [Ignore("mcs bug (Unexpected symbol `this', expecting `explicit', `implicit', `operator', or `type')")] public void ThisWithParameter() { ParseUtilCSharp.AssertDocumentationReference( @@ -115,6 +116,21 @@ namespace ICSharpCode.NRefactory.Documentation }); } + [Test] + public void IntParse() + { + ParseUtilCSharp.AssertDocumentationReference( + "int.Parse(string)", + new DocumentationReference { + DeclaringType = new PrimitiveType("int"), + MemberName = "Parse", + HasParameterList = true, + Parameters = { + new ParameterDeclaration { Type = new PrimitiveType("string") } + } + }); + } + [Test] public void Generic() { @@ -201,6 +217,7 @@ namespace ICSharpCode.NRefactory.Documentation } [Test] + [Ignore("mcs bug (Unexpected symbol `operator', expecting `identifier' or `this')")] public void OperatorPlusWithDeclaringType() { ParseUtilCSharp.AssertDocumentationReference( @@ -256,6 +273,7 @@ namespace ICSharpCode.NRefactory.Documentation } [Test] + [Ignore("mcs bug (Unexpected symbol `explicit', expecting `identifier' or `this')")] public void ExplicitOperatorWithParameterListAndDeclaringType() { ParseUtilCSharp.AssertDocumentationReference( diff --git a/ICSharpCode.NRefactory.Tests/TypeSystem/GetAllBaseTypesTest.cs b/ICSharpCode.NRefactory.Tests/TypeSystem/GetAllBaseTypesTest.cs index 2318208a55..1600e5372a 100644 --- a/ICSharpCode.NRefactory.Tests/TypeSystem/GetAllBaseTypesTest.cs +++ b/ICSharpCode.NRefactory.Tests/TypeSystem/GetAllBaseTypesTest.cs @@ -21,6 +21,7 @@ using System.Collections; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; +using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.TypeSystem.Implementation; using NUnit.Framework; @@ -31,12 +32,43 @@ namespace ICSharpCode.NRefactory.TypeSystem [TestFixture] public class GetAllBaseTypesTest { + const string corlib = @" +namespace System { + class Object {} + class ValueType {} + class String : System.Collections.Generic.IEnumerable, IComparable {} + class Array : System.Collections.IList, ICloneable {} + + interface ICloneable {} + interface IComparable {} + struct Int32 {} + struct Char {} +} +namespace System.Collections { + interface IEnumerable {} + interface ICollection : IEnumerable {} + interface IList : ICollection {} + interface IDictionary : ICollection {} +} +namespace System.Collections.Generic { + interface IEnumerable : IEnumerable {} + interface ICollection : IEnumerable {} + interface IList : ICollection {} + interface IDictionary : ICollection> {} + + class List : IList, IList {} + class Dictionary : IDictionary, IDictionary {} + struct KeyValuePair {} +} +"; + ICompilation compilation; [SetUp] public void SetUp() { - compilation = new SimpleCompilation(CecilLoaderTests.Mscorlib); + var parsedFile = new CSharpParser().Parse(corlib, "corlib.cs").ToTypeSystem(); + compilation = new CSharpProjectContent().SetAssemblyName("mscorlib").UpdateProjectContent(null, parsedFile).CreateCompilation(); } IType[] GetAllBaseTypes(Type type) @@ -46,7 +78,7 @@ namespace ICSharpCode.NRefactory.TypeSystem IType[] GetTypes(params Type[] types) { - return types.Select(t => compilation.FindType(t)).OrderBy(t => t.ReflectionName).ToArray();; + return types.Select(t => compilation.FindType(t)).OrderBy(t => t.ReflectionName).ToArray(); } ITypeDefinition Resolve(IUnresolvedTypeDefinition typeDef) @@ -58,24 +90,26 @@ namespace ICSharpCode.NRefactory.TypeSystem public void ObjectBaseTypes() { Assert.AreEqual(GetTypes(typeof(object)), GetAllBaseTypes(typeof(object))); + + Assert.That(compilation.FindType(KnownTypeCode.Object).DirectBaseTypes, Is.Empty); } [Test] public void StringBaseTypes() { - Assert.AreEqual(GetTypes(typeof(string), typeof(object), typeof(IComparable), typeof(ICloneable), typeof(IConvertible), - typeof(IComparable), typeof(IEquatable), typeof(IEnumerable), typeof(IEnumerable)), + Assert.AreEqual(GetTypes(typeof(string), typeof(object), + typeof(IComparable), typeof(IEnumerable), typeof(IEnumerable)), GetAllBaseTypes(typeof(string))); } [Test] - [Ignore("Produces different results in .NET 4.5 due to new read-only interfaces")] public void ArrayOfString() { - Assert.AreEqual(GetTypes(typeof(string[]), typeof(Array), typeof(object), - typeof(IList), typeof(ICollection), typeof(IEnumerable), - typeof(IList), typeof(ICollection), typeof(IEnumerable), - typeof(IStructuralEquatable), typeof(IStructuralComparable), typeof(ICloneable)), + var expectedTypes = GetTypes( + typeof(string[]), typeof(Array), typeof(object), + typeof(IList), typeof(ICollection), typeof(IEnumerable), typeof(ICloneable), + typeof(IList), typeof(ICollection), typeof(IEnumerable)); + Assert.AreEqual(expectedTypes, GetAllBaseTypes(typeof(string[]))); } @@ -83,8 +117,7 @@ namespace ICSharpCode.NRefactory.TypeSystem public unsafe void ArrayOfPointers() { Assert.AreEqual(GetTypes(typeof(int*[]), typeof(Array), typeof(object), - typeof(IList), typeof(ICollection), typeof(IEnumerable), - typeof(IStructuralEquatable), typeof(IStructuralComparable), typeof(ICloneable)), + typeof(IList), typeof(ICollection), typeof(IEnumerable), typeof(ICloneable)), GetAllBaseTypes(typeof(int*[]))); } @@ -92,8 +125,7 @@ namespace ICSharpCode.NRefactory.TypeSystem public void MultidimensionalArrayOfString() { Assert.AreEqual(GetTypes(typeof(string[,]), typeof(Array), typeof(object), - typeof(IList), typeof(ICollection), typeof(IEnumerable), - typeof(IStructuralEquatable), typeof(IStructuralComparable), typeof(ICloneable)), + typeof(IList), typeof(ICollection), typeof(IEnumerable), typeof(ICloneable)), GetAllBaseTypes(typeof(string[,]))); } @@ -175,7 +207,6 @@ namespace ICSharpCode.NRefactory.TypeSystem } [Test] - [Ignore("Produces different results in .NET 4.5 due to new read-only interfaces")] public void BaseTypesOfListOfString() { Assert.AreEqual( @@ -186,7 +217,6 @@ namespace ICSharpCode.NRefactory.TypeSystem } [Test] - [Ignore("Produces different results in .NET 4.5 due to new read-only interfaces")] public void BaseTypesOfUnboundDictionary() { Assert.AreEqual( @@ -198,15 +228,12 @@ namespace ICSharpCode.NRefactory.TypeSystem typeof(ICollection).FullName, typeof(IDictionary).FullName, typeof(IEnumerable).FullName, - typeof(object).FullName, - typeof(IDeserializationCallback).FullName, - typeof(ISerializable).FullName, + typeof(object).FullName }, - GetAllBaseTypes(typeof(Dictionary<,>)).Select(t => t.ReflectionName).ToArray()); + GetAllBaseTypes(typeof(Dictionary<,>)).Select(t => t.ReflectionName).OrderBy(n => n).ToArray()); } [Test] - [Ignore("Produces different results in .NET 4.5 due to new read-only interfaces")] public void BaseTypeDefinitionsOfListOfString() { Assert.AreEqual( @@ -217,12 +244,10 @@ namespace ICSharpCode.NRefactory.TypeSystem } [Test] - [Ignore("Produces different results in .NET 4.5 due to new read-only interfaces")] public void BaseTypeDefinitionsOfStringArray() { Assert.AreEqual( - GetTypes(typeof(Array), typeof(object), - typeof(ICloneable), typeof(IStructuralComparable), typeof(IStructuralEquatable), + GetTypes(typeof(Array), typeof(object), typeof(ICloneable), typeof(IList), typeof(ICollection), typeof(IEnumerable), typeof(IEnumerable<>), typeof(ICollection<>), typeof(IList<>)), compilation.FindType(typeof(string[])).GetAllBaseTypeDefinitions().OrderBy(t => t.ReflectionName).ToArray()); diff --git a/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.TestCase.cs b/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.TestCase.cs index 2efddd385a..f95b5a5d3f 100644 --- a/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.TestCase.cs +++ b/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.TestCase.cs @@ -44,6 +44,9 @@ namespace ICSharpCode.NRefactory.TypeSystem.TestCase public class ParamsAttribute : Attribute { public ParamsAttribute(params object[] x) {} + + [Params(Property = new string[] { "a", "b" })] + public string[] Property { get; set; } } [Double(1)] @@ -291,4 +294,18 @@ namespace ICSharpCode.NRefactory.TypeSystem.TestCase public class ClassThatImplementsEventExplicitly : IHasEvent { event EventHandler IHasEvent.Event { add {} remove {} } } + + public interface IShadowTestBase { + void Method(); + int this[int i] { get; set; } + int Prop { get; set; } + event EventHandler Evt; + } + + public interface IShadowTestDerived : IShadowTestBase { + new void Method(); + new int this[int i] { get; set; } + new int Prop { get; set; } + new event EventHandler Evt; + } } diff --git a/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs b/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs index 08ce99a935..f8e41711f0 100644 --- a/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs +++ b/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs @@ -821,6 +821,23 @@ namespace ICSharpCode.NRefactory.TypeSystem Assert.AreEqual("Test", rr.Input.ConstantValue); } + [Test] + public void ParamsAttribute_Property() + { + ITypeDefinition type = GetTypeDefinition(typeof(ParamsAttribute)); + IProperty prop = type.Properties.Single(p => p.Name == "Property"); + var attr = prop.Attributes.Single(); + Assert.AreEqual(type, attr.AttributeType); + + var normalArguments = ((ArrayCreateResolveResult)attr.PositionalArguments.Single()).InitializerElements; + Assert.AreEqual(0, normalArguments.Count); + + var namedArg = attr.NamedArguments.Single(); + Assert.AreEqual(prop, namedArg.Key); + var arrayElements = ((ArrayCreateResolveResult)namedArg.Value).InitializerElements; + Assert.AreEqual(2, arrayElements.Count); + } + [Test] public void DoubleAttribute_ImplicitNumericConversion() { @@ -978,6 +995,7 @@ namespace ICSharpCode.NRefactory.TypeSystem public void ExplicitIndexerImplementationReturnsTheCorrectMembers() { ITypeDefinition type = GetTypeDefinition(typeof(ClassThatImplementsIndexersExplicitly)); + Assert.That(type.Properties.All(p => p.EntityType == EntityType.Indexer)); Assert.That(type.Properties.All(p => p.ImplementedInterfaceMembers.Count == 1)); Assert.That(type.Properties.All(p => p.Getter.ImplementedInterfaceMembers.Count == 1)); Assert.That(type.Properties.All(p => p.Setter.ImplementedInterfaceMembers.Count == 1)); @@ -1094,5 +1112,25 @@ namespace ICSharpCode.NRefactory.TypeSystem Assert.That(evt.AddAccessor.ImplementedInterfaceMembers.Select(p => p.ReflectionName).ToList(), Is.EqualTo(new[] { "ICSharpCode.NRefactory.TypeSystem.TestCase.IHasEvent.add_Event" })); Assert.That(evt.RemoveAccessor.ImplementedInterfaceMembers.Select(p => p.ReflectionName).ToList(), Is.EqualTo(new[] { "ICSharpCode.NRefactory.TypeSystem.TestCase.IHasEvent.remove_Event" })); } + + [Test] + public void MembersDeclaredInDerivedInterfacesDoNotImplementBaseMembers() { + ITypeDefinition type = GetTypeDefinition(typeof(IShadowTestDerived)); + var method = type.Methods.Single(m => m.Name == "Method"); + var indexer = type.Properties.Single(p => p.IsIndexer); + var prop = type.Properties.Single(p => p.Name == "Prop"); + var evt = type.Events.Single(e => e.Name == "Evt"); + + Assert.That(method.ImplementedInterfaceMembers, Is.Empty); + Assert.That(indexer.ImplementedInterfaceMembers, Is.Empty); + Assert.That(indexer.Getter.ImplementedInterfaceMembers, Is.Empty); + Assert.That(indexer.Setter.ImplementedInterfaceMembers, Is.Empty); + Assert.That(prop.ImplementedInterfaceMembers, Is.Empty); + Assert.That(prop.Getter.ImplementedInterfaceMembers, Is.Empty); + Assert.That(prop.Setter.ImplementedInterfaceMembers, Is.Empty); + Assert.That(evt.ImplementedInterfaceMembers, Is.Empty); + Assert.That(evt.AddAccessor.ImplementedInterfaceMembers, Is.Empty); + Assert.That(evt.RemoveAccessor.ImplementedInterfaceMembers, Is.Empty); + } } } diff --git a/ICSharpCode.NRefactory/Properties/GlobalAssemblyInfo.cs b/ICSharpCode.NRefactory/Properties/GlobalAssemblyInfo.cs index f2381b878e..3c6b82ac4b 100644 --- a/ICSharpCode.NRefactory/Properties/GlobalAssemblyInfo.cs +++ b/ICSharpCode.NRefactory/Properties/GlobalAssemblyInfo.cs @@ -23,4 +23,4 @@ using System.Runtime.InteropServices; // [AssemblyFileVersion] is the version of the NuGet package, // should follow http://semver.org/ rules -[assembly: AssemblyFileVersion("5.0.1")] +[assembly: AssemblyFileVersion("5.1.0")] diff --git a/ICSharpCode.NRefactory/Semantics/Conversion.cs b/ICSharpCode.NRefactory/Semantics/Conversion.cs index a68a7f407f..fc5889d55f 100644 --- a/ICSharpCode.NRefactory/Semantics/Conversion.cs +++ b/ICSharpCode.NRefactory/Semantics/Conversion.cs @@ -185,6 +185,14 @@ namespace ICSharpCode.NRefactory.Semantics get { return type == 0; } } + public override bool IsNullLiteralConversion { + get { return type == 1; } + } + + public override bool IsConstantExpressionConversion { + get { return type == 2; } + } + public override bool IsReferenceConversion { get { return type == 3; } } @@ -365,6 +373,14 @@ namespace ICSharpCode.NRefactory.Semantics get { return false; } } + public virtual bool IsNullLiteralConversion { + get { return false; } + } + + public virtual bool IsConstantExpressionConversion { + get { return false; } + } + public virtual bool IsNumericConversion { get { return false; } } diff --git a/ICSharpCode.NRefactory/Semantics/MemberResolveResult.cs b/ICSharpCode.NRefactory/Semantics/MemberResolveResult.cs index f861920b45..3a2430856f 100644 --- a/ICSharpCode.NRefactory/Semantics/MemberResolveResult.cs +++ b/ICSharpCode.NRefactory/Semantics/MemberResolveResult.cs @@ -28,7 +28,7 @@ namespace ICSharpCode.NRefactory.Semantics /// /// Represents the result of a member invocation. /// Used for field/property/event access. - /// Also, derives from MemberResolveResult. + /// Also, derives from MemberResolveResult. /// public class MemberResolveResult : ResolveResult { diff --git a/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs b/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs index 8b97a43897..e4322822f9 100644 --- a/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs +++ b/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs @@ -98,6 +98,21 @@ namespace ICSharpCode.NRefactory.TypeSystem // Enable interning by default. this.InterningProvider = new SimpleInterningProvider(); } + + /// + /// Creates a nested CecilLoader for lazy-loading. + /// + private CecilLoader(CecilLoader loader) + { + // use a shared typeSystemTranslationTable + this.typeSystemTranslationTable = loader.typeSystemTranslationTable; + this.IncludeInternalMembers = loader.IncludeInternalMembers; + this.LazyLoad = loader.LazyLoad; + this.currentModule = loader.currentModule; + this.currentAssembly = loader.currentAssembly; + // don't use interning - the interning provider is most likely not thread-safe + // don't use cancellation for delay-loaded members + } #region Load From AssemblyDefinition /// @@ -124,6 +139,7 @@ namespace ICSharpCode.NRefactory.TypeSystem } this.currentAssembly = new CecilUnresolvedAssembly(assemblyDefinition.Name.Name, this.DocumentationProvider); + currentAssembly.Location = assemblyDefinition.MainModule.FullyQualifiedName; currentAssembly.AssemblyAttributes.AddRange(assemblyAttributes); currentAssembly.ModuleAttributes.AddRange(assemblyAttributes); @@ -133,13 +149,15 @@ namespace ICSharpCode.NRefactory.TypeSystem int typeParameterCount; string name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(type.Name, out typeParameterCount); var typeRef = new GetClassTypeReference(GetAssemblyReference(type.Scope), type.Namespace, name, typeParameterCount); - typeRef = this.InterningProvider.Intern(typeRef); + if (this.InterningProvider != null) + typeRef = this.InterningProvider.Intern(typeRef); var key = new FullNameAndTypeParameterCount(type.Namespace, name, typeParameterCount); currentAssembly.AddTypeForwarder(key, typeRef); } } // Create and register all types: + CecilLoader cecilLoaderCloneForLazyLoading = LazyLoad ? new CecilLoader(this) : null; List cecilTypeDefs = new List(); List typeDefs = new List(); foreach (ModuleDefinition module in assemblyDefinition.Modules) { @@ -150,10 +168,14 @@ namespace ICSharpCode.NRefactory.TypeSystem if (name.Length == 0) continue; - var t = CreateTopLevelTypeDefinition(td); - cecilTypeDefs.Add(td); - typeDefs.Add(t); - currentAssembly.AddTypeDefinition(t); + if (this.LazyLoad) { + currentAssembly.AddTypeDefinition(new LazyCecilTypeDefinition(cecilLoaderCloneForLazyLoading, td)); + } else { + var t = CreateTopLevelTypeDefinition(td); + cecilTypeDefs.Add(td); + typeDefs.Add(t); + currentAssembly.AddTypeDefinition(t); + } } } } @@ -162,8 +184,7 @@ namespace ICSharpCode.NRefactory.TypeSystem InitTypeDefinition(cecilTypeDefs[i], typeDefs[i]); } - if (HasCecilReferences) - typeSystemTranslationTable[this.currentAssembly] = assemblyDefinition; + RegisterCecilObject(this.currentAssembly, assemblyDefinition); var result = this.currentAssembly; this.currentAssembly = null; @@ -229,8 +250,7 @@ namespace ICSharpCode.NRefactory.TypeSystem var param = new ReaderParameters { AssemblyResolver = new DummyAssemblyResolver() }; AssemblyDefinition asm = AssemblyDefinition.ReadAssembly(fileName, param); var result = LoadAssembly(asm); - if (HasCecilReferences) - typeSystemTranslationTable[result] = asm; + RegisterCecilObject(result, asm); return result; } @@ -1175,7 +1195,8 @@ namespace ICSharpCode.NRefactory.TypeSystem public KeyValuePair ReadNamedArg(IType attributeType) { EntityType memberType; - switch (ReadByte()) { + var b = ReadByte(); + switch (b) { case 0x53: memberType = EntityType.Field; break; @@ -1183,7 +1204,7 @@ namespace ICSharpCode.NRefactory.TypeSystem memberType = EntityType.Property; break; default: - throw new NotSupportedException(); + throw new NotSupportedException(string.Format("Custom member type 0x{0:x} is not supported.", b)); } IType type = ReadCustomAttributeFieldOrPropType(); string name = ReadSerString(); @@ -1196,7 +1217,7 @@ namespace ICSharpCode.NRefactory.TypeSystem } return new KeyValuePair(member, val); } - + IType ReadCustomAttributeFieldOrPropType() { ICompilation compilation = currentResolvedAssembly.Compilation; @@ -1499,12 +1520,11 @@ namespace ICSharpCode.NRefactory.TypeSystem td.AddDefaultConstructorIfRequired = (td.Kind == TypeKind.Struct || td.Kind == TypeKind.Enum); InitMembers(typeDefinition, td, td.Members); - if (HasCecilReferences) - typeSystemTranslationTable[td] = typeDefinition; if (this.InterningProvider != null) { td.ApplyInterningProvider(this.InterningProvider); } td.Freeze(); + RegisterCecilObject(td, typeDefinition); } void InitBaseTypes(TypeDefinition typeDefinition, IList baseTypes) @@ -1656,7 +1676,18 @@ namespace ICSharpCode.NRefactory.TypeSystem bool getterVisible = property.GetMethod != null && IsVisible(property.GetMethod.Attributes); bool setterVisible = property.SetMethod != null && IsVisible(property.SetMethod.Attributes); if (getterVisible || setterVisible) { - EntityType type = property.Name == defaultMemberName ? EntityType.Indexer : EntityType.Property; + EntityType type = EntityType.Property; + if (property.HasParameters) { + // Try to detect indexer: + if (property.Name == defaultMemberName) { + type = EntityType.Indexer; // normal indexer + } else if (property.Name.EndsWith(".Item", StringComparison.Ordinal) && (property.GetMethod ?? property.SetMethod).HasOverrides) { + // explicit interface implementation of indexer + type = EntityType.Indexer; + // We can't really tell parameterized properties and indexers apart in this case without + // resolving the interface, so we rely on the "Item" naming convention instead. + } + } members.Add(ReadProperty(property, td, type)); } } @@ -1707,12 +1738,11 @@ namespace ICSharpCode.NRefactory.TypeSystem loader.AddAttributes(typeDefinition, this); flags[FlagHasExtensionMethods] = HasExtensionAttribute(typeDefinition); - if (loader.HasCecilReferences) - loader.typeSystemTranslationTable[this] = typeDefinition; if (loader.InterningProvider != null) { this.ApplyInterningProvider(loader.InterningProvider); } this.Freeze(); + loader.RegisterCecilObject(this, typeDefinition); } public override string Namespace { @@ -1720,6 +1750,15 @@ namespace ICSharpCode.NRefactory.TypeSystem set { throw new NotSupportedException(); } } + public override string FullName { + // This works because LazyCecilTypeDefinition is only used for top-level types + get { return cecilTypeDef.FullName; } + } + + public override string ReflectionName { + get { return cecilTypeDef.FullName; } + } + public TypeKind Kind { get { return kind; } } @@ -2123,14 +2162,24 @@ namespace ICSharpCode.NRefactory.TypeSystem void FinishReadMember(AbstractUnresolvedMember member, object cecilDefinition) { - member.ApplyInterningProvider(this.InterningProvider); + if (this.InterningProvider != null) + member.ApplyInterningProvider(this.InterningProvider); member.Freeze(); - if (HasCecilReferences) - typeSystemTranslationTable[member] = cecilDefinition; + RegisterCecilObject(member, cecilDefinition); } #region Type system translation table - Dictionary typeSystemTranslationTable; + readonly Dictionary typeSystemTranslationTable; + + void RegisterCecilObject(object typeSystemObject, object cecilObject) + { + if (typeSystemTranslationTable != null) { + // When lazy-loading, the dictionary might be shared between multiple cecil-loaders that are used concurrently + lock (typeSystemTranslationTable) { + typeSystemTranslationTable[typeSystemObject] = cecilObject; + } + } + } T InternalGetCecilObject (object typeSystemObject) where T : class { @@ -2139,8 +2188,10 @@ namespace ICSharpCode.NRefactory.TypeSystem if (!HasCecilReferences) throw new NotSupportedException ("This instance contains no cecil references."); object result; - if (!typeSystemTranslationTable.TryGetValue (typeSystemObject, out result)) - return null; + lock (typeSystemTranslationTable) { + if (!typeSystemTranslationTable.TryGetValue (typeSystemObject, out result)) + return null; + } return result as T; } diff --git a/ICSharpCode.NRefactory/TypeSystem/IAssembly.cs b/ICSharpCode.NRefactory/TypeSystem/IAssembly.cs index 9d5d5c2dfd..1c98a0498e 100644 --- a/ICSharpCode.NRefactory/TypeSystem/IAssembly.cs +++ b/ICSharpCode.NRefactory/TypeSystem/IAssembly.cs @@ -30,7 +30,13 @@ namespace ICSharpCode.NRefactory.TypeSystem /// Gets the assembly name (short name). /// string AssemblyName { get; } - + + /// + /// Gets the path to the assembly location. + /// For projects it is the same as the output path. + /// + string Location { get; } + /// /// Gets the list of all assembly attributes in the project. /// diff --git a/ICSharpCode.NRefactory/TypeSystem/INamespace.cs b/ICSharpCode.NRefactory/TypeSystem/INamespace.cs index 487e4fc3e3..b3631fe060 100644 --- a/ICSharpCode.NRefactory/TypeSystem/INamespace.cs +++ b/ICSharpCode.NRefactory/TypeSystem/INamespace.cs @@ -62,6 +62,11 @@ namespace ICSharpCode.NRefactory.TypeSystem /// IEnumerable Types { get; } + /// + /// Gets the assemblies that contribute types to this namespace (or to child namespaces). + /// + IEnumerable ContributingAssemblies { get; } + /// /// Gets a direct child namespace by its short name. /// Returns null when the namespace cannot be found. diff --git a/ICSharpCode.NRefactory/TypeSystem/IProjectContent.cs b/ICSharpCode.NRefactory/TypeSystem/IProjectContent.cs index 7acfb81de2..4a731c42ce 100644 --- a/ICSharpCode.NRefactory/TypeSystem/IProjectContent.cs +++ b/ICSharpCode.NRefactory/TypeSystem/IProjectContent.cs @@ -69,7 +69,12 @@ namespace ICSharpCode.NRefactory.TypeSystem /// Changes the assembly name of this project content. /// IProjectContent SetAssemblyName(string newAssemblyName); - + + /// + /// Changes the location of this project content. + /// + IProjectContent SetLocation(string newLocation); + /// /// Add assembly references to this project content. /// diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractResolvedMember.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractResolvedMember.cs index d027f0b21d..0ff0b25a58 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractResolvedMember.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractResolvedMember.cs @@ -76,7 +76,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation result.Add(member); } return result.ToArray(); - } else if (unresolved.IsStatic) { + } else if (unresolved.IsStatic || DeclaringTypeDefinition == null || DeclaringTypeDefinition.Kind == TypeKind.Interface) { return EmptyList.Instance; } else { // TODO: implement interface member mappings correctly diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedMethod.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedMethod.cs index 6a20f62c21..bb8744fc5c 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedMethod.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedMethod.cs @@ -19,6 +19,8 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text; + using ICSharpCode.NRefactory.Semantics; namespace ICSharpCode.NRefactory.TypeSystem.Implementation @@ -217,6 +219,29 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation } } + public override string ToString() + { + StringBuilder b = new StringBuilder("["); + b.Append(this.EntityType); + b.Append(' '); + b.Append(this.DeclaringType.ReflectionName); + b.Append('.'); + b.Append(this.Name); + if (this.TypeParameters.Count > 0) { + b.Append("``"); + b.Append(this.TypeParameters.Count); + } + b.Append('('); + for (int i = 0; i < this.Parameters.Count; i++) { + if (i > 0) b.Append(", "); + b.Append(this.Parameters[i].ToString()); + } + b.Append("):"); + b.Append(this.ReturnType.ReflectionName); + b.Append(']'); + return b.ToString(); + } + /// /// Gets a dummy constructor for the specified compilation. /// diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs index e53cb1422e..d74c1cae84 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs @@ -69,7 +69,18 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation assemblyName = value; } } - + + string location; + public string Location { + get { + return location; + } + set { + FreezableHelper.ThrowIfFrozen(this); + location = value; + } + } + public IList AssemblyAttributes { get { return assemblyAttributes; } } @@ -367,6 +378,10 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation get { return parentNamespace; } } + IEnumerable INamespace.ContributingAssemblies { + get { return new [] { assembly }; } + } + IEnumerable INamespace.ChildNamespaces { get { return childNamespaces; } } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/MergedNamespace.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/MergedNamespace.cs index 4363253f05..1ab85bcd4e 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/MergedNamespace.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/MergedNamespace.cs @@ -20,7 +20,6 @@ using System; using System.Collections.Generic; using System.Globalization; using System.Linq; -using System.Text; using ICSharpCode.NRefactory.Utils; namespace ICSharpCode.NRefactory.TypeSystem.Implementation @@ -96,6 +95,10 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation get { return compilation; } } + public IEnumerable ContributingAssemblies { + get { return namespaces.SelectMany(ns => ns.ContributingAssemblies); } + } + public IEnumerable ChildNamespaces { get { return GetChildNamespaces().Values; } } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMethod.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMethod.cs index 79b5d10fa7..a84201047d 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMethod.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMethod.cs @@ -195,6 +195,9 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation b.Append(this.TypeArguments[i].ReflectionName); } b.Append(']'); + } else if (this.TypeParameters.Count > 0) { + b.Append("``"); + b.Append(this.TypeParameters.Count); } b.Append('('); for (int i = 0; i < this.Parameters.Count; i++) { diff --git a/Packages/ICSharpCode.NRefactory.nuspec b/Packages/ICSharpCode.NRefactory.nuspec index 770e9c0094..230579c4ec 100644 --- a/Packages/ICSharpCode.NRefactory.nuspec +++ b/Packages/ICSharpCode.NRefactory.nuspec @@ -2,9 +2,9 @@ ICSharpCode.NRefactory - 5.0.1 + 5.1.0 NRefactory - Daniel Grunwald, Mike Krüger + Daniel Grunwald, Mike Krüger, Erik Källén Daniel Grunwald http://www.opensource.org/licenses/mit-license.php https://github.com/icsharpcode/NRefactory/