From 382f24666a17dc68986b009cf292b172e1e35cd9 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 25 Apr 2015 22:09:02 +0200 Subject: [PATCH] Add NRefactory 5.5.1 source code. For the moment, we need to use a customized Mono.Cecil in ILSpy (thread-safe branch + System.Runtime fix), and build NRefactory against that. Add script for building ICSharpCode.Decompiler NuGet package. This package is built against the NuGet Mono.Cecil and NRefactory packages, not against the sourcecode included in this repository. --- .../ICSharpCode.Decompiler.csproj | 23 +- .../Tests/ICSharpCode.Decompiler.Tests.csproj | 14 +- ICSharpCode.Decompiler/Tests/packages.config | 2 - ILSpy.AddIn/ILSpy.AddIn.csproj | 14 +- ILSpy.AddIn/packages.config | 2 - ILSpy.sln | 23 +- ILSpy/ILSpy.csproj | 18 +- ILSpy/VB/ILSpyEnvironmentProvider.cs | 2 +- ILSpy/packages.config | 5 - NRefactory/.gitattributes | 5 +- NRefactory/.gitignore | 1 + .../Analysis/AnnotationNames.cs | 48 + .../Analysis/ControlFlow.cs | 803 + .../DeclarationSpace/LocalDeclarationSpace.cs | 157 + .../LocalDeclarationSpaceVisitor.cs | 138 + .../Analysis/DefiniteAssignmentAnalysis.cs | 759 + .../Analysis/NullValueAnalysis.cs | 2215 +++ .../Analysis/NullValueStatus.cs | 84 + .../Analysis/ReachabilityAnalysis.cs | 209 + .../Analysis/SemanticHighlightingVisitor.cs | 685 + .../Ast/AstNode.cs | 1042 + .../Ast/AstNodeCollection.cs | 231 + .../Ast/AstType.cs | 280 + .../Ast/CSharpModifierToken.cs | 218 + .../Ast/CSharpTokenNode.cs | 131 + .../Ast/CSharpUtil.cs | 180 + .../Ast/ComposedType.cs | 230 + .../Ast/DepthFirstAstVisitor.cs | 1849 ++ .../Ast/DocumentationReference.cs | 149 + .../Ast/ErrorNode.cs | 88 + .../Expressions/AnonymousMethodExpression.cs | 117 + .../AnonymousTypeCreateExpression.cs | 91 + .../Ast/Expressions/ArrayCreateExpression.cs | 80 + .../Expressions/ArrayInitializerExpression.cs | 192 + .../Ast/Expressions/AsExpression.cs | 150 + .../Ast/Expressions/AssignmentExpression.cs | 304 + .../Expressions/BaseReferenceExpression.cs | 71 + .../Expressions/BinaryOperatorExpression.cs | 325 + .../Ast/Expressions/CastExpression.cs | 152 + .../Ast/Expressions/CheckedExpression.cs | 83 + .../Ast/Expressions/ConditionalExpression.cs | 162 + .../Ast/Expressions/DefaultValueExpression.cs | 84 + .../Ast/Expressions/DirectionExpression.cs | 89 + .../Ast/Expressions/ErrorExpression.cs | 129 + .../Ast/Expressions/Expression.cs | 230 + .../Ast/Expressions/IdentifierExpression.cs | 93 + .../Ast/Expressions/IndexerExpression.cs | 92 + .../Ast/Expressions/InvocationExpression.cs | 92 + .../Ast/Expressions/IsExpression.cs | 150 + .../Ast/Expressions/LambdaExpression.cs | 89 + .../Expressions/MemberReferenceExpression.cs | 119 + .../Expressions/NamedArgumentExpression.cs | 87 + .../Ast/Expressions/NamedExpression.cs | 97 + .../Expressions/NullReferenceExpression.cs | 83 + .../Ast/Expressions/ObjectCreateExpression.cs | 104 + .../Expressions/ParenthesizedExpression.cs | 98 + .../Expressions/PointerReferenceExpression.cs | 90 + .../Ast/Expressions/PrimitiveExpression.cs | 162 + .../Ast/Expressions/QueryExpression.cs | 655 + .../Ast/Expressions/SizeOfExpression.cs | 83 + .../Ast/Expressions/StackAllocExpression.cs | 79 + .../Expressions/ThisReferenceExpression.cs | 71 + .../Ast/Expressions/TypeOfExpression.cs | 84 + .../Expressions/TypeReferenceExpression.cs | 64 + .../Expressions/UnaryOperatorExpression.cs | 181 + .../Ast/Expressions/UncheckedExpression.cs | 83 + .../Ast/Expressions/UndocumentedExpression.cs | 105 + .../Ast/GeneralScope/Attribute.cs | 93 + .../Ast/GeneralScope/AttributeSection.cs | 174 + .../Ast/GeneralScope/Comment.cs | 140 + .../Ast/GeneralScope/Constraint.cs | 85 + .../Ast/GeneralScope/DelegateDeclaration.cs | 92 + .../GeneralScope/ExternAliasDeclaration.cs | 91 + .../Ast/GeneralScope/NamespaceDeclaration.cs | 158 + .../Ast/GeneralScope/NewLineNode.cs | 91 + .../Ast/GeneralScope/PreProcessorDirective.cs | 205 + .../Ast/GeneralScope/TextNode.cs | 94 + .../Ast/GeneralScope/TypeDeclaration.cs | 145 + .../GeneralScope/TypeParameterDeclaration.cs | 113 + .../Ast/GeneralScope/UsingAliasDeclaration.cs | 107 + .../Ast/GeneralScope/UsingDeclaration.cs | 122 + .../Ast/GeneralScope/WhitespaceNode.cs | 91 + .../Ast/IAstVisitor.cs | 418 + .../Ast/Identifier.cs | 173 + .../Ast/IdentifierExpressionBackreference.cs | 54 + .../Ast/MemberType.cs | 150 + .../Ast/Modifiers.cs | 65 + .../Ast/NodeType.cs | 56 + .../Ast/ObservableAstVisitor.cs | 857 + .../Ast/PrimitiveType.cs | 163 + .../Ast/Roles.cs | 96 + .../Ast/SimpleType.cs | 169 + .../Ast/Statements/BlockStatement.cs | 164 + .../Ast/Statements/BreakStatement.cs | 65 + .../Ast/Statements/CheckedStatement.cs | 75 + .../Ast/Statements/ContinueStatement.cs | 65 + .../Ast/Statements/DoWhileStatement.cs | 99 + .../Ast/Statements/EmptyStatement.cs | 72 + .../Ast/Statements/ExpressionStatement.cs | 73 + .../Ast/Statements/FixedStatement.cs | 85 + .../Ast/Statements/ForStatement.cs | 97 + .../Ast/Statements/ForeachStatement.cs | 108 + .../Ast/Statements/GotoStatement.cs | 178 + .../Ast/Statements/IfElseStatement.cs | 103 + .../Ast/Statements/LabelStatement.cs | 73 + .../Ast/Statements/LockStatement.cs | 79 + .../Ast/Statements/ReturnStatement.cs | 79 + .../Ast/Statements/Statement.cs | 132 + .../Ast/Statements/SwitchStatement.cs | 230 + .../Ast/Statements/ThrowStatement.cs | 79 + .../Ast/Statements/TryCatchStatement.cs | 241 + .../Ast/Statements/UncheckedStatement.cs | 75 + .../Ast/Statements/UnsafeStatement.cs | 66 + .../Ast/Statements/UsingStatement.cs | 83 + .../VariableDeclarationStatement.cs | 90 + .../Ast/Statements/WhileStatement.cs | 89 + .../Ast/Statements/YieldBreakStatement.cs | 70 + .../Ast/Statements/YieldReturnStatement.cs | 75 + .../Ast/SyntaxExtensions.cs | 45 + .../Ast/SyntaxTree.cs | 193 + .../Ast/TokenRole.cs | 61 + .../Ast/TypeMembers/Accessor.cs | 117 + .../Ast/TypeMembers/ConstructorDeclaration.cs | 190 + .../Ast/TypeMembers/DestructorDeclaration.cs | 76 + .../Ast/TypeMembers/EntityDeclaration.cs | 117 + .../Ast/TypeMembers/EnumMemberDeclaration.cs | 72 + .../Ast/TypeMembers/EventDeclaration.cs | 152 + .../Ast/TypeMembers/FieldDeclaration.cs | 79 + .../Ast/TypeMembers/FixedFieldDeclaration.cs | 71 + .../TypeMembers/FixedVariableInitializer.cs | 105 + .../Ast/TypeMembers/IndexerDeclaration.cs | 122 + .../Ast/TypeMembers/MethodDeclaration.cs | 104 + .../Ast/TypeMembers/OperatorDeclaration.cs | 268 + .../Ast/TypeMembers/ParameterDeclaration.cs | 147 + .../Ast/TypeMembers/PropertyDeclaration.cs | 92 + .../Ast/TypeMembers/VariableInitializer.cs | 174 + .../CSharpProjectContent.cs | 289 + .../CombineQueryExpressions.cs | 216 + .../Completion/CSharpCompletionEngine.cs | 3807 ++++ .../Completion/CSharpCompletionEngineBase.cs | 907 + .../CSharpParameterCompletionEngine.cs | 408 + .../Completion/CompletionDataWrapper.cs | 328 + .../Completion/ICompletionContextProvider.cs | 214 + .../Completion/ICompletionDataFactory.cs | 89 + .../IParameterCompletionDataFactory.cs | 54 + .../Formatter/CSharpFormatter.cs | 159 + .../Formatter/CSharpFormattingOptions.cs | 1039 + .../Formatter/ConstructFixer.cs | 514 + .../Formatter/FormattingChanges.cs | 169 + .../Formatter/FormattingOptionsFactory.cs | 446 + .../Formatter/FormattingVisitor.cs | 662 + .../FormattingVisitor_Expressions.cs | 735 + .../Formatter/FormattingVisitor_Global.cs | 351 + .../Formatter/FormattingVisitor_Query.cs | 124 + .../Formatter/FormattingVisitor_Statements.cs | 517 + .../FormattingVisitor_TypeMembers.cs | 477 + .../Formatter/GeneratedCodeSettings.cs | 216 + .../Formatter/Indent.cs | 246 + .../Formatter/TextEditorOptions.cs | 116 + .../ICSharpCode.NRefactory.CSharp.csproj | 420 + .../IndentEngine/CSharpIndentEngine.cs | 557 + .../IndentEngine/CacheIndentEngine.cs | 627 + .../IndentEngine/IDocumentIndentEngine.cs | 108 + .../IndentEngine/IStateMachineIndentEngine.cs | 60 + .../IndentEngine/IndentState.cs | 2017 ++ .../NullIStateMachineIndentEngine.cs | 215 + .../IndentEngine/TextPasteIndentEngine.cs | 632 + .../IntroduceQueryExpressions.cs | 386 + .../NameLookupMode.cs | 47 + .../OutputVisitor/CSharpAmbience.cs | 312 + .../OutputVisitor/CSharpOutputVisitor.cs | 2401 +++ .../OutputVisitor/CodeDomConvertVisitor.cs | 1429 ++ .../OutputVisitor/ITokenWriter.cs | 161 + .../InsertMissingTokensDecorator.cs | 123 + .../OutputVisitor/InsertParenthesesVisitor.cs | 345 + .../InsertRequiredSpacesDecorator.cs | 184 + .../OutputVisitor/InsertSpecialsDecorator.cs | 157 + .../TextWriterOutputFormatter.cs | 419 + .../Parser/CSharpParser.cs | 4096 ++++ .../Parser/CompilerSettings.cs | 151 + .../Parser/SeekableStreamReader.cs | 103 + .../Parser/mcs/CryptoConvert.cs | 754 + .../Parser/mcs/MonoSymbolFile.cs | 637 + .../Parser/mcs/MonoSymbolTable.cs | 1437 ++ .../Parser/mcs/MonoSymbolWriter.cs | 238 + .../Parser/mcs/SourceMethodBuilder.cs | 190 + .../Parser/mcs/anonymous.cs | 2292 +++ .../Parser/mcs/argument.cs | 674 + .../Parser/mcs/assembly.cs | 1257 ++ .../Parser/mcs/assign.cs | 956 + .../Parser/mcs/async.cs | 1005 + .../Parser/mcs/attribute.cs | 2140 ++ .../Parser/mcs/cfold.cs | 1192 ++ .../Parser/mcs/class.cs | 3895 ++++ .../Parser/mcs/codegen.cs | 1365 ++ .../Parser/mcs/complete.cs | 226 + .../Parser/mcs/const.cs | 237 + .../Parser/mcs/constant.cs | 2496 +++ .../Parser/mcs/context.cs | 751 + .../Parser/mcs/convert.cs | 2266 +++ .../Parser/mcs/cs-parser.cs | 16238 ++++++++++++++++ .../Parser/mcs/cs-parser.jay | 8195 ++++++++ .../Parser/mcs/cs-tokenizer.cs | 4033 ++++ .../Parser/mcs/decl.cs | 1297 ++ .../Parser/mcs/delegate.cs | 952 + .../Parser/mcs/doc.cs | 755 + .../Parser/mcs/driver.cs | 475 + .../Parser/mcs/dynamic.cs | 986 + .../Parser/mcs/ecore.cs | 7426 +++++++ .../Parser/mcs/enum.cs | 339 + .../Parser/mcs/eval.cs | 1333 ++ .../Parser/mcs/expression.cs | 11921 ++++++++++++ .../Parser/mcs/field.cs | 733 + .../Parser/mcs/flowanalysis.cs | 685 + .../Parser/mcs/generic.cs | 3660 ++++ .../Parser/mcs/import.cs | 2352 +++ .../Parser/mcs/iterators.cs | 1260 ++ .../Parser/mcs/lambda.cs | 229 + .../Parser/mcs/linq.cs | 904 + .../Parser/mcs/literal.cs | 333 + .../Parser/mcs/location.cs | 883 + .../Parser/mcs/membercache.cs | 1501 ++ .../Parser/mcs/method.cs | 2890 +++ .../Parser/mcs/modifiers.cs | 282 + .../Parser/mcs/module.cs | 713 + .../Parser/mcs/namespace.cs | 1584 ++ .../Parser/mcs/nullable.cs | 1380 ++ .../Parser/mcs/outline.cs | 1038 + .../Parser/mcs/parameter.cs | 1472 ++ .../Parser/mcs/pending.cs | 734 + .../Parser/mcs/property.cs | 1807 ++ .../Parser/mcs/reflection.cs | 550 + .../Parser/mcs/report.cs | 1144 ++ .../Parser/mcs/settings.cs | 1601 ++ .../Parser/mcs/statement.cs | 8074 ++++++++ .../Parser/mcs/support.cs | 364 + .../Parser/mcs/symbolwriter.cs | 234 + .../Parser/mcs/typemanager.cs | 1143 ++ .../Parser/mcs/typespec.cs | 2021 ++ .../Parser/mcs/visit.cs | 618 + .../PatternMatching/AnyType.cs | 37 + .../Properties/AssemblyInfo.cs | 33 + .../QueryExpressionExpander.cs | 430 + .../Refactoring/BaseRefactoringContext.cs | 319 + .../Refactoring/CodeAction.cs | 197 + .../Refactoring/CodeActionProvider.cs | 37 + ...veFieldRefactoryActionRefactoringAction.cs | 110 + .../Refactoring/CodeGenerationService.cs | 64 + .../Refactoring/CodeIssue.cs | 181 + .../Refactoring/CodeIssueProvider.cs | 76 + .../CodeIssues/Uncategorized/.DS_Store | Bin 0 -> 6148 bytes .../Refactoring/CommonSubIssues.cs | 37 + .../Refactoring/ContextActionAttribute.cs | 43 + .../Refactoring/DocumentScript.cs | 179 + .../Refactoring/FormatStringHelper.cs | 86 + .../Refactoring/IssueAttribute.cs | 91 + .../Refactoring/LambdaHelper.cs | 51 + .../Refactoring/LocalReferenceFinder.cs | 159 + .../Refactoring/NamingHelper.cs | 242 + .../Refactoring/PatternHelper.cs | 172 + .../Refactoring/RefactoringAstHelper.cs | 49 + .../Refactoring/RefactoringContext.cs | 108 + .../Refactoring/Script.cs | 651 + .../Refactoring/TypeGuessing.cs | 349 + .../Refactoring/TypeSystemAstBuilder.cs | 981 + .../Refactoring/UsingHelper.cs | 201 + .../Refactoring/VariableReferenceGraph.cs | 575 + .../Refactoring/WordParser.cs | 70 + .../Resolver/AliasNamespaceResolveResult.cs | 52 + .../Resolver/AliasTypeResolveResult.cs | 52 + .../Resolver/AwaitResolveResult.cs | 81 + .../Resolver/CSharpAstResolver.cs | 281 + .../Resolver/CSharpConversions.cs | 1248 ++ .../Resolver/CSharpInvocationResolveResult.cs | 145 + .../Resolver/CSharpOperators.cs | 1059 + .../Resolver/CSharpResolver.cs | 2619 +++ .../Resolver/CastResolveResult.cs | 63 + .../CompositeResolveVisitorNavigator.cs | 67 + .../Resolver/DetectSkippableNodesNavigator.cs | 88 + .../DynamicInvocationResolveResult.cs | 86 + .../Resolver/DynamicMemberResolveResult.cs | 57 + .../Resolver/FindReferenceSearchScope.cs | 63 + .../Resolver/FindReferencedEntities.cs | 100 + .../Resolver/FindReferences.cs | 1611 ++ .../Resolver/IResolveVisitorNavigator.cs | 103 + .../Resolver/LambdaResolveResult.cs | 101 + .../Resolver/Log.cs | 94 + .../Resolver/MemberLookup.cs | 696 + .../Resolver/MethodGroupResolveResult.cs | 292 + .../NodeListResolveVisitorNavigator.cs | 76 + .../Resolver/OverloadResolution.cs | 960 + .../Resolver/OverloadResolutionErrors.cs | 87 + .../Resolver/ReducedExtensionMethod.cs | 455 + .../Resolver/RenameCallbackArguments.cs | 41 + .../Resolver/ResolveAtLocation.cs | 124 + .../Resolver/ResolveVisitor.cs | 4103 ++++ .../Resolver/TypeInference.cs | 978 + .../TypeSystem/AliasNamespaceReference.cs | 76 + .../TypeSystem/AttributeTypeReference.cs | 91 + .../TypeSystem/CSharpAssembly.cs | 338 + .../TypeSystem/CSharpAttribute.cs | 169 + .../TypeSystem/CSharpDocumentationComment.cs | 63 + .../TypeSystem/CSharpTypeResolveContext.cs | 96 + .../TypeSystem/CSharpUnresolvedFile.cs | 213 + .../CSharpUnresolvedTypeDefinition.cs | 50 + .../TypeSystem/ConstantValues.cs | 521 + .../MemberTypeOrNamespaceReference.cs | 121 + ...odTypeParameterWithInheritedConstraints.cs | 121 + .../TypeSystem/ResolvedUsingScope.cs | 210 + .../SimpleTypeOrNamespaceReference.cs | 109 + .../TypeSystem/TypeOrNamespaceReference.cs | 80 + .../TypeSystem/TypeSystemConvertVisitor.cs | 1333 ++ .../TypeSystem/UsingScope.cs | 174 + .../Util/CloneableStack.cs | 201 + .../ICSharpCode.NRefactory.VB.csproj | 20 +- .../Visitors/CSharpToVBConverterVisitor.cs | 2 +- .../ICSharpCode.NRefactory.VB/packages.config | 5 - .../AXmlAttribute.cs | 118 + .../AXmlDocument.cs | 58 + .../ICSharpCode.NRefactory.Xml/AXmlElement.cs | 243 + .../ICSharpCode.NRefactory.Xml/AXmlObject.cs | 246 + .../ICSharpCode.NRefactory.Xml/AXmlParser.cs | 147 + .../ICSharpCode.NRefactory.Xml/AXmlReader.cs | 478 + .../ICSharpCode.NRefactory.Xml/AXmlTag.cs | 98 + .../ICSharpCode.NRefactory.Xml/AXmlText.cs | 63 + .../ICSharpCode.NRefactory.Xml/AXmlVisitor.cs | 62 + .../DocumentationElement.cs | 285 + .../ICSharpCode.NRefactory.Xml.csproj | 124 + .../IncrementalParserState.cs | 115 + .../InternalDocument.cs | 175 + NRefactory/ICSharpCode.NRefactory.Xml/Log.cs | 88 + .../ObjectIterator.cs | 109 + .../Properties/AssemblyInfo.cs | 15 + .../ReuseEqualityComparer.cs | 52 + .../ICSharpCode.NRefactory.Xml/SyntaxError.cs | 73 + .../TagMatchingHeuristics.cs | 352 + .../ICSharpCode.NRefactory.Xml/TagReader.cs | 836 + .../ICSharpCode.NRefactory.Xml/TextType.cs | 47 + .../ICSharpCode.NRefactory.Xml/TokenReader.cs | 350 + .../ICSharpCode.NRefactory.Xml/XmlSegment.cs | 48 + .../Analysis/AbiComparer.cs | 248 + .../Analysis/SymbolCollector.cs | 165 + .../Analysis/TypeGraph.cs | 82 + .../Analysis/TypeGraphNode.cs | 51 + .../Completion/CompletionCategory.cs | 49 + .../Completion/CompletionExtensionMethods.cs | 72 + .../Completion/DisplayFlags.cs | 42 + .../Completion/FrameworkLookup.cs | 490 + .../Completion/ICompletionData.cs | 53 + .../Completion/IEntityCompletionData.cs | 37 + .../Completion/IParameterDataProvider.cs | 84 + .../Completion/IVariableCompletionData.cs | 38 + .../Documentation/DocumentationComment.cs | 95 + .../GetPotentiallyNestedClassTypeReference.cs | 71 + .../Documentation/IDocumentationProvider.cs | 51 + .../Documentation/IdStringMemberReference.cs | 77 + .../Documentation/IdStringProvider.cs | 391 + .../Documentation/XmlDocumentationProvider.cs | 422 + .../Editor/IDocument.cs | 205 + .../Editor/IDocumentLine.cs | 60 + .../ICSharpCode.NRefactory/Editor/ISegment.cs | 71 + .../Editor/ITextAnchor.cs | 139 + .../Editor/ITextPasteHandler.cs | 51 + .../Editor/ITextSource.cs | 218 + .../Editor/ReadOnlyDocument.cs | 447 + .../Editor/StringBuilderDocument.cs | 493 + .../Editor/StringTextSource.cs | 160 + .../Editor/TextChangeEventArgs.cs | 118 + .../Editor/TextSourceVersionProvider.cs | 130 + .../Editor/UnicodeNewline.cs | 365 + .../ICSharpCode.NRefactory/IAnnotatable.cs | 252 + .../ICSharpCode.NRefactory.csproj | 313 + .../PatternMatching/AnyNode.cs | 47 + .../PatternMatching/AnyNodeOrNull.cs | 58 + .../PatternMatching/Backreference.cs | 50 + .../PatternMatching/BacktrackingInfo.cs | 32 + .../PatternMatching/Choice.cs | 68 + .../PatternMatching/INode.cs | 69 + .../PatternMatching/Match.cs | 100 + .../PatternMatching/NamedNode.cs | 53 + .../PatternMatching/OptionalNode.cs | 58 + .../PatternMatching/Pattern.cs | 114 + .../PatternMatching/Repeat.cs | 74 + .../Properties/AssemblyInfo.cs | 37 + .../Properties/GlobalAssemblyInfo.cs | 45 + .../Refactoring/IssueMarker.cs | 57 + .../Refactoring/Severity.cs | 61 + NRefactory/ICSharpCode.NRefactory/Role.cs | 113 + .../Semantics/AmbiguousResolveResult.cs | 51 + .../Semantics/ArrayAccessResolveResult.cs | 50 + .../Semantics/ArrayCreateResolveResult.cs | 60 + .../Semantics/ByReferenceResolveResult.cs | 66 + .../Semantics/ConstantResolveResult.cs | 55 + .../Semantics/Conversion.cs | 568 + .../Semantics/ConversionResolveResult.cs | 68 + .../Semantics/ErrorResolveResult.cs | 56 + .../Semantics/ForEachResolveResult.cs | 90 + .../InitializedObjectResolveResult.cs | 34 + .../Semantics/InvocationResolveResult.cs | 75 + .../Semantics/LocalResolveResult.cs | 77 + .../Semantics/MemberResolveResult.cs | 148 + .../Semantics/NamedArgumentResolveResult.cs | 81 + .../Semantics/NamespaceResolveResult.cs | 50 + .../Semantics/OperatorResolveResult.cs | 90 + .../Semantics/ResolveResult.cs | 78 + .../Semantics/SizeOfResolveResult.cs | 66 + .../Semantics/ThisResolveResult.cs | 44 + .../Semantics/TypeIsResolveResult.cs | 47 + .../Semantics/TypeOfResolveResult.cs | 46 + .../Semantics/TypeResolveResult.cs | 47 + .../Semantics/UnknownMemberResolveResult.cs | 122 + .../ICSharpCode.NRefactory/TextLocation.cs | 223 + .../TypeSystem/Accessibility.cs | 117 + .../TypeSystem/AnonymousType.cs | 221 + .../TypeSystem/ArrayType.cs | 200 + .../TypeSystem/AssemblyLoader.cs | 94 + .../TypeSystem/AssemblyQualifiedTypeName.cs | 79 + .../TypeSystem/ByReferenceType.cs | 112 + .../TypeSystem/ComHelper.cs | 63 + .../TypeSystem/DefaultSolutionSnapshot.cs | 86 + .../TypeSystem/DomRegion.cs | 230 + .../TypeSystem/EntityType.cs | 63 + .../TypeSystem/Error.cs | 139 + .../TypeSystem/FullTypeName.cs | 315 + .../TypeSystem/IAmbience.cs | 107 + .../TypeSystem/IAssembly.cs | 128 + .../TypeSystem/IAttribute.cs | 75 + .../TypeSystem/ICodeContext.cs | 38 + .../TypeSystem/ICompilation.cs | 91 + .../TypeSystem/IConstantValue.cs | 38 + .../TypeSystem/IEntity.cs | 178 + .../TypeSystem/IEvent.cs | 58 + .../TypeSystem/IField.cs | 99 + .../TypeSystem/IFreezable.cs | 35 + .../TypeSystem/IInterningProvider.cs | 102 + .../TypeSystem/IMember.cs | 196 + .../TypeSystem/IMethod.cs | 168 + .../TypeSystem/INamedElement.cs | 66 + .../TypeSystem/INamespace.cs | 88 + .../TypeSystem/IParameter.cs | 104 + .../TypeSystem/IParameterizedMember.cs | 40 + .../TypeSystem/IProjectContent.cs | 159 + .../TypeSystem/IProperty.cs | 62 + .../TypeSystem/ISolutionSnapshot.cs | 43 + .../TypeSystem/ISupportsInterning.cs | 39 + .../TypeSystem/ISymbol.cs | 100 + .../TypeSystem/IType.cs | 350 + .../TypeSystem/ITypeDefinition.cs | 170 + .../TypeSystem/ITypeParameter.cs | 151 + .../TypeSystem/ITypeReference.cs | 73 + .../TypeSystem/IUnresolvedFile.cs | 80 + .../TypeSystem/IVariable.cs | 54 + .../Implementation/AbstractFreezable.cs | 110 + .../Implementation/AbstractResolvedEntity.cs | 124 + .../Implementation/AbstractResolvedMember.cs | 167 + .../AbstractResolvedTypeParameter.cs | 412 + .../TypeSystem/Implementation/AbstractType.cs | 180 + .../AbstractUnresolvedEntity.cs | 334 + .../AbstractUnresolvedMember.cs | 272 + .../AccessorOwnerMemberReference.cs | 56 + .../Implementation/BaseTypeCollector.cs | 75 + .../TypeSystem/Implementation/BlobReader.cs | 387 + .../DefaultAssemblyReference.cs | 87 + .../Implementation/DefaultAttribute.cs | 97 + .../Implementation/DefaultMemberReference.cs | 118 + .../Implementation/DefaultParameter.cs | 202 + .../Implementation/DefaultResolvedEvent.cs | 69 + .../Implementation/DefaultResolvedField.cs | 87 + .../Implementation/DefaultResolvedMethod.cs | 322 + .../Implementation/DefaultResolvedProperty.cs | 83 + .../DefaultResolvedTypeDefinition.cs | 963 + .../DefaultResolvedTypeParameter.cs | 257 + .../DefaultUnresolvedAssembly.cs | 575 + .../DefaultUnresolvedAttribute.cs | 283 + .../Implementation/DefaultUnresolvedEvent.cs | 99 + .../Implementation/DefaultUnresolvedField.cs | 97 + .../Implementation/DefaultUnresolvedMethod.cs | 287 + .../DefaultUnresolvedParameter.cs | 275 + .../DefaultUnresolvedProperty.cs | 126 + .../DefaultUnresolvedTypeDefinition.cs | 240 + .../DefaultUnresolvedTypeParameter.cs | 194 + .../Implementation/DefaultVariable.cs | 109 + .../Implementation/DummyTypeParameter.cs | 217 + ...tInterfaceImplementationMemberReference.cs | 80 + .../FullNameAndTypeParameterCount.cs | 28 + .../Implementation/GetClassTypeReference.cs | 158 + .../Implementation/GetMembersHelper.cs | 296 + .../Implementation/KnownTypeCache.cs | 60 + .../Implementation/MergedNamespace.cs | 164 + .../Implementation/MinimalCorlib.cs | 65 + .../Implementation/NestedTypeReference.cs | 106 + .../Implementation/ResolvedAttributeBlob.cs | 176 + .../Implementation/SimpleCompilation.cs | 168 + .../Implementation/SimpleConstantValue.cs | 69 + .../Implementation/SimpleInterningProvider.cs | 143 + .../Implementation/SpecializedEvent.cs | 63 + .../Implementation/SpecializedField.cs | 61 + .../Implementation/SpecializedMember.cs | 410 + .../Implementation/SpecializedMethod.cs | 286 + .../Implementation/SpecializedProperty.cs | 59 + .../SpecializingMemberReference.cs | 67 + .../Implementation/TypeParameterReference.cs | 96 + .../Implementation/TypeWithElementType.cs | 61 + .../TypeSystem/Implementation/UnknownType.cs | 117 + .../Implementation/UnresolvedAttributeBlob.cs | 78 + .../UnresolvedSecurityDeclarationBlob.cs | 152 + .../Implementation/VoidTypeDefinition.cs | 73 + .../TypeSystem/InheritanceHelper.cs | 148 + .../TypeSystem/IntersectionType.cs | 178 + .../TypeSystem/KnownTypeReference.cs | 500 + .../TypeSystem/NullableType.cs | 87 + .../TypeSystem/ParameterListComparer.cs | 168 + .../TypeSystem/ParameterizedType.cs | 436 + .../TypeSystem/PointerType.cs | 113 + .../TypeSystem/ProjectReference.cs | 56 + .../TypeSystem/ReflectionHelper.cs | 424 + .../ReflectionNameParseException.cs | 63 + .../TypeSystem/SimpleTypeResolveContext.cs | 92 + .../TypeSystem/SpecialType.cs | 101 + .../TypeSystem/TaskType.cs | 78 + .../TypeSystem/TopLevelTypeName.cs | 144 + .../TypeSystem/TypeKind.cs | 84 + .../TypeSystem/TypeParameterSubstitution.cs | 193 + .../TypeSystem/TypeSystemExtensions.cs | 788 + .../TypeSystem/TypeVisitor.cs | 63 + .../Utils/7BitEncodedInts.cs | 123 + .../Utils/BitVector16.cs | 83 + .../Utils/BusyManager.cs | 73 + .../Utils/CSharpPrimitiveCast.cs | 431 + .../Utils/CacheManager.cs | 59 + .../Utils/CallbackOnDispose.cs | 51 + .../Utils/ComparableList.cs | 166 + .../CompositeFormatStringParser.cs | 343 + .../CompositeFormatStringParser/FormatItem.cs | 93 + .../FormatStringSegmentBase.cs | 66 + .../IFormatStringError.cs | 68 + .../IFormatStringSegment.cs | 48 + .../TextSegment.cs | 79 + .../ICSharpCode.NRefactory/Utils/EmptyList.cs | 118 + .../Utils/ExtensionMethods.cs | 44 + .../Utils/FastSerializer.cs | 1371 ++ .../Utils/GraphVizGraph.cs | 219 + .../Utils/ImmutableStack.cs | 132 + .../Utils/KeyComparer.cs | 81 + .../ICSharpCode.NRefactory/Utils/LazyInit.cs | 48 + .../Utils/MultiDictionary.cs | 154 + .../ICSharpCode.NRefactory/Utils/Platform.cs | 40 + .../Utils/ProjectedList.cs | 239 + .../Utils/ReferenceComparer.cs | 39 + .../Utils/TreeTraversal.cs | 112 + .../Packages/ICSharpCode.NRefactory.nuspec | 36 - NRefactory/README | 14 +- NRefactory/doc/Pattern Matching.html | 115 + NRefactory/doc/TODO | 13 + NRefactory/doc/XML Documentation.html | 60 + NRefactory/doc/copyright.txt | 10 + NRefactory/doc/license.txt | 17 + TestPlugin/TestPlugin.csproj | 16 +- TestPlugin/packages.config | 5 - packages/ICSharpCode.Decompiler.nuspec | 27 + packages/build_decompiler_package.cmd | 5 + packages/repositories.config | 3 - 562 files changed, 229746 insertions(+), 112 deletions(-) delete mode 100644 ILSpy/packages.config create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/AnnotationNames.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/ControlFlow.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/DeclarationSpace/LocalDeclarationSpace.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/DeclarationSpace/LocalDeclarationSpaceVisitor.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/DefiniteAssignmentAnalysis.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/NullValueAnalysis.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/NullValueStatus.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/ReachabilityAnalysis.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/SemanticHighlightingVisitor.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/AstNode.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/AstNodeCollection.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/AstType.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/CSharpModifierToken.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/CSharpTokenNode.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/CSharpUtil.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/ComposedType.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/DepthFirstAstVisitor.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/DocumentationReference.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/ErrorNode.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/AnonymousMethodExpression.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/AnonymousTypeCreateExpression.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ArrayCreateExpression.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ArrayInitializerExpression.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/AsExpression.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/AssignmentExpression.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/BaseReferenceExpression.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/BinaryOperatorExpression.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/CastExpression.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/CheckedExpression.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ConditionalExpression.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/DefaultValueExpression.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/DirectionExpression.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ErrorExpression.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/Expression.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/IdentifierExpression.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/IndexerExpression.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/InvocationExpression.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/IsExpression.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/LambdaExpression.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/MemberReferenceExpression.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/NamedArgumentExpression.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/NamedExpression.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/NullReferenceExpression.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ObjectCreateExpression.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ParenthesizedExpression.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/PointerReferenceExpression.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/PrimitiveExpression.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/QueryExpression.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/SizeOfExpression.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/StackAllocExpression.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ThisReferenceExpression.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/TypeOfExpression.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/TypeReferenceExpression.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/UnaryOperatorExpression.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/UncheckedExpression.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/UndocumentedExpression.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/Attribute.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/AttributeSection.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/Comment.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/Constraint.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/DelegateDeclaration.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/ExternAliasDeclaration.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/NamespaceDeclaration.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/NewLineNode.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/PreProcessorDirective.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/TextNode.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/TypeDeclaration.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/TypeParameterDeclaration.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/UsingAliasDeclaration.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/UsingDeclaration.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/WhitespaceNode.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/IAstVisitor.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Identifier.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/IdentifierExpressionBackreference.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/MemberType.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Modifiers.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/NodeType.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/ObservableAstVisitor.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/PrimitiveType.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Roles.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/SimpleType.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/BlockStatement.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/BreakStatement.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/CheckedStatement.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/ContinueStatement.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/DoWhileStatement.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/EmptyStatement.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/ExpressionStatement.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/FixedStatement.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/ForStatement.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/ForeachStatement.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/GotoStatement.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/IfElseStatement.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/LabelStatement.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/LockStatement.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/ReturnStatement.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/Statement.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/SwitchStatement.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/ThrowStatement.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/TryCatchStatement.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/UncheckedStatement.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/UnsafeStatement.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/UsingStatement.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/VariableDeclarationStatement.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/WhileStatement.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/YieldBreakStatement.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/YieldReturnStatement.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/SyntaxExtensions.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/SyntaxTree.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TokenRole.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/Accessor.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/ConstructorDeclaration.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/DestructorDeclaration.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/EntityDeclaration.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/EnumMemberDeclaration.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/EventDeclaration.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/FieldDeclaration.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/FixedFieldDeclaration.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/FixedVariableInitializer.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/IndexerDeclaration.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/MethodDeclaration.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/OperatorDeclaration.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/ParameterDeclaration.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/PropertyDeclaration.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/VariableInitializer.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/CSharpProjectContent.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/CombineQueryExpressions.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngineBase.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Completion/CSharpParameterCompletionEngine.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Completion/CompletionDataWrapper.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Completion/ICompletionContextProvider.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Completion/ICompletionDataFactory.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Completion/IParameterCompletionDataFactory.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/CSharpFormatter.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/CSharpFormattingOptions.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/ConstructFixer.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingChanges.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingOptionsFactory.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingVisitor.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingVisitor_Expressions.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingVisitor_Global.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingVisitor_Query.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingVisitor_Statements.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingVisitor_TypeMembers.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/GeneratedCodeSettings.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/Indent.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/TextEditorOptions.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/CSharpIndentEngine.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/CacheIndentEngine.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/IDocumentIndentEngine.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/IStateMachineIndentEngine.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/IndentState.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/NullIStateMachineIndentEngine.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/TextPasteIndentEngine.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/IntroduceQueryExpressions.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/NameLookupMode.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/CSharpAmbience.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/CSharpOutputVisitor.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/CodeDomConvertVisitor.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/ITokenWriter.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/InsertMissingTokensDecorator.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/InsertParenthesesVisitor.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/InsertRequiredSpacesDecorator.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/InsertSpecialsDecorator.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/TextWriterOutputFormatter.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/CSharpParser.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/CompilerSettings.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/SeekableStreamReader.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/CryptoConvert.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/MonoSymbolFile.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/MonoSymbolTable.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/MonoSymbolWriter.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/SourceMethodBuilder.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/anonymous.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/argument.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/assembly.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/assign.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/async.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/attribute.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/cfold.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/class.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/codegen.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/complete.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/const.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/constant.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/context.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/convert.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/cs-parser.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/cs-parser.jay create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/cs-tokenizer.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/decl.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/delegate.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/doc.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/driver.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/dynamic.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/ecore.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/enum.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/eval.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/expression.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/field.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/flowanalysis.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/generic.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/import.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/iterators.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/lambda.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/linq.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/literal.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/location.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/membercache.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/method.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/modifiers.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/module.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/namespace.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/nullable.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/outline.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/parameter.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/pending.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/property.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/reflection.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/report.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/settings.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/statement.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/support.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/symbolwriter.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/typemanager.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/typespec.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/visit.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/PatternMatching/AnyType.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Properties/AssemblyInfo.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/QueryExpressionExpander.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/BaseRefactoringContext.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/CodeAction.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActionProvider.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/RemoveFieldRefactoryActionRefactoringAction.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/CodeGenerationService.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssue.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssueProvider.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/Uncategorized/.DS_Store create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/CommonSubIssues.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/ContextActionAttribute.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/DocumentScript.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/FormatStringHelper.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/IssueAttribute.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/LambdaHelper.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/LocalReferenceFinder.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/NamingHelper.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/PatternHelper.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/RefactoringAstHelper.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/RefactoringContext.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/Script.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/TypeGuessing.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/TypeSystemAstBuilder.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/UsingHelper.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/VariableReferenceGraph.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/WordParser.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/AliasNamespaceResolveResult.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/AliasTypeResolveResult.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/AwaitResolveResult.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/CSharpAstResolver.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/CSharpConversions.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/CSharpInvocationResolveResult.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/CSharpOperators.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/CastResolveResult.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/CompositeResolveVisitorNavigator.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/DetectSkippableNodesNavigator.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/DynamicInvocationResolveResult.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/DynamicMemberResolveResult.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/FindReferenceSearchScope.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/FindReferencedEntities.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/FindReferences.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/IResolveVisitorNavigator.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/LambdaResolveResult.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/Log.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/MemberLookup.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/MethodGroupResolveResult.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/NodeListResolveVisitorNavigator.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/OverloadResolution.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/OverloadResolutionErrors.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/ReducedExtensionMethod.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/RenameCallbackArguments.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/ResolveAtLocation.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/TypeInference.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/TypeSystem/AliasNamespaceReference.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/TypeSystem/AttributeTypeReference.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/TypeSystem/CSharpAssembly.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/TypeSystem/CSharpAttribute.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/TypeSystem/CSharpDocumentationComment.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/TypeSystem/CSharpTypeResolveContext.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/TypeSystem/CSharpUnresolvedFile.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/TypeSystem/CSharpUnresolvedTypeDefinition.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/TypeSystem/ConstantValues.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/TypeSystem/MemberTypeOrNamespaceReference.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/TypeSystem/MethodTypeParameterWithInheritedConstraints.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/TypeSystem/ResolvedUsingScope.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/TypeSystem/SimpleTypeOrNamespaceReference.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/TypeSystem/TypeOrNamespaceReference.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/TypeSystem/TypeSystemConvertVisitor.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/TypeSystem/UsingScope.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.CSharp/Util/CloneableStack.cs delete mode 100644 NRefactory/ICSharpCode.NRefactory.VB/packages.config create mode 100644 NRefactory/ICSharpCode.NRefactory.Xml/AXmlAttribute.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.Xml/AXmlDocument.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.Xml/AXmlElement.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.Xml/AXmlObject.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.Xml/AXmlParser.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.Xml/AXmlReader.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.Xml/AXmlTag.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.Xml/AXmlText.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.Xml/AXmlVisitor.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.Xml/DocumentationElement.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.Xml/ICSharpCode.NRefactory.Xml.csproj create mode 100644 NRefactory/ICSharpCode.NRefactory.Xml/IncrementalParserState.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.Xml/InternalDocument.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.Xml/Log.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.Xml/ObjectIterator.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.Xml/Properties/AssemblyInfo.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.Xml/ReuseEqualityComparer.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.Xml/SyntaxError.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.Xml/TagMatchingHeuristics.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.Xml/TagReader.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.Xml/TextType.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.Xml/TokenReader.cs create mode 100644 NRefactory/ICSharpCode.NRefactory.Xml/XmlSegment.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Analysis/AbiComparer.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Analysis/SymbolCollector.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Analysis/TypeGraph.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Analysis/TypeGraphNode.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Completion/CompletionCategory.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Completion/CompletionExtensionMethods.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Completion/DisplayFlags.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Completion/FrameworkLookup.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Completion/ICompletionData.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Completion/IEntityCompletionData.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Completion/IParameterDataProvider.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Completion/IVariableCompletionData.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Documentation/DocumentationComment.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Documentation/GetPotentiallyNestedClassTypeReference.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Documentation/IDocumentationProvider.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Documentation/IdStringMemberReference.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Documentation/IdStringProvider.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Documentation/XmlDocumentationProvider.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Editor/IDocument.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Editor/IDocumentLine.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Editor/ISegment.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Editor/ITextAnchor.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Editor/ITextPasteHandler.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Editor/ITextSource.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Editor/ReadOnlyDocument.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Editor/StringBuilderDocument.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Editor/StringTextSource.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Editor/TextChangeEventArgs.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Editor/TextSourceVersionProvider.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Editor/UnicodeNewline.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/IAnnotatable.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj create mode 100644 NRefactory/ICSharpCode.NRefactory/PatternMatching/AnyNode.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/PatternMatching/AnyNodeOrNull.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/PatternMatching/Backreference.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/PatternMatching/BacktrackingInfo.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/PatternMatching/Choice.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/PatternMatching/INode.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/PatternMatching/Match.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/PatternMatching/NamedNode.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/PatternMatching/OptionalNode.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/PatternMatching/Pattern.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/PatternMatching/Repeat.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Properties/AssemblyInfo.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Properties/GlobalAssemblyInfo.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Refactoring/IssueMarker.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Refactoring/Severity.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Role.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Semantics/AmbiguousResolveResult.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Semantics/ArrayAccessResolveResult.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Semantics/ArrayCreateResolveResult.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Semantics/ByReferenceResolveResult.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Semantics/ConstantResolveResult.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Semantics/Conversion.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Semantics/ConversionResolveResult.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Semantics/ErrorResolveResult.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Semantics/ForEachResolveResult.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Semantics/InitializedObjectResolveResult.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Semantics/InvocationResolveResult.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Semantics/LocalResolveResult.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Semantics/MemberResolveResult.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Semantics/NamedArgumentResolveResult.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Semantics/NamespaceResolveResult.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Semantics/OperatorResolveResult.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Semantics/ResolveResult.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Semantics/SizeOfResolveResult.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Semantics/ThisResolveResult.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Semantics/TypeIsResolveResult.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Semantics/TypeOfResolveResult.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Semantics/TypeResolveResult.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Semantics/UnknownMemberResolveResult.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TextLocation.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Accessibility.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/AnonymousType.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/ArrayType.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/AssemblyLoader.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/AssemblyQualifiedTypeName.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/ByReferenceType.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/ComHelper.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/DefaultSolutionSnapshot.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/DomRegion.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/EntityType.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Error.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/FullTypeName.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/IAmbience.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/IAssembly.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/IAttribute.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/ICodeContext.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/ICompilation.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/IConstantValue.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/IEntity.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/IEvent.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/IField.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/IFreezable.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/IInterningProvider.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/IMember.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/IMethod.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/INamedElement.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/INamespace.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/IParameter.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/IParameterizedMember.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/IProjectContent.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/IProperty.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/ISolutionSnapshot.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/ISupportsInterning.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/ISymbol.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/IType.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/ITypeDefinition.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/ITypeParameter.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/ITypeReference.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/IUnresolvedFile.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/IVariable.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractFreezable.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractResolvedEntity.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractResolvedMember.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractResolvedTypeParameter.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractType.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractUnresolvedEntity.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractUnresolvedMember.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/AccessorOwnerMemberReference.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/BaseTypeCollector.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/BlobReader.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultAssemblyReference.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultAttribute.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultMemberReference.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultParameter.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedEvent.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedField.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedMethod.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedProperty.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedTypeDefinition.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedTypeParameter.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedAttribute.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedEvent.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedField.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedMethod.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedParameter.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedProperty.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedTypeDefinition.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedTypeParameter.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultVariable.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/DummyTypeParameter.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/ExplicitInterfaceImplementationMemberReference.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/FullNameAndTypeParameterCount.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/GetClassTypeReference.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/GetMembersHelper.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/KnownTypeCache.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/MergedNamespace.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/MinimalCorlib.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/NestedTypeReference.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/ResolvedAttributeBlob.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/SimpleCompilation.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/SimpleConstantValue.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/SimpleInterningProvider.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedEvent.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedField.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMember.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMethod.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedProperty.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializingMemberReference.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/TypeParameterReference.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/TypeWithElementType.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/UnknownType.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/UnresolvedAttributeBlob.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/UnresolvedSecurityDeclarationBlob.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/Implementation/VoidTypeDefinition.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/InheritanceHelper.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/IntersectionType.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/KnownTypeReference.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/NullableType.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/ParameterListComparer.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/ParameterizedType.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/PointerType.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/ProjectReference.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/ReflectionHelper.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/ReflectionNameParseException.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/SimpleTypeResolveContext.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/SpecialType.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/TaskType.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/TopLevelTypeName.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/TypeKind.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/TypeParameterSubstitution.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/TypeSystemExtensions.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/TypeSystem/TypeVisitor.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Utils/7BitEncodedInts.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Utils/BitVector16.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Utils/BusyManager.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Utils/CSharpPrimitiveCast.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Utils/CacheManager.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Utils/CallbackOnDispose.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Utils/ComparableList.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Utils/CompositeFormatStringParser/CompositeFormatStringParser.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Utils/CompositeFormatStringParser/FormatItem.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Utils/CompositeFormatStringParser/FormatStringSegmentBase.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Utils/CompositeFormatStringParser/IFormatStringError.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Utils/CompositeFormatStringParser/IFormatStringSegment.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Utils/CompositeFormatStringParser/TextSegment.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Utils/EmptyList.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Utils/ExtensionMethods.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Utils/FastSerializer.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Utils/GraphVizGraph.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Utils/ImmutableStack.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Utils/KeyComparer.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Utils/LazyInit.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Utils/MultiDictionary.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Utils/Platform.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Utils/ProjectedList.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Utils/ReferenceComparer.cs create mode 100644 NRefactory/ICSharpCode.NRefactory/Utils/TreeTraversal.cs delete mode 100644 NRefactory/Packages/ICSharpCode.NRefactory.nuspec create mode 100644 NRefactory/doc/Pattern Matching.html create mode 100644 NRefactory/doc/TODO create mode 100644 NRefactory/doc/XML Documentation.html create mode 100644 NRefactory/doc/copyright.txt create mode 100644 NRefactory/doc/license.txt delete mode 100644 TestPlugin/packages.config create mode 100644 packages/ICSharpCode.Decompiler.nuspec create mode 100644 packages/build_decompiler_package.cmd diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 4af789de8..e71d3486d 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -41,13 +41,19 @@ False TRACE + + bin\NuGet-$(Configuration)\ + - + ..\packages\ICSharpCode.NRefactory.5.5.1\lib\Net40\ICSharpCode.NRefactory.dll - + ..\packages\ICSharpCode.NRefactory.5.5.1\lib\Net40\ICSharpCode.NRefactory.CSharp.dll + + ..\packages\Mono.Cecil.0.9.5.4\lib\net40\Mono.Cecil.dll + 3.5 @@ -130,19 +136,26 @@ - - + {D68133BD-1E63-496E-9EDE-4FBDBF77B486} Mono.Cecil + + {53DCA265-3C3C-42F9-B647-F72BA678122B} + ICSharpCode.NRefactory.CSharp + + + {3B2A5653-EC97-4001-BB9B-D90F1AF2C371} + ICSharpCode.NRefactory + - + diff --git a/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj index 808290a49..8bb4934e7 100644 --- a/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj @@ -49,12 +49,6 @@ ..\..\packages\DiffLib.1.0.0.55\lib\net35-Client\DiffLib.dll - - ..\..\packages\ICSharpCode.NRefactory.5.5.1\lib\Net40\ICSharpCode.NRefactory.dll - - - ..\..\packages\ICSharpCode.NRefactory.5.5.1\lib\Net40\ICSharpCode.NRefactory.CSharp.dll - False .\nunit.framework.dll @@ -121,6 +115,14 @@ {D68133BD-1E63-496E-9EDE-4FBDBF77B486} Mono.Cecil + + {53DCA265-3C3C-42F9-B647-F72BA678122B} + ICSharpCode.NRefactory.CSharp + + + {3B2A5653-EC97-4001-BB9B-D90F1AF2C371} + ICSharpCode.NRefactory + {984CC812-9470-4A13-AFF9-CC44068D666C} ICSharpCode.Decompiler diff --git a/ICSharpCode.Decompiler/Tests/packages.config b/ICSharpCode.Decompiler/Tests/packages.config index b6522ccdb..4d992b424 100644 --- a/ICSharpCode.Decompiler/Tests/packages.config +++ b/ICSharpCode.Decompiler/Tests/packages.config @@ -1,6 +1,4 @@  - - \ No newline at end of file diff --git a/ILSpy.AddIn/ILSpy.AddIn.csproj b/ILSpy.AddIn/ILSpy.AddIn.csproj index 2e507061a..7c56424cb 100644 --- a/ILSpy.AddIn/ILSpy.AddIn.csproj +++ b/ILSpy.AddIn/ILSpy.AddIn.csproj @@ -57,12 +57,6 @@ False False - - ..\packages\ICSharpCode.NRefactory.5.5.1\lib\Net40\ICSharpCode.NRefactory.dll - - - ..\packages\ICSharpCode.NRefactory.5.5.1\lib\Net40\ICSharpCode.NRefactory.CSharp.dll - False @@ -220,10 +214,18 @@ {63e6915c-7ea4-4d76-ab28-0d7191eea626} Mono.Cecil.Pdb + + {53dca265-3c3c-42f9-b647-f72ba678122b} + ICSharpCode.NRefactory.CSharp + {7b82b671-419f-45f4-b778-d9286f996efa} ICSharpCode.NRefactory.VB + + {3b2a5653-ec97-4001-bb9b-d90f1af2c371} + ICSharpCode.NRefactory + {dde2a481-8271-4eac-a330-8fa6a38d13d1} ICSharpCode.TreeView diff --git a/ILSpy.AddIn/packages.config b/ILSpy.AddIn/packages.config index 5f793afd7..14a098365 100644 --- a/ILSpy.AddIn/packages.config +++ b/ILSpy.AddIn/packages.config @@ -1,7 +1,5 @@  - - diff --git a/ILSpy.sln b/ILSpy.sln index 0d57ce0ae..7a69bb14f 100644 --- a/ILSpy.sln +++ b/ILSpy.sln @@ -1,7 +1,6 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2012 -# SharpDevelop 5.1 +# Visual Studio 2013 VisualStudioVersion = 12.0.31101.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "doc", "doc", "{F45DB999-7E72-4000-B5AD-3A7B485A0896}" @@ -19,6 +18,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.AvalonEdit", "A EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Decompiler", "ICSharpCode.Decompiler\ICSharpCode.Decompiler.csproj", "{984CC812-9470-4A13-AFF9-CC44068D666C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.NRefactory", "NRefactory\ICSharpCode.NRefactory\ICSharpCode.NRefactory.csproj", "{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Decompiler.Tests", "ICSharpCode.Decompiler\Tests\ICSharpCode.Decompiler.Tests.csproj", "{FEC0DA52-C4A6-4710-BE36-B484A20C5E22}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestPlugin", "TestPlugin\TestPlugin.csproj", "{F32EBCC8-0E53-4421-867E-05B3D6E10C70}" @@ -31,6 +32,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ILSpy.BamlDecompiler", "ILS EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ILSpy.BamlDecompiler.Tests", "ILSpy.BamlDecompiler\Tests\ILSpy.BamlDecompiler.Tests.csproj", "{1169E6D1-1899-43D4-A500-07CE4235B388}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.NRefactory.CSharp", "NRefactory\ICSharpCode.NRefactory.CSharp\ICSharpCode.NRefactory.CSharp.csproj", "{53DCA265-3C3C-42F9-B647-F72BA678122B}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ILSpy.AddIn", "ILSpy.AddIn\ILSpy.AddIn.csproj", "{9D7BE6C0-B7B3-4A50-A54E-18A2D84A3384}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0A344E19-D1FC-4F4C-8883-0844AC669113}" @@ -86,6 +89,14 @@ Global {984CC812-9470-4A13-AFF9-CC44068D666C}.Release|Any CPU.Build.0 = Release|Any CPU {984CC812-9470-4A13-AFF9-CC44068D666C}.Release|x86.ActiveCfg = Release|Any CPU {984CC812-9470-4A13-AFF9-CC44068D666C}.Release|x86.Build.0 = Release|Any CPU + {3B2A5653-EC97-4001-BB9B-D90F1AF2C371}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3B2A5653-EC97-4001-BB9B-D90F1AF2C371}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3B2A5653-EC97-4001-BB9B-D90F1AF2C371}.Debug|x86.ActiveCfg = Debug|Any CPU + {3B2A5653-EC97-4001-BB9B-D90F1AF2C371}.Debug|x86.Build.0 = Debug|Any CPU + {3B2A5653-EC97-4001-BB9B-D90F1AF2C371}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3B2A5653-EC97-4001-BB9B-D90F1AF2C371}.Release|Any CPU.Build.0 = Release|Any CPU + {3B2A5653-EC97-4001-BB9B-D90F1AF2C371}.Release|x86.ActiveCfg = Release|Any CPU + {3B2A5653-EC97-4001-BB9B-D90F1AF2C371}.Release|x86.Build.0 = Release|Any CPU {FEC0DA52-C4A6-4710-BE36-B484A20C5E22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FEC0DA52-C4A6-4710-BE36-B484A20C5E22}.Debug|Any CPU.Build.0 = Debug|Any CPU {FEC0DA52-C4A6-4710-BE36-B484A20C5E22}.Debug|x86.ActiveCfg = Debug|x86 @@ -134,6 +145,14 @@ Global {1169E6D1-1899-43D4-A500-07CE4235B388}.Release|Any CPU.Build.0 = Release|Any CPU {1169E6D1-1899-43D4-A500-07CE4235B388}.Release|x86.ActiveCfg = Release|x86 {1169E6D1-1899-43D4-A500-07CE4235B388}.Release|x86.Build.0 = Release|x86 + {53DCA265-3C3C-42F9-B647-F72BA678122B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {53DCA265-3C3C-42F9-B647-F72BA678122B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {53DCA265-3C3C-42F9-B647-F72BA678122B}.Debug|x86.ActiveCfg = Debug|Any CPU + {53DCA265-3C3C-42F9-B647-F72BA678122B}.Debug|x86.Build.0 = Debug|Any CPU + {53DCA265-3C3C-42F9-B647-F72BA678122B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {53DCA265-3C3C-42F9-B647-F72BA678122B}.Release|Any CPU.Build.0 = Release|Any CPU + {53DCA265-3C3C-42F9-B647-F72BA678122B}.Release|x86.ActiveCfg = Release|Any CPU + {53DCA265-3C3C-42F9-B647-F72BA678122B}.Release|x86.Build.0 = Release|Any CPU {9D7BE6C0-B7B3-4A50-A54E-18A2D84A3384}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9D7BE6C0-B7B3-4A50-A54E-18A2D84A3384}.Debug|Any CPU.Build.0 = Debug|Any CPU {9D7BE6C0-B7B3-4A50-A54E-18A2D84A3384}.Debug|x86.ActiveCfg = Debug|Any CPU diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index 44c274807..46a29db40 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -55,15 +55,6 @@ TRACE - - ..\packages\ICSharpCode.NRefactory.5.5.1\lib\Net40\ICSharpCode.NRefactory.dll - - - ..\packages\ICSharpCode.NRefactory.5.5.1\lib\Net40\ICSharpCode.NRefactory.Cecil.dll - - - ..\packages\ICSharpCode.NRefactory.5.5.1\lib\Net40\ICSharpCode.NRefactory.CSharp.dll - 3.0 @@ -262,7 +253,6 @@ - @@ -392,10 +382,18 @@ {63E6915C-7EA4-4D76-AB28-0D7191EEA626} Mono.Cecil.Pdb + + {53DCA265-3C3C-42F9-B647-F72BA678122B} + ICSharpCode.NRefactory.CSharp + {7B82B671-419F-45F4-B778-D9286F996EFA} ICSharpCode.NRefactory.VB + + {3B2A5653-EC97-4001-BB9B-D90F1AF2C371} + ICSharpCode.NRefactory + {DDE2A481-8271-4EAC-A330-8FA6A38D13D1} ICSharpCode.TreeView diff --git a/ILSpy/VB/ILSpyEnvironmentProvider.cs b/ILSpy/VB/ILSpyEnvironmentProvider.cs index 279000d78..78c46da12 100644 --- a/ILSpy/VB/ILSpyEnvironmentProvider.cs +++ b/ILSpy/VB/ILSpyEnvironmentProvider.cs @@ -36,7 +36,7 @@ namespace ICSharpCode.ILSpy.VB } } - readonly CecilLoader loader = new CecilLoader(); + //readonly CecilLoader loader = new CecilLoader(); public string GetTypeNameForAttribute(ICSharpCode.NRefactory.CSharp.Attribute attribute) { diff --git a/ILSpy/packages.config b/ILSpy/packages.config deleted file mode 100644 index ade7de76c..000000000 --- a/ILSpy/packages.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/NRefactory/.gitattributes b/NRefactory/.gitattributes index 1a1533024..fbfb41be8 100644 --- a/NRefactory/.gitattributes +++ b/NRefactory/.gitattributes @@ -1,2 +1,3 @@ -*.sln -crlf -*.csproj -crlf \ No newline at end of file +*.cs text diff=csharp +*.sln text eol=crlf +*.csproj text eol=crlf diff --git a/NRefactory/.gitignore b/NRefactory/.gitignore index 0031dcbe2..813e7d15c 100644 --- a/NRefactory/.gitignore +++ b/NRefactory/.gitignore @@ -1,5 +1,6 @@ bin obj +*.suo /lib/*.dll /ICSharpCode.NRefactory.Tests/PartCover/* _ReSharper*/* diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/AnnotationNames.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/AnnotationNames.cs new file mode 100644 index 000000000..94d19e4b6 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/AnnotationNames.cs @@ -0,0 +1,48 @@ +// +// Annotations.cs +// +// Author: +// Luís Reis +// +// Copyright (c) 2013 Luís Reis +// +// 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + public static class AnnotationNames + { + //Used const instead of readonly to allow values to be used in switch cases. + + public const string AssertionMethodAttribute = "JetBrains.Annotations.AssertionMethodAttribute"; + public const string AssertionConditionAttribute = "JetBrains.Annotations.AssertionConditionAttribute"; + public const string AssertionConditionTypeAttribute = "JetBrains.Annotations.AssertionConditionType"; + + public const string AssertionConditionTypeIsTrue = "JetBrains.Annotations.AssertionConditionType.IS_TRUE"; + public const string AssertionConditionTypeIsFalse = "JetBrains.Annotations.AssertionConditionType.IS_FALSE"; + public const string AssertionConditionTypeIsNull = "JetBrains.Annotations.AssertionConditionType.IS_NULL"; + public const string AssertionConditionTypeIsNotNull = "JetBrains.Annotations.AssertionConditionType.IS_NOT_NULL"; + + public const string NotNullAttribute = "JetBrains.Annotations.NotNullAttribute"; + public const string CanBeNullAttribute = "JetBrains.Annotations.CanBeNullAttribute"; + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/ControlFlow.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/ControlFlow.cs new file mode 100644 index 000000000..2d3895b1e --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/ControlFlow.cs @@ -0,0 +1,803 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using ICSharpCode.NRefactory.CSharp.Resolver; +using ICSharpCode.NRefactory.CSharp.TypeSystem; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem.Implementation; +using ICSharpCode.NRefactory.Utils; + +namespace ICSharpCode.NRefactory.CSharp.Analysis +{ + /// + /// Represents a node in the control flow graph of a C# method. + /// + public class ControlFlowNode + { + public readonly Statement PreviousStatement; + public readonly Statement NextStatement; + + public readonly ControlFlowNodeType Type; + + public readonly List Outgoing = new List(); + public readonly List Incoming = new List(); + + public ControlFlowNode(Statement previousStatement, Statement nextStatement, ControlFlowNodeType type) + { + if (previousStatement == null && nextStatement == null) + throw new ArgumentException("previousStatement and nextStatement must not be both null"); + this.PreviousStatement = previousStatement; + this.NextStatement = nextStatement; + this.Type = type; + } + } + + public enum ControlFlowNodeType + { + /// + /// Unknown node type + /// + None, + /// + /// Node in front of a statement + /// + StartNode, + /// + /// Node between two statements + /// + BetweenStatements, + /// + /// Node at the end of a statement list + /// + EndNode, + /// + /// Node representing the position before evaluating the condition of a loop. + /// + LoopCondition + } + + public class ControlFlowEdge + { + public readonly ControlFlowNode From; + public readonly ControlFlowNode To; + public readonly ControlFlowEdgeType Type; + + List jumpOutOfTryFinally; + + public ControlFlowEdge(ControlFlowNode from, ControlFlowNode to, ControlFlowEdgeType type) + { + if (from == null) + throw new ArgumentNullException("from"); + if (to == null) + throw new ArgumentNullException("to"); + this.From = from; + this.To = to; + this.Type = type; + } + + internal void AddJumpOutOfTryFinally(TryCatchStatement tryFinally) + { + if (jumpOutOfTryFinally == null) + jumpOutOfTryFinally = new List(); + jumpOutOfTryFinally.Add(tryFinally); + } + + /// + /// Gets whether this control flow edge is leaving any try-finally statements. + /// + public bool IsLeavingTryFinally { + get { return jumpOutOfTryFinally != null; } + } + + /// + /// Gets the try-finally statements that this control flow edge is leaving. + /// + public IEnumerable TryFinallyStatements { + get { return jumpOutOfTryFinally ?? Enumerable.Empty(); } + } + } + + public enum ControlFlowEdgeType + { + /// + /// Regular control flow. + /// + Normal, + /// + /// Conditional control flow (edge taken if condition is true) + /// + ConditionTrue, + /// + /// Conditional control flow (edge taken if condition is false) + /// + ConditionFalse, + /// + /// A jump statement (goto, goto case, break or continue) + /// + Jump + } + + /// + /// Constructs the control flow graph for C# statements. + /// + public class ControlFlowGraphBuilder + { + // Written according to the reachability rules in the C# spec (§8.1 End points and reachability) + + protected virtual ControlFlowNode CreateNode(Statement previousStatement, Statement nextStatement, ControlFlowNodeType type) + { + cancellationToken.ThrowIfCancellationRequested(); + return new ControlFlowNode(previousStatement, nextStatement, type); + } + + protected virtual ControlFlowEdge CreateEdge(ControlFlowNode from, ControlFlowNode to, ControlFlowEdgeType type) + { + cancellationToken.ThrowIfCancellationRequested(); + return new ControlFlowEdge(from, to, type); + } + + Statement rootStatement; + CSharpTypeResolveContext typeResolveContext; + Func resolver; + List nodes; + Dictionary labels; + List gotoStatements; + CancellationToken cancellationToken; + + public IList BuildControlFlowGraph(Statement statement, CancellationToken cancellationToken = default(CancellationToken)) + { + if (statement == null) + throw new ArgumentNullException("statement"); + CSharpResolver r = new CSharpResolver(MinimalCorlib.Instance.CreateCompilation()); + return BuildControlFlowGraph(statement, new CSharpAstResolver(r, statement), cancellationToken); + } + + public IList BuildControlFlowGraph(Statement statement, CSharpAstResolver resolver, CancellationToken cancellationToken = default(CancellationToken)) + { + if (statement == null) + throw new ArgumentNullException("statement"); + if (resolver == null) + throw new ArgumentNullException("resolver"); + return BuildControlFlowGraph(statement, resolver.Resolve, resolver.TypeResolveContext, cancellationToken); + } + + internal IList BuildControlFlowGraph(Statement statement, Func resolver, CSharpTypeResolveContext typeResolveContext, CancellationToken cancellationToken) + { + NodeCreationVisitor nodeCreationVisitor = new NodeCreationVisitor(); + nodeCreationVisitor.builder = this; + try { + this.nodes = new List(); + this.labels = new Dictionary(); + this.gotoStatements = new List(); + this.rootStatement = statement; + this.resolver = resolver; + this.typeResolveContext = typeResolveContext; + this.cancellationToken = cancellationToken; + + ControlFlowNode entryPoint = CreateStartNode(statement); + statement.AcceptVisitor(nodeCreationVisitor, entryPoint); + + // Resolve goto statements: + foreach (ControlFlowNode gotoStmt in gotoStatements) { + string label = ((GotoStatement)gotoStmt.NextStatement).Label; + ControlFlowNode labelNode; + if (labels.TryGetValue(label, out labelNode)) + nodeCreationVisitor.Connect(gotoStmt, labelNode, ControlFlowEdgeType.Jump); + } + + AnnotateLeaveEdgesWithTryFinallyBlocks(); + + return nodes; + } finally { + this.nodes = null; + this.labels = null; + this.gotoStatements = null; + this.rootStatement = null; + this.resolver = null; + this.typeResolveContext = null; + this.cancellationToken = CancellationToken.None; + } + } + + void AnnotateLeaveEdgesWithTryFinallyBlocks() + { + foreach (ControlFlowEdge edge in nodes.SelectMany(n => n.Outgoing)) { + if (edge.Type != ControlFlowEdgeType.Jump) { + // Only jumps are potential candidates for leaving try-finally blocks. + // Note that the regular edges leaving try or catch blocks are already annotated by the visitor. + continue; + } + Statement gotoStatement = edge.From.NextStatement; + Debug.Assert(gotoStatement is GotoStatement || gotoStatement is GotoDefaultStatement || gotoStatement is GotoCaseStatement || gotoStatement is BreakStatement || gotoStatement is ContinueStatement); + Statement targetStatement = edge.To.PreviousStatement ?? edge.To.NextStatement; + if (gotoStatement.Parent == targetStatement.Parent) + continue; + HashSet targetParentTryCatch = new HashSet(targetStatement.Ancestors.OfType()); + for (AstNode node = gotoStatement.Parent; node != null; node = node.Parent) { + TryCatchStatement leftTryCatch = node as TryCatchStatement; + if (leftTryCatch != null) { + if (targetParentTryCatch.Contains(leftTryCatch)) + break; + if (!leftTryCatch.FinallyBlock.IsNull) + edge.AddJumpOutOfTryFinally(leftTryCatch); + } + } + } + } + + #region Create*Node + ControlFlowNode CreateStartNode(Statement statement) + { + if (statement.IsNull) + return null; + ControlFlowNode node = CreateNode(null, statement, ControlFlowNodeType.StartNode); + nodes.Add(node); + return node; + } + + ControlFlowNode CreateSpecialNode(Statement statement, ControlFlowNodeType type, bool addToNodeList = true) + { + ControlFlowNode node = CreateNode(null, statement, type); + if (addToNodeList) + nodes.Add(node); + return node; + } + + ControlFlowNode CreateEndNode(Statement statement, bool addToNodeList = true) + { + Statement nextStatement; + if (statement == rootStatement) { + nextStatement = null; + } else { + // Find the next statement in the same role: + AstNode next = statement; + do { + next = next.NextSibling; + } while (next != null && next.Role != statement.Role); + nextStatement = next as Statement; + } + ControlFlowNodeType type = nextStatement != null ? ControlFlowNodeType.BetweenStatements : ControlFlowNodeType.EndNode; + ControlFlowNode node = CreateNode(statement, nextStatement, type); + if (addToNodeList) + nodes.Add(node); + return node; + } + #endregion + + #region Constant evaluation + /// + /// Gets/Sets whether to handle only primitive expressions as constants (no complex expressions like "a + b"). + /// + public bool EvaluateOnlyPrimitiveConstants { get; set; } + + /// + /// Evaluates an expression. + /// + /// The constant value of the expression; or null if the expression is not a constant. + ResolveResult EvaluateConstant(Expression expr) + { + if (expr.IsNull) + return null; + if (EvaluateOnlyPrimitiveConstants) { + if (!(expr is PrimitiveExpression || expr is NullReferenceExpression)) + return null; + } + return resolver(expr, cancellationToken); + } + + /// + /// Evaluates an expression. + /// + /// The value of the constant boolean expression; or null if the value is not a constant boolean expression. + bool? EvaluateCondition(Expression expr) + { + ResolveResult rr = EvaluateConstant(expr); + if (rr != null && rr.IsCompileTimeConstant) + return rr.ConstantValue as bool?; + else + return null; + } + + bool AreEqualConstants(ResolveResult c1, ResolveResult c2) + { + if (c1 == null || c2 == null || !c1.IsCompileTimeConstant || !c2.IsCompileTimeConstant) + return false; + CSharpResolver r = new CSharpResolver(typeResolveContext); + ResolveResult c = r.ResolveBinaryOperator(BinaryOperatorType.Equality, c1, c2); + return c.IsCompileTimeConstant && (c.ConstantValue as bool?) == true; + } + #endregion + + sealed class NodeCreationVisitor : DepthFirstAstVisitor + { + // 'data' parameter: input control flow node (start of statement being visited) + // Return value: result control flow node (end of statement being visited) + + internal ControlFlowGraphBuilder builder; + Stack breakTargets = new Stack(); + Stack continueTargets = new Stack(); + List gotoCaseOrDefault = new List(); + + internal ControlFlowEdge Connect(ControlFlowNode from, ControlFlowNode to, ControlFlowEdgeType type = ControlFlowEdgeType.Normal) + { + if (from == null || to == null) + return null; + ControlFlowEdge edge = builder.CreateEdge(from, to, type); + from.Outgoing.Add(edge); + to.Incoming.Add(edge); + return edge; + } + + /// + /// Creates an end node for stmt and connects from with the new node. + /// + ControlFlowNode CreateConnectedEndNode(Statement stmt, ControlFlowNode from) + { + ControlFlowNode newNode = builder.CreateEndNode(stmt); + Connect(from, newNode); + return newNode; + } + + protected override ControlFlowNode VisitChildren(AstNode node, ControlFlowNode data) + { + // We have overrides for all possible statements and should visit statements only. + throw new NotSupportedException(); + } + + public override ControlFlowNode VisitBlockStatement(BlockStatement blockStatement, ControlFlowNode data) + { + // C# 4.0 spec: §8.2 Blocks + ControlFlowNode childNode = HandleStatementList(blockStatement.Statements, data); + return CreateConnectedEndNode(blockStatement, childNode); + } + + ControlFlowNode HandleStatementList(AstNodeCollection statements, ControlFlowNode source) + { + ControlFlowNode childNode = null; + foreach (Statement stmt in statements) { + if (childNode == null) { + childNode = builder.CreateStartNode(stmt); + if (source != null) + Connect(source, childNode); + } + Debug.Assert(childNode.NextStatement == stmt); + childNode = stmt.AcceptVisitor(this, childNode); + Debug.Assert(childNode.PreviousStatement == stmt); + } + return childNode ?? source; + } + + public override ControlFlowNode VisitEmptyStatement(EmptyStatement emptyStatement, ControlFlowNode data) + { + return CreateConnectedEndNode(emptyStatement, data); + } + + public override ControlFlowNode VisitLabelStatement(LabelStatement labelStatement, ControlFlowNode data) + { + ControlFlowNode end = CreateConnectedEndNode(labelStatement, data); + builder.labels[labelStatement.Label] = end; + return end; + } + + public override ControlFlowNode VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement, ControlFlowNode data) + { + return CreateConnectedEndNode(variableDeclarationStatement, data); + } + + public override ControlFlowNode VisitExpressionStatement(ExpressionStatement expressionStatement, ControlFlowNode data) + { + return CreateConnectedEndNode(expressionStatement, data); + } + + public override ControlFlowNode VisitIfElseStatement(IfElseStatement ifElseStatement, ControlFlowNode data) + { + bool? cond = builder.EvaluateCondition(ifElseStatement.Condition); + + ControlFlowNode trueBegin = builder.CreateStartNode(ifElseStatement.TrueStatement); + if (cond != false) + Connect(data, trueBegin, ControlFlowEdgeType.ConditionTrue); + ControlFlowNode trueEnd = ifElseStatement.TrueStatement.AcceptVisitor(this, trueBegin); + + ControlFlowNode falseBegin = builder.CreateStartNode(ifElseStatement.FalseStatement); + if (cond != true) + Connect(data, falseBegin, ControlFlowEdgeType.ConditionFalse); + ControlFlowNode falseEnd = ifElseStatement.FalseStatement.AcceptVisitor(this, falseBegin); + // (if no else statement exists, both falseBegin and falseEnd will be null) + + ControlFlowNode end = builder.CreateEndNode(ifElseStatement); + Connect(trueEnd, end); + if (falseEnd != null) { + Connect(falseEnd, end); + } else if (cond != true) { + Connect(data, end, ControlFlowEdgeType.ConditionFalse); + } + return end; + } + + public override ControlFlowNode VisitSwitchStatement(SwitchStatement switchStatement, ControlFlowNode data) + { + // First, figure out which switch section will get called (if the expression is constant): + ResolveResult constant = builder.EvaluateConstant(switchStatement.Expression); + SwitchSection defaultSection = null; + SwitchSection sectionMatchedByConstant = null; + foreach (SwitchSection section in switchStatement.SwitchSections) { + foreach (CaseLabel label in section.CaseLabels) { + if (label.Expression.IsNull) { + defaultSection = section; + } else if (constant != null && constant.IsCompileTimeConstant) { + ResolveResult labelConstant = builder.EvaluateConstant(label.Expression); + if (builder.AreEqualConstants(constant, labelConstant)) + sectionMatchedByConstant = section; + } + } + } + if (constant != null && constant.IsCompileTimeConstant && sectionMatchedByConstant == null) + sectionMatchedByConstant = defaultSection; + + int gotoCaseOrDefaultInOuterScope = gotoCaseOrDefault.Count; + List sectionStartNodes = new List(); + + ControlFlowNode end = builder.CreateEndNode(switchStatement, addToNodeList: false); + breakTargets.Push(end); + foreach (SwitchSection section in switchStatement.SwitchSections) { + int sectionStartNodeID = builder.nodes.Count; + if (constant == null || !constant.IsCompileTimeConstant || section == sectionMatchedByConstant) { + HandleStatementList(section.Statements, data); + } else { + // This section is unreachable: pass null to HandleStatementList. + HandleStatementList(section.Statements, null); + } + // Don't bother connecting the ends of the sections: the 'break' statement takes care of that. + + // Store the section start node for 'goto case' statements. + sectionStartNodes.Add(sectionStartNodeID < builder.nodes.Count ? builder.nodes[sectionStartNodeID] : null); + } + breakTargets.Pop(); + if (defaultSection == null && sectionMatchedByConstant == null) { + Connect(data, end); + } + + if (gotoCaseOrDefault.Count > gotoCaseOrDefaultInOuterScope) { + // Resolve 'goto case' statements: + for (int i = gotoCaseOrDefaultInOuterScope; i < gotoCaseOrDefault.Count; i++) { + ControlFlowNode gotoCaseNode = gotoCaseOrDefault[i]; + GotoCaseStatement gotoCaseStatement = gotoCaseNode.NextStatement as GotoCaseStatement; + ResolveResult gotoCaseConstant = null; + if (gotoCaseStatement != null) { + gotoCaseConstant = builder.EvaluateConstant(gotoCaseStatement.LabelExpression); + } + int targetSectionIndex = -1; + int currentSectionIndex = 0; + foreach (SwitchSection section in switchStatement.SwitchSections) { + foreach (CaseLabel label in section.CaseLabels) { + if (gotoCaseStatement != null) { + // goto case + if (!label.Expression.IsNull) { + ResolveResult labelConstant = builder.EvaluateConstant(label.Expression); + if (builder.AreEqualConstants(gotoCaseConstant, labelConstant)) + targetSectionIndex = currentSectionIndex; + } + } else { + // goto default + if (label.Expression.IsNull) + targetSectionIndex = currentSectionIndex; + } + } + currentSectionIndex++; + } + if (targetSectionIndex >= 0 && sectionStartNodes[targetSectionIndex] != null) + Connect(gotoCaseNode, sectionStartNodes[targetSectionIndex], ControlFlowEdgeType.Jump); + else + Connect(gotoCaseNode, end, ControlFlowEdgeType.Jump); + } + gotoCaseOrDefault.RemoveRange(gotoCaseOrDefaultInOuterScope, gotoCaseOrDefault.Count - gotoCaseOrDefaultInOuterScope); + } + + builder.nodes.Add(end); + return end; + } + + public override ControlFlowNode VisitGotoCaseStatement(GotoCaseStatement gotoCaseStatement, ControlFlowNode data) + { + gotoCaseOrDefault.Add(data); + return builder.CreateEndNode(gotoCaseStatement); + } + + public override ControlFlowNode VisitGotoDefaultStatement(GotoDefaultStatement gotoDefaultStatement, ControlFlowNode data) + { + gotoCaseOrDefault.Add(data); + return builder.CreateEndNode(gotoDefaultStatement); + } + + public override ControlFlowNode VisitWhileStatement(WhileStatement whileStatement, ControlFlowNode data) + { + // while (cond) { embeddedStmt; } + ControlFlowNode end = builder.CreateEndNode(whileStatement, addToNodeList: false); + ControlFlowNode conditionNode = builder.CreateSpecialNode(whileStatement, ControlFlowNodeType.LoopCondition); + breakTargets.Push(end); + continueTargets.Push(conditionNode); + + Connect(data, conditionNode); + + bool? cond = builder.EvaluateCondition(whileStatement.Condition); + ControlFlowNode bodyStart = builder.CreateStartNode(whileStatement.EmbeddedStatement); + if (cond != false) + Connect(conditionNode, bodyStart, ControlFlowEdgeType.ConditionTrue); + ControlFlowNode bodyEnd = whileStatement.EmbeddedStatement.AcceptVisitor(this, bodyStart); + Connect(bodyEnd, conditionNode); + if (cond != true) + Connect(conditionNode, end, ControlFlowEdgeType.ConditionFalse); + + breakTargets.Pop(); + continueTargets.Pop(); + builder.nodes.Add(end); + return end; + } + + public override ControlFlowNode VisitDoWhileStatement(DoWhileStatement doWhileStatement, ControlFlowNode data) + { + // do { embeddedStmt; } while(cond); + ControlFlowNode end = builder.CreateEndNode(doWhileStatement, addToNodeList: false); + ControlFlowNode conditionNode = builder.CreateSpecialNode(doWhileStatement, ControlFlowNodeType.LoopCondition, addToNodeList: false); + breakTargets.Push(end); + continueTargets.Push(conditionNode); + + ControlFlowNode bodyStart = builder.CreateStartNode(doWhileStatement.EmbeddedStatement); + Connect(data, bodyStart); + ControlFlowNode bodyEnd = doWhileStatement.EmbeddedStatement.AcceptVisitor(this, bodyStart); + Connect(bodyEnd, conditionNode); + + bool? cond = builder.EvaluateCondition(doWhileStatement.Condition); + if (cond != false) + Connect(conditionNode, bodyStart, ControlFlowEdgeType.ConditionTrue); + if (cond != true) + Connect(conditionNode, end, ControlFlowEdgeType.ConditionFalse); + + breakTargets.Pop(); + continueTargets.Pop(); + builder.nodes.Add(conditionNode); + builder.nodes.Add(end); + return end; + } + + public override ControlFlowNode VisitForStatement(ForStatement forStatement, ControlFlowNode data) + { + data = HandleStatementList(forStatement.Initializers, data); + // for (initializers ; cond; iterators) { embeddedStmt; } + ControlFlowNode end = builder.CreateEndNode(forStatement, addToNodeList: false); + ControlFlowNode conditionNode = builder.CreateSpecialNode(forStatement, ControlFlowNodeType.LoopCondition); + Connect(data, conditionNode); + + int iteratorStartNodeID = builder.nodes.Count; + ControlFlowNode iteratorEnd = HandleStatementList(forStatement.Iterators, null); + ControlFlowNode iteratorStart; + if (iteratorEnd != null) { + iteratorStart = builder.nodes[iteratorStartNodeID]; + Connect(iteratorEnd, conditionNode); + } else { + iteratorStart = conditionNode; + } + + breakTargets.Push(end); + continueTargets.Push(iteratorStart); + + ControlFlowNode bodyStart = builder.CreateStartNode(forStatement.EmbeddedStatement); + ControlFlowNode bodyEnd = forStatement.EmbeddedStatement.AcceptVisitor(this, bodyStart); + Connect(bodyEnd, iteratorStart); + + breakTargets.Pop(); + continueTargets.Pop(); + + bool? cond = forStatement.Condition.IsNull ? true : builder.EvaluateCondition(forStatement.Condition); + if (cond != false) + Connect(conditionNode, bodyStart, ControlFlowEdgeType.ConditionTrue); + if (cond != true) + Connect(conditionNode, end, ControlFlowEdgeType.ConditionFalse); + + builder.nodes.Add(end); + return end; + } + + ControlFlowNode HandleEmbeddedStatement(Statement embeddedStatement, ControlFlowNode source) + { + if (embeddedStatement == null || embeddedStatement.IsNull) + return source; + ControlFlowNode bodyStart = builder.CreateStartNode(embeddedStatement); + if (source != null) + Connect(source, bodyStart); + return embeddedStatement.AcceptVisitor(this, bodyStart); + } + + public override ControlFlowNode VisitForeachStatement(ForeachStatement foreachStatement, ControlFlowNode data) + { + // foreach (...) { embeddedStmt } + ControlFlowNode end = builder.CreateEndNode(foreachStatement, addToNodeList: false); + ControlFlowNode conditionNode = builder.CreateSpecialNode(foreachStatement, ControlFlowNodeType.LoopCondition); + Connect(data, conditionNode); + + breakTargets.Push(end); + continueTargets.Push(conditionNode); + + ControlFlowNode bodyEnd = HandleEmbeddedStatement(foreachStatement.EmbeddedStatement, conditionNode); + Connect(bodyEnd, conditionNode); + + breakTargets.Pop(); + continueTargets.Pop(); + + Connect(conditionNode, end); + builder.nodes.Add(end); + return end; + } + + public override ControlFlowNode VisitBreakStatement(BreakStatement breakStatement, ControlFlowNode data) + { + if (breakTargets.Count > 0) + Connect(data, breakTargets.Peek(), ControlFlowEdgeType.Jump); + return builder.CreateEndNode(breakStatement); + } + + public override ControlFlowNode VisitContinueStatement(ContinueStatement continueStatement, ControlFlowNode data) + { + if (continueTargets.Count > 0) + Connect(data, continueTargets.Peek(), ControlFlowEdgeType.Jump); + return builder.CreateEndNode(continueStatement); + } + + public override ControlFlowNode VisitGotoStatement(GotoStatement gotoStatement, ControlFlowNode data) + { + builder.gotoStatements.Add(data); + return builder.CreateEndNode(gotoStatement); + } + + public override ControlFlowNode VisitReturnStatement(ReturnStatement returnStatement, ControlFlowNode data) + { + return builder.CreateEndNode(returnStatement); // end not connected with data + } + + public override ControlFlowNode VisitThrowStatement(ThrowStatement throwStatement, ControlFlowNode data) + { + return builder.CreateEndNode(throwStatement); // end not connected with data + } + + public override ControlFlowNode VisitTryCatchStatement(TryCatchStatement tryCatchStatement, ControlFlowNode data) + { + ControlFlowNode end = builder.CreateEndNode(tryCatchStatement, addToNodeList: false); + var edge = Connect(HandleEmbeddedStatement(tryCatchStatement.TryBlock, data), end); + if (!tryCatchStatement.FinallyBlock.IsNull) + edge.AddJumpOutOfTryFinally(tryCatchStatement); + foreach (CatchClause cc in tryCatchStatement.CatchClauses) { + edge = Connect(HandleEmbeddedStatement(cc.Body, data), end); + if (!tryCatchStatement.FinallyBlock.IsNull) + edge.AddJumpOutOfTryFinally(tryCatchStatement); + } + if (!tryCatchStatement.FinallyBlock.IsNull) { + // Don't connect the end of the try-finally block to anything. + // Consumers of the CFG will have to special-case try-finally. + HandleEmbeddedStatement(tryCatchStatement.FinallyBlock, data); + } + builder.nodes.Add(end); + return end; + } + + public override ControlFlowNode VisitCheckedStatement(CheckedStatement checkedStatement, ControlFlowNode data) + { + ControlFlowNode bodyEnd = HandleEmbeddedStatement(checkedStatement.Body, data); + return CreateConnectedEndNode(checkedStatement, bodyEnd); + } + + public override ControlFlowNode VisitUncheckedStatement(UncheckedStatement uncheckedStatement, ControlFlowNode data) + { + ControlFlowNode bodyEnd = HandleEmbeddedStatement(uncheckedStatement.Body, data); + return CreateConnectedEndNode(uncheckedStatement, bodyEnd); + } + + public override ControlFlowNode VisitLockStatement(LockStatement lockStatement, ControlFlowNode data) + { + ControlFlowNode bodyEnd = HandleEmbeddedStatement(lockStatement.EmbeddedStatement, data); + return CreateConnectedEndNode(lockStatement, bodyEnd); + } + + public override ControlFlowNode VisitUsingStatement(UsingStatement usingStatement, ControlFlowNode data) + { + data = HandleEmbeddedStatement(usingStatement.ResourceAcquisition as Statement, data); + ControlFlowNode bodyEnd = HandleEmbeddedStatement(usingStatement.EmbeddedStatement, data); + return CreateConnectedEndNode(usingStatement, bodyEnd); + } + + public override ControlFlowNode VisitYieldReturnStatement(YieldReturnStatement yieldStatement, ControlFlowNode data) + { + return CreateConnectedEndNode(yieldStatement, data); + } + + public override ControlFlowNode VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement, ControlFlowNode data) + { + return builder.CreateEndNode(yieldBreakStatement); // end not connected with data + } + + public override ControlFlowNode VisitUnsafeStatement(UnsafeStatement unsafeStatement, ControlFlowNode data) + { + ControlFlowNode bodyEnd = HandleEmbeddedStatement(unsafeStatement.Body, data); + return CreateConnectedEndNode(unsafeStatement, bodyEnd); + } + + public override ControlFlowNode VisitFixedStatement(FixedStatement fixedStatement, ControlFlowNode data) + { + ControlFlowNode bodyEnd = HandleEmbeddedStatement(fixedStatement.EmbeddedStatement, data); + return CreateConnectedEndNode(fixedStatement, bodyEnd); + } + } + + /// + /// Debugging helper that exports a control flow graph. + /// + public static GraphVizGraph ExportGraph(IList nodes) + { + GraphVizGraph g = new GraphVizGraph(); + GraphVizNode[] n = new GraphVizNode[nodes.Count]; + Dictionary dict = new Dictionary(); + for (int i = 0; i < n.Length; i++) { + dict.Add(nodes[i], i); + n[i] = new GraphVizNode(i); + string name = "#" + i + " = "; + switch (nodes[i].Type) { + case ControlFlowNodeType.StartNode: + case ControlFlowNodeType.BetweenStatements: + name += nodes[i].NextStatement.DebugToString(); + break; + case ControlFlowNodeType.EndNode: + name += "End of " + nodes[i].PreviousStatement.DebugToString(); + break; + case ControlFlowNodeType.LoopCondition: + name += "Condition in " + nodes[i].NextStatement.DebugToString(); + break; + default: + name += "?"; + break; + } + n[i].label = name; + g.AddNode(n[i]); + } + for (int i = 0; i < n.Length; i++) { + foreach (ControlFlowEdge edge in nodes[i].Outgoing) { + GraphVizEdge ge = new GraphVizEdge(i, dict[edge.To]); + if (edge.IsLeavingTryFinally) + ge.style = "dashed"; + switch (edge.Type) { + case ControlFlowEdgeType.ConditionTrue: + ge.color = "green"; + break; + case ControlFlowEdgeType.ConditionFalse: + ge.color = "red"; + break; + case ControlFlowEdgeType.Jump: + ge.color = "blue"; + break; + } + g.AddEdge(ge); + } + } + return g; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/DeclarationSpace/LocalDeclarationSpace.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/DeclarationSpace/LocalDeclarationSpace.cs new file mode 100644 index 000000000..a412cba3a --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/DeclarationSpace/LocalDeclarationSpace.cs @@ -0,0 +1,157 @@ +// +// LovalVariableDeclarationSpace.cs +// +// Author: +// Simon Lindgren +// +// Copyright (c) 2013 Simon Lindgren +// +// 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 ICSharpCode.NRefactory.Utils; +using System.Collections.Generic; +using System.Linq; +using System; + +namespace ICSharpCode.NRefactory.CSharp.Analysis +{ + /// + /// Represents a declaration space. (§3.3) + /// + public class LocalDeclarationSpace + { + /// + /// Maps from variable name to the declarations in this declaration space. + /// + /// + /// This maps from variable name + /// + MultiDictionary declarations = new MultiDictionary (); + + public LocalDeclarationSpace() + { + Children = new List (); + } + + /// + /// The child declaration spaces. + /// + public IList Children { + get; + private set; + } + + /// + /// The parent declaration space. + /// + /// The parent. + public LocalDeclarationSpace Parent { + get; + private set; + } + + /// + /// The names declared in this declaration space, excluding child spaces. + /// + /// The declared names. + public ICollection DeclaredNames { + get { + return declarations.Keys; + } + } + + /// + /// Get all nodes declaring the name specified in . + /// + /// The declaring nodes. + /// The declaration name. + public IEnumerable GetNameDeclarations(string name) + { + return declarations [name].Concat(Children.SelectMany(child => child.GetNameDeclarations(name))); + } + + /// + /// Adds a child declaration space. + /// + /// The to add. + public void AddChildSpace(LocalDeclarationSpace child) + { + if (child == null) + throw new ArgumentNullException("child"); + if (Children.Contains(child)) + throw new InvalidOperationException("the child was already added"); + + Children.Add(child); + child.Parent = this; + } + + /// + /// Adds a new declaration to the declaration space. + /// + /// The name of the declared variable. + /// A node associated with the declaration. + public void AddDeclaration(string name, AstNode node) + { + if (name == null) + throw new ArgumentNullException("name"); + if (node == null) + throw new ArgumentNullException("node"); + declarations.Add(name, node); + } + + /// + /// Determines if the name exists in the this declaration space. + /// + /// true, if the name specified in is used in this variable declaration space, false otherwise. + /// The name to look for. + /// When true, child declaration spaces are included in the search. + public bool ContainsName(string name, bool includeChildren) + { + if (name == null) + throw new ArgumentNullException("name"); + + if (declarations.Keys.Contains(name)) + return true; + return includeChildren && Children.Any(child => child.ContainsName(name, true)); + } + + /// + /// Determines whether the name specified in is used in surrouding code. + /// + /// true if the name is used, false otherwise. + /// The name to check. + /// + /// Contrary to , this method also checks parent declaration spaces + /// for name conflicts. Typically, this will be the right method to use when determining if a name can be used. + /// + public bool IsNameUsed(string name) + { + if (name == null) + throw new ArgumentNullException("name"); + + return IsNameUsedBySelfOrParent(name) || Children.Any(child => child.ContainsName(name, true)); + } + + bool IsNameUsedBySelfOrParent(string name) + { + if (declarations.Keys.Contains(name)) + return true; + return Parent != null && Parent.IsNameUsedBySelfOrParent(name); + } + } +} \ No newline at end of file diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/DeclarationSpace/LocalDeclarationSpaceVisitor.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/DeclarationSpace/LocalDeclarationSpaceVisitor.cs new file mode 100644 index 000000000..08389a413 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/DeclarationSpace/LocalDeclarationSpaceVisitor.cs @@ -0,0 +1,138 @@ +// +// LocalDeclarationSpaceVisitor.cs +// +// Author: +// Simon Lindgren +// +// Copyright (c) 2013 Simon Lindgren +// +// 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; + +namespace ICSharpCode.NRefactory.CSharp.Analysis +{ + public class LocalDeclarationSpaceVisitor : DepthFirstAstVisitor + { + LocalDeclarationSpace currentDeclarationSpace; + Dictionary nodeDeclarationSpaces = new Dictionary(); + + public LocalDeclarationSpace GetDeclarationSpace(AstNode node) + { + if (node == null) + throw new ArgumentNullException("node"); + while (node != null) { + LocalDeclarationSpace declarationSpace; + if (nodeDeclarationSpaces.TryGetValue(node, out declarationSpace)) + return declarationSpace; + node = node.Parent; + } + return null; + } + + #region Visitor + + void AddDeclaration(string name, AstNode node) + { + if (currentDeclarationSpace != null) + currentDeclarationSpace.AddDeclaration(name, node); + } + + public override void VisitVariableInitializer(VariableInitializer variableInitializer) + { + AddDeclaration(variableInitializer.Name, variableInitializer); + base.VisitVariableInitializer(variableInitializer); + } + + public override void VisitParameterDeclaration(ParameterDeclaration parameterDeclaration) + { + AddDeclaration(parameterDeclaration.Name, parameterDeclaration); + base.VisitParameterDeclaration(parameterDeclaration); + } + + void VisitNewDeclarationSpace(AstNode node) + { + var oldDeclarationSpace = currentDeclarationSpace; + currentDeclarationSpace = new LocalDeclarationSpace(); + if (oldDeclarationSpace != null) + oldDeclarationSpace.AddChildSpace(currentDeclarationSpace); + + VisitChildren(node); + + nodeDeclarationSpaces.Add(node, currentDeclarationSpace); + currentDeclarationSpace = oldDeclarationSpace; + } + + #region Declaration space creating nodes + + public override void VisitMethodDeclaration(MethodDeclaration methodDeclaration) + { + VisitNewDeclarationSpace(methodDeclaration); + } + + public override void VisitBlockStatement(BlockStatement blockStatement) + { + VisitNewDeclarationSpace(blockStatement); + } + + public override void VisitSwitchStatement(SwitchStatement switchStatement) + { + VisitNewDeclarationSpace(switchStatement); + } + + public override void VisitForeachStatement(ForeachStatement foreachStatement) + { + AddDeclaration(foreachStatement.VariableName, foreachStatement); + VisitNewDeclarationSpace(foreachStatement); + } + + public override void VisitForStatement(ForStatement forStatement) + { + VisitNewDeclarationSpace(forStatement); + } + + public override void VisitUsingStatement(UsingStatement usingStatement) + { + VisitNewDeclarationSpace(usingStatement); + } + + public override void VisitLambdaExpression(LambdaExpression lambdaExpression) + { + VisitNewDeclarationSpace(lambdaExpression); + } + + public override void VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression) + { + VisitNewDeclarationSpace(anonymousMethodExpression); + } + + public override void VisitEventDeclaration(EventDeclaration eventDeclaration) + { + AddDeclaration(eventDeclaration.Name, eventDeclaration); + } + + public override void VisitCustomEventDeclaration(CustomEventDeclaration eventDeclaration) + { + VisitNewDeclarationSpace(eventDeclaration); + } + + #endregion + #endregion + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/DefiniteAssignmentAnalysis.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/DefiniteAssignmentAnalysis.cs new file mode 100644 index 000000000..9b33e74a7 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/DefiniteAssignmentAnalysis.cs @@ -0,0 +1,759 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading; + +using ICSharpCode.NRefactory.CSharp.Resolver; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem.Implementation; +using ICSharpCode.NRefactory.Utils; + +namespace ICSharpCode.NRefactory.CSharp.Analysis +{ + /// + /// Represents the definite assignment status of a variable at a specific location. + /// + public enum DefiniteAssignmentStatus + { + /// + /// The variable might be assigned or unassigned. + /// + PotentiallyAssigned, + /// + /// The variable is definitely assigned. + /// + DefinitelyAssigned, + /// + /// The variable is definitely assigned iff the expression results in the value 'true'. + /// + AssignedAfterTrueExpression, + /// + /// The variable is definitely assigned iff the expression results in the value 'false'. + /// + AssignedAfterFalseExpression, + /// + /// The code is unreachable. + /// + CodeUnreachable + } + + /// + /// Implements the C# definite assignment analysis (C# 4.0 Spec: §5.3 Definite assignment) + /// + public class DefiniteAssignmentAnalysis + { + sealed class DefiniteAssignmentNode : ControlFlowNode + { + public int Index; + public DefiniteAssignmentStatus NodeStatus; + + public DefiniteAssignmentNode(Statement previousStatement, Statement nextStatement, ControlFlowNodeType type) + : base(previousStatement, nextStatement, type) + { + } + } + + sealed class DerivedControlFlowGraphBuilder : ControlFlowGraphBuilder + { + protected override ControlFlowNode CreateNode(Statement previousStatement, Statement nextStatement, ControlFlowNodeType type) + { + return new DefiniteAssignmentNode(previousStatement, nextStatement, type); + } + } + + readonly DefiniteAssignmentVisitor visitor = new DefiniteAssignmentVisitor(); + readonly List allNodes = new List(); + readonly Dictionary beginNodeDict = new Dictionary(); + readonly Dictionary endNodeDict = new Dictionary(); + readonly Dictionary conditionNodeDict = new Dictionary(); + readonly CSharpAstResolver resolver; + Dictionary edgeStatus = new Dictionary(); + + string variableName; + List unassignedVariableUses = new List(); + int analyzedRangeStart, analyzedRangeEnd; + CancellationToken analysisCancellationToken; + + Queue nodesWithModifiedInput = new Queue(); + + public DefiniteAssignmentAnalysis(Statement rootStatement, CancellationToken cancellationToken) + : this(rootStatement, + new CSharpAstResolver(new CSharpResolver(MinimalCorlib.Instance.CreateCompilation()), rootStatement), + cancellationToken) + { + } + + public DefiniteAssignmentAnalysis(Statement rootStatement, CSharpAstResolver resolver, CancellationToken cancellationToken) + { + if (rootStatement == null) + throw new ArgumentNullException("rootStatement"); + if (resolver == null) + throw new ArgumentNullException("resolver"); + this.resolver = resolver; + + visitor.analysis = this; + DerivedControlFlowGraphBuilder cfgBuilder = new DerivedControlFlowGraphBuilder(); + if (resolver.TypeResolveContext.Compilation.MainAssembly.UnresolvedAssembly is MinimalCorlib) { + cfgBuilder.EvaluateOnlyPrimitiveConstants = true; + } + allNodes.AddRange(cfgBuilder.BuildControlFlowGraph(rootStatement, resolver, cancellationToken).Cast()); + for (int i = 0; i < allNodes.Count; i++) { + DefiniteAssignmentNode node = allNodes[i]; + node.Index = i; // assign numbers to the nodes + if (node.Type == ControlFlowNodeType.StartNode || node.Type == ControlFlowNodeType.BetweenStatements) { + // Anonymous methods have separate control flow graphs, but we also need to analyze those. + // Iterate backwards so that anonymous methods are inserted in the correct order + for (AstNode child = node.NextStatement.LastChild; child != null; child = child.PrevSibling) { + InsertAnonymousMethods(i + 1, child, cfgBuilder, cancellationToken); + } + } + // Now register the node in the dictionaries: + if (node.Type == ControlFlowNodeType.StartNode || node.Type == ControlFlowNodeType.BetweenStatements) + beginNodeDict.Add(node.NextStatement, node); + if (node.Type == ControlFlowNodeType.BetweenStatements || node.Type == ControlFlowNodeType.EndNode) + endNodeDict.Add(node.PreviousStatement, node); + if (node.Type == ControlFlowNodeType.LoopCondition) + conditionNodeDict.Add(node.NextStatement, node); + } + // Verify that we created nodes for all statements: + Debug.Assert(!rootStatement.DescendantsAndSelf.OfType().Except(allNodes.Select(n => n.NextStatement)).Any()); + // Verify that we put all nodes into the dictionaries: + Debug.Assert(rootStatement.DescendantsAndSelf.OfType().All(stmt => beginNodeDict.ContainsKey(stmt))); + Debug.Assert(rootStatement.DescendantsAndSelf.OfType().All(stmt => endNodeDict.ContainsKey(stmt))); + + this.analyzedRangeStart = 0; + this.analyzedRangeEnd = allNodes.Count - 1; + } + + void InsertAnonymousMethods(int insertPos, AstNode node, ControlFlowGraphBuilder cfgBuilder, CancellationToken cancellationToken) + { + // Ignore any statements, as those have their own ControlFlowNode and get handled separately + if (node is Statement) + return; + AnonymousMethodExpression ame = node as AnonymousMethodExpression; + if (ame != null) { + allNodes.InsertRange(insertPos, cfgBuilder.BuildControlFlowGraph(ame.Body, resolver, cancellationToken).Cast()); + return; + } + LambdaExpression lambda = node as LambdaExpression; + if (lambda != null && lambda.Body is Statement) { + allNodes.InsertRange(insertPos, cfgBuilder.BuildControlFlowGraph((Statement)lambda.Body, resolver, cancellationToken).Cast()); + return; + } + // Descend into child expressions + // Iterate backwards so that anonymous methods are inserted in the correct order + for (AstNode child = node.LastChild; child != null; child = child.PrevSibling) { + InsertAnonymousMethods(insertPos, child, cfgBuilder, cancellationToken); + } + } + + /// + /// Gets the unassigned usages of the previously analyzed variable. + /// + public IList UnassignedVariableUses { + get { + return unassignedVariableUses.AsReadOnly(); + } + } + + /// + /// Sets the range of statements to be analyzed. + /// This method can be used to restrict the analysis to only a part of the method. + /// Only the control flow paths that are fully contained within the selected part will be analyzed. + /// + /// By default, both 'start' and 'end' are inclusive. + public void SetAnalyzedRange(Statement start, Statement end, bool startInclusive = true, bool endInclusive = true) + { + var dictForStart = startInclusive ? beginNodeDict : endNodeDict; + var dictForEnd = endInclusive ? endNodeDict : beginNodeDict; + Debug.Assert(dictForStart.ContainsKey(start) && dictForEnd.ContainsKey(end)); + int startIndex = dictForStart[start].Index; + int endIndex = dictForEnd[end].Index; + if (startIndex > endIndex) + throw new ArgumentException("The start statement must be lexically preceding the end statement"); + this.analyzedRangeStart = startIndex; + this.analyzedRangeEnd = endIndex; + } + + public void Analyze(string variable, DefiniteAssignmentStatus initialStatus = DefiniteAssignmentStatus.PotentiallyAssigned, CancellationToken cancellationToken = default(CancellationToken)) + { + this.analysisCancellationToken = cancellationToken; + this.variableName = variable; + try { + // Reset the status: + unassignedVariableUses.Clear(); + foreach (DefiniteAssignmentNode node in allNodes) { + node.NodeStatus = DefiniteAssignmentStatus.CodeUnreachable; + foreach (ControlFlowEdge edge in node.Outgoing) + edgeStatus[edge] = DefiniteAssignmentStatus.CodeUnreachable; + } + + ChangeNodeStatus(allNodes[analyzedRangeStart], initialStatus); + // Iterate as long as the input status of some nodes is changing: + while (nodesWithModifiedInput.Count > 0) { + DefiniteAssignmentNode node = nodesWithModifiedInput.Dequeue(); + DefiniteAssignmentStatus inputStatus = DefiniteAssignmentStatus.CodeUnreachable; + foreach (ControlFlowEdge edge in node.Incoming) { + inputStatus = MergeStatus(inputStatus, edgeStatus[edge]); + } + ChangeNodeStatus(node, inputStatus); + } + } finally { + this.analysisCancellationToken = CancellationToken.None; + this.variableName = null; + } + } + + public DefiniteAssignmentStatus GetStatusBefore(Statement statement) + { + return beginNodeDict[statement].NodeStatus; + } + + public DefiniteAssignmentStatus GetStatusAfter(Statement statement) + { + return endNodeDict[statement].NodeStatus; + } + + public DefiniteAssignmentStatus GetStatusBeforeLoopCondition(Statement statement) + { + return conditionNodeDict[statement].NodeStatus; + } + + /// + /// Exports the CFG. This method is intended to help debugging issues related to definite assignment. + /// + public GraphVizGraph ExportGraph() + { + GraphVizGraph g = new GraphVizGraph(); + g.Title = "DefiniteAssignment - " + variableName; + for (int i = 0; i < allNodes.Count; i++) { + string name = "#" + i + " = " + allNodes[i].NodeStatus.ToString() + Environment.NewLine; + switch (allNodes[i].Type) { + case ControlFlowNodeType.StartNode: + case ControlFlowNodeType.BetweenStatements: + name += allNodes[i].NextStatement.ToString(); + break; + case ControlFlowNodeType.EndNode: + name += "End of " + allNodes[i].PreviousStatement.ToString(); + break; + case ControlFlowNodeType.LoopCondition: + name += "Condition in " + allNodes[i].NextStatement.ToString(); + break; + default: + name += allNodes[i].Type.ToString(); + break; + } + g.AddNode(new GraphVizNode(i) { label = name }); + foreach (ControlFlowEdge edge in allNodes[i].Outgoing) { + GraphVizEdge ge = new GraphVizEdge(i, ((DefiniteAssignmentNode)edge.To).Index); + if (edgeStatus.Count > 0) + ge.label = edgeStatus[edge].ToString(); + if (edge.IsLeavingTryFinally) + ge.style = "dashed"; + switch (edge.Type) { + case ControlFlowEdgeType.ConditionTrue: + ge.color = "green"; + break; + case ControlFlowEdgeType.ConditionFalse: + ge.color = "red"; + break; + case ControlFlowEdgeType.Jump: + ge.color = "blue"; + break; + } + g.AddEdge(ge); + } + } + return g; + } + + static DefiniteAssignmentStatus MergeStatus(DefiniteAssignmentStatus a, DefiniteAssignmentStatus b) + { + // The result will be DefinitelyAssigned if at least one incoming edge is DefinitelyAssigned and all others are unreachable. + // The result will be DefinitelyUnassigned if at least one incoming edge is DefinitelyUnassigned and all others are unreachable. + // The result will be Unreachable if all incoming edges are unreachable. + // Otherwise, the result will be PotentiallyAssigned. + + if (a == b) + return a; + else if (a == DefiniteAssignmentStatus.CodeUnreachable) + return b; + else if (b == DefiniteAssignmentStatus.CodeUnreachable) + return a; + else + return DefiniteAssignmentStatus.PotentiallyAssigned; + } + + void ChangeNodeStatus (DefiniteAssignmentNode node, DefiniteAssignmentStatus inputStatus) + { + if (node.NodeStatus == inputStatus) + return; + node.NodeStatus = inputStatus; + DefiniteAssignmentStatus outputStatus; + switch (node.Type) { + case ControlFlowNodeType.StartNode: + case ControlFlowNodeType.BetweenStatements: + if (node.NextStatement is IfElseStatement) { + // Handle if-else as a condition node + goto case ControlFlowNodeType.LoopCondition; + } + if (inputStatus == DefiniteAssignmentStatus.DefinitelyAssigned) { + // There isn't any way to un-assign variables, so we don't have to check the expression + // if the status already is definitely assigned. + outputStatus = DefiniteAssignmentStatus.DefinitelyAssigned; + } else { + outputStatus = CleanSpecialValues (node.NextStatement.AcceptVisitor (visitor, inputStatus)); + } + break; + case ControlFlowNodeType.EndNode: + outputStatus = inputStatus; + if (node.PreviousStatement.Role == TryCatchStatement.FinallyBlockRole + && (outputStatus == DefiniteAssignmentStatus.DefinitelyAssigned || outputStatus == DefiniteAssignmentStatus.PotentiallyAssigned)) { + TryCatchStatement tryFinally = (TryCatchStatement)node.PreviousStatement.Parent; + // Changing the status on a finally block potentially changes the status of all edges leaving that finally block: + foreach (ControlFlowEdge edge in allNodes.SelectMany(n => n.Outgoing)) { + if (edge.IsLeavingTryFinally && edge.TryFinallyStatements.Contains (tryFinally)) { + DefiniteAssignmentStatus s = edgeStatus [edge]; + if (s == DefiniteAssignmentStatus.PotentiallyAssigned) { + ChangeEdgeStatus (edge, outputStatus); + } + } + } + } + break; + case ControlFlowNodeType.LoopCondition: + ForeachStatement foreachStmt = node.NextStatement as ForeachStatement; + if (foreachStmt != null) { + outputStatus = CleanSpecialValues (foreachStmt.InExpression.AcceptVisitor (visitor, inputStatus)); + if (foreachStmt.VariableName == this.variableName) + outputStatus = DefiniteAssignmentStatus.DefinitelyAssigned; + break; + } else { + Debug.Assert (node.NextStatement is IfElseStatement || node.NextStatement is WhileStatement || node.NextStatement is ForStatement || node.NextStatement is DoWhileStatement); + Expression condition = node.NextStatement.GetChildByRole (Roles.Condition); + if (condition.IsNull) + outputStatus = inputStatus; + else + outputStatus = condition.AcceptVisitor(visitor, inputStatus); + foreach (ControlFlowEdge edge in node.Outgoing) { + if (edge.Type == ControlFlowEdgeType.ConditionTrue && outputStatus == DefiniteAssignmentStatus.AssignedAfterTrueExpression) { + ChangeEdgeStatus(edge, DefiniteAssignmentStatus.DefinitelyAssigned); + } else if (edge.Type == ControlFlowEdgeType.ConditionFalse && outputStatus == DefiniteAssignmentStatus.AssignedAfterFalseExpression) { + ChangeEdgeStatus(edge, DefiniteAssignmentStatus.DefinitelyAssigned); + } else { + ChangeEdgeStatus(edge, CleanSpecialValues(outputStatus)); + } + } + return; + } + default: + throw new InvalidOperationException(); + } + foreach (ControlFlowEdge edge in node.Outgoing) { + ChangeEdgeStatus(edge, outputStatus); + } + } + + void ChangeEdgeStatus(ControlFlowEdge edge, DefiniteAssignmentStatus newStatus) + { + DefiniteAssignmentStatus oldStatus = edgeStatus[edge]; + if (oldStatus == newStatus) + return; + // Ensure that status can cannot change back to CodeUnreachable after it once was reachable. + // Also, don't ever use AssignedAfter... for statements. + if (newStatus == DefiniteAssignmentStatus.CodeUnreachable + || newStatus == DefiniteAssignmentStatus.AssignedAfterFalseExpression + || newStatus == DefiniteAssignmentStatus.AssignedAfterTrueExpression) + { + throw new InvalidOperationException(); + } + // Note that the status can change from DefinitelyAssigned + // back to PotentiallyAssigned as unreachable input edges are + // discovered to be reachable. + + edgeStatus[edge] = newStatus; + DefiniteAssignmentNode targetNode = (DefiniteAssignmentNode)edge.To; + if (analyzedRangeStart <= targetNode.Index && targetNode.Index <= analyzedRangeEnd) { + // TODO: potential optimization: visit previously unreachable nodes with higher priority + // (e.g. use Deque and enqueue previously unreachable nodes at the front, but + // other nodes at the end) + nodesWithModifiedInput.Enqueue(targetNode); + } + } + + /// + /// Evaluates an expression. + /// + /// The constant value of the expression; or null if the expression is not a constant. + ResolveResult EvaluateConstant(Expression expr) + { + return resolver.Resolve(expr, analysisCancellationToken); + } + + /// + /// Evaluates an expression. + /// + /// The value of the constant boolean expression; or null if the value is not a constant boolean expression. + bool? EvaluateCondition(Expression expr) + { + ResolveResult rr = EvaluateConstant(expr); + if (rr != null && rr.IsCompileTimeConstant) + return rr.ConstantValue as bool?; + else + return null; + } + + static DefiniteAssignmentStatus CleanSpecialValues(DefiniteAssignmentStatus status) + { + if (status == DefiniteAssignmentStatus.AssignedAfterTrueExpression) + return DefiniteAssignmentStatus.PotentiallyAssigned; + else if (status == DefiniteAssignmentStatus.AssignedAfterFalseExpression) + return DefiniteAssignmentStatus.PotentiallyAssigned; + else + return status; + } + + sealed class DefiniteAssignmentVisitor : DepthFirstAstVisitor + { + internal DefiniteAssignmentAnalysis analysis; + + // The general approach for unknown nodes is to pass the status through all child nodes in order + protected override DefiniteAssignmentStatus VisitChildren(AstNode node, DefiniteAssignmentStatus data) + { + // the special values are valid as output only, not as input + Debug.Assert(data == CleanSpecialValues(data)); + DefiniteAssignmentStatus status = data; + for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) { + analysis.analysisCancellationToken.ThrowIfCancellationRequested(); + + Debug.Assert(!(child is Statement)); // statements are visited with the CFG, not with the visitor pattern + status = child.AcceptVisitor(this, status); + status = CleanSpecialValues(status); + } + return status; + } + + #region Statements + // For statements, the visitor only describes the effect of the statement itself; + // we do not consider the effect of any nested statements. + // This is done because the nested statements will be reached using the control flow graph. + + // In fact, these methods are present so that the default logic in VisitChildren does not try to visit the nested statements. + + public override DefiniteAssignmentStatus VisitBlockStatement(BlockStatement blockStatement, DefiniteAssignmentStatus data) + { + return data; + } + + public override DefiniteAssignmentStatus VisitCheckedStatement(CheckedStatement checkedStatement, DefiniteAssignmentStatus data) + { + return data; + } + + public override DefiniteAssignmentStatus VisitUncheckedStatement(UncheckedStatement uncheckedStatement, DefiniteAssignmentStatus data) + { + return data; + } + + // ExpressionStatement handled by default logic + // VariableDeclarationStatement handled by default logic + + public override DefiniteAssignmentStatus VisitVariableInitializer(VariableInitializer variableInitializer, DefiniteAssignmentStatus data) + { + if (variableInitializer.Initializer.IsNull) { + return data; + } else { + DefiniteAssignmentStatus status = variableInitializer.Initializer.AcceptVisitor(this, data); + if (variableInitializer.Name == analysis.variableName) + return DefiniteAssignmentStatus.DefinitelyAssigned; + else + return status; + } + } + + // IfStatement not handled by visitor, but special-cased in the code consuming the control flow graph + + public override DefiniteAssignmentStatus VisitSwitchStatement(SwitchStatement switchStatement, DefiniteAssignmentStatus data) + { + return switchStatement.Expression.AcceptVisitor(this, data); + } + + public override DefiniteAssignmentStatus VisitWhileStatement(WhileStatement whileStatement, DefiniteAssignmentStatus data) + { + return data; // condition is handled by special condition CFG node + } + + public override DefiniteAssignmentStatus VisitDoWhileStatement(DoWhileStatement doWhileStatement, DefiniteAssignmentStatus data) + { + return data; // condition is handled by special condition CFG node + } + + public override DefiniteAssignmentStatus VisitForStatement(ForStatement forStatement, DefiniteAssignmentStatus data) + { + return data; // condition is handled by special condition CFG node; initializer and iterator statements are handled by CFG + } + + // Break/Continue/Goto: handled by default logic + + // ThrowStatement: handled by default logic (just visit the expression) + // ReturnStatement: handled by default logic (just visit the expression) + + public override DefiniteAssignmentStatus VisitTryCatchStatement(TryCatchStatement tryCatchStatement, DefiniteAssignmentStatus data) + { + return data; // no special logic when entering the try-catch-finally statement + // TODO: where to put the special logic when exiting the try-finally statement? + } + + public override DefiniteAssignmentStatus VisitForeachStatement(ForeachStatement foreachStatement, DefiniteAssignmentStatus data) + { + return data; // assignment of the foreach loop variable is done when handling the condition node + } + + public override DefiniteAssignmentStatus VisitUsingStatement(UsingStatement usingStatement, DefiniteAssignmentStatus data) + { + if (usingStatement.ResourceAcquisition is Expression) + return usingStatement.ResourceAcquisition.AcceptVisitor(this, data); + else + return data; // don't handle resource acquisition statements, as those are connected in the control flow graph + } + + public override DefiniteAssignmentStatus VisitLockStatement(LockStatement lockStatement, DefiniteAssignmentStatus data) + { + return lockStatement.Expression.AcceptVisitor(this, data); + } + + // Yield statements use the default logic + + public override DefiniteAssignmentStatus VisitUnsafeStatement(UnsafeStatement unsafeStatement, DefiniteAssignmentStatus data) + { + return data; + } + + public override DefiniteAssignmentStatus VisitFixedStatement(FixedStatement fixedStatement, DefiniteAssignmentStatus data) + { + DefiniteAssignmentStatus status = data; + foreach (var variable in fixedStatement.Variables) + status = variable.AcceptVisitor(this, status); + return status; + } + #endregion + + #region Expressions + public override DefiniteAssignmentStatus VisitDirectionExpression(DirectionExpression directionExpression, DefiniteAssignmentStatus data) + { + if (directionExpression.FieldDirection == FieldDirection.Out) { + return HandleAssignment(directionExpression.Expression, null, data); + } else { + // use default logic for 'ref' + return VisitChildren(directionExpression, data); + } + } + + public override DefiniteAssignmentStatus VisitAssignmentExpression(AssignmentExpression assignmentExpression, DefiniteAssignmentStatus data) + { + if (assignmentExpression.Operator == AssignmentOperatorType.Assign) { + return HandleAssignment(assignmentExpression.Left, assignmentExpression.Right, data); + } else { + // use default logic for compound assignment operators + return VisitChildren(assignmentExpression, data); + } + } + + DefiniteAssignmentStatus HandleAssignment(Expression left, Expression right, DefiniteAssignmentStatus initialStatus) + { + IdentifierExpression ident = left as IdentifierExpression; + if (ident != null && ident.Identifier == analysis.variableName) { + // right==null is special case when handling 'out' expressions + if (right != null) + right.AcceptVisitor(this, initialStatus); + return DefiniteAssignmentStatus.DefinitelyAssigned; + } else { + DefiniteAssignmentStatus status = left.AcceptVisitor(this, initialStatus); + if (right != null) + status = right.AcceptVisitor(this, CleanSpecialValues(status)); + return CleanSpecialValues(status); + } + } + + public override DefiniteAssignmentStatus VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression, DefiniteAssignmentStatus data) + { + // Don't use the default logic here because we don't want to clean up the special values. + return parenthesizedExpression.Expression.AcceptVisitor(this, data); + } + + public override DefiniteAssignmentStatus VisitCheckedExpression(CheckedExpression checkedExpression, DefiniteAssignmentStatus data) + { + return checkedExpression.Expression.AcceptVisitor(this, data); + } + + public override DefiniteAssignmentStatus VisitUncheckedExpression(UncheckedExpression uncheckedExpression, DefiniteAssignmentStatus data) + { + return uncheckedExpression.Expression.AcceptVisitor(this, data); + } + + public override DefiniteAssignmentStatus VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression, DefiniteAssignmentStatus data) + { + if (binaryOperatorExpression.Operator == BinaryOperatorType.ConditionalAnd) { + // Handle constant left side of && expressions (not in the C# spec, but done by the MS compiler) + bool? cond = analysis.EvaluateCondition(binaryOperatorExpression.Left); + if (cond == true) + return binaryOperatorExpression.Right.AcceptVisitor(this, data); // right operand gets evaluated unconditionally + else if (cond == false) + return data; // right operand never gets evaluated + // C# 4.0 spec: §5.3.3.24 Definite Assignment for && expressions + DefiniteAssignmentStatus afterLeft = binaryOperatorExpression.Left.AcceptVisitor(this, data); + DefiniteAssignmentStatus beforeRight; + if (afterLeft == DefiniteAssignmentStatus.AssignedAfterTrueExpression) + beforeRight = DefiniteAssignmentStatus.DefinitelyAssigned; + else if (afterLeft == DefiniteAssignmentStatus.AssignedAfterFalseExpression) + beforeRight = DefiniteAssignmentStatus.PotentiallyAssigned; + else + beforeRight = afterLeft; + DefiniteAssignmentStatus afterRight = binaryOperatorExpression.Right.AcceptVisitor(this, beforeRight); + if (afterLeft == DefiniteAssignmentStatus.DefinitelyAssigned) + return DefiniteAssignmentStatus.DefinitelyAssigned; + else if (afterRight == DefiniteAssignmentStatus.DefinitelyAssigned && afterLeft == DefiniteAssignmentStatus.AssignedAfterFalseExpression) + return DefiniteAssignmentStatus.DefinitelyAssigned; + else if (afterRight == DefiniteAssignmentStatus.DefinitelyAssigned || afterRight == DefiniteAssignmentStatus.AssignedAfterTrueExpression) + return DefiniteAssignmentStatus.AssignedAfterTrueExpression; + else if (afterLeft == DefiniteAssignmentStatus.AssignedAfterFalseExpression && afterRight == DefiniteAssignmentStatus.AssignedAfterFalseExpression) + return DefiniteAssignmentStatus.AssignedAfterFalseExpression; + else + return DefiniteAssignmentStatus.PotentiallyAssigned; + } else if (binaryOperatorExpression.Operator == BinaryOperatorType.ConditionalOr) { + // C# 4.0 spec: §5.3.3.25 Definite Assignment for || expressions + bool? cond = analysis.EvaluateCondition(binaryOperatorExpression.Left); + if (cond == false) + return binaryOperatorExpression.Right.AcceptVisitor(this, data); // right operand gets evaluated unconditionally + else if (cond == true) + return data; // right operand never gets evaluated + DefiniteAssignmentStatus afterLeft = binaryOperatorExpression.Left.AcceptVisitor(this, data); + DefiniteAssignmentStatus beforeRight; + if (afterLeft == DefiniteAssignmentStatus.AssignedAfterTrueExpression) + beforeRight = DefiniteAssignmentStatus.PotentiallyAssigned; + else if (afterLeft == DefiniteAssignmentStatus.AssignedAfterFalseExpression) + beforeRight = DefiniteAssignmentStatus.DefinitelyAssigned; + else + beforeRight = afterLeft; + DefiniteAssignmentStatus afterRight = binaryOperatorExpression.Right.AcceptVisitor(this, beforeRight); + if (afterLeft == DefiniteAssignmentStatus.DefinitelyAssigned) + return DefiniteAssignmentStatus.DefinitelyAssigned; + else if (afterRight == DefiniteAssignmentStatus.DefinitelyAssigned && afterLeft == DefiniteAssignmentStatus.AssignedAfterTrueExpression) + return DefiniteAssignmentStatus.DefinitelyAssigned; + else if (afterRight == DefiniteAssignmentStatus.DefinitelyAssigned || afterRight == DefiniteAssignmentStatus.AssignedAfterFalseExpression) + return DefiniteAssignmentStatus.AssignedAfterFalseExpression; + else if (afterLeft == DefiniteAssignmentStatus.AssignedAfterTrueExpression && afterRight == DefiniteAssignmentStatus.AssignedAfterTrueExpression) + return DefiniteAssignmentStatus.AssignedAfterTrueExpression; + else + return DefiniteAssignmentStatus.PotentiallyAssigned; + } else if (binaryOperatorExpression.Operator == BinaryOperatorType.NullCoalescing) { + // C# 4.0 spec: §5.3.3.27 Definite assignment for ?? expressions + ResolveResult crr = analysis.EvaluateConstant(binaryOperatorExpression.Left); + if (crr != null && crr.IsCompileTimeConstant && crr.ConstantValue == null) + return binaryOperatorExpression.Right.AcceptVisitor(this, data); + DefiniteAssignmentStatus status = CleanSpecialValues(binaryOperatorExpression.Left.AcceptVisitor(this, data)); + binaryOperatorExpression.Right.AcceptVisitor(this, status); + return status; + } else { + // use default logic for other operators + return VisitChildren(binaryOperatorExpression, data); + } + } + + public override DefiniteAssignmentStatus VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression, DefiniteAssignmentStatus data) + { + if (unaryOperatorExpression.Operator == UnaryOperatorType.Not) { + // C# 4.0 spec: §5.3.3.26 Definite assignment for ! expressions + DefiniteAssignmentStatus status = unaryOperatorExpression.Expression.AcceptVisitor(this, data); + if (status == DefiniteAssignmentStatus.AssignedAfterFalseExpression) + return DefiniteAssignmentStatus.AssignedAfterTrueExpression; + else if (status == DefiniteAssignmentStatus.AssignedAfterTrueExpression) + return DefiniteAssignmentStatus.AssignedAfterFalseExpression; + else + return status; + } else { + // use default logic for other operators + return VisitChildren(unaryOperatorExpression, data); + } + } + + public override DefiniteAssignmentStatus VisitConditionalExpression(ConditionalExpression conditionalExpression, DefiniteAssignmentStatus data) + { + // C# 4.0 spec: §5.3.3.28 Definite assignment for ?: expressions + bool? cond = analysis.EvaluateCondition(conditionalExpression.Condition); + if (cond == true) { + return conditionalExpression.TrueExpression.AcceptVisitor(this, data); + } else if (cond == false) { + return conditionalExpression.FalseExpression.AcceptVisitor(this, data); + } else { + DefiniteAssignmentStatus afterCondition = conditionalExpression.Condition.AcceptVisitor(this, data); + + DefiniteAssignmentStatus beforeTrue, beforeFalse; + if (afterCondition == DefiniteAssignmentStatus.AssignedAfterTrueExpression) { + beforeTrue = DefiniteAssignmentStatus.DefinitelyAssigned; + beforeFalse = DefiniteAssignmentStatus.PotentiallyAssigned; + } else if (afterCondition == DefiniteAssignmentStatus.AssignedAfterFalseExpression) { + beforeTrue = DefiniteAssignmentStatus.PotentiallyAssigned; + beforeFalse = DefiniteAssignmentStatus.DefinitelyAssigned; + } else { + beforeTrue = afterCondition; + beforeFalse = afterCondition; + } + + DefiniteAssignmentStatus afterTrue = conditionalExpression.TrueExpression.AcceptVisitor(this, beforeTrue); + DefiniteAssignmentStatus afterFalse = conditionalExpression.FalseExpression.AcceptVisitor(this, beforeFalse); + return MergeStatus(CleanSpecialValues(afterTrue), CleanSpecialValues(afterFalse)); + } + } + + public override DefiniteAssignmentStatus VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression, DefiniteAssignmentStatus data) + { + BlockStatement body = anonymousMethodExpression.Body; + analysis.ChangeNodeStatus(analysis.beginNodeDict[body], data); + return data; + } + + public override DefiniteAssignmentStatus VisitLambdaExpression(LambdaExpression lambdaExpression, DefiniteAssignmentStatus data) + { + Statement body = lambdaExpression.Body as Statement; + if (body != null) { + analysis.ChangeNodeStatus(analysis.beginNodeDict[body], data); + } else { + lambdaExpression.Body.AcceptVisitor(this, data); + } + return data; + } + + public override DefiniteAssignmentStatus VisitIdentifierExpression(IdentifierExpression identifierExpression, DefiniteAssignmentStatus data) + { + if (data != DefiniteAssignmentStatus.DefinitelyAssigned + && identifierExpression.Identifier == analysis.variableName && identifierExpression.TypeArguments.Count == 0) + { + analysis.unassignedVariableUses.Add(identifierExpression); + } + return data; + } + #endregion + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/NullValueAnalysis.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/NullValueAnalysis.cs new file mode 100644 index 000000000..a20b4a0f0 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/NullValueAnalysis.cs @@ -0,0 +1,2215 @@ +// +// NullValueAnalysis.cs +// +// Author: +// Luís Reis +// +// Copyright (c) 2013 Luís Reis +// +// 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; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Text; +using ICSharpCode.NRefactory.CSharp.Resolver; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.CSharp.Refactoring; +using ICSharpCode.NRefactory.PatternMatching; +using ICSharpCode.NRefactory.CSharp; +using ICSharpCode.NRefactory.Utils; + +namespace ICSharpCode.NRefactory.CSharp.Analysis +{ + public class NullValueAnalysis + { + sealed class VariableStatusInfo : IEquatable, IEnumerable> + { + readonly Dictionary VariableStatus = new Dictionary(); + + public NullValueStatus this[string name] + { + get { + NullValueStatus status; + if (VariableStatus.TryGetValue(name, out status)) { + return status; + } + return NullValueStatus.UnreachableOrInexistent; + } + set { + if (value == NullValueStatus.UnreachableOrInexistent) { + VariableStatus.Remove(name); + } else { + VariableStatus [name] = value; + } + } + } + + /// + /// Modifies the variable state to consider a new incoming path + /// + /// true, if the state has changed, false otherwise. + /// The variable state of the incoming path + public bool ReceiveIncoming(VariableStatusInfo incomingState) + { + bool changed = false; + var listOfVariables = VariableStatus.Keys.Concat(incomingState.VariableStatus.Keys).ToList(); + foreach (string variable in listOfVariables) + { + var newValue = CombineStatus(this [variable], incomingState [variable]); + if (this [variable] != newValue) { + this [variable] = newValue; + changed = true; + } + } + + return changed; + } + + public static NullValueStatus CombineStatus(NullValueStatus oldValue, NullValueStatus incomingValue) + { + if (oldValue == NullValueStatus.Error || incomingValue == NullValueStatus.Error) + return NullValueStatus.Error; + + if (oldValue == NullValueStatus.UnreachableOrInexistent || + oldValue == NullValueStatus.Unassigned) + return incomingValue; + + if (incomingValue == NullValueStatus.Unassigned) { + return NullValueStatus.Unassigned; + } + + if (oldValue == NullValueStatus.CapturedUnknown || incomingValue == NullValueStatus.CapturedUnknown) { + //TODO: Check if this is right + return NullValueStatus.CapturedUnknown; + } + + if (oldValue == NullValueStatus.Unknown) { + return NullValueStatus.Unknown; + } + + if (oldValue == NullValueStatus.DefinitelyNull) { + return incomingValue == NullValueStatus.DefinitelyNull ? + NullValueStatus.DefinitelyNull : NullValueStatus.PotentiallyNull; + } + + if (oldValue == NullValueStatus.DefinitelyNotNull) { + if (incomingValue == NullValueStatus.Unknown) + return NullValueStatus.Unknown; + if (incomingValue == NullValueStatus.DefinitelyNotNull) + return NullValueStatus.DefinitelyNotNull; + return NullValueStatus.PotentiallyNull; + } + + Debug.Assert(oldValue == NullValueStatus.PotentiallyNull); + return NullValueStatus.PotentiallyNull; + } + + public bool HasVariable(string variable) { + return VariableStatus.ContainsKey(variable); + } + + public VariableStatusInfo Clone() { + var clone = new VariableStatusInfo(); + foreach (var item in VariableStatus) { + clone.VariableStatus.Add(item.Key, item.Value); + } + return clone; + } + + public override bool Equals(object obj) + { + return Equals(obj as VariableStatusInfo); + } + + public bool Equals(VariableStatusInfo obj) + { + if (obj == null) { + return false; + } + + if (VariableStatus.Count != obj.VariableStatus.Count) + return false; + + return VariableStatus.All(item => item.Value == obj[item.Key]); + } + + public override int GetHashCode() + { + //STUB + return VariableStatus.Count.GetHashCode(); + } + + public static bool operator ==(VariableStatusInfo obj1, VariableStatusInfo obj2) { + return object.ReferenceEquals(obj1, null) ? + object.ReferenceEquals(obj2, null) : obj1.Equals(obj2); + } + + public static bool operator !=(VariableStatusInfo obj1, VariableStatusInfo obj2) { + return !(obj1 == obj2); + } + + public IEnumerator> GetEnumerator() + { + return VariableStatus.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public override string ToString() + { + var builder = new StringBuilder("["); + foreach (var item in this) { + builder.Append(item.Key); + builder.Append("="); + builder.Append(item.Value); + } + builder.Append("]"); + return builder.ToString(); + } + } + + sealed class NullAnalysisNode : ControlFlowNode + { + public readonly VariableStatusInfo VariableState = new VariableStatusInfo(); + public bool Visited { get; private set; } + + public NullAnalysisNode(Statement previousStatement, Statement nextStatement, ControlFlowNodeType type) + : base(previousStatement, nextStatement, type) + { + } + + public bool ReceiveIncoming(VariableStatusInfo incomingState) + { + bool changed = VariableState.ReceiveIncoming(incomingState); + if (!Visited) { + Visited = true; + return true; + } + return changed; + } + } + + sealed class NullAnalysisGraphBuilder : ControlFlowGraphBuilder + { + protected override ControlFlowNode CreateNode(Statement previousStatement, Statement nextStatement, ControlFlowNodeType type) + { + return new NullAnalysisNode(previousStatement, nextStatement, type); + } + } + + class PendingNode : IEquatable { + internal readonly NullAnalysisNode nodeToVisit; + internal readonly VariableStatusInfo statusInfo; + internal readonly ComparableList pendingTryFinallyNodes; + internal readonly NullAnalysisNode nodeAfterFinally; + + internal PendingNode(NullAnalysisNode nodeToVisit, VariableStatusInfo statusInfo) + : this(nodeToVisit, statusInfo, new ComparableList(), null) + { + } + + public PendingNode(NullAnalysisNode nodeToVisit, VariableStatusInfo statusInfo, ComparableList pendingFinallyNodes, NullAnalysisNode nodeAfterFinally) + { + this.nodeToVisit = nodeToVisit; + this.statusInfo = statusInfo; + this.pendingTryFinallyNodes = pendingFinallyNodes; + this.nodeAfterFinally = nodeAfterFinally; + } + + public override bool Equals(object obj) + { + return Equals(obj as PendingNode); + } + + public bool Equals(PendingNode obj) { + if (obj == null) return false; + + if (nodeToVisit != obj.nodeToVisit) return false; + if (statusInfo != obj.statusInfo) return false; + if (pendingTryFinallyNodes != obj.pendingTryFinallyNodes) return false; + if (nodeAfterFinally != obj.nodeAfterFinally) return false; + + return true; + } + + public override int GetHashCode() + { + return nodeToVisit.GetHashCode() ^ + statusInfo.GetHashCode() ^ + pendingTryFinallyNodes.GetHashCode() ^ + (nodeAfterFinally == null ? 0 : nodeAfterFinally.GetHashCode()); + } + } + + readonly BaseRefactoringContext context; + readonly NullAnalysisVisitor visitor; + List allNodes; + readonly HashSet nodesToVisit = new HashSet(); + Dictionary nodeBeforeStatementDict; + Dictionary nodeAfterStatementDict; + readonly Dictionary expressionResult = new Dictionary(); + + public NullValueAnalysis(BaseRefactoringContext context, MethodDeclaration methodDeclaration, CancellationToken cancellationToken) + : this(context, methodDeclaration.Body, methodDeclaration.Parameters, cancellationToken) + { + } + + readonly IEnumerable parameters; + readonly Statement rootStatement; + + readonly CancellationToken cancellationToken; + + public NullValueAnalysis(BaseRefactoringContext context, Statement rootStatement, IEnumerable parameters, CancellationToken cancellationToken) + { + if (rootStatement == null) + throw new ArgumentNullException("rootStatement"); + if (context == null) + throw new ArgumentNullException("context"); + + this.context = context; + this.rootStatement = rootStatement; + this.parameters = parameters; + this.visitor = new NullAnalysisVisitor(this); + this.cancellationToken = cancellationToken; + } + + /// + /// Sets the local variable value. + /// This method does not change anything if identifier does not refer to a local variable. + /// Do not use this in variable declarations since resolving the variable won't work yet. + /// + /// true, if local variable value was set, false otherwise. + /// The variable status data to change. + /// The identifier to set. + /// The name of the identifier to set. + /// The value to set the identifier. + bool SetLocalVariableValue (VariableStatusInfo data, AstNode identifierNode, string identifierName, NullValueStatus value) { + var resolveResult = context.Resolve(identifierNode); + if (resolveResult is LocalResolveResult) { + if (data [identifierName] != NullValueStatus.CapturedUnknown) { + data [identifierName] = value; + + return true; + } + } + return false; + } + + bool SetLocalVariableValue (VariableStatusInfo data, IdentifierExpression identifierExpression, NullValueStatus value) { + return SetLocalVariableValue(data, identifierExpression, identifierExpression.Identifier, value); + } + + bool SetLocalVariableValue (VariableStatusInfo data, Identifier identifier, NullValueStatus value) { + return SetLocalVariableValue(data, identifier, identifier.Name, value); + } + + void SetupNode(NullAnalysisNode node) + { + foreach (var parameter in parameters) { + var resolveResult = context.Resolve(parameter.Type); + node.VariableState[parameter.Name] = GetInitialVariableStatus(resolveResult); + } + + nodesToVisit.Add(new PendingNode(node, node.VariableState)); + } + + static bool IsTypeNullable(IType type) + { + return type.IsReferenceType == true || type.FullName == "System.Nullable"; + } + + public bool IsParametersAreUninitialized { + get; + set; + } + + NullValueStatus GetInitialVariableStatus(ResolveResult resolveResult) + { + var typeResolveResult = resolveResult as TypeResolveResult; + if (typeResolveResult == null) { + return NullValueStatus.Error; + } + var type = typeResolveResult.Type; + if (type.IsReferenceType == null) { + return NullValueStatus.Error; + } + if (!IsParametersAreUninitialized) + return NullValueStatus.DefinitelyNotNull; + return IsTypeNullable(type) ? NullValueStatus.PotentiallyNull : NullValueStatus.DefinitelyNotNull; + } + + public void Analyze() + { + var cfgBuilder = new NullAnalysisGraphBuilder(); + allNodes = cfgBuilder.BuildControlFlowGraph(rootStatement, cancellationToken).Cast().ToList(); + nodeBeforeStatementDict = allNodes.Where(node => node.Type == ControlFlowNodeType.StartNode || node.Type == ControlFlowNodeType.BetweenStatements) + .ToDictionary(node => node.NextStatement); + nodeAfterStatementDict = allNodes.Where(node => node.Type == ControlFlowNodeType.BetweenStatements || node.Type == ControlFlowNodeType.EndNode) + .ToDictionary(node => node.PreviousStatement); + + foreach (var node in allNodes) { + if (node.Type == ControlFlowNodeType.StartNode && node.NextStatement == rootStatement) { + Debug.Assert(!nodesToVisit.Any()); + + SetupNode(node); + } + } + + while (nodesToVisit.Any()) { + var nodeToVisit = nodesToVisit.First(); + nodesToVisit.Remove(nodeToVisit); + + Visit(nodeToVisit); + } + } + + int visits = 0; + + public int NodeVisits + { + get { + return visits; + } + } + + void Visit(PendingNode nodeInfo) + { + cancellationToken.ThrowIfCancellationRequested(); + + var node = nodeInfo.nodeToVisit; + var statusInfo = nodeInfo.statusInfo; + + visits++; + if (visits > 100) { + //Visiting way too often, let's enter fast mode + //Fast mode is slighly less accurate but visits each node less times + nodesToVisit.RemoveWhere(candidate => candidate.nodeToVisit == nodeInfo.nodeToVisit && + candidate.pendingTryFinallyNodes.Equals(nodeInfo.pendingTryFinallyNodes) && + candidate.nodeAfterFinally == nodeInfo.nodeAfterFinally); + statusInfo = node.VariableState; + } + + var nextStatement = node.NextStatement; + VariableStatusInfo outgoingStatusInfo = statusInfo; + VisitorResult result = null; + + if (nextStatement != null && (!(nextStatement is DoWhileStatement) || node.Type == ControlFlowNodeType.LoopCondition)) { + result = nextStatement.AcceptVisitor(visitor, statusInfo); + if (result == null) { + Console.WriteLine("Failure in {0}", nextStatement); + throw new InvalidOperationException(); + } + + outgoingStatusInfo = result.Variables; + } + + if ((result == null || !result.ThrowsException) && node.Outgoing.Any()) { + var tryFinallyStatement = nextStatement as TryCatchStatement; + + foreach (var outgoingEdge in node.Outgoing) { + VariableStatusInfo edgeInfo; + edgeInfo = outgoingStatusInfo.Clone(); + + if (node.Type == ControlFlowNodeType.EndNode) { + var previousBlock = node.PreviousStatement as BlockStatement; + if (previousBlock != null) { + //We're leaving a block statement. + //As such, we'll remove the variables that were declared *in* the loop + //This helps GetVariableStatusAfter/BeforeStatement be more accurate + //and prevents some redundant revisiting. + + foreach (var variableInitializer in previousBlock.Statements + .OfType() + .SelectMany(declaration => declaration.Variables)) { + + edgeInfo [variableInitializer.Name] = NullValueStatus.UnreachableOrInexistent; + } + } + } + + if (tryFinallyStatement != null) { + //With the exception of try statements, this needs special handling: + //we'll set all changed variables to Unknown or CapturedUnknown + if (outgoingEdge.To.NextStatement == tryFinallyStatement.FinallyBlock) { + foreach (var identifierExpression in tryFinallyStatement.TryBlock.Descendants.OfType()) { + //TODO: Investigate CaptureUnknown + SetLocalVariableValue(edgeInfo, identifierExpression, NullValueStatus.Unknown); + } + } else { + var clause = tryFinallyStatement.CatchClauses + .FirstOrDefault(candidateClause => candidateClause.Body == outgoingEdge.To.NextStatement); + + if (clause != null) { + SetLocalVariableValue(edgeInfo, clause.VariableNameToken, NullValueStatus.DefinitelyNotNull); + + foreach (var identifierExpression in tryFinallyStatement.TryBlock.Descendants.OfType()) { + //TODO: Investigate CaptureUnknown + SetLocalVariableValue(edgeInfo, identifierExpression, NullValueStatus.Unknown); + } + } + } + } + + if (result != null) { + switch (outgoingEdge.Type) { + case ControlFlowEdgeType.ConditionTrue: + if (result.KnownBoolResult == false) { + //No need to explore this path -- expression is known to be false + continue; + } + edgeInfo = result.TruePathVariables; + break; + case ControlFlowEdgeType.ConditionFalse: + if (result.KnownBoolResult == true) { + //No need to explore this path -- expression is known to be true + continue; + } + edgeInfo = result.FalsePathVariables; + break; + } + } + + if (outgoingEdge.IsLeavingTryFinally) { + var nodeAfterFinally = (NullAnalysisNode)outgoingEdge.To; + var finallyNodes = outgoingEdge.TryFinallyStatements.Select(tryFinally => nodeBeforeStatementDict [tryFinally.FinallyBlock]).ToList(); + var nextNode = finallyNodes.First(); + var remainingFinallyNodes = new ComparableList(finallyNodes.Skip(1)); + //We have to visit the node even if ReceiveIncoming returns false + //since the finallyNodes/nodeAfterFinally might be different even if the values of variables are the same -- and they need to be visited either way! + //TODO 1: Is there any point in visiting the finally statement here? + //TODO 2: Do we need the ReceiveIncoming at all? + nextNode.ReceiveIncoming(edgeInfo); + nodesToVisit.Add(new PendingNode(nextNode, edgeInfo, remainingFinallyNodes, nodeAfterFinally)); + } else { + var outgoingNode = (NullAnalysisNode)outgoingEdge.To; + if (outgoingNode.ReceiveIncoming(edgeInfo)) { + nodesToVisit.Add(new PendingNode(outgoingNode, edgeInfo)); + } + } + } + } else { + //We found a return/throw/yield break or some other termination node + var finallyBlockStarts = nodeInfo.pendingTryFinallyNodes; + var nodeAfterFinally = nodeInfo.nodeAfterFinally; + + if (finallyBlockStarts.Any()) { + var nextNode = finallyBlockStarts.First(); + if (nextNode.ReceiveIncoming(outgoingStatusInfo)) + nodesToVisit.Add(new PendingNode(nextNode, outgoingStatusInfo, new ComparableList(finallyBlockStarts.Skip(1)), nodeInfo.nodeAfterFinally)); + } else if (nodeAfterFinally != null && nodeAfterFinally.ReceiveIncoming(outgoingStatusInfo)) { + nodesToVisit.Add(new PendingNode(nodeAfterFinally, outgoingStatusInfo)); + } else { + //Maybe we finished a try/catch/finally statement the "normal" way (no direct jumps) + //so let's check that case + var statement = node.PreviousStatement ?? node.NextStatement; + Debug.Assert(statement != null); + var parent = statement.GetParent(); + var parentTryCatch = parent as TryCatchStatement; + if (parentTryCatch != null) { + var nextNode = nodeAfterStatementDict [parentTryCatch]; + if (nextNode.ReceiveIncoming(outgoingStatusInfo)) { + nodesToVisit.Add(new PendingNode(nextNode, outgoingStatusInfo)); + } + } + } + } + } + + public NullValueStatus GetExpressionResult(Expression expr) + { + if (expr == null) + throw new ArgumentNullException("expr"); + + NullValueStatus info; + if (expressionResult.TryGetValue(expr, out info)) { + return info; + } + + return NullValueStatus.UnreachableOrInexistent; + } + + public NullValueStatus GetVariableStatusBeforeStatement(Statement stmt, string variableName) + { + if (stmt == null) + throw new ArgumentNullException("stmt"); + if (variableName == null) + throw new ArgumentNullException("variableName"); + + NullAnalysisNode node; + if (nodeBeforeStatementDict.TryGetValue(stmt, out node)) { + return node.VariableState [variableName]; + } + + return NullValueStatus.UnreachableOrInexistent; + } + + public NullValueStatus GetVariableStatusAfterStatement(Statement stmt, string variableName) + { + if (stmt == null) + throw new ArgumentNullException("stmt"); + if (variableName == null) + throw new ArgumentNullException("variableName"); + + NullAnalysisNode node; + if (nodeAfterStatementDict.TryGetValue(stmt, out node)) { + return node.VariableState [variableName]; + } + + return NullValueStatus.UnreachableOrInexistent; + } + + class ConditionalBranchInfo + { + /// + /// True if the variable is null for the true path, false if it is false for the true path. + /// + public Dictionary TrueResultVariableNullStates = new Dictionary(); + /// + /// True if the variable is null for the false path, false if it is false for the false path. + /// + public Dictionary FalseResultVariableNullStates = new Dictionary(); + } + + class VisitorResult + { + /// + /// Indicates the return value of the expression. + /// + /// + /// Only applicable for expressions. + /// + public NullValueStatus NullableReturnResult; + + /// + /// Indicates the value of each item in an array or linq query. + /// + public NullValueStatus EnumeratedValueResult; + + /// + /// Information that indicates the restrictions to add + /// when branching. + /// + /// + /// Used in if/else statements, conditional expressions and + /// while statements. + /// + public ConditionalBranchInfo ConditionalBranchInfo; + + /// + /// The state of the variables after the expression is executed. + /// + public VariableStatusInfo Variables; + + /// + /// The expression is known to be invalid and trigger an error + /// (e.g. a NullReferenceException) + /// + public bool ThrowsException; + + /// + /// The known bool result of an expression. + /// + public bool? KnownBoolResult; + + public static VisitorResult ForEnumeratedValue(VariableStatusInfo variables, NullValueStatus itemValues) + { + var result = new VisitorResult(); + result.NullableReturnResult = NullValueStatus.DefinitelyNotNull; + result.EnumeratedValueResult = itemValues; + result.Variables = variables.Clone(); + return result; + } + + public static VisitorResult ForValue(VariableStatusInfo variables, NullValueStatus returnValue) + { + var result = new VisitorResult(); + result.NullableReturnResult = returnValue; + result.Variables = variables.Clone(); + return result; + } + + public static VisitorResult ForBoolValue(VariableStatusInfo variables, bool newValue) + { + var result = new VisitorResult(); + result.NullableReturnResult = NullValueStatus.DefinitelyNotNull; //Bool expressions are never null + result.KnownBoolResult = newValue; + result.Variables = variables.Clone(); + return result; + } + + public static VisitorResult ForException(VariableStatusInfo variables) { + var result = new VisitorResult(); + result.NullableReturnResult = NullValueStatus.UnreachableOrInexistent; + result.ThrowsException = true; + result.Variables = variables.Clone(); + return result; + } + + public VisitorResult Negated { + get { + var result = new VisitorResult(); + if (NullableReturnResult.IsDefiniteValue()) { + result.NullableReturnResult = NullableReturnResult == NullValueStatus.DefinitelyNull + ? NullValueStatus.DefinitelyNotNull : NullValueStatus.DefinitelyNull; + } else { + result.NullableReturnResult = NullableReturnResult; + } + result.Variables = Variables.Clone(); + result.KnownBoolResult = !KnownBoolResult; + if (ConditionalBranchInfo != null) { + result.ConditionalBranchInfo = new ConditionalBranchInfo(); + foreach (var item in ConditionalBranchInfo.TrueResultVariableNullStates) { + result.ConditionalBranchInfo.FalseResultVariableNullStates [item.Key] = item.Value; + } + foreach (var item in ConditionalBranchInfo.FalseResultVariableNullStates) { + result.ConditionalBranchInfo.TrueResultVariableNullStates [item.Key] = item.Value; + } + } + return result; + } + } + + public VariableStatusInfo TruePathVariables { + get { + var variables = Variables.Clone(); + if (ConditionalBranchInfo != null) { + foreach (var item in ConditionalBranchInfo.TrueResultVariableNullStates) { + variables [item.Key] = item.Value ? NullValueStatus.DefinitelyNull : NullValueStatus.DefinitelyNotNull; + } + } + return variables; + } + } + + public VariableStatusInfo FalsePathVariables { + get { + var variables = Variables.Clone(); + if (ConditionalBranchInfo != null) { + foreach (var item in ConditionalBranchInfo.FalseResultVariableNullStates) { + variables [item.Key] = item.Value ? NullValueStatus.DefinitelyNull : NullValueStatus.DefinitelyNotNull; + } + } + return variables; + } + } + + public static VisitorResult AndOperation(VisitorResult tentativeLeftResult, VisitorResult tentativeRightResult) + { + var result = new VisitorResult(); + result.KnownBoolResult = tentativeLeftResult.KnownBoolResult & tentativeRightResult.KnownBoolResult; + + var trueTruePath = tentativeRightResult.TruePathVariables; + var trueFalsePath = tentativeRightResult.FalsePathVariables; + var falsePath = tentativeLeftResult.FalsePathVariables; + + var trueVariables = trueTruePath; + + var falseVariables = trueFalsePath.Clone(); + falseVariables.ReceiveIncoming(falsePath); + result.Variables = trueVariables.Clone(); + result.Variables.ReceiveIncoming(falseVariables); + + result.ConditionalBranchInfo = new ConditionalBranchInfo(); + + foreach (var variable in trueVariables) { + if (!variable.Value.IsDefiniteValue()) + continue; + + string variableName = variable.Key; + + if (variable.Value != result.Variables[variableName]) { + bool isNull = variable.Value == NullValueStatus.DefinitelyNull; + result.ConditionalBranchInfo.TrueResultVariableNullStates.Add(variableName, isNull); + } + } + + foreach (var variable in falseVariables) { + if (!variable.Value.IsDefiniteValue()) + continue; + + string variableName = variable.Key; + + if (variable.Value != result.Variables [variableName]) { + bool isNull = variable.Value == NullValueStatus.DefinitelyNull; + result.ConditionalBranchInfo.FalseResultVariableNullStates.Add(variableName, isNull); + } + } + + return result; + } + + public static VisitorResult OrOperation(VisitorResult tentativeLeftResult, VisitorResult tentativeRightResult) + { + return VisitorResult.AndOperation(tentativeLeftResult.Negated, tentativeRightResult.Negated).Negated; + } + } + + class NullAnalysisVisitor : DepthFirstAstVisitor + { + NullValueAnalysis analysis; + + public NullAnalysisVisitor(NullValueAnalysis analysis) { + this.analysis = analysis; + } + + protected override VisitorResult VisitChildren(AstNode node, VariableStatusInfo data) + { + Debug.Fail("Missing override for " + node.GetType().Name); + return VisitorResult.ForValue(data, NullValueStatus.Unknown); + } + + public override VisitorResult VisitNullNode(AstNode nullNode, VariableStatusInfo data) + { + // can occur due to syntax errors + return VisitorResult.ForValue(data, NullValueStatus.Unknown); + } + + public override VisitorResult VisitEmptyStatement(EmptyStatement emptyStatement, VariableStatusInfo data) + { + return VisitorResult.ForValue(data, NullValueStatus.Unknown); + } + + public override VisitorResult VisitBlockStatement(BlockStatement blockStatement, VariableStatusInfo data) + { + //We'll visit the child statements later (we'll visit each one directly from the CFG) + //As such this is mostly a dummy node. + return new VisitorResult { Variables = data }; + } + + public override VisitorResult VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement, VariableStatusInfo data) + { + foreach (var variable in variableDeclarationStatement.Variables) { + var result = variable.AcceptVisitor(this, data); + if (result.ThrowsException) + return result; + data = result.Variables; + } + + return VisitorResult.ForValue(data, NullValueStatus.Unknown); + } + + public override VisitorResult VisitVariableInitializer(VariableInitializer variableInitializer, VariableStatusInfo data) + { + if (variableInitializer.Initializer.IsNull) { + data = data.Clone(); + data[variableInitializer.Name] = NullValueStatus.Unassigned; + } else { + var result = variableInitializer.Initializer.AcceptVisitor(this, data); + if (result.ThrowsException) + return result; + data = result.Variables.Clone(); + data[variableInitializer.Name] = result.NullableReturnResult; + } + + return VisitorResult.ForValue(data, data [variableInitializer.Name]); + } + + public override VisitorResult VisitIfElseStatement(IfElseStatement ifElseStatement, VariableStatusInfo data) + { + //We'll visit the true/false statements later (directly from the CFG) + return ifElseStatement.Condition.AcceptVisitor(this, data); + } + + public override VisitorResult VisitWhileStatement(WhileStatement whileStatement, VariableStatusInfo data) + { + return whileStatement.Condition.AcceptVisitor(this, data); + } + + public override VisitorResult VisitDoWhileStatement(DoWhileStatement doWhileStatement, VariableStatusInfo data) + { + return doWhileStatement.Condition.AcceptVisitor(this, data); + } + + public override VisitorResult VisitForStatement(ForStatement forStatement, VariableStatusInfo data) + { + //The initializers, the embedded statement and the iterators aren't visited here + //because they have their own CFG nodes. + if (forStatement.Condition.IsNull) + return VisitorResult.ForValue(data, NullValueStatus.Unknown); + return forStatement.Condition.AcceptVisitor(this, data); + } + + public override VisitorResult VisitForeachStatement(ForeachStatement foreachStatement, VariableStatusInfo data) + { + var newVariable = foreachStatement.VariableNameToken; + var inExpressionResult = foreachStatement.InExpression.AcceptVisitor(this, data); + if (inExpressionResult.ThrowsException) + return inExpressionResult; + + var newData = inExpressionResult.Variables.Clone(); + + var resolveResult = analysis.context.Resolve(foreachStatement.VariableNameToken) as LocalResolveResult; + if (resolveResult != null) { + //C# 5.0 changed the meaning of foreach so that each iteration declares a new variable + //as such, the variable is "uncaptured" only for C# >= 5.0 + if (analysis.context.Supports(new Version(5, 0)) || data[newVariable.Name] != NullValueStatus.CapturedUnknown) { + newData[newVariable.Name] = NullValueAnalysis.IsTypeNullable(resolveResult.Type) ? inExpressionResult.EnumeratedValueResult : NullValueStatus.DefinitelyNotNull; + } + } + + return VisitorResult.ForValue(newData, NullValueStatus.Unknown); + } + + public override VisitorResult VisitUsingStatement(UsingStatement usingStatement, VariableStatusInfo data) + { + return usingStatement.ResourceAcquisition.AcceptVisitor(this, data); + } + + public override VisitorResult VisitFixedStatement(FixedStatement fixedStatement, VariableStatusInfo data) + { + foreach (var variable in fixedStatement.Variables) { + var result = variable.AcceptVisitor(this, data); + if (result.ThrowsException) + return result; + data = result.Variables; + } + + return VisitorResult.ForValue(data, NullValueStatus.Unknown); + } + + public override VisitorResult VisitSwitchStatement(SwitchStatement switchStatement, VariableStatusInfo data) + { + //We could do better than this, but it would require special handling outside the visitor + //so for now, for simplicity, we'll just take the easy way + + var tentativeResult = switchStatement.Expression.AcceptVisitor(this, data); + if (tentativeResult.ThrowsException) { + return tentativeResult; + } + + foreach (var section in switchStatement.SwitchSections) { + //No need to check for ThrowsException, since it will always be false (see VisitSwitchSection) + section.AcceptVisitor(this, tentativeResult.Variables); + } + + return VisitorResult.ForValue(tentativeResult.Variables, NullValueStatus.Unknown); + } + + public override VisitorResult VisitSwitchSection(SwitchSection switchSection, VariableStatusInfo data) + { + return VisitorResult.ForValue(data, NullValueStatus.Unknown); + } + + public override VisitorResult VisitExpressionStatement(ExpressionStatement expressionStatement, VariableStatusInfo data) + { + return expressionStatement.Expression.AcceptVisitor(this, data); + } + + public override VisitorResult VisitReturnStatement(ReturnStatement returnStatement, VariableStatusInfo data) + { + if (returnStatement.Expression.IsNull) + return VisitorResult.ForValue(data, NullValueStatus.Unknown); + return returnStatement.Expression.AcceptVisitor(this, data); + } + + public override VisitorResult VisitTryCatchStatement(TryCatchStatement tryCatchStatement, VariableStatusInfo data) + { + //The needs special treatment in the analyser itself + return VisitorResult.ForValue(data, NullValueStatus.Unknown); + } + + public override VisitorResult VisitBreakStatement(BreakStatement breakStatement, VariableStatusInfo data) + { + return VisitorResult.ForValue(data, NullValueStatus.Unknown); + } + + public override VisitorResult VisitContinueStatement(ContinueStatement continueStatement, VariableStatusInfo data) + { + return VisitorResult.ForValue(data, NullValueStatus.Unknown); + } + + public override VisitorResult VisitGotoStatement(GotoStatement gotoStatement, VariableStatusInfo data) + { + return VisitorResult.ForValue(data, NullValueStatus.Unknown); + } + + public override VisitorResult VisitGotoCaseStatement(GotoCaseStatement gotoCaseStatement, VariableStatusInfo data) + { + return VisitorResult.ForValue(data, NullValueStatus.Unknown); + } + + public override VisitorResult VisitGotoDefaultStatement(GotoDefaultStatement gotoDefaultStatement, VariableStatusInfo data) + { + return VisitorResult.ForValue(data, NullValueStatus.Unknown); + } + + public override VisitorResult VisitLabelStatement(LabelStatement labelStatement, VariableStatusInfo data) + { + return VisitorResult.ForValue(data, NullValueStatus.Unknown); + } + + public override VisitorResult VisitUnsafeStatement(UnsafeStatement unsafeStatement, VariableStatusInfo data) + { + return VisitorResult.ForValue(data, NullValueStatus.Unknown); + } + + public override VisitorResult VisitLockStatement(LockStatement lockStatement, VariableStatusInfo data) + { + var expressionResult = lockStatement.Expression.AcceptVisitor(this, data); + if (expressionResult.ThrowsException) + return expressionResult; + + if (expressionResult.NullableReturnResult == NullValueStatus.DefinitelyNull) { + return VisitorResult.ForException(expressionResult.Variables); + } + + var identifier = CSharpUtil.GetInnerMostExpression(lockStatement.Expression) as IdentifierExpression; + if (identifier != null) { + var identifierValue = expressionResult.Variables [identifier.Identifier]; + if (identifierValue != NullValueStatus.CapturedUnknown) { + var newVariables = expressionResult.Variables.Clone(); + analysis.SetLocalVariableValue(newVariables, identifier, NullValueStatus.DefinitelyNotNull); + + return VisitorResult.ForValue(newVariables, NullValueStatus.Unknown); + } + } + + return VisitorResult.ForValue(expressionResult.Variables, NullValueStatus.Unknown); + } + + public override VisitorResult VisitThrowStatement(ThrowStatement throwStatement, VariableStatusInfo data) + { + if (throwStatement.Expression.IsNull) + return VisitorResult.ForValue(data, NullValueStatus.DefinitelyNotNull); + return throwStatement.Expression.AcceptVisitor(this, data); + } + + public override VisitorResult VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement, VariableStatusInfo data) + { + return VisitorResult.ForValue(data, NullValueStatus.Unknown); + } + + public override VisitorResult VisitYieldReturnStatement(YieldReturnStatement yieldReturnStatement, VariableStatusInfo data) + { + return yieldReturnStatement.Expression.AcceptVisitor(this, data); + } + + public override VisitorResult VisitCheckedStatement(CheckedStatement checkedStatement, VariableStatusInfo data) + { + return VisitorResult.ForValue(data, NullValueStatus.Unknown); + } + + public override VisitorResult VisitUncheckedStatement(UncheckedStatement uncheckedStatement, VariableStatusInfo data) + { + return VisitorResult.ForValue(data, NullValueStatus.Unknown); + } + + void RegisterExpressionResult(Expression expression, NullValueStatus expressionResult) + { + NullValueStatus oldStatus; + if (analysis.expressionResult.TryGetValue(expression, out oldStatus)) { + analysis.expressionResult[expression] = VariableStatusInfo.CombineStatus(analysis.expressionResult[expression], expressionResult); + } + else { + analysis.expressionResult[expression] = expressionResult; + } + } + + VisitorResult HandleExpressionResult(Expression expression, VariableStatusInfo dataAfterExpression, NullValueStatus expressionResult) { + RegisterExpressionResult(expression, expressionResult); + + return VisitorResult.ForValue(dataAfterExpression, expressionResult); + } + + VisitorResult HandleExpressionResult(Expression expression, VariableStatusInfo dataAfterExpression, bool expressionResult) { + RegisterExpressionResult(expression, NullValueStatus.DefinitelyNotNull); + + return VisitorResult.ForBoolValue(dataAfterExpression, expressionResult); + } + + VisitorResult HandleExpressionResult(Expression expression, VisitorResult result) { + RegisterExpressionResult(expression, result.NullableReturnResult); + + return result; + } + + public override VisitorResult VisitAssignmentExpression(AssignmentExpression assignmentExpression, VariableStatusInfo data) + { + var tentativeResult = assignmentExpression.Left.AcceptVisitor(this, data); + if (tentativeResult.ThrowsException) + return HandleExpressionResult(assignmentExpression, tentativeResult); + tentativeResult = assignmentExpression.Right.AcceptVisitor(this, tentativeResult.Variables); + if (tentativeResult.ThrowsException) + return HandleExpressionResult(assignmentExpression, tentativeResult); + + var leftIdentifier = assignmentExpression.Left as IdentifierExpression; + if (leftIdentifier != null) { + var resolveResult = analysis.context.Resolve(leftIdentifier); + if (resolveResult.IsError) { + return HandleExpressionResult(assignmentExpression, data, NullValueStatus.Error); + } + + if (resolveResult is LocalResolveResult) { + var result = new VisitorResult(); + result.NullableReturnResult = tentativeResult.NullableReturnResult; + result.Variables = tentativeResult.Variables.Clone(); + var oldValue = result.Variables [leftIdentifier.Identifier]; + + if (assignmentExpression.Operator == AssignmentOperatorType.Assign || + oldValue == NullValueStatus.Unassigned || + oldValue == NullValueStatus.DefinitelyNotNull || + tentativeResult.NullableReturnResult == NullValueStatus.Error || + tentativeResult.NullableReturnResult == NullValueStatus.Unknown) { + analysis.SetLocalVariableValue(result.Variables, leftIdentifier, tentativeResult.NullableReturnResult); + } else { + if (oldValue == NullValueStatus.DefinitelyNull) { + //Do nothing --it'll remain null + } else { + analysis.SetLocalVariableValue(result.Variables, leftIdentifier, NullValueStatus.PotentiallyNull); + } + } + + return HandleExpressionResult(assignmentExpression, result); + } + } + + return HandleExpressionResult(assignmentExpression, tentativeResult); + } + + public override VisitorResult VisitIdentifierExpression(IdentifierExpression identifierExpression, VariableStatusInfo data) + { + var resolveResult = analysis.context.Resolve(identifierExpression); + if (resolveResult.IsError) { + return HandleExpressionResult(identifierExpression, data, NullValueStatus.Error); + } + var local = resolveResult as LocalResolveResult; + if (local != null) { + var value = data [local.Variable.Name]; + if (value == NullValueStatus.CapturedUnknown) + value = NullValueStatus.Unknown; + return HandleExpressionResult(identifierExpression, data, value); + } + if (resolveResult.IsCompileTimeConstant) { + object value = resolveResult.ConstantValue; + if (value == null) { + return HandleExpressionResult(identifierExpression, data, NullValueStatus.DefinitelyNull); + } + var boolValue = value as bool?; + if (boolValue != null) { + return VisitorResult.ForBoolValue(data, (bool)boolValue); + } + return HandleExpressionResult(identifierExpression, data, NullValueStatus.DefinitelyNotNull); + } + + var memberResolveResult = resolveResult as MemberResolveResult; + + var returnValue = GetFieldReturnValue(memberResolveResult, data); + + return HandleExpressionResult(identifierExpression, data, returnValue); + } + + public override VisitorResult VisitDefaultValueExpression(DefaultValueExpression defaultValueExpression, VariableStatusInfo data) + { + var resolveResult = analysis.context.Resolve(defaultValueExpression); + if (resolveResult.IsError) { + return HandleExpressionResult(defaultValueExpression, data, NullValueStatus.Unknown); + } + + Debug.Assert(resolveResult.IsCompileTimeConstant); + + var status = resolveResult.ConstantValue == null && resolveResult.Type.IsReferenceType != false ? NullValueStatus.DefinitelyNull : NullValueStatus.DefinitelyNotNull; + return HandleExpressionResult(defaultValueExpression, data, status); + } + + public override VisitorResult VisitNullReferenceExpression(NullReferenceExpression nullReferenceExpression, VariableStatusInfo data) + { + return HandleExpressionResult(nullReferenceExpression, data, NullValueStatus.DefinitelyNull); + } + + public override VisitorResult VisitPrimitiveExpression(PrimitiveExpression primitiveExpression, VariableStatusInfo data) + { + return HandleExpressionResult(primitiveExpression, data, NullValueStatus.DefinitelyNotNull); + } + + public override VisitorResult VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression, VariableStatusInfo data) + { + return HandleExpressionResult(parenthesizedExpression, parenthesizedExpression.Expression.AcceptVisitor(this, data)); + } + + public override VisitorResult VisitConditionalExpression(ConditionalExpression conditionalExpression, VariableStatusInfo data) + { + var tentativeBaseResult = conditionalExpression.Condition.AcceptVisitor(this, data); + if (tentativeBaseResult.ThrowsException) + return HandleExpressionResult(conditionalExpression, tentativeBaseResult); + + var conditionResolveResult = analysis.context.Resolve(conditionalExpression.Condition); + + if (tentativeBaseResult.KnownBoolResult == true || true.Equals(conditionResolveResult.ConstantValue)) { + return HandleExpressionResult(conditionalExpression, conditionalExpression.TrueExpression.AcceptVisitor(this, tentativeBaseResult.TruePathVariables)); + } + if (tentativeBaseResult.KnownBoolResult == false || false.Equals(conditionResolveResult.ConstantValue)) { + return HandleExpressionResult(conditionalExpression, conditionalExpression.FalseExpression.AcceptVisitor(this, tentativeBaseResult.FalsePathVariables)); + } + + //No known bool result + var trueCaseResult = conditionalExpression.TrueExpression.AcceptVisitor(this, tentativeBaseResult.TruePathVariables); + if (trueCaseResult.ThrowsException) { + //We know that the true case will never be completed, then the right case is the only possible route. + return HandleExpressionResult(conditionalExpression, conditionalExpression.FalseExpression.AcceptVisitor(this, tentativeBaseResult.FalsePathVariables)); + } + var falseCaseResult = conditionalExpression.FalseExpression.AcceptVisitor(this, tentativeBaseResult.FalsePathVariables); + if (falseCaseResult.ThrowsException) { + return HandleExpressionResult(conditionalExpression, trueCaseResult.Variables, true); + } + + return HandleExpressionResult(conditionalExpression, VisitorResult.OrOperation(trueCaseResult, falseCaseResult)); + } + + public override VisitorResult VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression, VariableStatusInfo data) + { + //Let's not evaluate the sides just yet because of ??, && and || + + //We'll register the results here (with HandleExpressionResult) + //so each Visit*Expression won't have to do it itself + switch (binaryOperatorExpression.Operator) { + case BinaryOperatorType.ConditionalAnd: + return HandleExpressionResult(binaryOperatorExpression, VisitConditionalAndExpression(binaryOperatorExpression, data)); + case BinaryOperatorType.ConditionalOr: + return HandleExpressionResult(binaryOperatorExpression, VisitConditionalOrExpression(binaryOperatorExpression, data)); + case BinaryOperatorType.NullCoalescing: + return HandleExpressionResult(binaryOperatorExpression, VisitNullCoalescing(binaryOperatorExpression, data)); + case BinaryOperatorType.Equality: + return HandleExpressionResult(binaryOperatorExpression, VisitEquality(binaryOperatorExpression, data)); + case BinaryOperatorType.InEquality: + return HandleExpressionResult(binaryOperatorExpression, VisitEquality(binaryOperatorExpression, data).Negated); + default: + return HandleExpressionResult(binaryOperatorExpression, VisitOtherBinaryExpression(binaryOperatorExpression, data)); + } + } + + VisitorResult VisitOtherBinaryExpression(BinaryOperatorExpression binaryOperatorExpression, VariableStatusInfo data) + { + var leftTentativeResult = binaryOperatorExpression.Left.AcceptVisitor(this, data); + if (leftTentativeResult.ThrowsException) + return leftTentativeResult; + var rightTentativeResult = binaryOperatorExpression.Right.AcceptVisitor(this, leftTentativeResult.Variables); + if (rightTentativeResult.ThrowsException) + return rightTentativeResult; + + //TODO: Assuming operators are not overloaded by users + // (or, if they are, that they retain similar behavior to the default ones) + + switch (binaryOperatorExpression.Operator) { + case BinaryOperatorType.LessThan: + case BinaryOperatorType.GreaterThan: + //Operations < and > with nulls always return false + //Those same operations will other values may or may not return false + if (leftTentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNull && + rightTentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNull) { + return VisitorResult.ForBoolValue(rightTentativeResult.Variables, false); + } + //We don't know what the value is, but we know that both true and false are != null. + return VisitorResult.ForValue(rightTentativeResult.Variables, NullValueStatus.DefinitelyNotNull); + case BinaryOperatorType.LessThanOrEqual: + case BinaryOperatorType.GreaterThanOrEqual: + if (leftTentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNull) { + if (rightTentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNull) + return VisitorResult.ForBoolValue(rightTentativeResult.Variables, true); + if (rightTentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNotNull) + return VisitorResult.ForBoolValue(rightTentativeResult.Variables, false); + } else if (leftTentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNotNull) { + if (rightTentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNull) + return VisitorResult.ForBoolValue(rightTentativeResult.Variables, false); + } + + return VisitorResult.ForValue(rightTentativeResult.Variables, NullValueStatus.Unknown); + default: + //Anything else: null + anything == anything + null == null. + //not null + not null = not null + if (leftTentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNull) { + return VisitorResult.ForValue(rightTentativeResult.Variables, NullValueStatus.DefinitelyNull); + } + if (leftTentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNotNull) { + if (rightTentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNull) + return VisitorResult.ForValue(rightTentativeResult.Variables, NullValueStatus.DefinitelyNull); + if (rightTentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNotNull) + return VisitorResult.ForValue(rightTentativeResult.Variables, NullValueStatus.DefinitelyNotNull); + } + + return VisitorResult.ForValue(rightTentativeResult.Variables, NullValueStatus.Unknown); + } + } + + VisitorResult WithVariableValue(VisitorResult result, IdentifierExpression identifier, bool isNull) + { + var localVariableResult = analysis.context.Resolve(identifier) as LocalResolveResult; + if (localVariableResult != null) { + result.ConditionalBranchInfo.TrueResultVariableNullStates[identifier.Identifier] = isNull; + if (isNull) { + result.ConditionalBranchInfo.FalseResultVariableNullStates[identifier.Identifier] = false; + } + } + return result; + } + + VisitorResult VisitEquality(BinaryOperatorExpression binaryOperatorExpression, VariableStatusInfo data) + { + //TODO: Should this check for user operators? + + var tentativeLeftResult = binaryOperatorExpression.Left.AcceptVisitor(this, data); + if (tentativeLeftResult.ThrowsException) + return tentativeLeftResult; + var tentativeRightResult = binaryOperatorExpression.Right.AcceptVisitor(this, tentativeLeftResult.Variables); + if (tentativeRightResult.ThrowsException) + return tentativeRightResult; + + if (tentativeLeftResult.KnownBoolResult != null && tentativeLeftResult.KnownBoolResult == tentativeRightResult.KnownBoolResult) { + return VisitorResult.ForBoolValue(tentativeRightResult.Variables, true); + } + + if (tentativeLeftResult.KnownBoolResult != null && tentativeLeftResult.KnownBoolResult == !tentativeRightResult.KnownBoolResult) { + return VisitorResult.ForBoolValue(tentativeRightResult.Variables, false); + } + + if (tentativeLeftResult.NullableReturnResult.IsDefiniteValue()) { + if (tentativeRightResult.NullableReturnResult.IsDefiniteValue()) { + if (tentativeLeftResult.NullableReturnResult == NullValueStatus.DefinitelyNull || tentativeRightResult.NullableReturnResult == NullValueStatus.DefinitelyNull) { + return VisitorResult.ForBoolValue(tentativeRightResult.Variables, tentativeLeftResult.NullableReturnResult == tentativeRightResult.NullableReturnResult); + } + } + } + + var result = new VisitorResult(); + result.Variables = tentativeRightResult.Variables; + result.NullableReturnResult = NullValueStatus.Unknown; + result.ConditionalBranchInfo = new ConditionalBranchInfo(); + + if (tentativeRightResult.NullableReturnResult.IsDefiniteValue()) { + var identifier = CSharpUtil.GetInnerMostExpression(binaryOperatorExpression.Left) as IdentifierExpression; + + if (identifier != null) { + bool isNull = (tentativeRightResult.NullableReturnResult == NullValueStatus.DefinitelyNull); + + WithVariableValue(result, identifier, isNull); + } + } + + if (tentativeLeftResult.NullableReturnResult.IsDefiniteValue()) { + var identifier = CSharpUtil.GetInnerMostExpression(binaryOperatorExpression.Right) as IdentifierExpression; + + if (identifier != null) { + bool isNull = (tentativeLeftResult.NullableReturnResult == NullValueStatus.DefinitelyNull); + + WithVariableValue(result, identifier, isNull); + } + } + + return result; + } + + VisitorResult VisitConditionalAndExpression(BinaryOperatorExpression binaryOperatorExpression, VariableStatusInfo data) + { + var tentativeLeftResult = binaryOperatorExpression.Left.AcceptVisitor(this, data); + if (tentativeLeftResult.KnownBoolResult == false || tentativeLeftResult.ThrowsException) { + return tentativeLeftResult; + } + + var truePath = tentativeLeftResult.TruePathVariables; + var tentativeRightResult = binaryOperatorExpression.Right.AcceptVisitor(this, truePath); + if (tentativeRightResult.ThrowsException) { + //If the true path throws an exception, then the only way for the expression to complete + //successfully is if the left expression is false + return VisitorResult.ForBoolValue(tentativeLeftResult.FalsePathVariables, false); + } + + return VisitorResult.AndOperation(tentativeLeftResult, tentativeRightResult); + } + + VisitorResult VisitConditionalOrExpression(BinaryOperatorExpression binaryOperatorExpression, VariableStatusInfo data) + { + var tentativeLeftResult = binaryOperatorExpression.Left.AcceptVisitor(this, data); + if (tentativeLeftResult.KnownBoolResult == true || tentativeLeftResult.ThrowsException) { + return tentativeLeftResult; + } + + var falsePath = tentativeLeftResult.FalsePathVariables; + var tentativeRightResult = binaryOperatorExpression.Right.AcceptVisitor(this, falsePath); + if (tentativeRightResult.ThrowsException) { + //If the false path throws an exception, then the only way for the expression to complete + //successfully is if the left expression is true + return VisitorResult.ForBoolValue(tentativeLeftResult.TruePathVariables, true); + } + + return VisitorResult.OrOperation(tentativeLeftResult, tentativeRightResult); + } + + VisitorResult VisitNullCoalescing(BinaryOperatorExpression binaryOperatorExpression, VariableStatusInfo data) + { + var leftTentativeResult = binaryOperatorExpression.Left.AcceptVisitor(this, data); + if (leftTentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNotNull || leftTentativeResult.ThrowsException) { + return leftTentativeResult; + } + + //If the right side is found, then the left side is known to be null + var newData = leftTentativeResult.Variables; + var leftIdentifier = CSharpUtil.GetInnerMostExpression(binaryOperatorExpression.Left) as IdentifierExpression; + if (leftIdentifier != null) { + newData = newData.Clone(); + analysis.SetLocalVariableValue(newData, leftIdentifier, NullValueStatus.DefinitelyNull); + } + + var rightTentativeResult = binaryOperatorExpression.Right.AcceptVisitor(this, newData); + if (rightTentativeResult.ThrowsException) { + //This means the left expression was not null all along (or else the expression will throw an exception) + + if (leftIdentifier != null) { + newData = newData.Clone(); + analysis.SetLocalVariableValue(newData, leftIdentifier, NullValueStatus.DefinitelyNotNull); + return VisitorResult.ForValue(newData, NullValueStatus.DefinitelyNotNull); + } + + return VisitorResult.ForValue(leftTentativeResult.Variables, NullValueStatus.DefinitelyNotNull); + } + + var mergedVariables = rightTentativeResult.Variables; + var nullValue = rightTentativeResult.NullableReturnResult; + + if (leftTentativeResult.NullableReturnResult != NullValueStatus.DefinitelyNull) { + mergedVariables = mergedVariables.Clone(); + mergedVariables.ReceiveIncoming(leftTentativeResult.Variables); + if (nullValue == NullValueStatus.DefinitelyNull) { + nullValue = NullValueStatus.PotentiallyNull; + } + } + + return VisitorResult.ForValue(mergedVariables, nullValue); + } + + public override VisitorResult VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression, VariableStatusInfo data) + { + //TODO: Again, what to do when overloaded operators are found? + + var tentativeResult = unaryOperatorExpression.Expression.AcceptVisitor(this, data); + if (tentativeResult.ThrowsException) + return HandleExpressionResult(unaryOperatorExpression, tentativeResult); + + if (unaryOperatorExpression.Operator == UnaryOperatorType.Not) { + return HandleExpressionResult(unaryOperatorExpression, tentativeResult.Negated); + } + return HandleExpressionResult(unaryOperatorExpression, tentativeResult); + } + + public override VisitorResult VisitInvocationExpression(InvocationExpression invocationExpression, VariableStatusInfo data) + { + //TODO: Handle some common methods such as string.IsNullOrEmpty + + var targetResult = invocationExpression.Target.AcceptVisitor(this, data); + if (targetResult.ThrowsException) + return HandleExpressionResult(invocationExpression, targetResult); + + data = targetResult.Variables; + + var methodResolveResult = analysis.context.Resolve(invocationExpression) as CSharpInvocationResolveResult; + + List parameterResults = new List(); + + foreach (var argumentToHandle in invocationExpression.Arguments.Select((argument, parameterIndex) => new { argument, parameterIndex })) { + var argument = argumentToHandle.argument; + var parameterIndex = argumentToHandle.parameterIndex; + + var result = argument.AcceptVisitor(this, data); + if (result.ThrowsException) + return HandleExpressionResult(invocationExpression, result); + parameterResults.Add(result); + + var namedArgument = argument as NamedArgumentExpression; + + var directionExpression = (namedArgument == null ? argument : namedArgument.Expression) as DirectionExpression; + if (directionExpression != null && methodResolveResult != null) { + var identifier = directionExpression.Expression as IdentifierExpression; + if (identifier != null) { + //out and ref parameters do *NOT* capture the variable (since they must stop changing it by the time they return) + var identifierResolveResult = analysis.context.Resolve(identifier) as LocalResolveResult; + if (identifierResolveResult != null && IsTypeNullable(identifierResolveResult.Type)) { + data = data.Clone(); + + FixParameter(argument, methodResolveResult.Member.Parameters, parameterIndex, identifier, data); + } + } + + + continue; + } + + data = result.Variables; + } + + var identifierExpression = CSharpUtil.GetInnerMostExpression(invocationExpression.Target) as IdentifierExpression; + if (identifierExpression != null) { + if (targetResult.NullableReturnResult == NullValueStatus.DefinitelyNull) { + return HandleExpressionResult(invocationExpression, VisitorResult.ForException(data)); + } + + var descendentIdentifiers = invocationExpression.Arguments.SelectMany(argument => argument.DescendantsAndSelf).OfType(); + if (!descendentIdentifiers.Any(identifier => identifier.Identifier == identifierExpression.Identifier)) { + //TODO: We can make this check better (see VisitIndexerExpression for more details) + data = data.Clone(); + analysis.SetLocalVariableValue(data, identifierExpression, NullValueStatus.DefinitelyNotNull); + } + } + + return HandleExpressionResult(invocationExpression, GetMethodVisitorResult(methodResolveResult, data, parameterResults)); + } + + static VisitorResult GetMethodVisitorResult(CSharpInvocationResolveResult methodResolveResult, VariableStatusInfo data, List parameterResults) + { + if (methodResolveResult == null) + return VisitorResult.ForValue(data, NullValueStatus.Unknown); + + var method = methodResolveResult.Member as IMethod; + if (method != null) { + if (method.GetAttribute(new FullTypeName(AnnotationNames.AssertionMethodAttribute)) != null) { + var assertionParameters = method.Parameters.Select((parameter, index) => new { index, parameter }) + .Select(parameter => new { parameter.index, parameter.parameter, attributes = parameter.parameter.Attributes.Where(attribute => attribute.AttributeType.FullName == AnnotationNames.AssertionConditionAttribute).ToList() }) + .Where(parameter => parameter.attributes.Count() == 1) + .Select(parameter => new { parameter.index, parameter.parameter, attribute = parameter.attributes[0] }) + .ToList(); + + //Unclear what should be done if there are multiple assertion conditions + if (assertionParameters.Count() == 1) { + Debug.Assert(methodResolveResult.Arguments.Count == parameterResults.Count); + + var assertionParameter = assertionParameters [0]; + VisitorResult assertionParameterResult = null; + + object intendedResult = true; + var positionalArgument = assertionParameter.attribute.PositionalArguments.FirstOrDefault() as MemberResolveResult; + if (positionalArgument != null && positionalArgument.Type.FullName == AnnotationNames.AssertionConditionTypeAttribute) { + switch (positionalArgument.Member.FullName) { + case AnnotationNames.AssertionConditionTypeIsTrue: + intendedResult = true; + break; + case AnnotationNames.AssertionConditionTypeIsFalse: + intendedResult = false; + break; + case AnnotationNames.AssertionConditionTypeIsNull: + intendedResult = null; + break; + case AnnotationNames.AssertionConditionTypeIsNotNull: + intendedResult = ""; + break; + } + } + + int parameterIndex = assertionParameter.index; + if (assertionParameter.index < methodResolveResult.Arguments.Count && !(methodResolveResult.Arguments [assertionParameter.index] is NamedArgumentResolveResult)) { + //Use index + assertionParameterResult = parameterResults [assertionParameter.index]; + } else { + //Use named argument + int? nameIndex = methodResolveResult.Arguments.Select((argument, index) => new { argument, index}) + .Where(argument => { + var namedArgument = argument.argument as NamedArgumentResolveResult; + return namedArgument != null && namedArgument.ParameterName == assertionParameter.parameter.Name; + }).Select(argument => (int?)argument.index).FirstOrDefault(); + + if (nameIndex != null) { + parameterIndex = nameIndex.Value; + assertionParameterResult = parameterResults [nameIndex.Value]; + } else if (assertionParameter.parameter.IsOptional) { + //Try to use default value + + if (intendedResult is string) { + if (assertionParameter.parameter.ConstantValue == null) { + return VisitorResult.ForException(data); + } + } else { + if (!object.Equals(assertionParameter.parameter.ConstantValue, intendedResult)) { + return VisitorResult.ForException(data); + } + } + } else { + //The parameter was not specified, yet it is not optional? + return VisitorResult.ForException(data); + } + } + + //Now check assertion + if (assertionParameterResult != null) { + if (intendedResult is bool) { + if (assertionParameterResult.KnownBoolResult == !(bool)intendedResult) { + return VisitorResult.ForException(data); + } + + data = (bool)intendedResult ? assertionParameterResult.TruePathVariables : assertionParameterResult.FalsePathVariables; + } else { + bool shouldBeNull = intendedResult == null; + + if (assertionParameterResult.NullableReturnResult == (shouldBeNull ? NullValueStatus.DefinitelyNotNull : NullValueStatus.DefinitelyNull)) { + return VisitorResult.ForException(data); + } + + var parameterResolveResult = methodResolveResult.Arguments [parameterIndex]; + + LocalResolveResult localVariableResult = null; + + var conversionResolveResult = parameterResolveResult as ConversionResolveResult; + if (conversionResolveResult != null) { + if (!IsTypeNullable(conversionResolveResult.Type)) { + if (intendedResult == null) { + return VisitorResult.ForException(data); + } + } else { + localVariableResult = conversionResolveResult.Input as LocalResolveResult; + } + } else { + localVariableResult = parameterResolveResult as LocalResolveResult; + } + + if (localVariableResult != null && data[localVariableResult.Variable.Name] != NullValueStatus.CapturedUnknown) { + data = data.Clone(); + data [localVariableResult.Variable.Name] = shouldBeNull ? NullValueStatus.DefinitelyNull : NullValueStatus.DefinitelyNotNull; + } + } + } + } + } + } + + bool isNullable = IsTypeNullable(methodResolveResult.Type); + if (!isNullable) { + return VisitorResult.ForValue(data, NullValueStatus.DefinitelyNotNull); + } + + if (method != null) + return VisitorResult.ForValue(data, GetNullableStatus(method)); + + return VisitorResult.ForValue(data, GetNullableStatus(methodResolveResult.TargetResult.Type.GetDefinition())); + } + + static NullValueStatus GetNullableStatus(IEntity entity) + { + if (entity.DeclaringType != null && entity.DeclaringType.Kind == TypeKind.Delegate) { + //Handle Delegate.Invoke method + return GetNullableStatus(entity.DeclaringTypeDefinition); + } + + return GetNullableStatus(fullTypeName => entity.GetAttribute(new FullTypeName(fullTypeName))); + } + + static NullValueStatus GetNullableStatus(IParameter parameter) + { + return GetNullableStatus(fullTypeName => parameter.Attributes.FirstOrDefault(attribute => attribute.AttributeType.FullName == fullTypeName)); + } + + static NullValueStatus GetNullableStatus(Func attributeGetter) + { + if (attributeGetter(AnnotationNames.NotNullAttribute) != null) { + return NullValueStatus.DefinitelyNotNull; + } + if (attributeGetter(AnnotationNames.CanBeNullAttribute) != null) { + return NullValueStatus.PotentiallyNull; + } + return NullValueStatus.Unknown; + } + + public override VisitorResult VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression, VariableStatusInfo data) + { + var targetResult = memberReferenceExpression.Target.AcceptVisitor(this, data); + if (targetResult.ThrowsException) + return HandleExpressionResult(memberReferenceExpression, targetResult); + + var variables = targetResult.Variables; + + var memberResolveResult = analysis.context.Resolve(memberReferenceExpression) as MemberResolveResult; + + var targetIdentifier = CSharpUtil.GetInnerMostExpression(memberReferenceExpression.Target) as IdentifierExpression; + if (targetIdentifier != null) { + if (memberResolveResult == null) { + var invocation = memberReferenceExpression.Parent as InvocationExpression; + if (invocation != null) { + memberResolveResult = analysis.context.Resolve(invocation) as MemberResolveResult; + } + } + + if (memberResolveResult != null && memberResolveResult.Member.FullName != "System.Nullable.HasValue") { + var method = memberResolveResult.Member as IMethod; + if (method == null || !method.IsExtensionMethod) { + if (targetResult.NullableReturnResult == NullValueStatus.DefinitelyNull) { + return HandleExpressionResult(memberReferenceExpression, VisitorResult.ForException(variables)); + } + if (variables [targetIdentifier.Identifier] != NullValueStatus.CapturedUnknown) { + variables = variables.Clone(); + analysis.SetLocalVariableValue(variables, targetIdentifier, NullValueStatus.DefinitelyNotNull); + } + } + } + } + + var returnValue = GetFieldReturnValue(memberResolveResult, data); + return HandleExpressionResult(memberReferenceExpression, variables, returnValue); + } + + static NullValueStatus GetFieldReturnValue(MemberResolveResult memberResolveResult, VariableStatusInfo data) + { + bool isNullable = memberResolveResult == null || IsTypeNullable(memberResolveResult.Type); + if (!isNullable) { + return NullValueStatus.DefinitelyNotNull; + } + + if (memberResolveResult != null) { + return GetNullableStatus(memberResolveResult.Member); + } + + return NullValueStatus.Unknown; + } + + public override VisitorResult VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression, VariableStatusInfo data) + { + return HandleExpressionResult(typeReferenceExpression, data, NullValueStatus.Unknown); + + } + + void FixParameter(Expression argument, IList parameters, int parameterIndex, IdentifierExpression identifier, VariableStatusInfo data) + { + NullValueStatus newValue = NullValueStatus.Unknown; + if (argument is NamedArgumentExpression) { + var namedResolveResult = analysis.context.Resolve(argument) as NamedArgumentResolveResult; + if (namedResolveResult != null) { + newValue = GetNullableStatus(namedResolveResult.Parameter); + } + } + else { + var parameter = parameters[parameterIndex]; + newValue = GetNullableStatus(parameter); + } + analysis.SetLocalVariableValue(data, identifier, newValue); + } + + public override VisitorResult VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression, VariableStatusInfo data) + { + foreach (var argumentToHandle in objectCreateExpression.Arguments.Select((argument, parameterIndex) => new { argument, parameterIndex })) { + var argument = argumentToHandle.argument; + var parameterIndex = argumentToHandle.parameterIndex; + + var namedArgument = argument as NamedArgumentExpression; + + var directionExpression = (namedArgument == null ? argument : namedArgument.Expression) as DirectionExpression; + if (directionExpression != null) { + var identifier = directionExpression.Expression as IdentifierExpression; + if (identifier != null && data [identifier.Identifier] != NullValueStatus.CapturedUnknown) { + //out and ref parameters do *NOT* capture the variable (since they must stop changing it by the time they return) + data = data.Clone(); + + var constructorResolveResult = analysis.context.Resolve(objectCreateExpression) as CSharpInvocationResolveResult; + if (constructorResolveResult != null) + FixParameter(argument, constructorResolveResult.Member.Parameters, parameterIndex, identifier, data); + } + continue; + } + + var argumentResult = argument.AcceptVisitor(this, data); + if (argumentResult.ThrowsException) + return argumentResult; + + data = argumentResult.Variables; + } + + //Constructors never return null + return HandleExpressionResult(objectCreateExpression, data, NullValueStatus.DefinitelyNotNull); + } + + public override VisitorResult VisitArrayCreateExpression(ArrayCreateExpression arrayCreateExpression, VariableStatusInfo data) + { + foreach (var argument in arrayCreateExpression.Arguments) { + var result = argument.AcceptVisitor(this, data); + if (result.ThrowsException) + return result; + data = result.Variables.Clone(); + } + + if (arrayCreateExpression.Initializer.IsNull) { + return HandleExpressionResult(arrayCreateExpression, data, NullValueStatus.DefinitelyNotNull); + } + + return HandleExpressionResult(arrayCreateExpression, arrayCreateExpression.Initializer.AcceptVisitor(this, data)); + } + + public override VisitorResult VisitArrayInitializerExpression(ArrayInitializerExpression arrayInitializerExpression, VariableStatusInfo data) + { + if (arrayInitializerExpression.IsSingleElement) { + return HandleExpressionResult(arrayInitializerExpression, arrayInitializerExpression.Elements.Single().AcceptVisitor(this, data)); + } + if (!arrayInitializerExpression.Elements.Any()) { + //Empty array + return HandleExpressionResult(arrayInitializerExpression, VisitorResult.ForValue(data, NullValueStatus.Unknown)); + } + + NullValueStatus enumeratedValue = NullValueStatus.UnreachableOrInexistent; + foreach (var element in arrayInitializerExpression.Elements) { + var result = element.AcceptVisitor(this, data); + if (result.ThrowsException) + return result; + data = result.Variables.Clone(); + enumeratedValue = VariableStatusInfo.CombineStatus(enumeratedValue, result.NullableReturnResult); + + } + return HandleExpressionResult(arrayInitializerExpression, VisitorResult.ForEnumeratedValue(data, enumeratedValue)); + } + + public override VisitorResult VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression, VariableStatusInfo data) + { + foreach (var initializer in anonymousTypeCreateExpression.Initializers) { + var result = initializer.AcceptVisitor(this, data); + if (result.ThrowsException) + return result; + data = result.Variables; + } + + return HandleExpressionResult(anonymousTypeCreateExpression, data, NullValueStatus.DefinitelyNotNull); + } + + public override VisitorResult VisitLambdaExpression(LambdaExpression lambdaExpression, VariableStatusInfo data) + { + var newData = data.Clone(); + + var identifiers = lambdaExpression.Descendants.OfType(); + foreach (var identifier in identifiers) { + //Check if it is in a "change-null-state" context + //For instance, x++ does not change the null state + //but `x = y` does. + if (identifier.Parent is AssignmentExpression && identifier.Role == AssignmentExpression.LeftRole) { + var parent = (AssignmentExpression)identifier.Parent; + if (parent.Operator != AssignmentOperatorType.Assign) { + continue; + } + } else { + //No other context matters + //Captured variables are never passed by reference (out/ref) + continue; + } + + //At this point, we know there's a good chance the variable has been changed + var identifierResolveResult = analysis.context.Resolve(identifier) as LocalResolveResult; + if (identifierResolveResult != null && IsTypeNullable(identifierResolveResult.Type)) { + analysis.SetLocalVariableValue(newData, identifier, NullValueStatus.CapturedUnknown); + } + } + + //The lambda itself is known not to be null + return HandleExpressionResult(lambdaExpression, newData, NullValueStatus.DefinitelyNotNull); + } + + public override VisitorResult VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression, VariableStatusInfo data) + { + var newData = data.Clone(); + + var identifiers = anonymousMethodExpression.Descendants.OfType(); + foreach (var identifier in identifiers) { + //Check if it is in a "change-null-state" context + //For instance, x++ does not change the null state + //but `x = y` does. + if (identifier.Parent is AssignmentExpression && identifier.Role == AssignmentExpression.LeftRole) { + var parent = (AssignmentExpression)identifier.Parent; + if (parent.Operator != AssignmentOperatorType.Assign) { + continue; + } + } else { + //No other context matters + //Captured variables are never passed by reference (out/ref) + continue; + } + + //At this point, we know there's a good chance the variable has been changed + var identifierResolveResult = analysis.context.Resolve(identifier) as LocalResolveResult; + if (identifierResolveResult != null && IsTypeNullable(identifierResolveResult.Type)) { + analysis.SetLocalVariableValue(newData, identifier, NullValueStatus.CapturedUnknown); + } + } + + //The anonymous method itself is known not to be null + return HandleExpressionResult(anonymousMethodExpression, newData, NullValueStatus.DefinitelyNotNull); + } + + + public override VisitorResult VisitNamedExpression(NamedExpression namedExpression, VariableStatusInfo data) + { + return HandleExpressionResult(namedExpression, namedExpression.Expression.AcceptVisitor(this, data)); + } + + public override VisitorResult VisitAsExpression(AsExpression asExpression, VariableStatusInfo data) + { + var tentativeResult = asExpression.Expression.AcceptVisitor(this, data); + if (tentativeResult.ThrowsException) + return tentativeResult; + + NullValueStatus result; + if (tentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNull) { + result = NullValueStatus.DefinitelyNull; + } else { + var asResolveResult = analysis.context.Resolve(asExpression) as CastResolveResult; + if (asResolveResult == null || + asResolveResult.IsError || + asResolveResult.Input.Type.Kind == TypeKind.Unknown || + asResolveResult.Type.Kind == TypeKind.Unknown) { + + result = NullValueStatus.Unknown; + } else { + var conversion = new CSharpConversions(analysis.context.Compilation); + var foundConversion = conversion.ExplicitConversion(asResolveResult.Input.Type, asResolveResult.Type); + + if (foundConversion == Conversion.None) { + result = NullValueStatus.DefinitelyNull; + } else if (foundConversion == Conversion.IdentityConversion) { + result = tentativeResult.NullableReturnResult; + } else { + result = NullValueStatus.PotentiallyNull; + } + } + } + return HandleExpressionResult(asExpression, tentativeResult.Variables, result); + } + + public override VisitorResult VisitCastExpression(CastExpression castExpression, VariableStatusInfo data) + { + var tentativeResult = castExpression.Expression.AcceptVisitor(this, data); + if (tentativeResult.ThrowsException) + return tentativeResult; + + NullValueStatus result; + if (tentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNull) { + result = NullValueStatus.DefinitelyNull; + } else { + result = NullValueStatus.Unknown; + } + + VariableStatusInfo variables = tentativeResult.Variables; + + var resolveResult = analysis.context.Resolve(castExpression) as CastResolveResult; + if (resolveResult != null && !IsTypeNullable(resolveResult.Type)) { + if (result == NullValueStatus.DefinitelyNull) { + return HandleExpressionResult(castExpression, VisitorResult.ForException(tentativeResult.Variables)); + } + + var identifierExpression = CSharpUtil.GetInnerMostExpression(castExpression.Expression) as IdentifierExpression; + if (identifierExpression != null) { + var currentValue = variables [identifierExpression.Identifier]; + if (currentValue != NullValueStatus.CapturedUnknown && + currentValue != NullValueStatus.UnreachableOrInexistent && + currentValue != NullValueStatus.DefinitelyNotNull) { + //DefinitelyNotNull is included in this list because if that's the status + // then we don't need to change anything + + variables = variables.Clone(); + variables [identifierExpression.Identifier] = NullValueStatus.DefinitelyNotNull; + } + } + + result = NullValueStatus.DefinitelyNotNull; + } + + return HandleExpressionResult(castExpression, variables, result); + } + + public override VisitorResult VisitIsExpression(IsExpression isExpression, VariableStatusInfo data) + { + var tentativeResult = isExpression.Expression.AcceptVisitor(this, data); + if (tentativeResult.ThrowsException) + return tentativeResult; + + //TODO: Consider, for instance: new X() is X. The result is known to be true, so we can use KnownBoolValue + return HandleExpressionResult(isExpression, tentativeResult.Variables, NullValueStatus.DefinitelyNotNull); + } + + public override VisitorResult VisitDirectionExpression(DirectionExpression directionExpression, VariableStatusInfo data) + { + return HandleExpressionResult(directionExpression, directionExpression.Expression.AcceptVisitor(this, data)); + } + + public override VisitorResult VisitCheckedExpression(CheckedExpression checkedExpression, VariableStatusInfo data) + { + return HandleExpressionResult(checkedExpression, checkedExpression.Expression.AcceptVisitor(this, data)); + } + + public override VisitorResult VisitUncheckedExpression(UncheckedExpression uncheckedExpression, VariableStatusInfo data) + { + return HandleExpressionResult(uncheckedExpression, uncheckedExpression.Expression.AcceptVisitor(this, data)); + } + + public override VisitorResult VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression, VariableStatusInfo data) + { + return HandleExpressionResult(thisReferenceExpression, data, NullValueStatus.DefinitelyNotNull); + } + + public override VisitorResult VisitIndexerExpression(IndexerExpression indexerExpression, VariableStatusInfo data) + { + var tentativeResult = indexerExpression.Target.AcceptVisitor(this, data); + if (tentativeResult.ThrowsException) + return tentativeResult; + + data = tentativeResult.Variables; + + foreach (var argument in indexerExpression.Arguments) { + var result = argument.AcceptVisitor(this, data); + if (result.ThrowsException) + return result; + data = result.Variables.Clone(); + } + + IdentifierExpression targetAsIdentifier = CSharpUtil.GetInnerMostExpression(indexerExpression.Target) as IdentifierExpression; + if (targetAsIdentifier != null) { + if (tentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNull) + return HandleExpressionResult(indexerExpression, VisitorResult.ForException(data)); + + //If this doesn't cause an exception, then the target is not null + //But we won't set it if it has been changed + var descendentIdentifiers = indexerExpression.Arguments + .SelectMany(argument => argument.DescendantsAndSelf).OfType(); + if (!descendentIdentifiers.Any(identifier => identifier.Identifier == targetAsIdentifier.Identifier)) { + //TODO: this check might be improved to include more legitimate cases + //A good check will necessarily have to consider captured variables + data = data.Clone(); + analysis.SetLocalVariableValue(data, targetAsIdentifier, NullValueStatus.DefinitelyNotNull); + } + } + + var indexerResolveResult = analysis.context.Resolve(indexerExpression) as CSharpInvocationResolveResult; + bool isNullable = indexerResolveResult == null || IsTypeNullable(indexerResolveResult.Type); + + var returnValue = isNullable ? NullValueStatus.Unknown : NullValueStatus.DefinitelyNotNull; + return HandleExpressionResult(indexerExpression, data, returnValue); + } + + public override VisitorResult VisitBaseReferenceExpression(BaseReferenceExpression baseReferenceExpression, VariableStatusInfo data) + { + return HandleExpressionResult(baseReferenceExpression, data, NullValueStatus.DefinitelyNotNull); + } + + public override VisitorResult VisitTypeOfExpression(TypeOfExpression typeOfExpression, VariableStatusInfo data) + { + return HandleExpressionResult(typeOfExpression, data, NullValueStatus.DefinitelyNotNull); + } + + public override VisitorResult VisitSizeOfExpression(SizeOfExpression sizeOfExpression, VariableStatusInfo data) + { + return HandleExpressionResult(sizeOfExpression, data, NullValueStatus.DefinitelyNotNull); + } + + public override VisitorResult VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression, VariableStatusInfo data) + { + var targetResult = pointerReferenceExpression.Target.AcceptVisitor(this, data); + if (targetResult.ThrowsException) + return targetResult; + return HandleExpressionResult(pointerReferenceExpression, targetResult.Variables, NullValueStatus.DefinitelyNotNull); + } + + public override VisitorResult VisitStackAllocExpression(StackAllocExpression stackAllocExpression, VariableStatusInfo data) + { + var countResult = stackAllocExpression.CountExpression.AcceptVisitor(this, data); + if (countResult.ThrowsException) + return countResult; + return HandleExpressionResult(stackAllocExpression, countResult.Variables, NullValueStatus.DefinitelyNotNull); + } + + public override VisitorResult VisitNamedArgumentExpression(NamedArgumentExpression namedArgumentExpression, VariableStatusInfo data) + { + return HandleExpressionResult(namedArgumentExpression, namedArgumentExpression.Expression.AcceptVisitor(this, data)); + } + + public override VisitorResult VisitUndocumentedExpression(UndocumentedExpression undocumentedExpression, VariableStatusInfo data) + { + throw new NotImplementedException(); + } + + public override VisitorResult VisitQueryExpression(QueryExpression queryExpression, VariableStatusInfo data) + { + VariableStatusInfo outgoingData = data.Clone(); + NullValueStatus? outgoingEnumeratedValue = null; + var clauses = queryExpression.Clauses.ToList(); + + var backtracingClauses = (from item in clauses.Select((clause, i) => new { clause, i }) + where item.clause is QueryFromClause || item.clause is QueryJoinClause || item.clause is QueryContinuationClause + select item.i).ToList(); + + var beforeClauseVariableStates = Enumerable.Range(0, clauses.Count).ToDictionary(clauseIndex => clauseIndex, + clauseIndex => new VariableStatusInfo()); + var afterClauseVariableStates = Enumerable.Range(0, clauses.Count).ToDictionary(clauseIndex => clauseIndex, + clauseIndex => new VariableStatusInfo()); + + VisitorResult lastValidResult = null; + int currentClauseIndex = 0; + for (;;) { + VisitorResult result = null; + QueryClause clause = null; + bool backtrack = false; + + if (currentClauseIndex >= clauses.Count) { + backtrack = true; + } else { + clause = clauses [currentClauseIndex]; + beforeClauseVariableStates [currentClauseIndex].ReceiveIncoming(data); + result = clause.AcceptVisitor(this, data); + data = result.Variables; + lastValidResult = result; + if (result.KnownBoolResult == false) { + backtrack = true; + } + if (result.ThrowsException) { + //Don't backtrack. Exceptions completely stop the query. + break; + } + else { + afterClauseVariableStates [currentClauseIndex].ReceiveIncoming(data); + } + } + + if (backtrack) { + int? newIndex; + for (;;) { + newIndex = backtracingClauses.LastOrDefault(index => index < currentClauseIndex); + if (newIndex == null) { + //We've reached the end + break; + } + + currentClauseIndex = (int)newIndex + 1; + + if (!beforeClauseVariableStates[currentClauseIndex].ReceiveIncoming(lastValidResult.Variables)) { + newIndex = null; + break; + } + } + + if (newIndex == null) { + break; + } + + } else { + if (clause is QuerySelectClause) { + outgoingData.ReceiveIncoming(data); + if (outgoingEnumeratedValue == null) + outgoingEnumeratedValue = result.EnumeratedValueResult; + else + outgoingEnumeratedValue = VariableStatusInfo.CombineStatus(outgoingEnumeratedValue.Value, result.EnumeratedValueResult); + } + + ++currentClauseIndex; + } + } + + var finalData = new VariableStatusInfo(); + var endingClauseIndices = from item in clauses.Select((clause, i) => new { clause, i }) + let clause = item.clause + where clause is QueryFromClause || + clause is QueryContinuationClause || + clause is QueryJoinClause || + clause is QuerySelectClause || + clause is QueryWhereClause + select item.i; + foreach (var clauseIndex in endingClauseIndices) { + finalData.ReceiveIncoming(afterClauseVariableStates [clauseIndex]); + } + + return VisitorResult.ForEnumeratedValue(finalData, outgoingEnumeratedValue ?? NullValueStatus.Unknown); + } + + public override VisitorResult VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause, VariableStatusInfo data) + { + return IntroduceVariableFromEnumeratedValue(queryContinuationClause.Identifier, queryContinuationClause.PrecedingQuery, data); + } + + VisitorResult IntroduceVariableFromEnumeratedValue(string newVariable, Expression expression, VariableStatusInfo data) + { + var result = expression.AcceptVisitor(this, data); + var newVariables = result.Variables.Clone(); + newVariables[newVariable] = result.EnumeratedValueResult; + return VisitorResult.ForValue(newVariables, NullValueStatus.Unknown); + } + + public override VisitorResult VisitQueryFromClause(QueryFromClause queryFromClause, VariableStatusInfo data) + { + return IntroduceVariableFromEnumeratedValue(queryFromClause.Identifier, queryFromClause.Expression, data); + } + + public override VisitorResult VisitQueryJoinClause(QueryJoinClause queryJoinClause, VariableStatusInfo data) + { + //TODO: Check if this really works in weird edge-cases. + var tentativeResult = IntroduceVariableFromEnumeratedValue(queryJoinClause.JoinIdentifier, queryJoinClause.InExpression, data); + tentativeResult = queryJoinClause.OnExpression.AcceptVisitor(this, tentativeResult.Variables); + tentativeResult = queryJoinClause.EqualsExpression.AcceptVisitor(this, tentativeResult.Variables); + + if (queryJoinClause.IsGroupJoin) { + var newVariables = tentativeResult.Variables.Clone(); + analysis.SetLocalVariableValue(newVariables, queryJoinClause.IntoIdentifierToken, NullValueStatus.DefinitelyNotNull); + return VisitorResult.ForValue(newVariables, NullValueStatus.Unknown); + } + + return tentativeResult; + } + + public override VisitorResult VisitQueryLetClause(QueryLetClause queryLetClause, VariableStatusInfo data) + { + var result = queryLetClause.Expression.AcceptVisitor(this, data); + + string newVariable = queryLetClause.Identifier; + var newVariables = result.Variables.Clone(); + newVariables [newVariable] = result.NullableReturnResult; + + return VisitorResult.ForValue(newVariables, NullValueStatus.Unknown); + } + + public override VisitorResult VisitQuerySelectClause(QuerySelectClause querySelectClause, VariableStatusInfo data) + { + var result = querySelectClause.Expression.AcceptVisitor(this, data); + + //The value of the expression in select becomes the "enumerated" value + return VisitorResult.ForEnumeratedValue(result.Variables, result.NullableReturnResult); + } + + public override VisitorResult VisitQueryWhereClause(QueryWhereClause queryWhereClause, VariableStatusInfo data) + { + var result = queryWhereClause.Condition.AcceptVisitor(this, data); + + return VisitorResult.ForEnumeratedValue(result.TruePathVariables, NullValueStatus.Unknown); + } + + public override VisitorResult VisitQueryOrderClause(QueryOrderClause queryOrderClause, VariableStatusInfo data) + { + foreach (var ordering in queryOrderClause.Orderings) { + data = ordering.AcceptVisitor(this, data).Variables; + } + + return VisitorResult.ForValue(data, NullValueStatus.Unknown); + } + + public override VisitorResult VisitQueryOrdering(QueryOrdering queryOrdering, VariableStatusInfo data) + { + return VisitorResult.ForValue(queryOrdering.Expression.AcceptVisitor(this, data).Variables, NullValueStatus.Unknown); + } + + public override VisitorResult VisitQueryGroupClause(QueryGroupClause queryGroupClause, VariableStatusInfo data) + { + var projectionResult = queryGroupClause.Projection.AcceptVisitor(this, data); + data = projectionResult.Variables; + data = queryGroupClause.Key.AcceptVisitor(this, data).Variables; + + return VisitorResult.ForEnumeratedValue(data, projectionResult.NullableReturnResult); + } + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/NullValueStatus.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/NullValueStatus.cs new file mode 100644 index 000000000..f6816dfd5 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/NullValueStatus.cs @@ -0,0 +1,84 @@ +// +// NullValueAnalysis.cs +// +// Author: +// Luís Reis +// +// Copyright (c) 2013 Luís Reis +// +// 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; + +namespace ICSharpCode.NRefactory.CSharp.Analysis +{ + /// + /// Represents the null value status of a variable at a specific location. + /// + public enum NullValueStatus + { + /// + /// The value of the variable is unknown, possibly due to limitations + /// of the null value analysis. + /// + Unknown = 0, + /// + /// The value of the variable is unknown and even assigning it to a + /// value won't change its state, since it has been captured by a lambda + /// that may change it at any time (potentially even from a different thread). + /// Only going out of scope and creating a new variable may change the value + /// of this variable. + /// + CapturedUnknown, + /// + /// This variable is potentially unassigned. + /// + Unassigned, + /// + /// The value of the variable is provably null. + /// + DefinitelyNull, + /// + /// The value of the variable might or might not be null + /// + PotentiallyNull, + /// + /// The value of the variable is provably not null + /// + DefinitelyNotNull, + /// + /// The position of this node is unreachable, therefore the value + /// of the variable is not meaningful. + /// Alternatively, it might mean no local variable exists with the requested name. + /// + UnreachableOrInexistent, + /// + /// The analyser has encountered an error when attempting to find the value + /// of this variable. + /// + Error + } + + public static class NullValueStatusExtensions + { + public static bool IsDefiniteValue (this NullValueStatus self) { + return self == NullValueStatus.DefinitelyNull || self == NullValueStatus.DefinitelyNotNull; + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/ReachabilityAnalysis.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/ReachabilityAnalysis.cs new file mode 100644 index 000000000..ce64f5f3b --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/ReachabilityAnalysis.cs @@ -0,0 +1,209 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using ICSharpCode.NRefactory.CSharp.Resolver; +using ICSharpCode.NRefactory.CSharp.TypeSystem; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.CSharp; + +namespace ICSharpCode.NRefactory.CSharp.Analysis +{ + /// + /// Statement reachability analysis. + /// + public sealed class ReachabilityAnalysis + { + HashSet reachableStatements = new HashSet(); + HashSet reachableEndPoints = new HashSet(); + HashSet visitedNodes = new HashSet(); + Stack stack = new Stack(); + RecursiveDetectorVisitor recursiveDetectorVisitor = null; + + private ReachabilityAnalysis() {} + + public static ReachabilityAnalysis Create(Statement statement, CSharpAstResolver resolver = null, RecursiveDetectorVisitor recursiveDetectorVisitor = null, CancellationToken cancellationToken = default(CancellationToken)) + { + var cfgBuilder = new ControlFlowGraphBuilder(); + var cfg = cfgBuilder.BuildControlFlowGraph(statement, resolver, cancellationToken); + return Create(cfg, recursiveDetectorVisitor, cancellationToken); + } + + internal static ReachabilityAnalysis Create(Statement statement, Func resolver, CSharpTypeResolveContext typeResolveContext, CancellationToken cancellationToken) + { + var cfgBuilder = new ControlFlowGraphBuilder(); + var cfg = cfgBuilder.BuildControlFlowGraph(statement, resolver, typeResolveContext, cancellationToken); + return Create(cfg, null, cancellationToken); + } + + public static ReachabilityAnalysis Create(IList controlFlowGraph, RecursiveDetectorVisitor recursiveDetectorVisitor = null, CancellationToken cancellationToken = default(CancellationToken)) + { + if (controlFlowGraph == null) + throw new ArgumentNullException("controlFlowGraph"); + ReachabilityAnalysis ra = new ReachabilityAnalysis(); + ra.recursiveDetectorVisitor = recursiveDetectorVisitor; + // Analysing a null node can result in an empty control flow graph + if (controlFlowGraph.Count > 0) { + ra.stack.Push(controlFlowGraph[0]); + while (ra.stack.Count > 0) { + cancellationToken.ThrowIfCancellationRequested(); + ra.MarkReachable(ra.stack.Pop()); + } + } + ra.stack = null; + ra.visitedNodes = null; + return ra; + } + + void MarkReachable(ControlFlowNode node) + { + if (node.PreviousStatement != null) { + if (node.PreviousStatement is LabelStatement) { + reachableStatements.Add(node.PreviousStatement); + } + reachableEndPoints.Add(node.PreviousStatement); + } + if (node.NextStatement != null) { + reachableStatements.Add(node.NextStatement); + if (IsRecursive(node.NextStatement)) { + return; + } + } + foreach (var edge in node.Outgoing) { + if (visitedNodes.Add(edge.To)) + stack.Push(edge.To); + } + } + + bool IsRecursive(Statement statement) + { + return recursiveDetectorVisitor != null && statement.AcceptVisitor(recursiveDetectorVisitor); + } + + public IEnumerable ReachableStatements { + get { return reachableStatements; } + } + + public bool IsReachable(Statement statement) + { + return reachableStatements.Contains(statement); + } + + public bool IsEndpointReachable(Statement statement) + { + return reachableEndPoints.Contains(statement); + } + + public class RecursiveDetectorVisitor : DepthFirstAstVisitor + { + public override bool VisitConditionalExpression(ConditionalExpression conditionalExpression) + { + if (conditionalExpression.Condition.AcceptVisitor(this)) + return true; + + if (!conditionalExpression.TrueExpression.AcceptVisitor(this)) + return false; + + return conditionalExpression.FalseExpression.AcceptVisitor(this); + } + + public override bool VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression) + { + if (binaryOperatorExpression.Operator == BinaryOperatorType.NullCoalescing) { + return binaryOperatorExpression.Left.AcceptVisitor(this); + } + return base.VisitBinaryOperatorExpression(binaryOperatorExpression); + } + + public override bool VisitIfElseStatement(IfElseStatement ifElseStatement) + { + if (ifElseStatement.Condition.AcceptVisitor(this)) + return true; + + if (!ifElseStatement.TrueStatement.AcceptVisitor(this)) + return false; + + //No need to worry about null ast nodes, since AcceptVisitor will just + //return false in those cases + return ifElseStatement.FalseStatement.AcceptVisitor(this); + } + + public override bool VisitForeachStatement(ForeachStatement foreachStatement) + { + //Even if the body is always recursive, the function may stop if the collection + // is empty. + return foreachStatement.InExpression.AcceptVisitor(this); + } + + public override bool VisitForStatement(ForStatement forStatement) + { + if (forStatement.Initializers.Any(initializer => initializer.AcceptVisitor(this))) + return true; + + return forStatement.Condition.AcceptVisitor(this); + } + + public override bool VisitSwitchStatement(SwitchStatement switchStatement) + { + if (switchStatement.Expression.AcceptVisitor(this)) { + return true; + } + + bool foundDefault = false; + foreach (var section in switchStatement.SwitchSections) { + foundDefault = foundDefault || section.CaseLabels.Any(label => label.Expression.IsNull); + if (!section.AcceptVisitor(this)) + return false; + } + + return foundDefault; + } + + public override bool VisitBlockStatement(BlockStatement blockStatement) + { + //If the block has a recursive statement, then that statement will be visited + //individually by the CFG construction algorithm later. + return false; + } + + protected override bool VisitChildren(AstNode node) + { + return VisitNodeList(node.Children); + } + + bool VisitNodeList(IEnumerable nodes) { + return nodes.Any(node => node.AcceptVisitor(this)); + } + + public override bool VisitQueryExpression(QueryExpression queryExpression) + { + //We only care about the first from clause because: + //in "from x in Method() select x", Method() might be recursive + //but in "from x in Bar() from y in Method() select x + y", even if Method() is recursive + //Bar might still be empty. + var queryFromClause = queryExpression.Clauses.OfType().FirstOrDefault(); + if (queryFromClause == null) + return true; + return queryFromClause.AcceptVisitor(this); + } + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/SemanticHighlightingVisitor.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/SemanticHighlightingVisitor.cs new file mode 100644 index 000000000..0528d4885 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Analysis/SemanticHighlightingVisitor.cs @@ -0,0 +1,685 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using ICSharpCode.NRefactory.CSharp.Resolver; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; +using System.Threading; +using ICSharpCode.NRefactory.CSharp.Completion; +using System.Collections.ObjectModel; + +namespace ICSharpCode.NRefactory.CSharp.Analysis +{ + /// + /// C# Semantic highlighter. + /// + public abstract class SemanticHighlightingVisitor : DepthFirstAstVisitor + { + protected CancellationToken cancellationToken = default (CancellationToken); + + protected TColor defaultTextColor; + protected TColor referenceTypeColor; + protected TColor valueTypeColor; + protected TColor interfaceTypeColor; + protected TColor enumerationTypeColor; + protected TColor typeParameterTypeColor; + protected TColor delegateTypeColor; + + protected TColor methodCallColor; + protected TColor methodDeclarationColor; + + protected TColor eventDeclarationColor; + protected TColor eventAccessColor; + + protected TColor propertyDeclarationColor; + protected TColor propertyAccessColor; + + protected TColor fieldDeclarationColor; + protected TColor fieldAccessColor; + + protected TColor variableDeclarationColor; + protected TColor variableAccessColor; + + protected TColor parameterDeclarationColor; + protected TColor parameterAccessColor; + + protected TColor valueKeywordColor; + protected TColor externAliasKeywordColor; + protected TColor varKeywordTypeColor; + + /// + /// Used for 'in' modifiers on type parameters. + /// + /// + /// 'in' may have a different color when used with 'foreach'. + /// 'out' is not colored by semantic highlighting, as syntax highlighting can already detect it as a parameter modifier. + /// + protected TColor parameterModifierColor; + + /// + /// Used for inactive code (excluded by preprocessor or ConditionalAttribute) + /// + protected TColor inactiveCodeColor; + + protected TColor stringFormatItemColor; + + + protected TColor syntaxErrorColor; + + protected TextLocation regionStart; + protected TextLocation regionEnd; + + protected CSharpAstResolver resolver; + protected bool isInAccessorContainingValueParameter; + + protected abstract void Colorize(TextLocation start, TextLocation end, TColor color); + + #region Colorize helper methods + protected void Colorize(Identifier identifier, ResolveResult rr) + { + if (identifier.IsNull) + return; + if (rr.IsError) { + Colorize(identifier, syntaxErrorColor); + return; + } + if (rr is TypeResolveResult) { + if (blockDepth > 0 && identifier.Name == "var" && rr.Type.Kind != TypeKind.Null && rr.Type.Name != "var" ) { + Colorize(identifier, varKeywordTypeColor); + return; + } + + TColor color; + if (TryGetTypeHighlighting (rr.Type.Kind, out color)) { + Colorize(identifier, color); + } + return; + } + var mrr = rr as MemberResolveResult; + if (mrr != null) { + TColor color; + if (TryGetMemberColor (mrr.Member, out color)) { + Colorize(identifier, color); + return; + } + } + + if (rr is MethodGroupResolveResult) { + Colorize (identifier, methodCallColor); + return; + } + + var localResult = rr as LocalResolveResult; + if (localResult != null) { + if (localResult.Variable is IParameter) { + Colorize (identifier, parameterAccessColor); + } else { + Colorize (identifier, variableAccessColor); + } + } + + + VisitIdentifier(identifier); // un-colorize contextual keywords + } + + protected void Colorize(AstNode node, TColor color) + { + if (node.IsNull) + return; + Colorize(node.StartLocation, node.EndLocation, color); + } + #endregion + + protected override void VisitChildren(AstNode node) + { + for (var child = node.FirstChild; child != null; child = child.NextSibling) { + if (child.StartLocation < regionEnd && child.EndLocation > regionStart) + child.AcceptVisitor(this); + } + } + + /// + /// Visit all children of node until (but excluding) end. + /// If end is a null node, nothing will be visited. + /// + protected void VisitChildrenUntil(AstNode node, AstNode end) + { + if (end.IsNull) + return; + Debug.Assert(node == end.Parent); + for (var child = node.FirstChild; child != end; child = child.NextSibling) { + cancellationToken.ThrowIfCancellationRequested(); + if (child.StartLocation < regionEnd && child.EndLocation > regionStart) + child.AcceptVisitor(this); + } + } + + /// + /// Visit all children of node after (excluding) start. + /// If start is a null node, all children will be visited. + /// + protected void VisitChildrenAfter(AstNode node, AstNode start) + { + Debug.Assert(start.IsNull || start.Parent == node); + for (var child = (start.IsNull ? node.FirstChild : start.NextSibling); child != null; child = child.NextSibling) { + cancellationToken.ThrowIfCancellationRequested(); + if (child.StartLocation < regionEnd && child.EndLocation > regionStart) + child.AcceptVisitor(this); + } + } + + public override void VisitIdentifier(Identifier identifier) + { + switch (identifier.Name) { + case "add": + case "async": + case "await": + case "get": + case "partial": + case "remove": + case "set": + case "where": + case "yield": + case "from": + case "select": + case "group": + case "into": + case "orderby": + case "join": + case "let": + case "on": + case "equals": + case "by": + case "ascending": + case "descending": + case "dynamic": + case "var": + // Reset color of contextual keyword to default if it's used as an identifier. + // Note that this method does not get called when 'var' or 'dynamic' is used as a type, + // because types get highlighted with valueTypeColor/referenceTypeColor instead. + Colorize(identifier, defaultTextColor); + break; + case "global": + // Reset color of 'global' keyword to default unless its used as part of 'global::'. + MemberType parentMemberType = identifier.Parent as MemberType; + if (parentMemberType == null || !parentMemberType.IsDoubleColon) + Colorize(identifier, defaultTextColor); + break; + } + // "value" is handled in VisitIdentifierExpression() + // "alias" is handled in VisitExternAliasDeclaration() + } + + public override void VisitSimpleType(SimpleType simpleType) + { + var identifierToken = simpleType.IdentifierToken; + VisitChildrenUntil(simpleType, identifierToken); + Colorize(identifierToken, resolver.Resolve(simpleType, cancellationToken)); + VisitChildrenAfter(simpleType, identifierToken); + } + + public override void VisitMemberType(MemberType memberType) + { + var memberNameToken = memberType.MemberNameToken; + VisitChildrenUntil(memberType, memberNameToken); + Colorize(memberNameToken, resolver.Resolve(memberType, cancellationToken)); + VisitChildrenAfter(memberType, memberNameToken); + } + + public override void VisitIdentifierExpression(IdentifierExpression identifierExpression) + { + var identifier = identifierExpression.IdentifierToken; + VisitChildrenUntil(identifierExpression, identifier); + if (isInAccessorContainingValueParameter && identifierExpression.Identifier == "value") { + Colorize(identifier, valueKeywordColor); + } else { + Colorize(identifier, resolver.Resolve(identifierExpression, cancellationToken)); + } + VisitChildrenAfter(identifierExpression, identifier); + } + + public override void VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression) + { + var memberNameToken = memberReferenceExpression.MemberNameToken; + VisitChildrenUntil(memberReferenceExpression, memberNameToken); + ResolveResult rr = resolver.Resolve(memberReferenceExpression, cancellationToken); + Colorize(memberNameToken, rr); + VisitChildrenAfter(memberReferenceExpression, memberNameToken); + } + + void HighlightStringFormatItems(PrimitiveExpression expr) + { + if (!(expr.Value is string)) + return; + int line = expr.StartLocation.Line; + int col = expr.StartLocation.Column; + TextLocation start = TextLocation.Empty; + for (int i = 0; i < expr.LiteralValue.Length; i++) { + char ch = expr.LiteralValue [i]; + + if (NewLine.GetDelimiterType(ch, i + 1 < expr.LiteralValue.Length ? expr.LiteralValue [i + 1] : '\0') != UnicodeNewline.Unknown) { + line++; + col = 1; + continue; + } + + + if (ch == '{' && start.IsEmpty) { + char next = i + 1 < expr.LiteralValue.Length ? expr.LiteralValue [i + 1] : '\0'; + if (next == '{') { + i++; + col += 2; + continue; + } + start = new TextLocation(line, col); + } + if (ch == '}' &&!start.IsEmpty) { + Colorize(start, new TextLocation(line, col + 1), stringFormatItemColor); + start = TextLocation.Empty; + } + col++; + } + + } + + public override void VisitInvocationExpression(InvocationExpression invocationExpression) + { + Expression target = invocationExpression.Target; + if (target is IdentifierExpression || target is MemberReferenceExpression || target is PointerReferenceExpression) { + var invocationRR = resolver.Resolve(invocationExpression, cancellationToken) as CSharpInvocationResolveResult; + if (invocationRR != null) { + if (invocationExpression.Parent is ExpressionStatement && (IsInactiveConditionalMethod(invocationRR.Member) || IsEmptyPartialMethod(invocationRR.Member))) { + // mark the whole invocation statement as inactive code + Colorize(invocationExpression.Parent, inactiveCodeColor); + return; + } + + Expression fmtArgumets; + IList args; + if (invocationRR.Arguments.Count > 1 && FormatStringHelper.TryGetFormattingParameters(invocationRR, invocationExpression, out fmtArgumets, out args, null)) { + var expr = invocationExpression.Arguments.First() as PrimitiveExpression; + if (expr != null) + HighlightStringFormatItems(expr); + } + } + + VisitChildrenUntil(invocationExpression, target); + + // highlight the method call + var identifier = target.GetChildByRole(Roles.Identifier); + VisitChildrenUntil(target, identifier); + if (invocationRR != null && !invocationRR.IsDelegateInvocation) { + Colorize(identifier, methodCallColor); + } else { + ResolveResult targetRR = resolver.Resolve(target, cancellationToken); + Colorize(identifier, targetRR); + } + VisitChildrenAfter(target, identifier); + VisitChildrenAfter(invocationExpression, target); + } else { + VisitChildren(invocationExpression); + } + } + + #region IsInactiveConditional helper methods + bool IsInactiveConditionalMethod(IParameterizedMember member) + { + if (member.SymbolKind != SymbolKind.Method || member.ReturnType.Kind != TypeKind.Void) + return false; + foreach (var baseMember in InheritanceHelper.GetBaseMembers(member, false)) { + if (IsInactiveConditional (baseMember.Attributes)) + return true; + } + return IsInactiveConditional(member.Attributes); + } + + static bool IsEmptyPartialMethod(IParameterizedMember member) + { + if (member.SymbolKind != SymbolKind.Method || member.ReturnType.Kind != TypeKind.Void) + return false; + var method = (IMethod)member; + return method.IsPartial && !method.HasBody; + } + + bool IsInactiveConditional(IList attributes) + { + bool hasConditionalAttribute = false; + foreach (var attr in attributes) { + if (attr.AttributeType.Name == "ConditionalAttribute" && attr.AttributeType.Namespace == "System.Diagnostics" && attr.PositionalArguments.Count == 1) { + string symbol = attr.PositionalArguments[0].ConstantValue as string; + if (symbol != null) { + hasConditionalAttribute = true; + var cu = this.resolver.RootNode as SyntaxTree; + if (cu != null) { + if (cu.ConditionalSymbols.Contains(symbol)) + return false; // conditional is active + } + } + } + } + + return hasConditionalAttribute; + } + #endregion + + public override void VisitExternAliasDeclaration (ExternAliasDeclaration externAliasDeclaration) + { + var aliasToken = externAliasDeclaration.AliasToken; + VisitChildrenUntil(externAliasDeclaration, aliasToken); + Colorize (aliasToken, externAliasKeywordColor); + VisitChildrenAfter(externAliasDeclaration, aliasToken); + } + + public override void VisitAccessor(Accessor accessor) + { + isInAccessorContainingValueParameter = accessor.Role != PropertyDeclaration.GetterRole; + try { + VisitChildren(accessor); + } finally { + isInAccessorContainingValueParameter = false; + } + } + + bool CheckInterfaceImplementation (EntityDeclaration entityDeclaration) + { + var result = resolver.Resolve (entityDeclaration, cancellationToken) as MemberResolveResult; + if (result == null) + return false; + if (result.Member.ImplementedInterfaceMembers.Count == 0) { + Colorize (entityDeclaration.NameToken, syntaxErrorColor); + return false; + } + return true; + } + + public override void VisitMethodDeclaration(MethodDeclaration methodDeclaration) + { + var nameToken = methodDeclaration.NameToken; + VisitChildrenUntil(methodDeclaration, nameToken); + if (!methodDeclaration.PrivateImplementationType.IsNull) { + if (!CheckInterfaceImplementation (methodDeclaration)) { + VisitChildrenAfter(methodDeclaration, nameToken); + return; + } + } + Colorize(nameToken, methodDeclarationColor); + VisitChildrenAfter(methodDeclaration, nameToken); + } + + public override void VisitParameterDeclaration(ParameterDeclaration parameterDeclaration) + { + var nameToken = parameterDeclaration.NameToken; + VisitChildrenUntil(parameterDeclaration, nameToken); + Colorize(nameToken, parameterDeclarationColor); + VisitChildrenAfter(parameterDeclaration, nameToken); + } + + public override void VisitEventDeclaration(EventDeclaration eventDeclaration) + { + var nameToken = eventDeclaration.NameToken; + VisitChildrenUntil(eventDeclaration, nameToken); + Colorize(nameToken, eventDeclarationColor); + VisitChildrenAfter(eventDeclaration, nameToken); + } + + public override void VisitCustomEventDeclaration(CustomEventDeclaration eventDeclaration) + { + var nameToken = eventDeclaration.NameToken; + VisitChildrenUntil(eventDeclaration, nameToken); + if (!eventDeclaration.PrivateImplementationType.IsNull) { + if (!CheckInterfaceImplementation (eventDeclaration)) { + VisitChildrenAfter(eventDeclaration, nameToken); + return; + } + } + Colorize(nameToken, eventDeclarationColor); + VisitChildrenAfter(eventDeclaration, nameToken); + } + + public override void VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration) + { + var nameToken = propertyDeclaration.NameToken; + VisitChildrenUntil(propertyDeclaration, nameToken); + if (!propertyDeclaration.PrivateImplementationType.IsNull) { + if (!CheckInterfaceImplementation (propertyDeclaration)) { + VisitChildrenAfter(propertyDeclaration, nameToken); + return; + } + } + Colorize(nameToken, propertyDeclarationColor); + VisitChildrenAfter(propertyDeclaration, nameToken); + } + + public override void VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration) + { + base.VisitIndexerDeclaration(indexerDeclaration); + if (!indexerDeclaration.PrivateImplementationType.IsNull) { + CheckInterfaceImplementation (indexerDeclaration); + } + } + + public override void VisitFieldDeclaration(FieldDeclaration fieldDeclaration) + { + fieldDeclaration.ReturnType.AcceptVisitor (this); + foreach (var init in fieldDeclaration.Variables) { + Colorize (init.NameToken, fieldDeclarationColor); + init.Initializer.AcceptVisitor (this); + } + } + + public override void VisitFixedFieldDeclaration(FixedFieldDeclaration fixedFieldDeclaration) + { + fixedFieldDeclaration.ReturnType.AcceptVisitor (this); + foreach (var init in fixedFieldDeclaration.Variables) { + Colorize (init.NameToken, fieldDeclarationColor); + init.CountExpression.AcceptVisitor (this); + } + } + + public override void VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration) + { + HandleConstructorOrDestructor(constructorDeclaration); + } + + public override void VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration) + { + HandleConstructorOrDestructor(destructorDeclaration); + } + + void HandleConstructorOrDestructor(AstNode constructorDeclaration) + { + Identifier nameToken = constructorDeclaration.GetChildByRole(Roles.Identifier); + VisitChildrenUntil(constructorDeclaration, nameToken); + var currentTypeDef = resolver.GetResolverStateBefore(constructorDeclaration).CurrentTypeDefinition; + if (currentTypeDef != null && nameToken.Name == currentTypeDef.Name) { + TColor color; + if (TryGetTypeHighlighting (currentTypeDef.Kind, out color)) + Colorize(nameToken, color); + } + VisitChildrenAfter(constructorDeclaration, nameToken); + } + + bool TryGetMemberColor(IMember member, out TColor color) + { + switch (member.SymbolKind) { + case SymbolKind.Field: + color = fieldAccessColor; + return true; + case SymbolKind.Property: + color = propertyAccessColor; + return true; + case SymbolKind.Event: + color = eventAccessColor; + return true; + case SymbolKind.Method: + color = methodCallColor; + return true; + case SymbolKind.Constructor: + case SymbolKind.Destructor: + return TryGetTypeHighlighting (member.DeclaringType.Kind, out color); + default: + color = default (TColor); + return false; + } + } + + TColor GetTypeHighlighting (ClassType classType) + { + switch (classType) { + case ClassType.Class: + return referenceTypeColor; + case ClassType.Struct: + return valueTypeColor; + case ClassType.Interface: + return interfaceTypeColor; + case ClassType.Enum: + return enumerationTypeColor; + default: + throw new InvalidOperationException ("Unknown class type :" + classType); + } + } + + bool TryGetTypeHighlighting (TypeKind kind, out TColor color) + { + switch (kind) { + case TypeKind.Class: + color = referenceTypeColor; + return true; + case TypeKind.Struct: + color = valueTypeColor; + return true; + case TypeKind.Interface: + color = interfaceTypeColor; + return true; + case TypeKind.Enum: + color = enumerationTypeColor; + return true; + case TypeKind.TypeParameter: + color = typeParameterTypeColor; + return true; + case TypeKind.Delegate: + color = delegateTypeColor; + return true; + case TypeKind.Unknown: + case TypeKind.Null: + color = syntaxErrorColor; + return true; + default: + color = default (TColor); + return false; + } + } + + public override void VisitTypeDeclaration(TypeDeclaration typeDeclaration) + { + var nameToken = typeDeclaration.NameToken; + VisitChildrenUntil(typeDeclaration, nameToken); + Colorize(nameToken, GetTypeHighlighting (typeDeclaration.ClassType)); + VisitChildrenAfter(typeDeclaration, nameToken); + } + + public override void VisitTypeParameterDeclaration(TypeParameterDeclaration typeParameterDeclaration) + { + if (typeParameterDeclaration.Variance == VarianceModifier.Contravariant) + Colorize(typeParameterDeclaration.VarianceToken, parameterModifierColor); + + // bool isValueType = false; + // if (typeParameterDeclaration.Parent != null) { + // foreach (var constraint in typeParameterDeclaration.Parent.GetChildrenByRole(Roles.Constraint)) { + // if (constraint.TypeParameter.Identifier == typeParameterDeclaration.Name) { + // isValueType = constraint.BaseTypes.OfType().Any(p => p.Keyword == "struct"); + // } + // } + // } + var nameToken = typeParameterDeclaration.NameToken; + VisitChildrenUntil(typeParameterDeclaration, nameToken); + Colorize(nameToken, typeParameterTypeColor); /*isValueType ? valueTypeColor : referenceTypeColor*/ + VisitChildrenAfter(typeParameterDeclaration, nameToken); + } + + public override void VisitDelegateDeclaration(DelegateDeclaration delegateDeclaration) + { + var nameToken = delegateDeclaration.NameToken; + VisitChildrenUntil(delegateDeclaration, nameToken); + Colorize(nameToken, delegateTypeColor); + VisitChildrenAfter(delegateDeclaration, nameToken); + } + + public override void VisitVariableInitializer(VariableInitializer variableInitializer) + { + var nameToken = variableInitializer.NameToken; + VisitChildrenUntil(variableInitializer, nameToken); + if (variableInitializer.Parent is FieldDeclaration) { + Colorize(nameToken, fieldDeclarationColor); + } else if (variableInitializer.Parent is EventDeclaration) { + Colorize(nameToken, eventDeclarationColor); + } else { + Colorize(nameToken, variableDeclarationColor); + } + VisitChildrenAfter(variableInitializer, nameToken); + } + + public override void VisitComment(Comment comment) + { + if (comment.CommentType == CommentType.InactiveCode) { + Colorize(comment, inactiveCodeColor); + } + } + + public override void VisitPreProcessorDirective(PreProcessorDirective preProcessorDirective) + { + } + + public override void VisitAttribute(ICSharpCode.NRefactory.CSharp.Attribute attribute) + { + ITypeDefinition attrDef = resolver.Resolve(attribute.Type, cancellationToken).Type.GetDefinition(); + if (attrDef != null && IsInactiveConditional(attrDef.Attributes)) { + Colorize(attribute, inactiveCodeColor); + } else { + VisitChildren(attribute); + } + } + + public override void VisitArrayInitializerExpression (ArrayInitializerExpression arrayInitializerExpression) + { + foreach (var a in arrayInitializerExpression.Elements) { + var namedElement = a as NamedExpression; + if (namedElement != null) { + var result = resolver.Resolve (namedElement, cancellationToken); + if (result.IsError) + Colorize (namedElement.NameToken, syntaxErrorColor); + namedElement.Expression.AcceptVisitor (this); + } else { + a.AcceptVisitor (this); + } + } + } + + int blockDepth; + public override void VisitBlockStatement(BlockStatement blockStatement) + { + blockDepth++; + base.VisitBlockStatement(blockStatement); + blockDepth--; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/AstNode.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/AstNode.cs new file mode 100644 index 000000000..8676beb67 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/AstNode.cs @@ -0,0 +1,1042 @@ +// +// AstNode.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public abstract class AstNode : AbstractAnnotatable, ICSharpCode.NRefactory.TypeSystem.IFreezable, PatternMatching.INode, ICloneable + { + // the Root role must be available when creating the null nodes, so we can't put it in the Roles class + internal static readonly Role RootRole = new Role ("Root"); + + #region Null + public static readonly AstNode Null = new NullAstNode (); + + sealed class NullAstNode : AstNode + { + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public override bool IsNull { + get { + return true; + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullNode(this, data); + } + + protected internal override bool DoMatch (AstNode other, PatternMatching.Match match) + { + return other == null || other.IsNull; + } + } + #endregion + + #region PatternPlaceholder + public static implicit operator AstNode (PatternMatching.Pattern pattern) + { + return pattern != null ? new PatternPlaceholder (pattern) : null; + } + + sealed class PatternPlaceholder : AstNode, PatternMatching.INode + { + readonly PatternMatching.Pattern child; + + public PatternPlaceholder (PatternMatching.Pattern child) + { + this.child = child; + } + + public override NodeType NodeType { + get { return NodeType.Pattern; } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPatternPlaceholder (this, child); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPatternPlaceholder (this, child); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitPatternPlaceholder (this, child, data); + } + + protected internal override bool DoMatch (AstNode other, PatternMatching.Match match) + { + return child.DoMatch (other, match); + } + + bool PatternMatching.INode.DoMatchCollection (Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) + { + return child.DoMatchCollection (role, pos, match, backtrackingInfo); + } + } + #endregion + + AstNode parent; + AstNode prevSibling; + AstNode nextSibling; + AstNode firstChild; + AstNode lastChild; + + // Flags, from least significant to most significant bits: + // - Role.RoleIndexBits: role index + // - 1 bit: IsFrozen + protected uint flags = RootRole.Index; + // Derived classes may also use a few bits, + // for example Identifier uses 1 bit for IsVerbatim + + const uint roleIndexMask = (1u << Role.RoleIndexBits) - 1; + const uint frozenBit = 1u << Role.RoleIndexBits; + protected const int AstNodeFlagsUsedBits = Role.RoleIndexBits + 1; + + protected AstNode() + { + if (IsNull) + Freeze(); + } + + public bool IsFrozen { + get { return (flags & frozenBit) != 0; } + } + + public void Freeze() + { + if (!IsFrozen) { + for (AstNode child = firstChild; child != null; child = child.nextSibling) + child.Freeze(); + flags |= frozenBit; + } + } + + protected void ThrowIfFrozen() + { + if (IsFrozen) + throw new InvalidOperationException("Cannot mutate frozen " + GetType().Name); + } + + public abstract NodeType NodeType { + get; + } + + public virtual bool IsNull { + get { + return false; + } + } + + public virtual TextLocation StartLocation { + get { + var child = firstChild; + if (child == null) + return TextLocation.Empty; + return child.StartLocation; + } + } + + public virtual TextLocation EndLocation { + get { + var child = lastChild; + if (child == null) + return TextLocation.Empty; + return child.EndLocation; + } + } + + public DomRegion Region { + get { + return new DomRegion (StartLocation, EndLocation); + } + } + + /// + /// Gets the region from StartLocation to EndLocation for this node. + /// The file name of the region is set based on the parent SyntaxTree's file name. + /// If this node is not connected to a whole compilation, the file name will be null. + /// + public ICSharpCode.NRefactory.TypeSystem.DomRegion GetRegion() + { + var syntaxTree = (this.Ancestors.LastOrDefault() ?? this) as SyntaxTree; + string fileName = (syntaxTree != null ? syntaxTree.FileName : null); + return new ICSharpCode.NRefactory.TypeSystem.DomRegion(fileName, this.StartLocation, this.EndLocation); + } + + public AstNode Parent { + get { return parent; } + } + + public Role Role { + get { + return Role.GetByIndex(flags & roleIndexMask); + } + set { + if (value == null) + throw new ArgumentNullException("value"); + if (!value.IsValid(this)) + throw new ArgumentException("This node is not valid in the new role."); + ThrowIfFrozen(); + SetRole(value); + } + } + + internal uint RoleIndex { + get { return flags & roleIndexMask; } + } + + void SetRole(Role role) + { + flags = (flags & ~roleIndexMask) | role.Index; + } + + public AstNode NextSibling { + get { return nextSibling; } + } + + public AstNode PrevSibling { + get { return prevSibling; } + } + + public AstNode FirstChild { + get { return firstChild; } + } + + public AstNode LastChild { + get { return lastChild; } + } + + public bool HasChildren { + get { + return firstChild != null; + } + } + + public IEnumerable Children { + get { + AstNode next; + for (AstNode cur = firstChild; cur != null; cur = next) { + Debug.Assert (cur.parent == this); + // Remember next before yielding cur. + // This allows removing/replacing nodes while iterating through the list. + next = cur.nextSibling; + yield return cur; + } + } + } + + /// + /// Gets the ancestors of this node (excluding this node itself) + /// + public IEnumerable Ancestors { + get { + for (AstNode cur = parent; cur != null; cur = cur.parent) { + yield return cur; + } + } + } + + /// + /// Gets the ancestors of this node (including this node itself) + /// + public IEnumerable AncestorsAndSelf { + get { + for (AstNode cur = this; cur != null; cur = cur.parent) { + yield return cur; + } + } + } + + /// + /// Gets all descendants of this node (excluding this node itself) in pre-order. + /// + public IEnumerable Descendants { + get { return GetDescendantsImpl(false); } + } + + /// + /// Gets all descendants of this node (including this node itself) in pre-order. + /// + public IEnumerable DescendantsAndSelf { + get { return GetDescendantsImpl(true); } + } + + static bool IsInsideRegion(DomRegion region, AstNode pos) + { + if (region.IsEmpty) + return true; + var nodeRegion = pos.Region; + return region.IntersectsWith(nodeRegion) || region.OverlapsWith(nodeRegion); + } + + public IEnumerable DescendantNodes (Func descendIntoChildren = null) + { + return GetDescendantsImpl(false, new DomRegion (), descendIntoChildren); + } + + public IEnumerable DescendantNodes (DomRegion region, Func descendIntoChildren = null) + { + return GetDescendantsImpl(false, region, descendIntoChildren); + } + + public IEnumerable DescendantNodesAndSelf (Func descendIntoChildren = null) + { + return GetDescendantsImpl(true, new DomRegion (), descendIntoChildren); + } + + public IEnumerable DescendantNodesAndSelf (DomRegion region, Func descendIntoChildren = null) + { + return GetDescendantsImpl(true, region, descendIntoChildren); + } + + IEnumerable GetDescendantsImpl(bool includeSelf, DomRegion region = new DomRegion (), Func descendIntoChildren = null) + { + if (includeSelf) { + if (IsInsideRegion (region, this)) + yield return this; + if (descendIntoChildren != null && !descendIntoChildren(this)) + yield break; + } + + Stack nextStack = new Stack(); + nextStack.Push(null); + AstNode pos = firstChild; + while (pos != null) { + // Remember next before yielding pos. + // This allows removing/replacing nodes while iterating through the list. + if (pos.nextSibling != null) + nextStack.Push(pos.nextSibling); + if (IsInsideRegion(region, pos)) + yield return pos; + if (pos.firstChild != null && (descendIntoChildren == null || descendIntoChildren(pos))) + pos = pos.firstChild; + else + pos = nextStack.Pop(); + } + } + + /// + /// Gets the first child with the specified role. + /// Returns the role's null object if the child is not found. + /// + public T GetChildByRole(Role role) where T : AstNode + { + if (role == null) + throw new ArgumentNullException ("role"); + uint roleIndex = role.Index; + for (var cur = firstChild; cur != null; cur = cur.nextSibling) { + if ((cur.flags & roleIndexMask) == roleIndex) + return (T)cur; + } + return role.NullObject; + } + + public T GetParent() where T : AstNode + { + return Ancestors.OfType().FirstOrDefault(); + } + + public AstNode GetParent(Func pred) + { + return Ancestors.FirstOrDefault(pred); + } + + public AstNodeCollection GetChildrenByRole (Role role) where T : AstNode + { + return new AstNodeCollection (this, role); + } + + protected void SetChildByRole (Role role, T newChild) where T : AstNode + { + AstNode oldChild = GetChildByRole (role); + if (oldChild.IsNull) + AddChild (newChild, role); + else + oldChild.ReplaceWith (newChild); + } + + public void AddChild (T child, Role role) where T : AstNode + { + if (role == null) + throw new ArgumentNullException ("role"); + if (child == null || child.IsNull) + return; + ThrowIfFrozen(); + if (child == this) + throw new ArgumentException ("Cannot add a node to itself as a child.", "child"); + if (child.parent != null) + throw new ArgumentException ("Node is already used in another tree.", "child"); + if (child.IsFrozen) + throw new ArgumentException ("Cannot add a frozen node.", "child"); + AddChildUnsafe (child, role); + } + + public void AddChildWithExistingRole (AstNode child) + { + if (child == null || child.IsNull) + return; + ThrowIfFrozen(); + if (child == this) + throw new ArgumentException ("Cannot add a node to itself as a child.", "child"); + if (child.parent != null) + throw new ArgumentException ("Node is already used in another tree.", "child"); + if (child.IsFrozen) + throw new ArgumentException ("Cannot add a frozen node.", "child"); + AddChildUnsafe (child, child.Role); + } + + /// + /// Adds a child without performing any safety checks. + /// + internal void AddChildUnsafe (AstNode child, Role role) + { + child.parent = this; + child.SetRole(role); + if (firstChild == null) { + lastChild = firstChild = child; + } else { + lastChild.nextSibling = child; + child.prevSibling = lastChild; + lastChild = child; + } + } + + public void InsertChildBefore (AstNode nextSibling, T child, Role role) where T : AstNode + { + if (role == null) + throw new ArgumentNullException ("role"); + if (nextSibling == null || nextSibling.IsNull) { + AddChild (child, role); + return; + } + + if (child == null || child.IsNull) + return; + ThrowIfFrozen(); + if (child.parent != null) + throw new ArgumentException ("Node is already used in another tree.", "child"); + if (child.IsFrozen) + throw new ArgumentException ("Cannot add a frozen node.", "child"); + if (nextSibling.parent != this) + throw new ArgumentException ("NextSibling is not a child of this node.", "nextSibling"); + // No need to test for "Cannot add children to null nodes", + // as there isn't any valid nextSibling in null nodes. + InsertChildBeforeUnsafe (nextSibling, child, role); + } + + internal void InsertChildBeforeUnsafe (AstNode nextSibling, AstNode child, Role role) + { + child.parent = this; + child.SetRole(role); + child.nextSibling = nextSibling; + child.prevSibling = nextSibling.prevSibling; + + if (nextSibling.prevSibling != null) { + Debug.Assert (nextSibling.prevSibling.nextSibling == nextSibling); + nextSibling.prevSibling.nextSibling = child; + } else { + Debug.Assert (firstChild == nextSibling); + firstChild = child; + } + nextSibling.prevSibling = child; + } + + public void InsertChildAfter (AstNode prevSibling, T child, Role role) where T : AstNode + { + InsertChildBefore ((prevSibling == null || prevSibling.IsNull) ? firstChild : prevSibling.nextSibling, child, role); + } + + /// + /// Removes this node from its parent. + /// + public void Remove () + { + if (parent != null) { + ThrowIfFrozen(); + if (prevSibling != null) { + Debug.Assert (prevSibling.nextSibling == this); + prevSibling.nextSibling = nextSibling; + } else { + Debug.Assert (parent.firstChild == this); + parent.firstChild = nextSibling; + } + if (nextSibling != null) { + Debug.Assert (nextSibling.prevSibling == this); + nextSibling.prevSibling = prevSibling; + } else { + Debug.Assert (parent.lastChild == this); + parent.lastChild = prevSibling; + } + parent = null; + prevSibling = null; + nextSibling = null; + } + } + + /// + /// Replaces this node with the new node. + /// + public void ReplaceWith (AstNode newNode) + { + if (newNode == null || newNode.IsNull) { + Remove (); + return; + } + if (newNode == this) + return; // nothing to do... + if (parent == null) { + throw new InvalidOperationException (this.IsNull ? "Cannot replace the null nodes" : "Cannot replace the root node"); + } + ThrowIfFrozen(); + // Because this method doesn't statically check the new node's type with the role, + // we perform a runtime test: + if (!this.Role.IsValid (newNode)) { + throw new ArgumentException (string.Format ("The new node '{0}' is not valid in the role {1}", newNode.GetType ().Name, this.Role.ToString ()), "newNode"); + } + if (newNode.parent != null) { + // newNode is used within this tree? + if (newNode.Ancestors.Contains (this)) { + // e.g. "parenthesizedExpr.ReplaceWith(parenthesizedExpr.Expression);" + // enable automatic removal + newNode.Remove (); + } else { + throw new ArgumentException ("Node is already used in another tree.", "newNode"); + } + } + if (newNode.IsFrozen) + throw new ArgumentException ("Cannot add a frozen node.", "newNode"); + + newNode.parent = parent; + newNode.SetRole(this.Role); + newNode.prevSibling = prevSibling; + newNode.nextSibling = nextSibling; + + if (prevSibling != null) { + Debug.Assert (prevSibling.nextSibling == this); + prevSibling.nextSibling = newNode; + } else { + Debug.Assert (parent.firstChild == this); + parent.firstChild = newNode; + } + if (nextSibling != null) { + Debug.Assert (nextSibling.prevSibling == this); + nextSibling.prevSibling = newNode; + } else { + Debug.Assert (parent.lastChild == this); + parent.lastChild = newNode; + } + parent = null; + prevSibling = null; + nextSibling = null; + } + + public AstNode ReplaceWith (Func replaceFunction) + { + if (replaceFunction == null) + throw new ArgumentNullException ("replaceFunction"); + if (parent == null) { + throw new InvalidOperationException (this.IsNull ? "Cannot replace the null nodes" : "Cannot replace the root node"); + } + AstNode oldParent = parent; + AstNode oldSuccessor = nextSibling; + Role oldRole = this.Role; + Remove (); + AstNode replacement = replaceFunction (this); + if (oldSuccessor != null && oldSuccessor.parent != oldParent) + throw new InvalidOperationException ("replace function changed nextSibling of node being replaced?"); + if (!(replacement == null || replacement.IsNull)) { + if (replacement.parent != null) + throw new InvalidOperationException ("replace function must return the root of a tree"); + if (!oldRole.IsValid (replacement)) { + throw new InvalidOperationException (string.Format ("The new node '{0}' is not valid in the role {1}", replacement.GetType ().Name, oldRole.ToString ())); + } + + if (oldSuccessor != null) + oldParent.InsertChildBeforeUnsafe (oldSuccessor, replacement, oldRole); + else + oldParent.AddChildUnsafe (replacement, oldRole); + } + return replacement; + } + + /// + /// Clones the whole subtree starting at this AST node. + /// + /// Annotations are copied over to the new nodes; and any annotations implementing ICloneable will be cloned. + public AstNode Clone () + { + AstNode copy = (AstNode)MemberwiseClone (); + // First, reset the shallow pointer copies + copy.parent = null; + copy.firstChild = null; + copy.lastChild = null; + copy.prevSibling = null; + copy.nextSibling = null; + copy.flags &= ~frozenBit; // unfreeze the copy + + // Then perform a deep copy: + for (AstNode cur = firstChild; cur != null; cur = cur.nextSibling) { + copy.AddChildUnsafe (cur.Clone (), cur.Role); + } + + // Finally, clone the annotation, if necessary + copy.CloneAnnotations(); + + return copy; + } + + object ICloneable.Clone() + { + return Clone(); + } + + public abstract void AcceptVisitor (IAstVisitor visitor); + + public abstract T AcceptVisitor (IAstVisitor visitor); + + public abstract S AcceptVisitor (IAstVisitor visitor, T data); + + #region Pattern Matching + protected static bool MatchString (string pattern, string text) + { + return PatternMatching.Pattern.MatchString(pattern, text); + } + + protected internal abstract bool DoMatch (AstNode other, PatternMatching.Match match); + + bool PatternMatching.INode.DoMatch (PatternMatching.INode other, PatternMatching.Match match) + { + AstNode o = other as AstNode; + // try matching if other is null, or if other is an AstNode + return (other == null || o != null) && DoMatch (o, match); + } + + bool PatternMatching.INode.DoMatchCollection (Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) + { + AstNode o = pos as AstNode; + return (pos == null || o != null) && DoMatch (o, match); + } + + PatternMatching.INode PatternMatching.INode.NextSibling { + get { return nextSibling; } + } + + PatternMatching.INode PatternMatching.INode.FirstChild { + get { return firstChild; } + } + + #endregion + + public AstNode GetNextNode () + { + if (NextSibling != null) + return NextSibling; + if (Parent != null) + return Parent.GetNextNode (); + return null; + } + + /// + /// Gets the next node which fullfills a given predicate + /// + /// The next node. + /// The predicate. + public AstNode GetNextNode (Func pred) + { + var next = GetNextNode(); + while (next != null && !pred (next)) + next = next.GetNextNode(); + return next; + } + + public AstNode GetPrevNode () + { + if (PrevSibling != null) + return PrevSibling; + if (Parent != null) + return Parent.GetPrevNode (); + return null; + } + + /// + /// Gets the previous node which fullfills a given predicate + /// + /// The next node. + /// The predicate. + public AstNode GetPrevNode (Func pred) + { + var prev = GetPrevNode(); + while (prev != null && !pred (prev)) + prev = prev.GetPrevNode(); + return prev; + } + // filters all non c# nodes (comments, white spaces or pre processor directives) + public AstNode GetCSharpNodeBefore (AstNode node) + { + var n = node.PrevSibling; + while (n != null) { + if (n.Role != Roles.Comment) + return n; + n = n.GetPrevNode (); + } + return null; + } + + /// + /// Gets the next sibling which fullfills a given predicate + /// + /// The next node. + /// The predicate. + public AstNode GetNextSibling (Func pred) + { + var next = NextSibling; + while (next != null && !pred (next)) + next = next.NextSibling; + return next; + } + + /// + /// Gets the next sibling which fullfills a given predicate + /// + /// The next node. + /// The predicate. + public AstNode GetPrevSibling (Func pred) + { + var prev = PrevSibling; + while (prev != null && !pred (prev)) + prev = prev.PrevSibling; + return prev; + } + + #region GetNodeAt + /// + /// Gets the node specified by T at the location line, column. This is useful for getting a specific node from the tree. For example searching + /// the current method declaration. + /// (End exclusive) + /// + public AstNode GetNodeAt (int line, int column, Predicate pred = null) + { + return GetNodeAt (new TextLocation (line, column), pred); + } + + /// + /// Gets the node specified by pred at location. This is useful for getting a specific node from the tree. For example searching + /// the current method declaration. + /// (End exclusive) + /// + public AstNode GetNodeAt (TextLocation location, Predicate pred = null) + { + AstNode result = null; + AstNode node = this; + while (node.LastChild != null) { + var child = node.LastChild; + while (child != null && child.StartLocation > location) + child = child.prevSibling; + if (child != null && location < child.EndLocation) { + if (pred == null || pred (child)) + result = child; + node = child; + } else { + // found no better child node - therefore the parent is the right one. + break; + } + } + return result; + } + + /// + /// Gets the node specified by T at the location line, column. This is useful for getting a specific node from the tree. For example searching + /// the current method declaration. + /// (End exclusive) + /// + public T GetNodeAt (int line, int column) where T : AstNode + { + return GetNodeAt (new TextLocation (line, column)); + } + + /// + /// Gets the node specified by T at location. This is useful for getting a specific node from the tree. For example searching + /// the current method declaration. + /// (End exclusive) + /// + public T GetNodeAt (TextLocation location) where T : AstNode + { + T result = null; + AstNode node = this; + while (node.LastChild != null) { + var child = node.LastChild; + while (child != null && child.StartLocation > location) + child = child.prevSibling; + if (child != null && location < child.EndLocation) { + if (child is T) + result = (T)child; + node = child; + } else { + // found no better child node - therefore the parent is the right one. + break; + } + } + return result; + } + + #endregion + + #region GetAdjacentNodeAt + /// + /// Gets the node specified by pred at the location line, column. This is useful for getting a specific node from the tree. For example searching + /// the current method declaration. + /// (End inclusive) + /// + public AstNode GetAdjacentNodeAt(int line, int column, Predicate pred = null) + { + return GetAdjacentNodeAt (new TextLocation (line, column), pred); + } + + /// + /// Gets the node specified by pred at location. This is useful for getting a specific node from the tree. For example searching + /// the current method declaration. + /// (End inclusive) + /// + public AstNode GetAdjacentNodeAt (TextLocation location, Predicate pred = null) + { + AstNode result = null; + AstNode node = this; + while (node.LastChild != null) { + var child = node.LastChild; + while (child != null && child.StartLocation > location) + child = child.prevSibling; + if (child != null && location <= child.EndLocation) { + if (pred == null || pred (child)) + result = child; + node = child; + } else { + // found no better child node - therefore the parent is the right one. + break; + } + } + return result; + } + + /// + /// Gets the node specified by T at the location line, column. This is useful for getting a specific node from the tree. For example searching + /// the current method declaration. + /// (End inclusive) + /// + public T GetAdjacentNodeAt(int line, int column) where T : AstNode + { + return GetAdjacentNodeAt (new TextLocation (line, column)); + } + + /// + /// Gets the node specified by T at location. This is useful for getting a specific node from the tree. For example searching + /// the current method declaration. + /// (End inclusive) + /// + public T GetAdjacentNodeAt (TextLocation location) where T : AstNode + { + T result = null; + AstNode node = this; + while (node.LastChild != null) { + var child = node.LastChild; + while (child != null && child.StartLocation > location) + child = child.prevSibling; + if (child != null && location <= child.EndLocation) { + if (child is T) + result = (T)child; + node = child; + } else { + // found no better child node - therefore the parent is the right one. + break; + } + } + return result; + } + #endregion + + + /// + /// Gets the node that fully contains the range from startLocation to endLocation. + /// + public AstNode GetNodeContaining(TextLocation startLocation, TextLocation endLocation) + { + for (AstNode child = firstChild; child != null; child = child.nextSibling) { + if (child.StartLocation <= startLocation && endLocation <= child.EndLocation) + return child.GetNodeContaining(startLocation, endLocation); + } + return this; + } + + /// + /// Returns the root nodes of all subtrees that are fully contained in the specified region. + /// + public IEnumerable GetNodesBetween (int startLine, int startColumn, int endLine, int endColumn) + { + return GetNodesBetween (new TextLocation (startLine, startColumn), new TextLocation (endLine, endColumn)); + } + + /// + /// Returns the root nodes of all subtrees that are fully contained between and (inclusive). + /// + public IEnumerable GetNodesBetween (TextLocation start, TextLocation end) + { + AstNode node = this; + while (node != null) { + AstNode next; + if (start <= node.StartLocation && node.EndLocation <= end) { + // Remember next before yielding node. + // This allows iteration to continue when the caller removes/replaces the node. + next = node.GetNextNode(); + yield return node; + } else { + if (node.EndLocation <= start) { + next = node.GetNextNode(); + } else { + next = node.FirstChild; + } + } + + if (next != null && next.StartLocation > end) + yield break; + node = next; + } + } + [Obsolete("Use ToString(options).")] + public string GetText (CSharpFormattingOptions formattingOptions = null) + { + return ToString(formattingOptions); + } + + /// + /// Gets the node as formatted C# output. + /// + /// + /// Formatting options. + /// + public virtual string ToString (CSharpFormattingOptions formattingOptions) + { + if (IsNull) + return ""; + var w = new StringWriter (); + AcceptVisitor (new CSharpOutputVisitor (w, formattingOptions ?? FormattingOptionsFactory.CreateMono ())); + return w.ToString (); + } + + public sealed override string ToString() + { + return ToString(null); + } + + /// + /// Returns true, if the given coordinates (line, column) are in the node. + /// + /// + /// True, if the given coordinates are between StartLocation and EndLocation (exclusive); otherwise, false. + /// + public bool Contains (int line, int column) + { + return Contains (new TextLocation (line, column)); + } + + /// + /// Returns true, if the given coordinates are in the node. + /// + /// + /// True, if location is between StartLocation and EndLocation (exclusive); otherwise, false. + /// + public bool Contains (TextLocation location) + { + return this.StartLocation <= location && location < this.EndLocation; + } + + /// + /// Returns true, if the given coordinates (line, column) are in the node. + /// + /// + /// True, if the given coordinates are between StartLocation and EndLocation (inclusive); otherwise, false. + /// + public bool IsInside (int line, int column) + { + return IsInside (new TextLocation (line, column)); + } + + /// + /// Returns true, if the given coordinates are in the node. + /// + /// + /// True, if location is between StartLocation and EndLocation (inclusive); otherwise, false. + /// + public bool IsInside (TextLocation location) + { + return this.StartLocation <= location && location <= this.EndLocation; + } + + public override void AddAnnotation (object annotation) + { + if (this.IsNull) + throw new InvalidOperationException ("Cannot add annotations to the null node"); + base.AddAnnotation (annotation); + } + + internal string DebugToString() + { + if (IsNull) + return "Null"; + string text = ToString(); + text = text.TrimEnd().Replace("\t", "").Replace(Environment.NewLine, " "); + if (text.Length > 100) + return text.Substring(0, 97) + "..."; + else + return text; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/AstNodeCollection.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/AstNodeCollection.cs new file mode 100644 index 000000000..32d08b2e4 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/AstNodeCollection.cs @@ -0,0 +1,231 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// 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; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using ICSharpCode.NRefactory.PatternMatching; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Represents the children of an AstNode that have a specific role. + /// + public class AstNodeCollection : ICollection + #if NET_4_5 + , IReadOnlyCollection + #endif + where T : AstNode + { + readonly AstNode node; + readonly Role role; + + public AstNodeCollection(AstNode node, Role role) + { + if (node == null) + throw new ArgumentNullException("node"); + if (role == null) + throw new ArgumentNullException("role"); + this.node = node; + this.role = role; + } + + public int Count { + get { + int count = 0; + uint roleIndex = role.Index; + for (AstNode cur = node.FirstChild; cur != null; cur = cur.NextSibling) { + if (cur.RoleIndex == roleIndex) + count++; + } + return count; + } + } + + public void Add(T element) + { + node.AddChild(element, role); + } + + public void AddRange(IEnumerable nodes) + { + // Evaluate 'nodes' first, since it might change when we add the new children + // Example: collection.AddRange(collection); + if (nodes != null) { + foreach (T node in nodes.ToList()) + Add(node); + } + } + + public void AddRange(T[] nodes) + { + // Fast overload for arrays - we don't need to create a copy + if (nodes != null) { + foreach (T node in nodes) + Add(node); + } + } + + public void ReplaceWith(IEnumerable nodes) + { + // Evaluate 'nodes' first, since it might change when we call Clear() + // Example: collection.ReplaceWith(collection); + if (nodes != null) + nodes = nodes.ToList(); + Clear(); + if (nodes != null) { + foreach (T node in nodes) + Add(node); + } + } + + public void MoveTo(ICollection targetCollection) + { + if (targetCollection == null) + throw new ArgumentNullException("targetCollection"); + foreach (T node in this) { + node.Remove(); + targetCollection.Add(node); + } + } + + public bool Contains(T element) + { + return element != null && element.Parent == node && element.RoleIndex == role.Index; + } + + public bool Remove(T element) + { + if (Contains(element)) { + element.Remove(); + return true; + } else { + return false; + } + } + + public void CopyTo(T[] array, int arrayIndex) + { + foreach (T item in this) + array[arrayIndex++] = item; + } + + public void Clear() + { + foreach (T item in this) + item.Remove(); + } + + /// + /// Returns the first element for which the predicate returns true, + /// or the null node (AstNode with IsNull=true) if no such object is found. + /// + public T FirstOrNullObject(Func predicate = null) + { + foreach (T item in this) + if (predicate == null || predicate(item)) + return item; + return role.NullObject; + } + + /// + /// Returns the last element for which the predicate returns true, + /// or the null node (AstNode with IsNull=true) if no such object is found. + /// + public T LastOrNullObject(Func predicate = null) + { + T result = role.NullObject; + foreach (T item in this) + if (predicate == null || predicate(item)) + result = item; + return result; + } + + bool ICollection.IsReadOnly { + get { return false; } + } + + public IEnumerator GetEnumerator() + { + uint roleIndex = role.Index; + AstNode next; + for (AstNode cur = node.FirstChild; cur != null; cur = next) { + Debug.Assert(cur.Parent == node); + // Remember next before yielding cur. + // This allows removing/replacing nodes while iterating through the list. + next = cur.NextSibling; + if (cur.RoleIndex == roleIndex) + yield return (T)cur; + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + #region Equals and GetHashCode implementation + public override int GetHashCode() + { + return node.GetHashCode() ^ role.GetHashCode(); + } + + public override bool Equals(object obj) + { + AstNodeCollection other = obj as AstNodeCollection; + if (other == null) + return false; + return this.node == other.node && this.role == other.role; + } + #endregion + + internal bool DoMatch(AstNodeCollection other, Match match) + { + return Pattern.DoMatchCollection(role, node.FirstChild, other.node.FirstChild, match); + } + + public void InsertAfter(T existingItem, T newItem) + { + node.InsertChildAfter(existingItem, newItem, role); + } + + public void InsertBefore(T existingItem, T newItem) + { + node.InsertChildBefore(existingItem, newItem, role); + } + + /// + /// Applies the to all nodes in this collection. + /// + public void AcceptVisitor(IAstVisitor visitor) + { + uint roleIndex = role.Index; + AstNode next; + for (AstNode cur = node.FirstChild; cur != null; cur = next) { + Debug.Assert(cur.Parent == node); + // Remember next before yielding cur. + // This allows removing/replacing nodes while iterating through the list. + next = cur.NextSibling; + if (cur.RoleIndex == roleIndex) + cur.AcceptVisitor(visitor); + } + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/AstType.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/AstType.cs new file mode 100644 index 000000000..2f13f0fdd --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/AstType.cs @@ -0,0 +1,280 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// 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.CSharp.Resolver; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// A type reference in the C# AST. + /// + public abstract class AstType : AstNode + { + #region Null + public new static readonly AstType Null = new NullAstType (); + + sealed class NullAstType : AstType + { + public override bool IsNull { + get { + return true; + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullNode(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return other == null || other.IsNull; + } + + public override ITypeReference ToTypeReference(NameLookupMode lookupMode, InterningProvider interningProvider) + { + return SpecialType.UnknownType; + } + } + #endregion + + #region PatternPlaceholder + public static implicit operator AstType(PatternMatching.Pattern pattern) + { + return pattern != null ? new PatternPlaceholder(pattern) : null; + } + + sealed class PatternPlaceholder : AstType, PatternMatching.INode + { + readonly PatternMatching.Pattern child; + + public PatternPlaceholder(PatternMatching.Pattern child) + { + this.child = child; + } + + public override NodeType NodeType { + get { return NodeType.Pattern; } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPatternPlaceholder (this, child); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPatternPlaceholder (this, child); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitPatternPlaceholder (this, child, data); + } + + public override ITypeReference ToTypeReference(NameLookupMode lookupMode, InterningProvider interningProvider) + { + throw new NotSupportedException(); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return child.DoMatch(other, match); + } + + bool PatternMatching.INode.DoMatchCollection(Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) + { + return child.DoMatchCollection(role, pos, match, backtrackingInfo); + } + } + #endregion + + public override NodeType NodeType { + get { return NodeType.TypeReference; } + } + + public new AstType Clone() + { + return (AstType)base.Clone(); + } + + /// + /// Gets whether this type is a SimpleType "var". + /// + public bool IsVar() + { + SimpleType st = this as SimpleType; + return st != null && st.Identifier == "var" && st.TypeArguments.Count == 0; + } + + /// + /// Create an ITypeReference for this AstType. + /// Uses the context (ancestors of this node) to determine the correct . + /// + /// + /// The resulting type reference will read the context information from the + /// : + /// For resolving type parameters, the CurrentTypeDefinition/CurrentMember is used. + /// For resolving simple names, the current namespace and usings from the CurrentUsingScope + /// (on CSharpTypeResolveContext only) is used. + /// + public ITypeReference ToTypeReference(InterningProvider interningProvider = null) + { + return ToTypeReference(GetNameLookupMode(), interningProvider); + } + + /// + /// Create an ITypeReference for this AstType. + /// + /// + /// The resulting type reference will read the context information from the + /// : + /// For resolving type parameters, the CurrentTypeDefinition/CurrentMember is used. + /// For resolving simple names, the current namespace and usings from the CurrentUsingScope + /// (on CSharpTypeResolveContext only) is used. + /// + public abstract ITypeReference ToTypeReference(NameLookupMode lookupMode, InterningProvider interningProvider = null); + + /// + /// Gets the name lookup mode from the context (looking at the ancestors of this ). + /// + public NameLookupMode GetNameLookupMode() + { + AstType outermostType = this; + while (outermostType.Parent is AstType) + outermostType = (AstType)outermostType.Parent; + + if (outermostType.Parent is UsingDeclaration || outermostType.Parent is UsingAliasDeclaration) { + return NameLookupMode.TypeInUsingDeclaration; + } else if (outermostType.Role == Roles.BaseType) { + // Use BaseTypeReference for a type's base type, and for a constraint on a type. + // Do not use it for a constraint on a method. + if (outermostType.Parent is TypeDeclaration || (outermostType.Parent is Constraint && outermostType.Parent.Parent is TypeDeclaration)) + return NameLookupMode.BaseTypeReference; + } + return NameLookupMode.Type; + } + + /// + /// Creates a pointer type from this type by nesting it in a . + /// If this type already is a pointer type, this method just increases the PointerRank of the existing pointer type. + /// + public virtual AstType MakePointerType() + { + return new ComposedType { BaseType = this }.MakePointerType(); + } + + /// + /// Creates an array type from this type by nesting it in a . + /// If this type already is an array type, the additional rank is prepended to the existing array specifier list. + /// Thus, new SimpleType("T").MakeArrayType(1).MakeArrayType(2) will result in "T[,][]". + /// + public virtual AstType MakeArrayType(int rank = 1) + { + return new ComposedType { BaseType = this }.MakeArrayType(rank); + } + + /// + /// Creates a nullable type from this type by nesting it in a . + /// + public AstType MakeNullableType() + { + return new ComposedType { BaseType = this, HasNullableSpecifier = true }; + } + + /// + /// Builds an expression that can be used to access a static member on this type. + /// + public MemberReferenceExpression Member(string memberName) + { + return new TypeReferenceExpression { Type = this }.Member(memberName); + } + + /// + /// Builds an expression that can be used to access a static member on this type. + /// + public MemberType MemberType(string memberName, params AstType[] typeArguments) + { + var memberType = new MemberType(this, memberName); + memberType.TypeArguments.AddRange(typeArguments); + return memberType; + } + + /// + /// Builds an expression that can be used to access a static member on this type. + /// + public MemberType MemberType(string memberName, IEnumerable typeArguments) + { + var memberType = new MemberType(this, memberName); + memberType.TypeArguments.AddRange(typeArguments); + return memberType; + } + + /// + /// Builds an invocation expression using this type as target. + /// + public InvocationExpression Invoke(string methodName, IEnumerable arguments) + { + return new TypeReferenceExpression { Type = this }.Invoke(methodName, arguments); + } + + /// + /// Builds an invocation expression using this type as target. + /// + public InvocationExpression Invoke(string methodName, params Expression[] arguments) + { + return new TypeReferenceExpression { Type = this }.Invoke(methodName, arguments); + } + + /// + /// Builds an invocation expression using this type as target. + /// + public InvocationExpression Invoke(string methodName, IEnumerable typeArguments, IEnumerable arguments) + { + return new TypeReferenceExpression { Type = this }.Invoke(methodName, typeArguments, arguments); + } + + /// + /// Creates a simple AstType from a dotted name. + /// Does not support generics, arrays, etc. - just simple dotted names, + /// e.g. namespace names. + /// + public static AstType Create(string dottedName) + { + string[] parts = dottedName.Split('.'); + AstType type = new SimpleType(parts[0]); + for (int i = 1; i < parts.Length; i++) { + type = new MemberType(type, parts[i]); + } + return type; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/CSharpModifierToken.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/CSharpModifierToken.cs new file mode 100644 index 000000000..1a46006f2 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/CSharpModifierToken.cs @@ -0,0 +1,218 @@ +// +// CSharpModifierToken.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.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 System.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class CSharpModifierToken : CSharpTokenNode + { + Modifiers modifier; + + public Modifiers Modifier { + get { return modifier; } + set { + ThrowIfFrozen(); + this.modifier = value; + } + } + + public override TextLocation EndLocation { + get { + return new TextLocation (StartLocation.Line, StartLocation.Column + GetModifierLength (Modifier)); + } + } + + public override string ToString(CSharpFormattingOptions formattingOptions) + { + return GetModifierName (Modifier); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + CSharpModifierToken o = other as CSharpModifierToken; + return o != null && this.modifier == o.modifier; + } + + // Not worth using a dictionary for such few elements. + // This table is sorted in the order that modifiers should be output when generating code. + static readonly Modifiers[] allModifiers = { + Modifiers.Public, Modifiers.Protected, Modifiers.Private, Modifiers.Internal, + Modifiers.New, + Modifiers.Unsafe, + Modifiers.Abstract, Modifiers.Virtual, Modifiers.Sealed, Modifiers.Static, Modifiers.Override, + Modifiers.Readonly, Modifiers.Volatile, + Modifiers.Extern, Modifiers.Partial, Modifiers.Const, + Modifiers.Async, + Modifiers.Any + }; + + public static IEnumerable AllModifiers { + get { return allModifiers; } + } + + public CSharpModifierToken (TextLocation location, Modifiers modifier) : base (location, null) + { + this.Modifier = modifier; + } + + public static string GetModifierName(Modifiers modifier) + { + switch (modifier) { + case Modifiers.Private: + return "private"; + case Modifiers.Internal: + return "internal"; + case Modifiers.Protected: + return "protected"; + case Modifiers.Public: + return "public"; + case Modifiers.Abstract: + return "abstract"; + case Modifiers.Virtual: + return "virtual"; + case Modifiers.Sealed: + return "sealed"; + case Modifiers.Static: + return "static"; + case Modifiers.Override: + return "override"; + case Modifiers.Readonly: + return "readonly"; + case Modifiers.Const: + return "const"; + case Modifiers.New: + return "new"; + case Modifiers.Partial: + return "partial"; + case Modifiers.Extern: + return "extern"; + case Modifiers.Volatile: + return "volatile"; + case Modifiers.Unsafe: + return "unsafe"; + case Modifiers.Async: + return "async"; + case Modifiers.Any: + // even though it's used for pattern matching only, 'any' needs to be in this list to be usable in the AST + return "any"; + default: + throw new NotSupportedException("Invalid value for Modifiers"); + } + } + + public static int GetModifierLength(Modifiers modifier) + { + switch (modifier) { + case Modifiers.Private: + return "private".Length; + case Modifiers.Internal: + return "internal".Length; + case Modifiers.Protected: + return "protected".Length; + case Modifiers.Public: + return "public".Length; + case Modifiers.Abstract: + return "abstract".Length; + case Modifiers.Virtual: + return "virtual".Length; + case Modifiers.Sealed: + return "sealed".Length; + case Modifiers.Static: + return "static".Length; + case Modifiers.Override: + return "override".Length; + case Modifiers.Readonly: + return "readonly".Length; + case Modifiers.Const: + return "const".Length; + case Modifiers.New: + return "new".Length; + case Modifiers.Partial: + return "partial".Length; + case Modifiers.Extern: + return "extern".Length; + case Modifiers.Volatile: + return "volatile".Length; + case Modifiers.Unsafe: + return "unsafe".Length; + case Modifiers.Async: + return "async".Length; + case Modifiers.Any: + // even though it's used for pattern matching only, 'any' needs to be in this list to be usable in the AST + return "any".Length; + default: + throw new NotSupportedException("Invalid value for Modifiers"); + } + } + + public static Modifiers GetModifierValue(string modifier) + { + switch (modifier) { + case "private": + return Modifiers.Private; + case "internal": + return Modifiers.Internal; + case "protected": + return Modifiers.Protected; + case "public": + return Modifiers.Public; + case "abstract": + return Modifiers.Abstract; + case "virtual": + return Modifiers.Virtual; + case "sealed": + return Modifiers.Sealed; + case "static": + return Modifiers.Static; + case "override": + return Modifiers.Override; + case "readonly": + return Modifiers.Readonly; + case "const": + return Modifiers.Const; + case "new": + return Modifiers.New; + case "partial": + return Modifiers.Partial; + case "extern": + return Modifiers.Extern; + case "volatile": + return Modifiers.Volatile; + case "unsafe": + return Modifiers.Unsafe; + case "async": + return Modifiers.Async; + case "any": + // even though it's used for pattern matching only, 'any' needs to be in this list to be usable in the AST + return Modifiers.Any; + default: + throw new NotSupportedException("Invalid value for Modifiers"); + } + } + } +} \ No newline at end of file diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/CSharpTokenNode.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/CSharpTokenNode.cs new file mode 100644 index 000000000..713f664b3 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/CSharpTokenNode.cs @@ -0,0 +1,131 @@ +// +// TokenNode.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.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; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Represents a token in C#. Note that the type of the token is defined through the TokenRole. + /// + /// + /// In all non null c# token nodes the Role of a CSharpToken must be a TokenRole. + /// + public class CSharpTokenNode : AstNode + { + public static new readonly CSharpTokenNode Null = new NullCSharpTokenNode (); + class NullCSharpTokenNode : CSharpTokenNode + { + public override bool IsNull { + get { + return true; + } + } + + public NullCSharpTokenNode () : base (TextLocation.Empty, null) + { + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullNode(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return other == null || other.IsNull; + } + } + + public override NodeType NodeType { + get { + return NodeType.Token; + } + } + + TextLocation startLocation; + public override TextLocation StartLocation { + get { + return startLocation; + } + } + + int TokenLength { + get { + return TokenRole.TokenLengths [(int)(this.flags >> AstNodeFlagsUsedBits)]; + } + } + + public override TextLocation EndLocation { + get { + return new TextLocation (StartLocation.Line, StartLocation.Column + TokenLength); + } + } + + public CSharpTokenNode (TextLocation location, TokenRole role) + { + this.startLocation = location; + if (role != null) + this.flags |= role.TokenIndex << AstNodeFlagsUsedBits; + } + + public override string ToString(CSharpFormattingOptions formattingOptions) + { + return TokenRole.Tokens [(int)(this.flags >> AstNodeFlagsUsedBits)]; + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitCSharpTokenNode (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitCSharpTokenNode (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitCSharpTokenNode (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + CSharpTokenNode o = other as CSharpTokenNode; + return o != null && !o.IsNull && !(o is CSharpModifierToken); + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/CSharpUtil.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/CSharpUtil.cs new file mode 100644 index 000000000..a2a07ad6e --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/CSharpUtil.cs @@ -0,0 +1,180 @@ +// +// CSharpUtil.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.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 ICSharpCode.NRefactory.CSharp; +using ICSharpCode.NRefactory.PatternMatching; + +namespace ICSharpCode.NRefactory.CSharp +{ + public static class CSharpUtil + { + /// + /// Inverts a boolean condition. Note: The condition object can be frozen (from AST) it's cloned internally. + /// + /// The condition to invert. + public static Expression InvertCondition(Expression condition) + { + return InvertConditionInternal(condition); + } + + static Expression InvertConditionInternal(Expression condition) + { + if (condition is ParenthesizedExpression) { + return new ParenthesizedExpression(InvertCondition(((ParenthesizedExpression)condition).Expression)); + } + + if (condition is UnaryOperatorExpression) { + var uOp = (UnaryOperatorExpression)condition; + if (uOp.Operator == UnaryOperatorType.Not) { + if (!(uOp.Parent is Expression)) + return GetInnerMostExpression(uOp.Expression).Clone(); + return uOp.Expression.Clone(); + } + return new UnaryOperatorExpression(UnaryOperatorType.Not, uOp.Clone()); + } + + if (condition is BinaryOperatorExpression) { + var bOp = (BinaryOperatorExpression)condition; + + if ((bOp.Operator == BinaryOperatorType.ConditionalAnd) || (bOp.Operator == BinaryOperatorType.ConditionalOr)) { + return new BinaryOperatorExpression(InvertCondition(bOp.Left), NegateConditionOperator(bOp.Operator), InvertCondition(bOp.Right)); + } else if ((bOp.Operator == BinaryOperatorType.Equality) || (bOp.Operator == BinaryOperatorType.InEquality) || (bOp.Operator == BinaryOperatorType.GreaterThan) + || (bOp.Operator == BinaryOperatorType.GreaterThanOrEqual) || (bOp.Operator == BinaryOperatorType.LessThan) || + (bOp.Operator == BinaryOperatorType.LessThanOrEqual)) { + return new BinaryOperatorExpression(bOp.Left.Clone(), NegateRelationalOperator(bOp.Operator), bOp.Right.Clone()); + } else { + var negatedOp = NegateRelationalOperator(bOp.Operator); + if (negatedOp == BinaryOperatorType.Any) + return new UnaryOperatorExpression(UnaryOperatorType.Not, new ParenthesizedExpression(condition.Clone())); + bOp = (BinaryOperatorExpression)bOp.Clone(); + bOp.Operator = negatedOp; + return bOp; + } + } + if (condition is ConditionalExpression) { + var cEx = condition.Clone() as ConditionalExpression; + cEx.Condition = InvertCondition(cEx.Condition); + return cEx; + } + if (condition is PrimitiveExpression) { + var pex = condition as PrimitiveExpression; + if (pex.Value is bool) { + return new PrimitiveExpression(!((bool)pex.Value)); + } + } + + return new UnaryOperatorExpression(UnaryOperatorType.Not, AddParensForUnaryExpressionIfRequired(condition.Clone())); + } + + /// + /// When negating an expression this is required, otherwise you would end up with + /// a or b -> !a or b + /// + internal static Expression AddParensForUnaryExpressionIfRequired(Expression expression) + { + if ((expression is BinaryOperatorExpression) || + (expression is AssignmentExpression) || + (expression is CastExpression) || + (expression is AsExpression) || + (expression is IsExpression) || + (expression is LambdaExpression) || + (expression is ConditionalExpression)) { + return new ParenthesizedExpression(expression); + } + + return expression; + } + + /// + /// Get negation of the specified relational operator + /// + /// + /// negation of the specified relational operator, or BinaryOperatorType.Any if it's not a relational operator + /// + public static BinaryOperatorType NegateRelationalOperator(BinaryOperatorType op) + { + switch (op) { + case BinaryOperatorType.GreaterThan: + return BinaryOperatorType.LessThanOrEqual; + case BinaryOperatorType.GreaterThanOrEqual: + return BinaryOperatorType.LessThan; + case BinaryOperatorType.Equality: + return BinaryOperatorType.InEquality; + case BinaryOperatorType.InEquality: + return BinaryOperatorType.Equality; + case BinaryOperatorType.LessThan: + return BinaryOperatorType.GreaterThanOrEqual; + case BinaryOperatorType.LessThanOrEqual: + return BinaryOperatorType.GreaterThan; + case BinaryOperatorType.ConditionalOr: + return BinaryOperatorType.ConditionalAnd; + case BinaryOperatorType.ConditionalAnd: + return BinaryOperatorType.ConditionalOr; + } + return BinaryOperatorType.Any; + } + + /// + /// Returns true, if the specified operator is a relational operator + /// + public static bool IsRelationalOperator(BinaryOperatorType op) + { + return NegateRelationalOperator(op) != BinaryOperatorType.Any; + } + + /// + /// Get negation of the condition operator + /// + /// + /// negation of the specified condition operator, or BinaryOperatorType.Any if it's not a condition operator + /// + public static BinaryOperatorType NegateConditionOperator(BinaryOperatorType op) + { + switch (op) { + case BinaryOperatorType.ConditionalOr: + return BinaryOperatorType.ConditionalAnd; + case BinaryOperatorType.ConditionalAnd: + return BinaryOperatorType.ConditionalOr; + } + return BinaryOperatorType.Any; + } + + public static bool AreConditionsEqual(Expression cond1, Expression cond2) + { + if (cond1 == null || cond2 == null) + return false; + return GetInnerMostExpression(cond1).IsMatch(GetInnerMostExpression(cond2)); + } + + public static Expression GetInnerMostExpression(Expression target) + { + while (target is ParenthesizedExpression) + target = ((ParenthesizedExpression)target).Expression; + return target; + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/ComposedType.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/ComposedType.cs new file mode 100644 index 000000000..0c0f96c62 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/ComposedType.cs @@ -0,0 +1,230 @@ +// +// ComposedType.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.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 System.Linq; +using System.Text; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class ComposedType : AstType + { + public static readonly TokenRole NullableRole = new TokenRole("?"); + public static readonly TokenRole PointerRole = new TokenRole("*"); + public static readonly Role ArraySpecifierRole = new Role("ArraySpecifier"); + + public AstType BaseType { + get { return GetChildByRole(Roles.Type); } + set { SetChildByRole(Roles.Type, value); } + } + + public bool HasNullableSpecifier { + get { + return !GetChildByRole(NullableRole).IsNull; + } + set { + SetChildByRole(NullableRole, value ? new CSharpTokenNode(TextLocation.Empty, null) : null); + } + } + + public CSharpTokenNode NullableSpecifierToken { + get { + return GetChildByRole(NullableRole); + } + } + + public int PointerRank { + get { + return GetChildrenByRole(PointerRole).Count; + } + set { + if (value < 0) + throw new ArgumentOutOfRangeException(); + int d = this.PointerRank; + while (d > value) { + GetChildByRole(PointerRole).Remove(); + d--; + } + while (d < value) { + InsertChildBefore(GetChildByRole(PointerRole), new CSharpTokenNode(TextLocation.Empty, PointerRole), PointerRole); + d++; + } + } + } + + public AstNodeCollection ArraySpecifiers { + get { return GetChildrenByRole (ArraySpecifierRole); } + } + + public AstNodeCollection PointerTokens { + get { return GetChildrenByRole (PointerRole); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitComposedType (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitComposedType (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitComposedType (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ComposedType o = other as ComposedType; + return o != null && this.HasNullableSpecifier == o.HasNullableSpecifier && this.PointerRank == o.PointerRank + && this.BaseType.DoMatch(o.BaseType, match) + && this.ArraySpecifiers.DoMatch(o.ArraySpecifiers, match); + } + + public override string ToString(CSharpFormattingOptions formattingOptions) + { + StringBuilder b = new StringBuilder(); + b.Append(this.BaseType.ToString()); + if (this.HasNullableSpecifier) + b.Append('?'); + b.Append('*', this.PointerRank); + foreach (var arraySpecifier in this.ArraySpecifiers) { + b.Append('['); + b.Append(',', arraySpecifier.Dimensions - 1); + b.Append(']'); + } + return b.ToString(); + } + + public override AstType MakePointerType() + { + if (ArraySpecifiers.Any()) { + return base.MakePointerType(); + } else { + this.PointerRank++; + return this; + } + } + + public override AstType MakeArrayType(int dimensions) + { + InsertChildBefore(this.ArraySpecifiers.FirstOrDefault(), new ArraySpecifier(dimensions), ArraySpecifierRole); + return this; + } + + public override ITypeReference ToTypeReference(NameLookupMode lookupMode, InterningProvider interningProvider = null) + { + if (interningProvider == null) + interningProvider = InterningProvider.Dummy; + ITypeReference t = this.BaseType.ToTypeReference(lookupMode, interningProvider); + if (this.HasNullableSpecifier) { + t = interningProvider.Intern(NullableType.Create(t)); + } + int pointerRank = this.PointerRank; + for (int i = 0; i < pointerRank; i++) { + t = interningProvider.Intern(new PointerTypeReference(t)); + } + foreach (var a in this.ArraySpecifiers.Reverse()) { + t = interningProvider.Intern(new ArrayTypeReference(t, a.Dimensions)); + } + return t; + } + } + + /// + /// [,,,] + /// + public class ArraySpecifier : AstNode + { + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public ArraySpecifier() + { + } + + public ArraySpecifier(int dimensions) + { + this.Dimensions = dimensions; + } + + public CSharpTokenNode LBracketToken { + get { return GetChildByRole (Roles.LBracket); } + } + + public int Dimensions { + get { return 1 + GetChildrenByRole(Roles.Comma).Count; } + set { + int d = this.Dimensions; + while (d > value) { + GetChildByRole(Roles.Comma).Remove(); + d--; + } + while (d < value) { + InsertChildBefore(GetChildByRole(Roles.Comma), new CSharpTokenNode(TextLocation.Empty, Roles.Comma), Roles.Comma); + d++; + } + } + } + + public CSharpTokenNode RBracketToken { + get { return GetChildByRole (Roles.RBracket); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitArraySpecifier (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitArraySpecifier (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitArraySpecifier(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ArraySpecifier o = other as ArraySpecifier; + return o != null && this.Dimensions == o.Dimensions; + } + + public override string ToString(CSharpFormattingOptions formattingOptions) + { + return "[" + new string(',', this.Dimensions - 1) + "]"; + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/DepthFirstAstVisitor.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/DepthFirstAstVisitor.cs new file mode 100644 index 000000000..d8c678e2b --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/DepthFirstAstVisitor.cs @@ -0,0 +1,1849 @@ +// +// IAstVisitor.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.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; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// AST visitor with a default implementation that visits all node depth-first. + /// + public abstract class DepthFirstAstVisitor : IAstVisitor + { + protected virtual void VisitChildren (AstNode node) + { + AstNode next; + for (var child = node.FirstChild; child != null; child = next) { + // Store next to allow the loop to continue + // if the visitor removes/replaces child. + next = child.NextSibling; + child.AcceptVisitor (this); + } + } + + public virtual void VisitNullNode(AstNode nullNode) + { + // Should we call VisitChildren here? + // We usually want to ignore null nodes. + // Older NR versions (before VisitNullNode was introduced) didn't call VisitChildren() with null nodes; + // so changing this might break VisitChildren() overrides that expect the node to be part of the AST. + } + + public virtual void VisitSyntaxTree (SyntaxTree syntaxTree) + { + VisitChildren (syntaxTree); + } + + public virtual void VisitComment(Comment comment) + { + VisitChildren(comment); + } + + public virtual void VisitNewLine(NewLineNode newLineNode) + { + VisitChildren(newLineNode); + } + + public virtual void VisitWhitespace(WhitespaceNode whitespaceNode) + { + VisitChildren(whitespaceNode); + } + + public virtual void VisitText(TextNode textNode) + { + VisitChildren(textNode); + } + + public virtual void VisitDocumentationReference (DocumentationReference documentationReference) + { + VisitChildren (documentationReference); + } + + public virtual void VisitPreProcessorDirective (PreProcessorDirective preProcessorDirective) + { + VisitChildren (preProcessorDirective); + } + + public virtual void VisitIdentifier (Identifier identifier) + { + VisitChildren (identifier); + } + + public virtual void VisitCSharpTokenNode (CSharpTokenNode token) + { + VisitChildren (token); + } + + public virtual void VisitPrimitiveType (PrimitiveType primitiveType) + { + VisitChildren (primitiveType); + } + + public virtual void VisitComposedType (ComposedType composedType) + { + VisitChildren (composedType); + } + + public virtual void VisitSimpleType(SimpleType simpleType) + { + VisitChildren (simpleType); + } + + public virtual void VisitMemberType(MemberType memberType) + { + VisitChildren (memberType); + } + + public virtual void VisitAttribute (Attribute attribute) + { + VisitChildren (attribute); + } + + public virtual void VisitAttributeSection (AttributeSection attributeSection) + { + VisitChildren (attributeSection); + } + + public virtual void VisitDelegateDeclaration (DelegateDeclaration delegateDeclaration) + { + VisitChildren (delegateDeclaration); + } + + public virtual void VisitNamespaceDeclaration (NamespaceDeclaration namespaceDeclaration) + { + VisitChildren (namespaceDeclaration); + } + + public virtual void VisitTypeDeclaration (TypeDeclaration typeDeclaration) + { + VisitChildren (typeDeclaration); + } + + public virtual void VisitTypeParameterDeclaration (TypeParameterDeclaration typeParameterDeclaration) + { + VisitChildren (typeParameterDeclaration); + } + + public virtual void VisitEnumMemberDeclaration (EnumMemberDeclaration enumMemberDeclaration) + { + VisitChildren (enumMemberDeclaration); + } + + public virtual void VisitUsingDeclaration (UsingDeclaration usingDeclaration) + { + VisitChildren (usingDeclaration); + } + + public virtual void VisitUsingAliasDeclaration (UsingAliasDeclaration usingDeclaration) + { + VisitChildren (usingDeclaration); + } + + public virtual void VisitExternAliasDeclaration(ExternAliasDeclaration externAliasDeclaration) + { + VisitChildren (externAliasDeclaration); + } + + public virtual void VisitConstructorDeclaration (ConstructorDeclaration constructorDeclaration) + { + VisitChildren (constructorDeclaration); + } + + public virtual void VisitConstructorInitializer (ConstructorInitializer constructorInitializer) + { + VisitChildren (constructorInitializer); + } + + public virtual void VisitDestructorDeclaration (DestructorDeclaration destructorDeclaration) + { + VisitChildren (destructorDeclaration); + } + + public virtual void VisitEventDeclaration (EventDeclaration eventDeclaration) + { + VisitChildren (eventDeclaration); + } + + public virtual void VisitCustomEventDeclaration (CustomEventDeclaration eventDeclaration) + { + VisitChildren (eventDeclaration); + } + + public virtual void VisitFieldDeclaration (FieldDeclaration fieldDeclaration) + { + VisitChildren (fieldDeclaration); + } + + public virtual void VisitFixedFieldDeclaration (FixedFieldDeclaration fixedFieldDeclaration) + { + VisitChildren (fixedFieldDeclaration); + } + + public virtual void VisitFixedVariableInitializer (FixedVariableInitializer fixedVariableInitializer) + { + VisitChildren (fixedVariableInitializer); + } + + public virtual void VisitIndexerDeclaration (IndexerDeclaration indexerDeclaration) + { + VisitChildren (indexerDeclaration); + } + + public virtual void VisitMethodDeclaration (MethodDeclaration methodDeclaration) + { + VisitChildren (methodDeclaration); + } + + public virtual void VisitOperatorDeclaration (OperatorDeclaration operatorDeclaration) + { + VisitChildren (operatorDeclaration); + } + + public virtual void VisitPropertyDeclaration (PropertyDeclaration propertyDeclaration) + { + VisitChildren (propertyDeclaration); + } + + public virtual void VisitAccessor (Accessor accessor) + { + VisitChildren (accessor); + } + + public virtual void VisitVariableInitializer (VariableInitializer variableInitializer) + { + VisitChildren (variableInitializer); + } + + public virtual void VisitParameterDeclaration (ParameterDeclaration parameterDeclaration) + { + VisitChildren (parameterDeclaration); + } + + public virtual void VisitConstraint (Constraint constraint) + { + VisitChildren (constraint); + } + + public virtual void VisitBlockStatement (BlockStatement blockStatement) + { + VisitChildren (blockStatement); + } + + public virtual void VisitExpressionStatement (ExpressionStatement expressionStatement) + { + VisitChildren (expressionStatement); + } + + public virtual void VisitBreakStatement (BreakStatement breakStatement) + { + VisitChildren (breakStatement); + } + + public virtual void VisitCheckedStatement (CheckedStatement checkedStatement) + { + VisitChildren (checkedStatement); + } + + public virtual void VisitContinueStatement (ContinueStatement continueStatement) + { + VisitChildren (continueStatement); + } + + public virtual void VisitDoWhileStatement (DoWhileStatement doWhileStatement) + { + VisitChildren (doWhileStatement); + } + + public virtual void VisitEmptyStatement (EmptyStatement emptyStatement) + { + VisitChildren (emptyStatement); + } + + public virtual void VisitFixedStatement (FixedStatement fixedStatement) + { + VisitChildren (fixedStatement); + } + + public virtual void VisitForeachStatement (ForeachStatement foreachStatement) + { + VisitChildren (foreachStatement); + } + + public virtual void VisitForStatement (ForStatement forStatement) + { + VisitChildren (forStatement); + } + + public virtual void VisitGotoCaseStatement (GotoCaseStatement gotoCaseStatement) + { + VisitChildren (gotoCaseStatement); + } + + public virtual void VisitGotoDefaultStatement (GotoDefaultStatement gotoDefaultStatement) + { + VisitChildren (gotoDefaultStatement); + } + + public virtual void VisitGotoStatement (GotoStatement gotoStatement) + { + VisitChildren (gotoStatement); + } + + public virtual void VisitIfElseStatement (IfElseStatement ifElseStatement) + { + VisitChildren (ifElseStatement); + } + + public virtual void VisitLabelStatement (LabelStatement labelStatement) + { + VisitChildren (labelStatement); + } + + public virtual void VisitLockStatement (LockStatement lockStatement) + { + VisitChildren (lockStatement); + } + + public virtual void VisitReturnStatement (ReturnStatement returnStatement) + { + VisitChildren (returnStatement); + } + + public virtual void VisitSwitchStatement (SwitchStatement switchStatement) + { + VisitChildren (switchStatement); + } + + public virtual void VisitSwitchSection (SwitchSection switchSection) + { + VisitChildren (switchSection); + } + + public virtual void VisitCaseLabel (CaseLabel caseLabel) + { + VisitChildren (caseLabel); + } + + public virtual void VisitThrowStatement (ThrowStatement throwStatement) + { + VisitChildren (throwStatement); + } + + public virtual void VisitTryCatchStatement (TryCatchStatement tryCatchStatement) + { + VisitChildren (tryCatchStatement); + } + + public virtual void VisitCatchClause (CatchClause catchClause) + { + VisitChildren (catchClause); + } + + public virtual void VisitUncheckedStatement (UncheckedStatement uncheckedStatement) + { + VisitChildren (uncheckedStatement); + } + + public virtual void VisitUnsafeStatement (UnsafeStatement unsafeStatement) + { + VisitChildren (unsafeStatement); + } + + public virtual void VisitUsingStatement (UsingStatement usingStatement) + { + VisitChildren (usingStatement); + } + + public virtual void VisitVariableDeclarationStatement (VariableDeclarationStatement variableDeclarationStatement) + { + VisitChildren (variableDeclarationStatement); + } + + public virtual void VisitWhileStatement (WhileStatement whileStatement) + { + VisitChildren (whileStatement); + } + + public virtual void VisitYieldBreakStatement (YieldBreakStatement yieldBreakStatement) + { + VisitChildren (yieldBreakStatement); + } + + public virtual void VisitYieldReturnStatement (YieldReturnStatement yieldReturnStatement) + { + VisitChildren (yieldReturnStatement); + } + + public virtual void VisitAnonymousMethodExpression (AnonymousMethodExpression anonymousMethodExpression) + { + VisitChildren (anonymousMethodExpression); + } + + public virtual void VisitLambdaExpression (LambdaExpression lambdaExpression) + { + VisitChildren (lambdaExpression); + } + + public virtual void VisitAssignmentExpression (AssignmentExpression assignmentExpression) + { + VisitChildren (assignmentExpression); + } + + public virtual void VisitBaseReferenceExpression (BaseReferenceExpression baseReferenceExpression) + { + VisitChildren (baseReferenceExpression); + } + + public virtual void VisitBinaryOperatorExpression (BinaryOperatorExpression binaryOperatorExpression) + { + VisitChildren (binaryOperatorExpression); + } + + public virtual void VisitCastExpression (CastExpression castExpression) + { + VisitChildren (castExpression); + } + + public virtual void VisitCheckedExpression (CheckedExpression checkedExpression) + { + VisitChildren (checkedExpression); + } + + public virtual void VisitConditionalExpression (ConditionalExpression conditionalExpression) + { + VisitChildren (conditionalExpression); + } + + public virtual void VisitIdentifierExpression (IdentifierExpression identifierExpression) + { + VisitChildren (identifierExpression); + } + + public virtual void VisitIndexerExpression (IndexerExpression indexerExpression) + { + VisitChildren (indexerExpression); + } + + public virtual void VisitInvocationExpression (InvocationExpression invocationExpression) + { + VisitChildren (invocationExpression); + } + + public virtual void VisitDirectionExpression (DirectionExpression directionExpression) + { + VisitChildren (directionExpression); + } + + public virtual void VisitMemberReferenceExpression (MemberReferenceExpression memberReferenceExpression) + { + VisitChildren (memberReferenceExpression); + } + + public virtual void VisitNullReferenceExpression (NullReferenceExpression nullReferenceExpression) + { + VisitChildren (nullReferenceExpression); + } + + public virtual void VisitObjectCreateExpression (ObjectCreateExpression objectCreateExpression) + { + VisitChildren (objectCreateExpression); + } + + public virtual void VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression) + { + VisitChildren (anonymousTypeCreateExpression); + } + + public virtual void VisitArrayCreateExpression (ArrayCreateExpression arrayCreateExpression) + { + VisitChildren (arrayCreateExpression); + } + + public virtual void VisitParenthesizedExpression (ParenthesizedExpression parenthesizedExpression) + { + VisitChildren (parenthesizedExpression); + } + + public virtual void VisitPointerReferenceExpression (PointerReferenceExpression pointerReferenceExpression) + { + VisitChildren (pointerReferenceExpression); + } + + public virtual void VisitPrimitiveExpression(PrimitiveExpression primitiveExpression) + { + VisitChildren (primitiveExpression); + } + + public virtual void VisitSizeOfExpression (SizeOfExpression sizeOfExpression) + { + VisitChildren (sizeOfExpression); + } + + public virtual void VisitStackAllocExpression (StackAllocExpression stackAllocExpression) + { + VisitChildren (stackAllocExpression); + } + + public virtual void VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression) + { + VisitChildren (thisReferenceExpression); + } + + public virtual void VisitTypeOfExpression (TypeOfExpression typeOfExpression) + { + VisitChildren (typeOfExpression); + } + + public virtual void VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression) + { + VisitChildren (typeReferenceExpression); + } + + public virtual void VisitUnaryOperatorExpression (UnaryOperatorExpression unaryOperatorExpression) + { + VisitChildren (unaryOperatorExpression); + } + + public virtual void VisitUncheckedExpression (UncheckedExpression uncheckedExpression) + { + VisitChildren (uncheckedExpression); + } + + public virtual void VisitQueryExpression(QueryExpression queryExpression) + { + VisitChildren (queryExpression); + } + + public virtual void VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause) + { + VisitChildren (queryContinuationClause); + } + + public virtual void VisitQueryFromClause(QueryFromClause queryFromClause) + { + VisitChildren (queryFromClause); + } + + public virtual void VisitQueryLetClause(QueryLetClause queryLetClause) + { + VisitChildren (queryLetClause); + } + + public virtual void VisitQueryWhereClause(QueryWhereClause queryWhereClause) + { + VisitChildren (queryWhereClause); + } + + public virtual void VisitQueryJoinClause(QueryJoinClause queryJoinClause) + { + VisitChildren (queryJoinClause); + } + + public virtual void VisitQueryOrderClause(QueryOrderClause queryOrderClause) + { + VisitChildren (queryOrderClause); + } + + public virtual void VisitQueryOrdering(QueryOrdering queryOrdering) + { + VisitChildren (queryOrdering); + } + + public virtual void VisitQuerySelectClause(QuerySelectClause querySelectClause) + { + VisitChildren (querySelectClause); + } + + public virtual void VisitQueryGroupClause(QueryGroupClause queryGroupClause) + { + VisitChildren (queryGroupClause); + } + + public virtual void VisitAsExpression (AsExpression asExpression) + { + VisitChildren (asExpression); + } + + public virtual void VisitIsExpression (IsExpression isExpression) + { + VisitChildren (isExpression); + } + + public virtual void VisitDefaultValueExpression (DefaultValueExpression defaultValueExpression) + { + VisitChildren (defaultValueExpression); + } + + public virtual void VisitUndocumentedExpression (UndocumentedExpression undocumentedExpression) + { + VisitChildren (undocumentedExpression); + } + + public virtual void VisitArrayInitializerExpression (ArrayInitializerExpression arrayInitializerExpression) + { + VisitChildren (arrayInitializerExpression); + } + + public virtual void VisitArraySpecifier (ArraySpecifier arraySpecifier) + { + VisitChildren (arraySpecifier); + } + + public virtual void VisitNamedArgumentExpression (NamedArgumentExpression namedArgumentExpression) + { + VisitChildren (namedArgumentExpression); + } + + public virtual void VisitNamedExpression (NamedExpression namedExpression) + { + VisitChildren (namedExpression); + } + + public virtual void VisitErrorNode(AstNode errorNode) + { + VisitChildren(errorNode); + } + + public virtual void VisitPatternPlaceholder(AstNode placeholder, PatternMatching.Pattern pattern) + { + VisitChildren (placeholder); + } + } + + /// + /// AST visitor with a default implementation that visits all node depth-first. + /// + public abstract class DepthFirstAstVisitor : IAstVisitor + { + protected virtual T VisitChildren (AstNode node) + { + AstNode next; + for (var child = node.FirstChild; child != null; child = next) { + // Store next to allow the loop to continue + // if the visitor removes/replaces child. + next = child.NextSibling; + child.AcceptVisitor (this); + } + return default (T); + } + + public virtual T VisitNullNode(AstNode nullNode) + { + // Should we call VisitChildren here? + // We usually want to ignore null nodes. + // Older NR versions (before VisitNullNode was introduced) didn't call VisitChildren() with null nodes; + // so changing this might break VisitChildren() overrides that expect the node to be part of the AST. + return default (T); + } + + public virtual T VisitSyntaxTree (SyntaxTree unit) + { + return VisitChildren (unit); + } + + public virtual T VisitComment (Comment comment) + { + return VisitChildren (comment); + } + + public virtual T VisitNewLine(NewLineNode newLineNode) + { + return VisitChildren(newLineNode); + } + + public virtual T VisitWhitespace(WhitespaceNode whitespaceNode) + { + return VisitChildren(whitespaceNode); + } + + public virtual T VisitText(TextNode textNode) + { + return VisitChildren(textNode); + } + + public virtual T VisitDocumentationReference (DocumentationReference documentationReference) + { + return VisitChildren (documentationReference); + } + + public virtual T VisitPreProcessorDirective (PreProcessorDirective preProcessorDirective) + { + return VisitChildren (preProcessorDirective); + } + + public virtual T VisitIdentifier (Identifier identifier) + { + return VisitChildren (identifier); + } + + public virtual T VisitCSharpTokenNode (CSharpTokenNode token) + { + return VisitChildren (token); + } + + public virtual T VisitPrimitiveType (PrimitiveType primitiveType) + { + return VisitChildren (primitiveType); + } + + public virtual T VisitComposedType (ComposedType composedType) + { + return VisitChildren (composedType); + } + + public virtual T VisitSimpleType(SimpleType simpleType) + { + return VisitChildren (simpleType); + } + + public virtual T VisitMemberType(MemberType memberType) + { + return VisitChildren (memberType); + } + + public virtual T VisitAttribute (Attribute attribute) + { + return VisitChildren (attribute); + } + + public virtual T VisitAttributeSection (AttributeSection attributeSection) + { + return VisitChildren (attributeSection); + } + + public virtual T VisitDelegateDeclaration (DelegateDeclaration delegateDeclaration) + { + return VisitChildren (delegateDeclaration); + } + + public virtual T VisitNamespaceDeclaration (NamespaceDeclaration namespaceDeclaration) + { + return VisitChildren (namespaceDeclaration); + } + + public virtual T VisitTypeDeclaration (TypeDeclaration typeDeclaration) + { + return VisitChildren (typeDeclaration); + } + + public virtual T VisitTypeParameterDeclaration (TypeParameterDeclaration typeParameterDeclaration) + { + return VisitChildren (typeParameterDeclaration); + } + + public virtual T VisitEnumMemberDeclaration (EnumMemberDeclaration enumMemberDeclaration) + { + return VisitChildren (enumMemberDeclaration); + } + + public virtual T VisitUsingDeclaration (UsingDeclaration usingDeclaration) + { + return VisitChildren (usingDeclaration); + } + + public virtual T VisitUsingAliasDeclaration (UsingAliasDeclaration usingDeclaration) + { + return VisitChildren (usingDeclaration); + } + + public virtual T VisitExternAliasDeclaration(ExternAliasDeclaration externAliasDeclaration) + { + return VisitChildren (externAliasDeclaration); + } + + public virtual T VisitConstructorDeclaration (ConstructorDeclaration constructorDeclaration) + { + return VisitChildren (constructorDeclaration); + } + + public virtual T VisitConstructorInitializer (ConstructorInitializer constructorInitializer) + { + return VisitChildren (constructorInitializer); + } + + public virtual T VisitDestructorDeclaration (DestructorDeclaration destructorDeclaration) + { + return VisitChildren (destructorDeclaration); + } + + public virtual T VisitEventDeclaration (EventDeclaration eventDeclaration) + { + return VisitChildren (eventDeclaration); + } + + public virtual T VisitCustomEventDeclaration (CustomEventDeclaration eventDeclaration) + { + return VisitChildren (eventDeclaration); + } + + public virtual T VisitFieldDeclaration (FieldDeclaration fieldDeclaration) + { + return VisitChildren (fieldDeclaration); + } + + public virtual T VisitFixedFieldDeclaration (FixedFieldDeclaration fixedFieldDeclaration) + { + return VisitChildren (fixedFieldDeclaration); + } + + public virtual T VisitFixedVariableInitializer (FixedVariableInitializer fixedVariableInitializer) + { + return VisitChildren (fixedVariableInitializer); + } + + public virtual T VisitIndexerDeclaration (IndexerDeclaration indexerDeclaration) + { + return VisitChildren (indexerDeclaration); + } + + public virtual T VisitMethodDeclaration (MethodDeclaration methodDeclaration) + { + return VisitChildren (methodDeclaration); + } + + public virtual T VisitOperatorDeclaration (OperatorDeclaration operatorDeclaration) + { + return VisitChildren (operatorDeclaration); + } + + public virtual T VisitPropertyDeclaration (PropertyDeclaration propertyDeclaration) + { + return VisitChildren (propertyDeclaration); + } + + public virtual T VisitAccessor (Accessor accessor) + { + return VisitChildren (accessor); + } + + public virtual T VisitVariableInitializer (VariableInitializer variableInitializer) + { + return VisitChildren (variableInitializer); + } + + public virtual T VisitParameterDeclaration (ParameterDeclaration parameterDeclaration) + { + return VisitChildren (parameterDeclaration); + } + + public virtual T VisitConstraint (Constraint constraint) + { + return VisitChildren (constraint); + } + + public virtual T VisitBlockStatement (BlockStatement blockStatement) + { + return VisitChildren (blockStatement); + } + + public virtual T VisitExpressionStatement (ExpressionStatement expressionStatement) + { + return VisitChildren (expressionStatement); + } + + public virtual T VisitBreakStatement (BreakStatement breakStatement) + { + return VisitChildren (breakStatement); + } + + public virtual T VisitCheckedStatement (CheckedStatement checkedStatement) + { + return VisitChildren (checkedStatement); + } + + public virtual T VisitContinueStatement (ContinueStatement continueStatement) + { + return VisitChildren (continueStatement); + } + + public virtual T VisitDoWhileStatement (DoWhileStatement doWhileStatement) + { + return VisitChildren (doWhileStatement); + } + + public virtual T VisitEmptyStatement (EmptyStatement emptyStatement) + { + return VisitChildren (emptyStatement); + } + + public virtual T VisitFixedStatement (FixedStatement fixedStatement) + { + return VisitChildren (fixedStatement); + } + + public virtual T VisitForeachStatement (ForeachStatement foreachStatement) + { + return VisitChildren (foreachStatement); + } + + public virtual T VisitForStatement (ForStatement forStatement) + { + return VisitChildren (forStatement); + } + + public virtual T VisitGotoCaseStatement (GotoCaseStatement gotoCaseStatement) + { + return VisitChildren (gotoCaseStatement); + } + + public virtual T VisitGotoDefaultStatement (GotoDefaultStatement gotoDefaultStatement) + { + return VisitChildren (gotoDefaultStatement); + } + + public virtual T VisitGotoStatement (GotoStatement gotoStatement) + { + return VisitChildren (gotoStatement); + } + + public virtual T VisitIfElseStatement (IfElseStatement ifElseStatement) + { + return VisitChildren (ifElseStatement); + } + + public virtual T VisitLabelStatement (LabelStatement labelStatement) + { + return VisitChildren (labelStatement); + } + + public virtual T VisitLockStatement (LockStatement lockStatement) + { + return VisitChildren (lockStatement); + } + + public virtual T VisitReturnStatement (ReturnStatement returnStatement) + { + return VisitChildren (returnStatement); + } + + public virtual T VisitSwitchStatement (SwitchStatement switchStatement) + { + return VisitChildren (switchStatement); + } + + public virtual T VisitSwitchSection (SwitchSection switchSection) + { + return VisitChildren (switchSection); + } + + public virtual T VisitCaseLabel (CaseLabel caseLabel) + { + return VisitChildren (caseLabel); + } + + public virtual T VisitThrowStatement (ThrowStatement throwStatement) + { + return VisitChildren (throwStatement); + } + + public virtual T VisitTryCatchStatement (TryCatchStatement tryCatchStatement) + { + return VisitChildren (tryCatchStatement); + } + + public virtual T VisitCatchClause (CatchClause catchClause) + { + return VisitChildren (catchClause); + } + + public virtual T VisitUncheckedStatement (UncheckedStatement uncheckedStatement) + { + return VisitChildren (uncheckedStatement); + } + + public virtual T VisitUnsafeStatement (UnsafeStatement unsafeStatement) + { + return VisitChildren (unsafeStatement); + } + + public virtual T VisitUsingStatement (UsingStatement usingStatement) + { + return VisitChildren (usingStatement); + } + + public virtual T VisitVariableDeclarationStatement (VariableDeclarationStatement variableDeclarationStatement) + { + return VisitChildren (variableDeclarationStatement); + } + + public virtual T VisitWhileStatement (WhileStatement whileStatement) + { + return VisitChildren (whileStatement); + } + + public virtual T VisitYieldBreakStatement (YieldBreakStatement yieldBreakStatement) + { + return VisitChildren (yieldBreakStatement); + } + + public virtual T VisitYieldReturnStatement (YieldReturnStatement yieldReturnStatement) + { + return VisitChildren (yieldReturnStatement); + } + + public virtual T VisitAnonymousMethodExpression (AnonymousMethodExpression anonymousMethodExpression) + { + return VisitChildren (anonymousMethodExpression); + } + + public virtual T VisitLambdaExpression (LambdaExpression lambdaExpression) + { + return VisitChildren (lambdaExpression); + } + + public virtual T VisitAssignmentExpression (AssignmentExpression assignmentExpression) + { + return VisitChildren (assignmentExpression); + } + + public virtual T VisitBaseReferenceExpression (BaseReferenceExpression baseReferenceExpression) + { + return VisitChildren (baseReferenceExpression); + } + + public virtual T VisitBinaryOperatorExpression (BinaryOperatorExpression binaryOperatorExpression) + { + return VisitChildren (binaryOperatorExpression); + } + + public virtual T VisitCastExpression (CastExpression castExpression) + { + return VisitChildren (castExpression); + } + + public virtual T VisitCheckedExpression (CheckedExpression checkedExpression) + { + return VisitChildren (checkedExpression); + } + + public virtual T VisitConditionalExpression (ConditionalExpression conditionalExpression) + { + return VisitChildren (conditionalExpression); + } + + public virtual T VisitIdentifierExpression (IdentifierExpression identifierExpression) + { + return VisitChildren (identifierExpression); + } + + public virtual T VisitIndexerExpression (IndexerExpression indexerExpression) + { + return VisitChildren (indexerExpression); + } + + public virtual T VisitInvocationExpression (InvocationExpression invocationExpression) + { + return VisitChildren (invocationExpression); + } + + public virtual T VisitDirectionExpression (DirectionExpression directionExpression) + { + return VisitChildren (directionExpression); + } + + public virtual T VisitMemberReferenceExpression (MemberReferenceExpression memberReferenceExpression) + { + return VisitChildren (memberReferenceExpression); + } + + public virtual T VisitNullReferenceExpression (NullReferenceExpression nullReferenceExpression) + { + return VisitChildren (nullReferenceExpression); + } + + public virtual T VisitObjectCreateExpression (ObjectCreateExpression objectCreateExpression) + { + return VisitChildren (objectCreateExpression); + } + + public virtual T VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression) + { + return VisitChildren (anonymousTypeCreateExpression); + } + + public virtual T VisitArrayCreateExpression (ArrayCreateExpression arrayCreateExpression) + { + return VisitChildren (arrayCreateExpression); + } + + public virtual T VisitParenthesizedExpression (ParenthesizedExpression parenthesizedExpression) + { + return VisitChildren (parenthesizedExpression); + } + + public virtual T VisitPointerReferenceExpression (PointerReferenceExpression pointerReferenceExpression) + { + return VisitChildren (pointerReferenceExpression); + } + + public virtual T VisitPrimitiveExpression(PrimitiveExpression primitiveExpression) + { + return VisitChildren (primitiveExpression); + } + + public virtual T VisitSizeOfExpression (SizeOfExpression sizeOfExpression) + { + return VisitChildren (sizeOfExpression); + } + + public virtual T VisitStackAllocExpression (StackAllocExpression stackAllocExpression) + { + return VisitChildren (stackAllocExpression); + } + + public virtual T VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression) + { + return VisitChildren (thisReferenceExpression); + } + + public virtual T VisitTypeOfExpression (TypeOfExpression typeOfExpression) + { + return VisitChildren (typeOfExpression); + } + + public virtual T VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression) + { + return VisitChildren (typeReferenceExpression); + } + + public virtual T VisitUnaryOperatorExpression (UnaryOperatorExpression unaryOperatorExpression) + { + return VisitChildren (unaryOperatorExpression); + } + + public virtual T VisitUncheckedExpression (UncheckedExpression uncheckedExpression) + { + return VisitChildren (uncheckedExpression); + } + + public virtual T VisitQueryExpression(QueryExpression queryExpression) + { + return VisitChildren (queryExpression); + } + + public virtual T VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause) + { + return VisitChildren (queryContinuationClause); + } + + public virtual T VisitQueryFromClause(QueryFromClause queryFromClause) + { + return VisitChildren (queryFromClause); + } + + public virtual T VisitQueryLetClause(QueryLetClause queryLetClause) + { + return VisitChildren (queryLetClause); + } + + public virtual T VisitQueryWhereClause(QueryWhereClause queryWhereClause) + { + return VisitChildren (queryWhereClause); + } + + public virtual T VisitQueryJoinClause(QueryJoinClause queryJoinClause) + { + return VisitChildren (queryJoinClause); + } + + public virtual T VisitQueryOrderClause(QueryOrderClause queryOrderClause) + { + return VisitChildren (queryOrderClause); + } + + public virtual T VisitQueryOrdering(QueryOrdering queryOrdering) + { + return VisitChildren (queryOrdering); + } + + public virtual T VisitQuerySelectClause(QuerySelectClause querySelectClause) + { + return VisitChildren (querySelectClause); + } + + public virtual T VisitQueryGroupClause(QueryGroupClause queryGroupClause) + { + return VisitChildren (queryGroupClause); + } + + public virtual T VisitAsExpression (AsExpression asExpression) + { + return VisitChildren (asExpression); + } + + public virtual T VisitIsExpression (IsExpression isExpression) + { + return VisitChildren (isExpression); + } + + public virtual T VisitDefaultValueExpression (DefaultValueExpression defaultValueExpression) + { + return VisitChildren (defaultValueExpression); + } + + public virtual T VisitUndocumentedExpression (UndocumentedExpression undocumentedExpression) + { + return VisitChildren (undocumentedExpression); + } + + public virtual T VisitArrayInitializerExpression (ArrayInitializerExpression arrayInitializerExpression) + { + return VisitChildren (arrayInitializerExpression); + } + + public virtual T VisitArraySpecifier (ArraySpecifier arraySpecifier) + { + return VisitChildren (arraySpecifier); + } + + public virtual T VisitNamedArgumentExpression (NamedArgumentExpression namedArgumentExpression) + { + return VisitChildren (namedArgumentExpression); + } + + public virtual T VisitNamedExpression (NamedExpression namedExpression) + { + return VisitChildren (namedExpression); + } + + public virtual T VisitErrorNode(AstNode errorNode) + { + return VisitChildren(errorNode); + } + + public virtual T VisitPatternPlaceholder(AstNode placeholder, PatternMatching.Pattern pattern) + { + return VisitChildren (placeholder); + } + } + + /// + /// AST visitor with a default implementation that visits all node depth-first. + /// + public abstract class DepthFirstAstVisitor : IAstVisitor + { + protected virtual S VisitChildren (AstNode node, T data) + { + AstNode next; + for (var child = node.FirstChild; child != null; child = next) { + // Store next to allow the loop to continue + // if the visitor removes/replaces child. + next = child.NextSibling; + child.AcceptVisitor (this, data); + } + return default (S); + } + + public virtual S VisitNullNode(AstNode nullNode, T data) + { + // Should we call VisitChildren here? + // We usually want to ignore null nodes. + // Older NR versions (before VisitNullNode was introduced) didn't call VisitChildren() with null nodes; + // so changing this might break VisitChildren() overrides that expect the node to be part of the AST. + return default (S); + } + + public virtual S VisitSyntaxTree (SyntaxTree unit, T data) + { + return VisitChildren (unit, data); + } + + public virtual S VisitComment (Comment comment, T data) + { + return VisitChildren (comment, data); + } + + public virtual S VisitNewLine(NewLineNode newLineNode, T data) + { + return VisitChildren(newLineNode, data); + } + + public virtual S VisitWhitespace(WhitespaceNode whitespaceNode, T data) + { + return VisitChildren(whitespaceNode, data); + } + + public virtual S VisitText(TextNode textNode, T data) + { + return VisitChildren(textNode, data); + } + + public virtual S VisitDocumentationReference (DocumentationReference documentationReference, T data) + { + return VisitChildren (documentationReference, data); + } + + public virtual S VisitPreProcessorDirective (PreProcessorDirective preProcessorDirective, T data) + { + return VisitChildren (preProcessorDirective, data); + } + + public virtual S VisitIdentifier (Identifier identifier, T data) + { + return VisitChildren (identifier, data); + } + + public virtual S VisitCSharpTokenNode (CSharpTokenNode token, T data) + { + return VisitChildren (token, data); + } + + public virtual S VisitPrimitiveType (PrimitiveType primitiveType, T data) + { + return VisitChildren (primitiveType, data); + } + + public virtual S VisitComposedType (ComposedType composedType, T data) + { + return VisitChildren (composedType, data); + } + + public virtual S VisitSimpleType(SimpleType simpleType, T data) + { + return VisitChildren (simpleType, data); + } + + public virtual S VisitMemberType(MemberType memberType, T data) + { + return VisitChildren (memberType, data); + } + + public virtual S VisitAttribute (Attribute attribute, T data) + { + return VisitChildren (attribute, data); + } + + public virtual S VisitAttributeSection (AttributeSection attributeSection, T data) + { + return VisitChildren (attributeSection, data); + } + + public virtual S VisitDelegateDeclaration (DelegateDeclaration delegateDeclaration, T data) + { + return VisitChildren (delegateDeclaration, data); + } + + public virtual S VisitNamespaceDeclaration (NamespaceDeclaration namespaceDeclaration, T data) + { + return VisitChildren (namespaceDeclaration, data); + } + + public virtual S VisitTypeDeclaration (TypeDeclaration typeDeclaration, T data) + { + return VisitChildren (typeDeclaration, data); + } + + public virtual S VisitTypeParameterDeclaration (TypeParameterDeclaration typeParameterDeclaration, T data) + { + return VisitChildren (typeParameterDeclaration, data); + } + + public virtual S VisitEnumMemberDeclaration (EnumMemberDeclaration enumMemberDeclaration, T data) + { + return VisitChildren (enumMemberDeclaration, data); + } + + public virtual S VisitUsingDeclaration (UsingDeclaration usingDeclaration, T data) + { + return VisitChildren (usingDeclaration, data); + } + + public virtual S VisitUsingAliasDeclaration (UsingAliasDeclaration usingDeclaration, T data) + { + return VisitChildren (usingDeclaration, data); + } + + public virtual S VisitExternAliasDeclaration(ExternAliasDeclaration externAliasDeclaration, T data) + { + return VisitChildren (externAliasDeclaration, data); + } + + public virtual S VisitConstructorDeclaration (ConstructorDeclaration constructorDeclaration, T data) + { + return VisitChildren (constructorDeclaration, data); + } + + public virtual S VisitConstructorInitializer (ConstructorInitializer constructorInitializer, T data) + { + return VisitChildren (constructorInitializer, data); + } + + public virtual S VisitDestructorDeclaration (DestructorDeclaration destructorDeclaration, T data) + { + return VisitChildren (destructorDeclaration, data); + } + + public virtual S VisitEventDeclaration (EventDeclaration eventDeclaration, T data) + { + return VisitChildren (eventDeclaration, data); + } + + public virtual S VisitCustomEventDeclaration (CustomEventDeclaration eventDeclaration, T data) + { + return VisitChildren (eventDeclaration, data); + } + + public virtual S VisitFieldDeclaration (FieldDeclaration fieldDeclaration, T data) + { + return VisitChildren (fieldDeclaration, data); + } + + public virtual S VisitFixedFieldDeclaration (FixedFieldDeclaration fixedFieldDeclaration, T data) + { + return VisitChildren (fixedFieldDeclaration, data); + } + + public virtual S VisitFixedVariableInitializer (FixedVariableInitializer fixedVariableInitializer, T data) + { + return VisitChildren (fixedVariableInitializer, data); + } + + public virtual S VisitIndexerDeclaration (IndexerDeclaration indexerDeclaration, T data) + { + return VisitChildren (indexerDeclaration, data); + } + + public virtual S VisitMethodDeclaration (MethodDeclaration methodDeclaration, T data) + { + return VisitChildren (methodDeclaration, data); + } + + public virtual S VisitOperatorDeclaration (OperatorDeclaration operatorDeclaration, T data) + { + return VisitChildren (operatorDeclaration, data); + } + + public virtual S VisitPropertyDeclaration (PropertyDeclaration propertyDeclaration, T data) + { + return VisitChildren (propertyDeclaration, data); + } + + public virtual S VisitAccessor (Accessor accessor, T data) + { + return VisitChildren (accessor, data); + } + + public virtual S VisitVariableInitializer (VariableInitializer variableInitializer, T data) + { + return VisitChildren (variableInitializer, data); + } + + public virtual S VisitParameterDeclaration (ParameterDeclaration parameterDeclaration, T data) + { + return VisitChildren (parameterDeclaration, data); + } + + public virtual S VisitConstraint (Constraint constraint, T data) + { + return VisitChildren (constraint, data); + } + + public virtual S VisitBlockStatement (BlockStatement blockStatement, T data) + { + return VisitChildren (blockStatement, data); + } + + public virtual S VisitExpressionStatement (ExpressionStatement expressionStatement, T data) + { + return VisitChildren (expressionStatement, data); + } + + public virtual S VisitBreakStatement (BreakStatement breakStatement, T data) + { + return VisitChildren (breakStatement, data); + } + + public virtual S VisitCheckedStatement (CheckedStatement checkedStatement, T data) + { + return VisitChildren (checkedStatement, data); + } + + public virtual S VisitContinueStatement (ContinueStatement continueStatement, T data) + { + return VisitChildren (continueStatement, data); + } + + public virtual S VisitDoWhileStatement (DoWhileStatement doWhileStatement, T data) + { + return VisitChildren (doWhileStatement, data); + } + + public virtual S VisitEmptyStatement (EmptyStatement emptyStatement, T data) + { + return VisitChildren (emptyStatement, data); + } + + public virtual S VisitFixedStatement (FixedStatement fixedStatement, T data) + { + return VisitChildren (fixedStatement, data); + } + + public virtual S VisitForeachStatement (ForeachStatement foreachStatement, T data) + { + return VisitChildren (foreachStatement, data); + } + + public virtual S VisitForStatement (ForStatement forStatement, T data) + { + return VisitChildren (forStatement, data); + } + + public virtual S VisitGotoCaseStatement (GotoCaseStatement gotoCaseStatement, T data) + { + return VisitChildren (gotoCaseStatement, data); + } + + public virtual S VisitGotoDefaultStatement (GotoDefaultStatement gotoDefaultStatement, T data) + { + return VisitChildren (gotoDefaultStatement, data); + } + + public virtual S VisitGotoStatement (GotoStatement gotoStatement, T data) + { + return VisitChildren (gotoStatement, data); + } + + public virtual S VisitIfElseStatement (IfElseStatement ifElseStatement, T data) + { + return VisitChildren (ifElseStatement, data); + } + + public virtual S VisitLabelStatement (LabelStatement labelStatement, T data) + { + return VisitChildren (labelStatement, data); + } + + public virtual S VisitLockStatement (LockStatement lockStatement, T data) + { + return VisitChildren (lockStatement, data); + } + + public virtual S VisitReturnStatement (ReturnStatement returnStatement, T data) + { + return VisitChildren (returnStatement, data); + } + + public virtual S VisitSwitchStatement (SwitchStatement switchStatement, T data) + { + return VisitChildren (switchStatement, data); + } + + public virtual S VisitSwitchSection (SwitchSection switchSection, T data) + { + return VisitChildren (switchSection, data); + } + + public virtual S VisitCaseLabel (CaseLabel caseLabel, T data) + { + return VisitChildren (caseLabel, data); + } + + public virtual S VisitThrowStatement (ThrowStatement throwStatement, T data) + { + return VisitChildren (throwStatement, data); + } + + public virtual S VisitTryCatchStatement (TryCatchStatement tryCatchStatement, T data) + { + return VisitChildren (tryCatchStatement, data); + } + + public virtual S VisitCatchClause (CatchClause catchClause, T data) + { + return VisitChildren (catchClause, data); + } + + public virtual S VisitUncheckedStatement (UncheckedStatement uncheckedStatement, T data) + { + return VisitChildren (uncheckedStatement, data); + } + + public virtual S VisitUnsafeStatement (UnsafeStatement unsafeStatement, T data) + { + return VisitChildren (unsafeStatement, data); + } + + public virtual S VisitUsingStatement (UsingStatement usingStatement, T data) + { + return VisitChildren (usingStatement, data); + } + + public virtual S VisitVariableDeclarationStatement (VariableDeclarationStatement variableDeclarationStatement, T data) + { + return VisitChildren (variableDeclarationStatement, data); + } + + public virtual S VisitWhileStatement (WhileStatement whileStatement, T data) + { + return VisitChildren (whileStatement, data); + } + + public virtual S VisitYieldBreakStatement (YieldBreakStatement yieldBreakStatement, T data) + { + return VisitChildren (yieldBreakStatement, data); + } + + public virtual S VisitYieldReturnStatement (YieldReturnStatement yieldReturnStatement, T data) + { + return VisitChildren (yieldReturnStatement, data); + } + + public virtual S VisitAnonymousMethodExpression (AnonymousMethodExpression anonymousMethodExpression, T data) + { + return VisitChildren (anonymousMethodExpression, data); + } + + public virtual S VisitLambdaExpression (LambdaExpression lambdaExpression, T data) + { + return VisitChildren (lambdaExpression, data); + } + + public virtual S VisitAssignmentExpression (AssignmentExpression assignmentExpression, T data) + { + return VisitChildren (assignmentExpression, data); + } + + public virtual S VisitBaseReferenceExpression (BaseReferenceExpression baseReferenceExpression, T data) + { + return VisitChildren (baseReferenceExpression, data); + } + + public virtual S VisitBinaryOperatorExpression (BinaryOperatorExpression binaryOperatorExpression, T data) + { + return VisitChildren (binaryOperatorExpression, data); + } + + public virtual S VisitCastExpression (CastExpression castExpression, T data) + { + return VisitChildren (castExpression, data); + } + + public virtual S VisitCheckedExpression (CheckedExpression checkedExpression, T data) + { + return VisitChildren (checkedExpression, data); + } + + public virtual S VisitConditionalExpression (ConditionalExpression conditionalExpression, T data) + { + return VisitChildren (conditionalExpression, data); + } + + public virtual S VisitIdentifierExpression (IdentifierExpression identifierExpression, T data) + { + return VisitChildren (identifierExpression, data); + } + + public virtual S VisitIndexerExpression (IndexerExpression indexerExpression, T data) + { + return VisitChildren (indexerExpression, data); + } + + public virtual S VisitInvocationExpression (InvocationExpression invocationExpression, T data) + { + return VisitChildren (invocationExpression, data); + } + + public virtual S VisitDirectionExpression (DirectionExpression directionExpression, T data) + { + return VisitChildren (directionExpression, data); + } + + public virtual S VisitMemberReferenceExpression (MemberReferenceExpression memberReferenceExpression, T data) + { + return VisitChildren (memberReferenceExpression, data); + } + + public virtual S VisitNullReferenceExpression (NullReferenceExpression nullReferenceExpression, T data) + { + return VisitChildren (nullReferenceExpression, data); + } + + public virtual S VisitObjectCreateExpression (ObjectCreateExpression objectCreateExpression, T data) + { + return VisitChildren (objectCreateExpression, data); + } + + public virtual S VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression, T data) + { + return VisitChildren (anonymousTypeCreateExpression, data); + } + + public virtual S VisitArrayCreateExpression (ArrayCreateExpression arrayCreateExpression, T data) + { + return VisitChildren (arrayCreateExpression, data); + } + + public virtual S VisitParenthesizedExpression (ParenthesizedExpression parenthesizedExpression, T data) + { + return VisitChildren (parenthesizedExpression, data); + } + + public virtual S VisitPointerReferenceExpression (PointerReferenceExpression pointerReferenceExpression, T data) + { + return VisitChildren (pointerReferenceExpression, data); + } + + public virtual S VisitPrimitiveExpression(PrimitiveExpression primitiveExpression, T data) + { + return VisitChildren (primitiveExpression, data); + } + + public virtual S VisitSizeOfExpression (SizeOfExpression sizeOfExpression, T data) + { + return VisitChildren (sizeOfExpression, data); + } + + public virtual S VisitStackAllocExpression (StackAllocExpression stackAllocExpression, T data) + { + return VisitChildren (stackAllocExpression, data); + } + + public virtual S VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression, T data) + { + return VisitChildren (thisReferenceExpression, data); + } + + public virtual S VisitTypeOfExpression (TypeOfExpression typeOfExpression, T data) + { + return VisitChildren (typeOfExpression, data); + } + + public virtual S VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression, T data) + { + return VisitChildren (typeReferenceExpression, data); + } + + public virtual S VisitUnaryOperatorExpression (UnaryOperatorExpression unaryOperatorExpression, T data) + { + return VisitChildren (unaryOperatorExpression, data); + } + + public virtual S VisitUncheckedExpression (UncheckedExpression uncheckedExpression, T data) + { + return VisitChildren (uncheckedExpression, data); + } + + public virtual S VisitQueryExpression(QueryExpression queryExpression, T data) + { + return VisitChildren (queryExpression, data); + } + + public virtual S VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause, T data) + { + return VisitChildren (queryContinuationClause, data); + } + + public virtual S VisitQueryFromClause(QueryFromClause queryFromClause, T data) + { + return VisitChildren (queryFromClause, data); + } + + public virtual S VisitQueryLetClause(QueryLetClause queryLetClause, T data) + { + return VisitChildren (queryLetClause, data); + } + + public virtual S VisitQueryWhereClause(QueryWhereClause queryWhereClause, T data) + { + return VisitChildren (queryWhereClause, data); + } + + public virtual S VisitQueryJoinClause(QueryJoinClause queryJoinClause, T data) + { + return VisitChildren (queryJoinClause, data); + } + + public virtual S VisitQueryOrderClause(QueryOrderClause queryOrderClause, T data) + { + return VisitChildren (queryOrderClause, data); + } + + public virtual S VisitQueryOrdering(QueryOrdering queryOrdering, T data) + { + return VisitChildren (queryOrdering, data); + } + + public virtual S VisitQuerySelectClause(QuerySelectClause querySelectClause, T data) + { + return VisitChildren (querySelectClause, data); + } + + public virtual S VisitQueryGroupClause(QueryGroupClause queryGroupClause, T data) + { + return VisitChildren (queryGroupClause, data); + } + + public virtual S VisitAsExpression (AsExpression asExpression, T data) + { + return VisitChildren (asExpression, data); + } + + public virtual S VisitIsExpression (IsExpression isExpression, T data) + { + return VisitChildren (isExpression, data); + } + + public virtual S VisitDefaultValueExpression (DefaultValueExpression defaultValueExpression, T data) + { + return VisitChildren (defaultValueExpression, data); + } + + public virtual S VisitUndocumentedExpression (UndocumentedExpression undocumentedExpression, T data) + { + return VisitChildren (undocumentedExpression, data); + } + + public virtual S VisitArrayInitializerExpression (ArrayInitializerExpression arrayInitializerExpression, T data) + { + return VisitChildren (arrayInitializerExpression, data); + } + + public virtual S VisitArraySpecifier (ArraySpecifier arraySpecifier, T data) + { + return VisitChildren (arraySpecifier, data); + } + + public virtual S VisitNamedArgumentExpression (NamedArgumentExpression namedArgumentExpression, T data) + { + return VisitChildren (namedArgumentExpression, data); + } + + public virtual S VisitNamedExpression (NamedExpression namedExpression, T data) + { + return VisitChildren (namedExpression, data); + } + + public virtual S VisitErrorNode(AstNode errorNode, T data) + { + return VisitChildren(errorNode, data); + } + + public virtual S VisitPatternPlaceholder(AstNode placeholder, PatternMatching.Pattern pattern, T data) + { + return VisitChildren (placeholder, data); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/DocumentationReference.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/DocumentationReference.cs new file mode 100644 index 000000000..633f921b2 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/DocumentationReference.cs @@ -0,0 +1,149 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Represents a 'cref' reference in XML documentation. + /// + public class DocumentationReference : AstNode + { + public static readonly Role DeclaringTypeRole = new Role("DeclaringType", AstType.Null); + public static readonly Role ConversionOperatorReturnTypeRole = new Role("ConversionOperatorReturnType", AstType.Null); + + SymbolKind symbolKind; + OperatorType operatorType; + bool hasParameterList; + + /// + /// Gets/Sets the entity type. + /// Possible values are: + /// SymbolKind.Operator for operators, + /// SymbolKind.Indexer for indexers, + /// SymbolKind.TypeDefinition for references to primitive types, + /// and SymbolKind.None for everything else. + /// + public SymbolKind SymbolKind { + get { return symbolKind; } + set { + ThrowIfFrozen(); + symbolKind = value; + } + } + + /// + /// Gets/Sets the operator type. + /// This property is only used when SymbolKind==Operator. + /// + public OperatorType OperatorType { + get { return operatorType; } + set { + ThrowIfFrozen(); + operatorType = value; + } + } + + /// + /// Gets/Sets whether a parameter list was provided. + /// + public bool HasParameterList { + get { return hasParameterList; } + set { + ThrowIfFrozen(); + hasParameterList = value; + } + } + + public override NodeType NodeType { + get { return NodeType.Unknown; } + } + + /// + /// Gets/Sets the declaring type. + /// + public AstType DeclaringType { + get { return GetChildByRole(DeclaringTypeRole); } + set { SetChildByRole(DeclaringTypeRole, value); } + } + + /// + /// Gets/sets the member name. + /// This property is only used when SymbolKind==None. + /// + public string MemberName { + get { return GetChildByRole(Roles.Identifier).Name; } + set { SetChildByRole(Roles.Identifier, Identifier.Create(value)); } + } + + /// + /// Gets/Sets the return type of conversion operators. + /// This property is only used when SymbolKind==Operator and OperatorType is explicit or implicit. + /// + public AstType ConversionOperatorReturnType { + get { return GetChildByRole(ConversionOperatorReturnTypeRole); } + set { SetChildByRole(ConversionOperatorReturnTypeRole, value); } + } + + public AstNodeCollection TypeArguments { + get { return GetChildrenByRole (Roles.TypeArgument); } + } + + public AstNodeCollection Parameters { + get { return GetChildrenByRole (Roles.Parameter); } + } + + protected internal override bool DoMatch(AstNode other, ICSharpCode.NRefactory.PatternMatching.Match match) + { + DocumentationReference o = other as DocumentationReference; + if (!(o != null && this.SymbolKind == o.SymbolKind && this.HasParameterList == o.HasParameterList)) + return false; + if (this.SymbolKind == SymbolKind.Operator) { + if (this.OperatorType != o.OperatorType) + return false; + if (this.OperatorType == OperatorType.Implicit || this.OperatorType == OperatorType.Explicit) { + if (!this.ConversionOperatorReturnType.DoMatch(o.ConversionOperatorReturnType, match)) + return false; + } + } else if (this.SymbolKind == SymbolKind.None) { + if (!MatchString(this.MemberName, o.MemberName)) + return false; + if (!this.TypeArguments.DoMatch(o.TypeArguments, match)) + return false; + } + return this.Parameters.DoMatch(o.Parameters, match); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitDocumentationReference (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitDocumentationReference (this); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitDocumentationReference (this, data); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/ErrorNode.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/ErrorNode.cs new file mode 100644 index 000000000..36d9ef6e0 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/ErrorNode.cs @@ -0,0 +1,88 @@ +// +// ErrorNode.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Xamarin (http://www.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; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Represents a parsing error in the ast. At the moment it only represents missing closing bracket. + /// This closing bracket is replaced by a node at the highest possible position. + /// (To make GetAstNodeAt (line, col) working). + /// + public class ErrorNode : AstNode + { + static TextLocation maxLoc = new TextLocation (int.MaxValue, int.MaxValue); + + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public override TextLocation StartLocation { + get { + return maxLoc; + } + } + + public override TextLocation EndLocation { + get { + return maxLoc; + } + } + + public ErrorNode () + { + } + + public override void AcceptVisitor(IAstVisitor visitor) + { + visitor.VisitErrorNode(this); + } + + public override T AcceptVisitor(IAstVisitor visitor) + { + return visitor.VisitErrorNode(this); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitErrorNode(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + var o = other as ErrorNode; + return o != null; + } + + public override string ToString(CSharpFormattingOptions formattingOptions) + { + return "[ErrorNode]"; + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/AnonymousMethodExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/AnonymousMethodExpression.cs new file mode 100644 index 000000000..e8de95431 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/AnonymousMethodExpression.cs @@ -0,0 +1,117 @@ +// +// AnonymousMethodExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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.Collections.Generic; +using System.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// [async] delegate(Parameters) {Body} + /// + public class AnonymousMethodExpression : Expression + { + public readonly static TokenRole DelegateKeywordRole = new TokenRole ("delegate"); + public readonly static TokenRole AsyncModifierRole = LambdaExpression.AsyncModifierRole; + + bool isAsync; + + public bool IsAsync { + get { return isAsync; } + set { ThrowIfFrozen(); isAsync = value; } + } + + // used to tell the difference between delegate {} and delegate () {} + bool hasParameterList; + + public bool HasParameterList { + get { return hasParameterList || Parameters.Any(); } + set { ThrowIfFrozen(); hasParameterList = value; } + } + + public CSharpTokenNode DelegateToken { + get { return GetChildByRole (DelegateKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstNodeCollection Parameters { + get { return GetChildrenByRole (Roles.Parameter); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public BlockStatement Body { + get { return GetChildByRole (Roles.Body); } + set { SetChildByRole (Roles.Body, value); } + } + + public AnonymousMethodExpression () + { + } + + public AnonymousMethodExpression (BlockStatement body, IEnumerable parameters = null) + { + if (parameters != null) { + hasParameterList = true; + foreach (var parameter in parameters) { + AddChild (parameter, Roles.Parameter); + } + } + AddChild (body, Roles.Body); + } + + public AnonymousMethodExpression (BlockStatement body, params ParameterDeclaration[] parameters) : this (body, (IEnumerable)parameters) + { + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitAnonymousMethodExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitAnonymousMethodExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitAnonymousMethodExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + AnonymousMethodExpression o = other as AnonymousMethodExpression; + return o != null && this.IsAsync == o.IsAsync && this.HasParameterList == o.HasParameterList + && this.Parameters.DoMatch(o.Parameters, match) && this.Body.DoMatch(o.Body, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/AnonymousTypeCreateExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/AnonymousTypeCreateExpression.cs new file mode 100644 index 000000000..944bf61f5 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/AnonymousTypeCreateExpression.cs @@ -0,0 +1,91 @@ +// +// AnonymousTypeCreateExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.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; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// new { [ExpressionList] } + /// + public class AnonymousTypeCreateExpression : Expression + { + public readonly static TokenRole NewKeywordRole = new TokenRole ("new"); + + public CSharpTokenNode NewToken { + get { return GetChildByRole (NewKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstNodeCollection Initializers { + get { return GetChildrenByRole (Roles.Expression); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public AnonymousTypeCreateExpression () + { + } + + public AnonymousTypeCreateExpression (IEnumerable initializers) + { + foreach (var ini in initializers) { + AddChild (ini, Roles.Expression); + } + } + + public AnonymousTypeCreateExpression (params Expression[] initializer) : this ((IEnumerable)initializer) + { + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitAnonymousTypeCreateExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitAnonymousTypeCreateExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitAnonymousTypeCreateExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + var o = other as AnonymousTypeCreateExpression; + return o != null && this.Initializers.DoMatch(o.Initializers, match); + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ArrayCreateExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ArrayCreateExpression.cs new file mode 100644 index 000000000..3720a3fc8 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ArrayCreateExpression.cs @@ -0,0 +1,80 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// new Type[Dimensions] + /// + public class ArrayCreateExpression : Expression + { + public readonly static TokenRole NewKeywordRole = new TokenRole ("new"); + public readonly static Role AdditionalArraySpecifierRole = new Role("AdditionalArraySpecifier"); + public readonly static Role InitializerRole = new Role("Initializer", ArrayInitializerExpression.Null); + + public CSharpTokenNode NewToken { + get { return GetChildByRole (NewKeywordRole); } + } + + public AstType Type { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole (Roles.Type, value); } + } + + public AstNodeCollection Arguments { + get { return GetChildrenByRole (Roles.Argument); } + } + + /// + /// Gets additional array ranks (those without size info). + /// Empty for "new int[5,1]"; will contain a single element for "new int[5][]". + /// + public AstNodeCollection AdditionalArraySpecifiers { + get { return GetChildrenByRole(AdditionalArraySpecifierRole); } + } + + public ArrayInitializerExpression Initializer { + get { return GetChildByRole (InitializerRole); } + set { SetChildByRole (InitializerRole, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitArrayCreateExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitArrayCreateExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitArrayCreateExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ArrayCreateExpression o = other as ArrayCreateExpression; + return o != null && this.Type.DoMatch(o.Type, match) && this.Arguments.DoMatch(o.Arguments, match) && this.AdditionalArraySpecifiers.DoMatch(o.AdditionalArraySpecifiers, match) && this.Initializer.DoMatch(o.Initializer, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ArrayInitializerExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ArrayInitializerExpression.cs new file mode 100644 index 000000000..fa3246f92 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ArrayInitializerExpression.cs @@ -0,0 +1,192 @@ +// +// ArrayInitializerExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// { Elements } + /// + public class ArrayInitializerExpression : Expression + { + /// + /// For ease of use purposes in the resolver the ast representation + /// of { a, b, c } is { {a}, {b}, {c} }. + /// If IsSingleElement is true then this array initializer expression is a generated one. + /// That has no meaning in the source code (and contains no brace tokens). + /// + public virtual bool IsSingleElement { + get { + return false; + } + } + + public ArrayInitializerExpression() + { + } + + public ArrayInitializerExpression(IEnumerable elements) + { + this.Elements.AddRange(elements); + } + + public ArrayInitializerExpression(params Expression[] elements) + { + this.Elements.AddRange(elements); + } + + #region Null + public new static readonly ArrayInitializerExpression Null = new NullArrayInitializerExpression (); + + sealed class NullArrayInitializerExpression : ArrayInitializerExpression + { + public override bool IsNull { + get { + return true; + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullNode(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return other == null || other.IsNull; + } + } + #endregion + + public CSharpTokenNode LBraceToken { + get { return GetChildByRole (Roles.LBrace); } + } + + public AstNodeCollection Elements { + get { return GetChildrenByRole(Roles.Expression); } + } + + public CSharpTokenNode RBraceToken { + get { return GetChildByRole (Roles.RBrace); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitArrayInitializerExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitArrayInitializerExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitArrayInitializerExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ArrayInitializerExpression o = other as ArrayInitializerExpression; + return o != null && this.Elements.DoMatch(o.Elements, match); + } + + public static ArrayInitializerExpression CreateSingleElementInitializer () + { + return new SingleArrayInitializerExpression(); + } + /// + /// Single elements in array initializers are represented with this special class. + /// + class SingleArrayInitializerExpression : ArrayInitializerExpression + { + public override bool IsSingleElement { + get { + return true; + } + } + + } + + #region PatternPlaceholder + public static implicit operator ArrayInitializerExpression(PatternMatching.Pattern pattern) + { + return pattern != null ? new PatternPlaceholder(pattern) : null; + } + + sealed class PatternPlaceholder : ArrayInitializerExpression, PatternMatching.INode + { + readonly PatternMatching.Pattern child; + + public PatternPlaceholder(PatternMatching.Pattern child) + { + this.child = child; + } + + public override NodeType NodeType { + get { return NodeType.Pattern; } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPatternPlaceholder(this, child); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPatternPlaceholder(this, child); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitPatternPlaceholder(this, child, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return child.DoMatch(other, match); + } + + bool PatternMatching.INode.DoMatchCollection(Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) + { + return child.DoMatchCollection(role, pos, match, backtrackingInfo); + } + } + #endregion + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/AsExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/AsExpression.cs new file mode 100644 index 000000000..5a7b5ac5d --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/AsExpression.cs @@ -0,0 +1,150 @@ +// +// AsExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.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.Collections.Generic; +using System; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Expression as TypeReference + /// + public class AsExpression : Expression + { + public readonly static TokenRole AsKeywordRole = new TokenRole ("as"); + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole(Roles.Expression, value); } + } + + public CSharpTokenNode AsToken { + get { return GetChildByRole (AsKeywordRole); } + } + + public AstType Type { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole(Roles.Type, value); } + } + + public AsExpression () + { + } + + public AsExpression (Expression expression, AstType type) + { + AddChild (expression, Roles.Expression); + AddChild (type, Roles.Type); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitAsExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitAsExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitAsExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + AsExpression o = other as AsExpression; + return o != null && this.Expression.DoMatch(o.Expression, match) && this.Type.DoMatch(o.Type, match); + } + + #region Builder methods + public override MemberReferenceExpression Member(string memberName) + { + return new MemberReferenceExpression { Target = this, MemberName = memberName }; + } + + public override IndexerExpression Indexer(IEnumerable arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = new ParenthesizedExpression(this); + expr.Arguments.AddRange(arguments); + return expr; + } + + public override IndexerExpression Indexer(params Expression[] arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = new ParenthesizedExpression(this); + expr.Arguments.AddRange(arguments); + return expr; + } + + public override InvocationExpression Invoke(string methodName, IEnumerable typeArguments, IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + MemberReferenceExpression mre = new MemberReferenceExpression(); + mre.Target = new ParenthesizedExpression(this); + mre.MemberName = methodName; + mre.TypeArguments.AddRange(typeArguments); + ie.Target = mre; + ie.Arguments.AddRange(arguments); + return ie; + } + + public override InvocationExpression Invoke(IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = new ParenthesizedExpression(this); + ie.Arguments.AddRange(arguments); + return ie; + } + + public override InvocationExpression Invoke(params Expression[] arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = new ParenthesizedExpression(this); + ie.Arguments.AddRange(arguments); + return ie; + } + + public override CastExpression CastTo(AstType type) + { + return new CastExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + + public override AsExpression CastAs(AstType type) + { + return new AsExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + + public override IsExpression IsType(AstType type) + { + return new IsExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + #endregion + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/AssignmentExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/AssignmentExpression.cs new file mode 100644 index 000000000..95d0cdf28 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/AssignmentExpression.cs @@ -0,0 +1,304 @@ +// +// AssignmentExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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.Linq.Expressions; +using System.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Left Operator= Right + /// + public class AssignmentExpression : Expression + { + // reuse roles from BinaryOperatorExpression + public readonly static Role LeftRole = BinaryOperatorExpression.LeftRole; + public readonly static Role RightRole = BinaryOperatorExpression.RightRole; + + public readonly static TokenRole AssignRole = new TokenRole ("="); + public readonly static TokenRole AddRole = new TokenRole ("+="); + public readonly static TokenRole SubtractRole = new TokenRole ("-="); + public readonly static TokenRole MultiplyRole = new TokenRole ("*="); + public readonly static TokenRole DivideRole = new TokenRole ("/="); + public readonly static TokenRole ModulusRole = new TokenRole ("%="); + public readonly static TokenRole ShiftLeftRole = new TokenRole ("<<="); + public readonly static TokenRole ShiftRightRole = new TokenRole (">>="); + public readonly static TokenRole BitwiseAndRole = new TokenRole ("&="); + public readonly static TokenRole BitwiseOrRole = new TokenRole ("|="); + public readonly static TokenRole ExclusiveOrRole = new TokenRole ("^="); + + public AssignmentExpression() + { + } + + public AssignmentExpression(Expression left, Expression right) + { + this.Left = left; + this.Right = right; + } + + public AssignmentExpression(Expression left, AssignmentOperatorType op, Expression right) + { + this.Left = left; + this.Operator = op; + this.Right = right; + } + + public AssignmentOperatorType Operator { + get; + set; + } + + public Expression Left { + get { return GetChildByRole (LeftRole); } + set { SetChildByRole(LeftRole, value); } + } + + public CSharpTokenNode OperatorToken { + get { return GetChildByRole (GetOperatorRole(Operator)); } + } + + public Expression Right { + get { return GetChildByRole (RightRole); } + set { SetChildByRole(RightRole, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitAssignmentExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitAssignmentExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitAssignmentExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + AssignmentExpression o = other as AssignmentExpression; + return o != null && (this.Operator == AssignmentOperatorType.Any || this.Operator == o.Operator) + && this.Left.DoMatch(o.Left, match) && this.Right.DoMatch(o.Right, match); + } + + public static TokenRole GetOperatorRole(AssignmentOperatorType op) + { + switch (op) { + case AssignmentOperatorType.Assign: + return AssignRole; + case AssignmentOperatorType.Add: + return AddRole; + case AssignmentOperatorType.Subtract: + return SubtractRole; + case AssignmentOperatorType.Multiply: + return MultiplyRole; + case AssignmentOperatorType.Divide: + return DivideRole; + case AssignmentOperatorType.Modulus: + return ModulusRole; + case AssignmentOperatorType.ShiftLeft: + return ShiftLeftRole; + case AssignmentOperatorType.ShiftRight: + return ShiftRightRole; + case AssignmentOperatorType.BitwiseAnd: + return BitwiseAndRole; + case AssignmentOperatorType.BitwiseOr: + return BitwiseOrRole; + case AssignmentOperatorType.ExclusiveOr: + return ExclusiveOrRole; + default: + throw new NotSupportedException("Invalid value for AssignmentOperatorType"); + } + } + + /// + /// Gets the binary operator for the specified compound assignment operator. + /// Returns null if 'op' is not a compound assignment. + /// + public static BinaryOperatorType? GetCorrespondingBinaryOperator(AssignmentOperatorType op) + { + switch (op) { + case AssignmentOperatorType.Assign: + return null; + case AssignmentOperatorType.Add: + return BinaryOperatorType.Add; + case AssignmentOperatorType.Subtract: + return BinaryOperatorType.Subtract; + case AssignmentOperatorType.Multiply: + return BinaryOperatorType.Multiply; + case AssignmentOperatorType.Divide: + return BinaryOperatorType.Divide; + case AssignmentOperatorType.Modulus: + return BinaryOperatorType.Modulus; + case AssignmentOperatorType.ShiftLeft: + return BinaryOperatorType.ShiftLeft; + case AssignmentOperatorType.ShiftRight: + return BinaryOperatorType.ShiftRight; + case AssignmentOperatorType.BitwiseAnd: + return BinaryOperatorType.BitwiseAnd; + case AssignmentOperatorType.BitwiseOr: + return BinaryOperatorType.BitwiseOr; + case AssignmentOperatorType.ExclusiveOr: + return BinaryOperatorType.ExclusiveOr; + default: + throw new NotSupportedException("Invalid value for AssignmentOperatorType"); + } + } + + public static ExpressionType GetLinqNodeType(AssignmentOperatorType op, bool checkForOverflow) + { + switch (op) { + case AssignmentOperatorType.Assign: + return ExpressionType.Assign; + case AssignmentOperatorType.Add: + return checkForOverflow ? ExpressionType.AddAssignChecked : ExpressionType.AddAssign; + case AssignmentOperatorType.Subtract: + return checkForOverflow ? ExpressionType.SubtractAssignChecked : ExpressionType.SubtractAssign; + case AssignmentOperatorType.Multiply: + return checkForOverflow ? ExpressionType.MultiplyAssignChecked : ExpressionType.MultiplyAssign; + case AssignmentOperatorType.Divide: + return ExpressionType.DivideAssign; + case AssignmentOperatorType.Modulus: + return ExpressionType.ModuloAssign; + case AssignmentOperatorType.ShiftLeft: + return ExpressionType.LeftShiftAssign; + case AssignmentOperatorType.ShiftRight: + return ExpressionType.RightShiftAssign; + case AssignmentOperatorType.BitwiseAnd: + return ExpressionType.AndAssign; + case AssignmentOperatorType.BitwiseOr: + return ExpressionType.OrAssign; + case AssignmentOperatorType.ExclusiveOr: + return ExpressionType.ExclusiveOrAssign; + default: + throw new NotSupportedException("Invalid value for AssignmentOperatorType"); + } + } + + #region Builder methods + public override MemberReferenceExpression Member(string memberName) + { + return new MemberReferenceExpression { Target = this, MemberName = memberName }; + } + + public override IndexerExpression Indexer(IEnumerable arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = new ParenthesizedExpression(this); + expr.Arguments.AddRange(arguments); + return expr; + } + + public override IndexerExpression Indexer(params Expression[] arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = new ParenthesizedExpression(this); + expr.Arguments.AddRange(arguments); + return expr; + } + + public override InvocationExpression Invoke(string methodName, IEnumerable typeArguments, IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + MemberReferenceExpression mre = new MemberReferenceExpression(); + mre.Target = new ParenthesizedExpression(this); + mre.MemberName = methodName; + mre.TypeArguments.AddRange(typeArguments); + ie.Target = mre; + ie.Arguments.AddRange(arguments); + return ie; + } + + public override InvocationExpression Invoke(IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = new ParenthesizedExpression(this); + ie.Arguments.AddRange(arguments); + return ie; + } + + public override InvocationExpression Invoke(params Expression[] arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = new ParenthesizedExpression(this); + ie.Arguments.AddRange(arguments); + return ie; + } + + public override CastExpression CastTo(AstType type) + { + return new CastExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + + public override AsExpression CastAs(AstType type) + { + return new AsExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + + public override IsExpression IsType(AstType type) + { + return new IsExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + #endregion + } + + public enum AssignmentOperatorType + { + /// left = right + Assign, + + /// left += right + Add, + /// left -= right + Subtract, + /// left *= right + Multiply, + /// left /= right + Divide, + /// left %= right + Modulus, + + /// left <<= right + ShiftLeft, + /// left >>= right + ShiftRight, + + /// left &= right + BitwiseAnd, + /// left |= right + BitwiseOr, + /// left ^= right + ExclusiveOr, + + /// Any operator (for pattern matching) + Any + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/BaseReferenceExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/BaseReferenceExpression.cs new file mode 100644 index 000000000..399c36c22 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/BaseReferenceExpression.cs @@ -0,0 +1,71 @@ +// +// BaseReferenceExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// base + /// + public class BaseReferenceExpression : Expression + { + public TextLocation Location { + get; + set; + } + + public override TextLocation StartLocation { + get { + return Location; + } + } + public override TextLocation EndLocation { + get { + return new TextLocation (Location.Line, Location.Column + "base".Length); + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitBaseReferenceExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitBaseReferenceExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitBaseReferenceExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + BaseReferenceExpression o = other as BaseReferenceExpression; + return o != null; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/BinaryOperatorExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/BinaryOperatorExpression.cs new file mode 100644 index 000000000..e4408e184 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/BinaryOperatorExpression.cs @@ -0,0 +1,325 @@ +// +// BinaryOperatorExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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.Linq.Expressions; +using System.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Left Operator Right + /// + public class BinaryOperatorExpression : Expression + { + public readonly static TokenRole BitwiseAndRole = new TokenRole ("&"); + public readonly static TokenRole BitwiseOrRole = new TokenRole ("|"); + public readonly static TokenRole ConditionalAndRole = new TokenRole ("&&"); + public readonly static TokenRole ConditionalOrRole = new TokenRole ("||"); + public readonly static TokenRole ExclusiveOrRole = new TokenRole ("^"); + public readonly static TokenRole GreaterThanRole = new TokenRole (">"); + public readonly static TokenRole GreaterThanOrEqualRole = new TokenRole (">="); + public readonly static TokenRole EqualityRole = new TokenRole ("=="); + public readonly static TokenRole InEqualityRole = new TokenRole ("!="); + public readonly static TokenRole LessThanRole = new TokenRole ("<"); + public readonly static TokenRole LessThanOrEqualRole = new TokenRole ("<="); + public readonly static TokenRole AddRole = new TokenRole ("+"); + public readonly static TokenRole SubtractRole = new TokenRole ("-"); + public readonly static TokenRole MultiplyRole = new TokenRole ("*"); + public readonly static TokenRole DivideRole = new TokenRole ("/"); + public readonly static TokenRole ModulusRole = new TokenRole ("%"); + public readonly static TokenRole ShiftLeftRole = new TokenRole ("<<"); + public readonly static TokenRole ShiftRightRole = new TokenRole (">>"); + public readonly static TokenRole NullCoalescingRole = new TokenRole ("??"); + + public readonly static Role LeftRole = new Role("Left", Expression.Null); + public readonly static Role RightRole = new Role("Right", Expression.Null); + + public BinaryOperatorExpression() + { + } + + public BinaryOperatorExpression(Expression left, BinaryOperatorType op, Expression right) + { + this.Left = left; + this.Operator = op; + this.Right = right; + } + + public BinaryOperatorType Operator { + get; + set; + } + + public Expression Left { + get { return GetChildByRole (LeftRole); } + set { SetChildByRole(LeftRole, value); } + } + + public CSharpTokenNode OperatorToken { + get { return GetChildByRole (GetOperatorRole (Operator)); } + } + + public Expression Right { + get { return GetChildByRole (RightRole); } + set { SetChildByRole (RightRole, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitBinaryOperatorExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitBinaryOperatorExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitBinaryOperatorExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + BinaryOperatorExpression o = other as BinaryOperatorExpression; + return o != null && (this.Operator == BinaryOperatorType.Any || this.Operator == o.Operator) + && this.Left.DoMatch(o.Left, match) && this.Right.DoMatch(o.Right, match); + } + + public static TokenRole GetOperatorRole (BinaryOperatorType op) + { + switch (op) { + case BinaryOperatorType.BitwiseAnd: + return BitwiseAndRole; + case BinaryOperatorType.BitwiseOr: + return BitwiseOrRole; + case BinaryOperatorType.ConditionalAnd: + return ConditionalAndRole; + case BinaryOperatorType.ConditionalOr: + return ConditionalOrRole; + case BinaryOperatorType.ExclusiveOr: + return ExclusiveOrRole; + case BinaryOperatorType.GreaterThan: + return GreaterThanRole; + case BinaryOperatorType.GreaterThanOrEqual: + return GreaterThanOrEqualRole; + case BinaryOperatorType.Equality: + return EqualityRole; + case BinaryOperatorType.InEquality: + return InEqualityRole; + case BinaryOperatorType.LessThan: + return LessThanRole; + case BinaryOperatorType.LessThanOrEqual: + return LessThanOrEqualRole; + case BinaryOperatorType.Add: + return AddRole; + case BinaryOperatorType.Subtract: + return SubtractRole; + case BinaryOperatorType.Multiply: + return MultiplyRole; + case BinaryOperatorType.Divide: + return DivideRole; + case BinaryOperatorType.Modulus: + return ModulusRole; + case BinaryOperatorType.ShiftLeft: + return ShiftLeftRole; + case BinaryOperatorType.ShiftRight: + return ShiftRightRole; + case BinaryOperatorType.NullCoalescing: + return NullCoalescingRole; + default: + throw new NotSupportedException("Invalid value for BinaryOperatorType"); + } + } + + public static ExpressionType GetLinqNodeType(BinaryOperatorType op, bool checkForOverflow) + { + switch (op) { + case BinaryOperatorType.BitwiseAnd: + return ExpressionType.And; + case BinaryOperatorType.BitwiseOr: + return ExpressionType.Or; + case BinaryOperatorType.ConditionalAnd: + return ExpressionType.AndAlso; + case BinaryOperatorType.ConditionalOr: + return ExpressionType.OrElse; + case BinaryOperatorType.ExclusiveOr: + return ExpressionType.ExclusiveOr; + case BinaryOperatorType.GreaterThan: + return ExpressionType.GreaterThan; + case BinaryOperatorType.GreaterThanOrEqual: + return ExpressionType.GreaterThanOrEqual; + case BinaryOperatorType.Equality: + return ExpressionType.Equal; + case BinaryOperatorType.InEquality: + return ExpressionType.NotEqual; + case BinaryOperatorType.LessThan: + return ExpressionType.LessThan; + case BinaryOperatorType.LessThanOrEqual: + return ExpressionType.LessThanOrEqual; + case BinaryOperatorType.Add: + return checkForOverflow ? ExpressionType.AddChecked : ExpressionType.Add; + case BinaryOperatorType.Subtract: + return checkForOverflow ? ExpressionType.SubtractChecked : ExpressionType.Subtract; + case BinaryOperatorType.Multiply: + return checkForOverflow ? ExpressionType.MultiplyChecked : ExpressionType.Multiply; + case BinaryOperatorType.Divide: + return ExpressionType.Divide; + case BinaryOperatorType.Modulus: + return ExpressionType.Modulo; + case BinaryOperatorType.ShiftLeft: + return ExpressionType.LeftShift; + case BinaryOperatorType.ShiftRight: + return ExpressionType.RightShift; + case BinaryOperatorType.NullCoalescing: + return ExpressionType.Coalesce; + default: + throw new NotSupportedException("Invalid value for BinaryOperatorType"); + } + } + #region Builder methods + public override MemberReferenceExpression Member(string memberName) + { + return new MemberReferenceExpression { Target = this, MemberName = memberName }; + } + + public override IndexerExpression Indexer(IEnumerable arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = new ParenthesizedExpression(this); + expr.Arguments.AddRange(arguments); + return expr; + } + + public override IndexerExpression Indexer(params Expression[] arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = new ParenthesizedExpression(this); + expr.Arguments.AddRange(arguments); + return expr; + } + + public override InvocationExpression Invoke(string methodName, IEnumerable typeArguments, IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + MemberReferenceExpression mre = new MemberReferenceExpression(); + mre.Target = new ParenthesizedExpression(this); + mre.MemberName = methodName; + mre.TypeArguments.AddRange(typeArguments); + ie.Target = mre; + ie.Arguments.AddRange(arguments); + return ie; + } + + public override InvocationExpression Invoke(IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = new ParenthesizedExpression(this); + ie.Arguments.AddRange(arguments); + return ie; + } + + public override InvocationExpression Invoke(params Expression[] arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = new ParenthesizedExpression(this); + ie.Arguments.AddRange(arguments); + return ie; + } + + public override CastExpression CastTo(AstType type) + { + return new CastExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + + public override AsExpression CastAs(AstType type) + { + return new AsExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + + public override IsExpression IsType(AstType type) + { + return new IsExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + #endregion + } + + public enum BinaryOperatorType + { + /// + /// Any binary operator (used in pattern matching) + /// + Any, + + // We avoid 'logical or' on purpose, because it's not clear if that refers to the bitwise + // or to the short-circuiting (conditional) operator: + // MCS and old NRefactory used bitwise='|', logical='||' + // but the C# spec uses logical='|', conditional='||' + /// left & right + BitwiseAnd, + /// left | right + BitwiseOr, + /// left && right + ConditionalAnd, + /// left || right + ConditionalOr, + /// left ^ right + ExclusiveOr, + + /// left > right + GreaterThan, + /// left >= right + GreaterThanOrEqual, + /// left == right + Equality, + /// left != right + InEquality, + /// left < right + LessThan, + /// left <= right + LessThanOrEqual, + + /// left + right + Add, + /// left - right + Subtract, + /// left * right + Multiply, + /// left / right + Divide, + /// left % right + Modulus, + + /// left << right + ShiftLeft, + /// left >> right + ShiftRight, + + /// left ?? right + NullCoalescing + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/CastExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/CastExpression.cs new file mode 100644 index 000000000..e771d18fe --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/CastExpression.cs @@ -0,0 +1,152 @@ +// +// CastExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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.Collections.Generic; +using System; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// (CastTo)Expression + /// + public class CastExpression : Expression + { + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstType Type { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole (Roles.Type, value); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public CastExpression () + { + } + + public CastExpression (AstType castToType, Expression expression) + { + AddChild (castToType, Roles.Type); + AddChild (expression, Roles.Expression); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitCastExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitCastExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitCastExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + CastExpression o = other as CastExpression; + return o != null && this.Type.DoMatch(o.Type, match) && this.Expression.DoMatch(o.Expression, match); + } + + #region Builder methods + public override MemberReferenceExpression Member(string memberName) + { + return new MemberReferenceExpression { Target = this, MemberName = memberName }; + } + + public override IndexerExpression Indexer(IEnumerable arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = new ParenthesizedExpression(this); + expr.Arguments.AddRange(arguments); + return expr; + } + + public override IndexerExpression Indexer(params Expression[] arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = new ParenthesizedExpression(this); + expr.Arguments.AddRange(arguments); + return expr; + } + + public override InvocationExpression Invoke(string methodName, IEnumerable typeArguments, IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + MemberReferenceExpression mre = new MemberReferenceExpression(); + mre.Target = new ParenthesizedExpression(this); + mre.MemberName = methodName; + mre.TypeArguments.AddRange(typeArguments); + ie.Target = mre; + ie.Arguments.AddRange(arguments); + return ie; + } + + public override InvocationExpression Invoke(IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = new ParenthesizedExpression(this); + ie.Arguments.AddRange(arguments); + return ie; + } + + public override InvocationExpression Invoke(params Expression[] arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = new ParenthesizedExpression(this); + ie.Arguments.AddRange(arguments); + return ie; + } + + public override CastExpression CastTo(AstType type) + { + return new CastExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + + public override AsExpression CastAs(AstType type) + { + return new AsExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + + public override IsExpression IsType(AstType type) + { + return new IsExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + #endregion + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/CheckedExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/CheckedExpression.cs new file mode 100644 index 000000000..66bdcb54d --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/CheckedExpression.cs @@ -0,0 +1,83 @@ +// +// CheckedExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// checked(Expression) + /// + public class CheckedExpression : Expression + { + public readonly static TokenRole CheckedKeywordRole = new TokenRole ("checked"); + + public CSharpTokenNode CheckedToken { + get { return GetChildByRole (CheckedKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole(Roles.Expression, value); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public CheckedExpression () + { + } + + public CheckedExpression (Expression expression) + { + AddChild (expression, Roles.Expression); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitCheckedExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitCheckedExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitCheckedExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + CheckedExpression o = other as CheckedExpression; + return o != null && this.Expression.DoMatch(o.Expression, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ConditionalExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ConditionalExpression.cs new file mode 100644 index 000000000..4367a0cce --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ConditionalExpression.cs @@ -0,0 +1,162 @@ +// +// ConditionalExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Condition ? TrueExpression : FalseExpression + /// + public class ConditionalExpression : Expression + { + public readonly static Role ConditionRole = Roles.Condition; + public readonly static TokenRole QuestionMarkRole = new TokenRole("?"); + public readonly static Role TrueRole = new Role("True", Expression.Null); + public readonly static TokenRole ColonRole = Roles.Colon; + public readonly static Role FalseRole = new Role("False", Expression.Null); + + public Expression Condition { + get { return GetChildByRole(ConditionRole); } + set { SetChildByRole(ConditionRole, value); } + } + + public CSharpTokenNode QuestionMarkToken { + get { return GetChildByRole (QuestionMarkRole); } + } + + public Expression TrueExpression { + get { return GetChildByRole(TrueRole); } + set { SetChildByRole(TrueRole, value); } + } + + public CSharpTokenNode ColonToken { + get { return GetChildByRole (ColonRole); } + } + + public Expression FalseExpression { + get { return GetChildByRole(FalseRole); } + set { SetChildByRole(FalseRole, value); } + } + + public ConditionalExpression () + { + } + + public ConditionalExpression (Expression condition, Expression trueExpression, Expression falseExpression) + { + AddChild (condition, ConditionRole); + AddChild (trueExpression, TrueRole); + AddChild (falseExpression, FalseRole); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitConditionalExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitConditionalExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitConditionalExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ConditionalExpression o = other as ConditionalExpression; + return o != null && this.Condition.DoMatch(o.Condition, match) && this.TrueExpression.DoMatch(o.TrueExpression, match) && this.FalseExpression.DoMatch(o.FalseExpression, match); + } + + #region Builder methods + public override MemberReferenceExpression Member(string memberName) + { + return new MemberReferenceExpression { Target = this, MemberName = memberName }; + } + + public override IndexerExpression Indexer(IEnumerable arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = new ParenthesizedExpression(this); + expr.Arguments.AddRange(arguments); + return expr; + } + + public override IndexerExpression Indexer(params Expression[] arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = new ParenthesizedExpression(this); + expr.Arguments.AddRange(arguments); + return expr; + } + + public override InvocationExpression Invoke(string methodName, IEnumerable typeArguments, IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + MemberReferenceExpression mre = new MemberReferenceExpression(); + mre.Target = new ParenthesizedExpression(this); + mre.MemberName = methodName; + mre.TypeArguments.AddRange(typeArguments); + ie.Target = mre; + ie.Arguments.AddRange(arguments); + return ie; + } + + public override InvocationExpression Invoke(IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = new ParenthesizedExpression(this); + ie.Arguments.AddRange(arguments); + return ie; + } + + public override InvocationExpression Invoke(params Expression[] arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = new ParenthesizedExpression(this); + ie.Arguments.AddRange(arguments); + return ie; + } + + public override CastExpression CastTo(AstType type) + { + return new CastExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + + public override AsExpression CastAs(AstType type) + { + return new AsExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + + public override IsExpression IsType(AstType type) + { + return new IsExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + #endregion + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/DefaultValueExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/DefaultValueExpression.cs new file mode 100644 index 000000000..0aab343f3 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/DefaultValueExpression.cs @@ -0,0 +1,84 @@ +// +// DefaultValueExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// default(Type) + /// + public class DefaultValueExpression : Expression + { + public readonly static TokenRole DefaultKeywordRole = new TokenRole ("default"); + + public CSharpTokenNode DefaultToken { + get { return GetChildByRole (DefaultKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstType Type { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole(Roles.Type, value); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public DefaultValueExpression () + { + } + + public DefaultValueExpression (AstType type) + { + AddChild (type, Roles.Type); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitDefaultValueExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitDefaultValueExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitDefaultValueExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + DefaultValueExpression o = other as DefaultValueExpression; + return o != null && this.Type.DoMatch(o.Type, match); + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/DirectionExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/DirectionExpression.cs new file mode 100644 index 000000000..a17c117bb --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/DirectionExpression.cs @@ -0,0 +1,89 @@ +// +// DirectionExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.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. + +namespace ICSharpCode.NRefactory.CSharp +{ + public enum FieldDirection + { + None, + Out, + Ref + } + + /// + /// ref Expression + /// + public class DirectionExpression : Expression + { + public readonly static TokenRole RefKeywordRole = new TokenRole ("ref"); + public readonly static TokenRole OutKeywordRole = new TokenRole ("out"); + + public FieldDirection FieldDirection { + get; + set; + } + + public CSharpTokenNode FieldDirectionToken { + get { return FieldDirection == ICSharpCode.NRefactory.CSharp.FieldDirection.Ref ? GetChildByRole (RefKeywordRole) : GetChildByRole (OutKeywordRole); } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public DirectionExpression () + { + } + + public DirectionExpression (FieldDirection direction, Expression expression) + { + this.FieldDirection = direction; + AddChild (expression, Roles.Expression); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitDirectionExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitDirectionExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitDirectionExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + DirectionExpression o = other as DirectionExpression; + return o != null && this.FieldDirection == o.FieldDirection && this.Expression.DoMatch(o.Expression, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ErrorExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ErrorExpression.cs new file mode 100644 index 000000000..6054792e4 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ErrorExpression.cs @@ -0,0 +1,129 @@ +// +// ErrorExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Xamarin Inc. +// +// 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + [Obsolete("This class is obsolete. Remove all referencing code.")] + public class EmptyExpression : AstNode + { + #region implemented abstract members of AstNode + + public override void AcceptVisitor(IAstVisitor visitor) + { + throw new NotImplementedException(); + } + + public override T AcceptVisitor(IAstVisitor visitor) + { + throw new NotImplementedException(); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + throw new NotImplementedException(); + } + + protected internal override bool DoMatch(AstNode other, ICSharpCode.NRefactory.PatternMatching.Match match) + { + throw new NotImplementedException(); + } + + public override NodeType NodeType { + get { + throw new NotImplementedException(); + } + } + + #endregion + + + } + + public class ErrorExpression : Expression + { + TextLocation location; + + public override TextLocation StartLocation { + get { + return location; + } + } + + public override TextLocation EndLocation { + get { + return location; + } + } + + public string Error { + get; + private set; + } + + public ErrorExpression () + { + } + + public ErrorExpression (TextLocation location) + { + this.location = location; + } + + public ErrorExpression (string error) + { + this.Error = error; + } + + public ErrorExpression (string error, TextLocation location) + { + this.location = location; + this.Error = error; + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitErrorNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitErrorNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitErrorNode(this, data); + } + + protected internal override bool DoMatch (AstNode other, PatternMatching.Match match) + { + var o = other as ErrorExpression; + return o != null; + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/Expression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/Expression.cs new file mode 100644 index 000000000..22962a606 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/Expression.cs @@ -0,0 +1,230 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Base class for expressions. + /// + /// + /// This class is useful even though it doesn't provide any additional functionality: + /// It can be used to communicate more information in APIs, e.g. "this subnode will always be an expression" + /// + public abstract class Expression : AstNode + { + #region Null + public new static readonly Expression Null = new NullExpression (); + + sealed class NullExpression : Expression + { + public override bool IsNull { + get { + return true; + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullNode(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return other == null || other.IsNull; + } + } + #endregion + + #region PatternPlaceholder + public static implicit operator Expression(PatternMatching.Pattern pattern) + { + return pattern != null ? new PatternPlaceholder(pattern) : null; + } + + sealed class PatternPlaceholder : Expression, PatternMatching.INode + { + readonly PatternMatching.Pattern child; + + public PatternPlaceholder(PatternMatching.Pattern child) + { + this.child = child; + } + + public override NodeType NodeType { + get { return NodeType.Pattern; } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPatternPlaceholder(this, child); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPatternPlaceholder(this, child); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitPatternPlaceholder(this, child, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return child.DoMatch(other, match); + } + + bool PatternMatching.INode.DoMatchCollection(Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) + { + return child.DoMatchCollection(role, pos, match, backtrackingInfo); + } + } + #endregion + + public override NodeType NodeType { + get { + return NodeType.Expression; + } + } + + public new Expression Clone() + { + return (Expression)base.Clone(); + } + + public Expression ReplaceWith(Func replaceFunction) + { + if (replaceFunction == null) + throw new ArgumentNullException("replaceFunction"); + return (Expression)base.ReplaceWith(node => replaceFunction((Expression)node)); + } + + #region Builder methods + /// + /// Builds an member reference expression using this expression as target. + /// + public virtual MemberReferenceExpression Member(string memberName) + { + return new MemberReferenceExpression { Target = this, MemberName = memberName }; + } + + /// + /// Builds an indexer expression using this expression as target. + /// + public virtual IndexerExpression Indexer(IEnumerable arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = this; + expr.Arguments.AddRange(arguments); + return expr; + } + + /// + /// Builds an indexer expression using this expression as target. + /// + public virtual IndexerExpression Indexer(params Expression[] arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = this; + expr.Arguments.AddRange(arguments); + return expr; + } + + /// + /// Builds an invocation expression using this expression as target. + /// + public virtual InvocationExpression Invoke(string methodName, IEnumerable arguments) + { + return Invoke(methodName, null, arguments); + } + + /// + /// Builds an invocation expression using this expression as target. + /// + public virtual InvocationExpression Invoke(string methodName, params Expression[] arguments) + { + return Invoke(methodName, null, arguments); + } + + /// + /// Builds an invocation expression using this expression as target. + /// + public virtual InvocationExpression Invoke(string methodName, IEnumerable typeArguments, IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + MemberReferenceExpression mre = new MemberReferenceExpression(); + mre.Target = this; + mre.MemberName = methodName; + mre.TypeArguments.AddRange(typeArguments); + ie.Target = mre; + ie.Arguments.AddRange(arguments); + return ie; + } + + /// + /// Builds an invocation expression using this expression as target. + /// + public virtual InvocationExpression Invoke(IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = this; + ie.Arguments.AddRange(arguments); + return ie; + } + + /// + /// Builds an invocation expression using this expression as target. + /// + public virtual InvocationExpression Invoke(params Expression[] arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = this; + ie.Arguments.AddRange(arguments); + return ie; + } + + public virtual CastExpression CastTo(AstType type) + { + return new CastExpression { Type = type, Expression = this }; + } + + public virtual AsExpression CastAs(AstType type) + { + return new AsExpression { Type = type, Expression = this }; + } + + public virtual IsExpression IsType(AstType type) + { + return new IsExpression { Type = type, Expression = this }; + } + #endregion + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/IdentifierExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/IdentifierExpression.cs new file mode 100644 index 000000000..0ec466c3f --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/IdentifierExpression.cs @@ -0,0 +1,93 @@ +// +// IdentifierExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class IdentifierExpression : Expression + { + public IdentifierExpression() + { + } + + public IdentifierExpression(string identifier) + { + this.Identifier = identifier; + } + + public IdentifierExpression(string identifier, TextLocation location) + { + SetChildByRole(Roles.Identifier, CSharp.Identifier.Create (identifier, location)); + } + +// public Identifier IdentifierToken { +// get { return GetChildByRole (Roles.Identifier); } +// } + + public string Identifier { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole(Roles.Identifier, CSharp.Identifier.Create (value)); + } + } + + public Identifier IdentifierToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole (Roles.Identifier, value); + } + } + + public AstNodeCollection TypeArguments { + get { return GetChildrenByRole (Roles.TypeArgument); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitIdentifierExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitIdentifierExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitIdentifierExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + IdentifierExpression o = other as IdentifierExpression; + return o != null && MatchString(this.Identifier, o.Identifier) && this.TypeArguments.DoMatch(o.TypeArguments, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/IndexerExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/IndexerExpression.cs new file mode 100644 index 000000000..cbc80c2cf --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/IndexerExpression.cs @@ -0,0 +1,92 @@ +// +// IndexerExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Target[Arguments] + /// + public class IndexerExpression : Expression + { + public Expression Target { + get { return GetChildByRole (Roles.TargetExpression); } + set { SetChildByRole(Roles.TargetExpression, value); } + } + + public CSharpTokenNode LBracketToken { + get { return GetChildByRole (Roles.LBracket); } + } + + public AstNodeCollection Arguments { + get { return GetChildrenByRole(Roles.Argument); } + } + + public CSharpTokenNode RBracketToken { + get { return GetChildByRole (Roles.RBracket); } + } + + public IndexerExpression () + { + } + + public IndexerExpression (Expression target, IEnumerable arguments) + { + AddChild (target, Roles.TargetExpression); + if (arguments != null) { + foreach (var arg in arguments) { + AddChild (arg, Roles.Argument); + } + } + } + + public IndexerExpression (Expression target, params Expression[] arguments) : this (target, (IEnumerable)arguments) + { + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitIndexerExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitIndexerExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitIndexerExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + IndexerExpression o = other as IndexerExpression; + return o != null && this.Target.DoMatch(o.Target, match) && this.Arguments.DoMatch(o.Arguments, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/InvocationExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/InvocationExpression.cs new file mode 100644 index 000000000..ad768d541 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/InvocationExpression.cs @@ -0,0 +1,92 @@ +// +// InvocationExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Target(Arguments) + /// + public class InvocationExpression : Expression + { + public Expression Target { + get { return GetChildByRole (Roles.TargetExpression); } + set { SetChildByRole(Roles.TargetExpression, value); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstNodeCollection Arguments { + get { return GetChildrenByRole(Roles.Argument); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitInvocationExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitInvocationExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitInvocationExpression (this, data); + } + + public InvocationExpression () + { + } + + public InvocationExpression (Expression target, IEnumerable arguments) + { + AddChild (target, Roles.TargetExpression); + if (arguments != null) { + foreach (var arg in arguments) { + AddChild (arg, Roles.Argument); + } + } + } + + public InvocationExpression (Expression target, params Expression[] arguments) : this (target, (IEnumerable)arguments) + { + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + InvocationExpression o = other as InvocationExpression; + return o != null && this.Target.DoMatch(o.Target, match) && this.Arguments.DoMatch(o.Arguments, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/IsExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/IsExpression.cs new file mode 100644 index 000000000..791ab25d7 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/IsExpression.cs @@ -0,0 +1,150 @@ +// +// TypeOfIsExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Expression is Type + /// + public class IsExpression : Expression + { + public readonly static TokenRole IsKeywordRole = new TokenRole ("is"); + + public Expression Expression { + get { return GetChildByRole(Roles.Expression); } + set { SetChildByRole(Roles.Expression, value); } + } + + public CSharpTokenNode IsToken { + get { return GetChildByRole (IsKeywordRole); } + } + + public AstType Type { + get { return GetChildByRole(Roles.Type); } + set { SetChildByRole(Roles.Type, value); } + } + + public IsExpression() + { + } + + public IsExpression (Expression expression, AstType type) + { + AddChild (expression, Roles.Expression); + AddChild (type, Roles.Type); + } + + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitIsExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitIsExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitIsExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + IsExpression o = other as IsExpression; + return o != null && this.Expression.DoMatch(o.Expression, match) && this.Type.DoMatch(o.Type, match); + } + + #region Builder methods + public override MemberReferenceExpression Member(string memberName) + { + return new MemberReferenceExpression { Target = this, MemberName = memberName }; + } + + public override IndexerExpression Indexer(IEnumerable arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = new ParenthesizedExpression(this); + expr.Arguments.AddRange(arguments); + return expr; + } + + public override IndexerExpression Indexer(params Expression[] arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = new ParenthesizedExpression(this); + expr.Arguments.AddRange(arguments); + return expr; + } + + public override InvocationExpression Invoke(string methodName, IEnumerable typeArguments, IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + MemberReferenceExpression mre = new MemberReferenceExpression(); + mre.Target = new ParenthesizedExpression(this); + mre.MemberName = methodName; + mre.TypeArguments.AddRange(typeArguments); + ie.Target = mre; + ie.Arguments.AddRange(arguments); + return ie; + } + + public override InvocationExpression Invoke(IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = new ParenthesizedExpression(this); + ie.Arguments.AddRange(arguments); + return ie; + } + + public override InvocationExpression Invoke(params Expression[] arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = new ParenthesizedExpression(this); + ie.Arguments.AddRange(arguments); + return ie; + } + + public override CastExpression CastTo(AstType type) + { + return new CastExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + + public override AsExpression CastAs(AstType type) + { + return new AsExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + + public override IsExpression IsType(AstType type) + { + return new IsExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + #endregion + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/LambdaExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/LambdaExpression.cs new file mode 100644 index 000000000..e85902d84 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/LambdaExpression.cs @@ -0,0 +1,89 @@ +// +// LambdaExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.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.Collections.Generic; +using System.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// [async] Parameters => Body + /// + public class LambdaExpression : Expression + { + public readonly static TokenRole AsyncModifierRole = new TokenRole ("async"); + public readonly static TokenRole ArrowRole = new TokenRole ("=>"); + public static readonly Role BodyRole = new Role("Body", AstNode.Null); + + bool isAsync; + + public bool IsAsync { + get { return isAsync; } + set { ThrowIfFrozen(); isAsync = value; } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstNodeCollection Parameters { + get { return GetChildrenByRole (Roles.Parameter); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public CSharpTokenNode ArrowToken { + get { return GetChildByRole (ArrowRole); } + } + + public AstNode Body { + get { return GetChildByRole (BodyRole); } + set { SetChildByRole (BodyRole, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitLambdaExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitLambdaExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitLambdaExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + LambdaExpression o = other as LambdaExpression; + return o != null && this.IsAsync == o.IsAsync && this.Parameters.DoMatch(o.Parameters, match) && this.Body.DoMatch(o.Body, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/MemberReferenceExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/MemberReferenceExpression.cs new file mode 100644 index 000000000..334c6a260 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/MemberReferenceExpression.cs @@ -0,0 +1,119 @@ +// +// MemberReferenceExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Target.MemberName + /// + public class MemberReferenceExpression : Expression + { + public Expression Target { + get { + return GetChildByRole(Roles.TargetExpression); + } + set { + SetChildByRole(Roles.TargetExpression, value); + } + } + + public CSharpTokenNode DotToken { + get { return GetChildByRole (Roles.Dot); } + } + + public string MemberName { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole (Roles.Identifier, Identifier.Create (value)); + } + } + + public Identifier MemberNameToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole (Roles.Identifier, value); + } + } + + public CSharpTokenNode LChevronToken { + get { return GetChildByRole (Roles.LChevron); } + } + + public AstNodeCollection TypeArguments { + get { return GetChildrenByRole (Roles.TypeArgument); } + } + + public CSharpTokenNode RChevronToken { + get { return GetChildByRole (Roles.RChevron); } + } + + public MemberReferenceExpression () + { + } + + public MemberReferenceExpression (Expression target, string memberName, IEnumerable arguments = null) + { + AddChild (target, Roles.TargetExpression); + MemberName = memberName; + if (arguments != null) { + foreach (var arg in arguments) { + AddChild (arg, Roles.TypeArgument); + } + } + } + + public MemberReferenceExpression (Expression target, string memberName, params AstType[] arguments) : this (target, memberName, (IEnumerable)arguments) + { + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitMemberReferenceExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitMemberReferenceExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitMemberReferenceExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + MemberReferenceExpression o = other as MemberReferenceExpression; + return o != null && this.Target.DoMatch(o.Target, match) && MatchString(this.MemberName, o.MemberName) && this.TypeArguments.DoMatch(o.TypeArguments, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/NamedArgumentExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/NamedArgumentExpression.cs new file mode 100644 index 000000000..6b485f015 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/NamedArgumentExpression.cs @@ -0,0 +1,87 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Represents a named argument passed to a method or attribute. + /// name: expression + /// + public class NamedArgumentExpression : Expression + { + public NamedArgumentExpression() + { + } + + public NamedArgumentExpression(string name, Expression expression) + { + this.Name = name; + this.Expression = expression; + } + + public string Name { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole(Roles.Identifier, CSharp.Identifier.Create (value)); + } + } + + public Identifier NameToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole(Roles.Identifier, value); + } + } + + public CSharpTokenNode ColonToken { + get { return GetChildByRole (Roles.Colon); } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNamedArgumentExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNamedArgumentExpression (this); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitNamedArgumentExpression(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + NamedArgumentExpression o = other as NamedArgumentExpression; + return o != null && MatchString(this.Name, o.Name) && this.Expression.DoMatch(o.Expression, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/NamedExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/NamedExpression.cs new file mode 100644 index 000000000..92bc993b1 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/NamedExpression.cs @@ -0,0 +1,97 @@ +// +// NamedExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Xamarin +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System.Collections.Generic; +using System.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// name = expression + /// This isn't the same as 'assign' even though it has the same syntax. + /// This expression is used in object initializers and for named attribute arguments [Attr(FieldName = value)]. + /// + public class NamedExpression : Expression + { + public NamedExpression() + { + } + + public NamedExpression (string name, Expression expression) + { + this.Name = name; + this.Expression = expression; + } + + public string Name { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole(Roles.Identifier, CSharp.Identifier.Create (value)); + } + } + + public Identifier NameToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole(Roles.Identifier, value); + } + } + + public CSharpTokenNode AssignToken { + get { return GetChildByRole (Roles.Assign); } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNamedExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNamedExpression (this); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitNamedExpression(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + var o = other as NamedExpression; + return o != null && MatchString(this.Name, o.Name) && this.Expression.DoMatch(o.Expression, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/NullReferenceExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/NullReferenceExpression.cs new file mode 100644 index 000000000..fbfeb6f91 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/NullReferenceExpression.cs @@ -0,0 +1,83 @@ +// +// NullReferenceExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// null + /// + public class NullReferenceExpression : Expression + { + TextLocation location; + public override TextLocation StartLocation { + get { + return location; + } + } + + internal void SetStartLocation(TextLocation value) + { + ThrowIfFrozen(); + this.location = value; + } + + public override TextLocation EndLocation { + get { + return new TextLocation (location.Line, location.Column + "null".Length); + } + } + + public NullReferenceExpression () + { + } + + public NullReferenceExpression (TextLocation location) + { + this.location = location; + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullReferenceExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullReferenceExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullReferenceExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + NullReferenceExpression o = other as NullReferenceExpression; + return o != null; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ObjectCreateExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ObjectCreateExpression.cs new file mode 100644 index 000000000..a9665f8c7 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ObjectCreateExpression.cs @@ -0,0 +1,104 @@ +// +// ObjectCreateExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// new Type(Arguments) { Initializer } + /// + public class ObjectCreateExpression : Expression + { + public readonly static TokenRole NewKeywordRole = new TokenRole ("new"); + public readonly static Role InitializerRole = ArrayCreateExpression.InitializerRole; + + public CSharpTokenNode NewToken { + get { return GetChildByRole (NewKeywordRole); } + } + + public AstType Type { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole (Roles.Type, value); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstNodeCollection Arguments { + get { return GetChildrenByRole (Roles.Argument); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public ArrayInitializerExpression Initializer { + get { return GetChildByRole (InitializerRole); } + set { SetChildByRole (InitializerRole, value); } + } + + public ObjectCreateExpression () + { + } + + public ObjectCreateExpression (AstType type, IEnumerable arguments = null) + { + AddChild (type, Roles.Type); + if (arguments != null) { + foreach (var arg in arguments) { + AddChild (arg, Roles.Argument); + } + } + } + + public ObjectCreateExpression (AstType type, params Expression[] arguments) : this (type, (IEnumerable)arguments) + { + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitObjectCreateExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitObjectCreateExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitObjectCreateExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ObjectCreateExpression o = other as ObjectCreateExpression; + return o != null && this.Type.DoMatch(o.Type, match) && this.Arguments.DoMatch(o.Arguments, match) && this.Initializer.DoMatch(o.Initializer, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ParenthesizedExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ParenthesizedExpression.cs new file mode 100644 index 000000000..7dddcb3fb --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ParenthesizedExpression.cs @@ -0,0 +1,98 @@ +// +// ParenthesizedExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// ( Expression ) + /// + public class ParenthesizedExpression : Expression + { + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public ParenthesizedExpression() + { + } + + public ParenthesizedExpression(Expression expr) + { + Expression = expr; + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitParenthesizedExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitParenthesizedExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitParenthesizedExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ParenthesizedExpression o = other as ParenthesizedExpression; + return o != null && this.Expression.DoMatch(o.Expression, match); + } + + /// + /// Gets whether the expression acts like a parenthesized expression, + /// i.e. whether information about the expected type (for lambda type inference) flows + /// into the inner expression. + /// + /// Returns true for ParenthesizedExpression, CheckedExpression or UncheckedExpression; false otherwise. + public static bool ActsAsParenthesizedExpression(AstNode expression) + { + return expression is ParenthesizedExpression || expression is CheckedExpression || expression is UncheckedExpression; + } + + /// + /// Unpacks the given expression if it is a ParenthesizedExpression, CheckedExpression or UncheckedExpression. + /// + public static Expression UnpackParenthesizedExpression(Expression expr) + { + while (ActsAsParenthesizedExpression(expr)) + expr = expr.GetChildByRole(Roles.Expression); + return expr; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/PointerReferenceExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/PointerReferenceExpression.cs new file mode 100644 index 000000000..35c4a7203 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/PointerReferenceExpression.cs @@ -0,0 +1,90 @@ +// +// PointerReferenceExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Target->MemberName + /// + public class PointerReferenceExpression : Expression + { + public readonly static TokenRole ArrowRole = new TokenRole ("->"); + + public Expression Target { + get { return GetChildByRole (Roles.TargetExpression); } + set { SetChildByRole(Roles.TargetExpression, value); } + } + + public CSharpTokenNode ArrowToken { + get { return GetChildByRole (ArrowRole); } + } + + public string MemberName { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole(Roles.Identifier, Identifier.Create (value)); + } + } + + public Identifier MemberNameToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole (Roles.Identifier, value); + } + } + + public AstNodeCollection TypeArguments { + get { return GetChildrenByRole (Roles.TypeArgument); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPointerReferenceExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPointerReferenceExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitPointerReferenceExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + PointerReferenceExpression o = other as PointerReferenceExpression; + return o != null && MatchString(this.MemberName, o.MemberName) && this.TypeArguments.DoMatch(o.TypeArguments, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/PrimitiveExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/PrimitiveExpression.cs new file mode 100644 index 000000000..adfe7c3ba --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/PrimitiveExpression.cs @@ -0,0 +1,162 @@ +// +// PrimitiveExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Represents a literal value. + /// + public class PrimitiveExpression : Expression + { + public static readonly object AnyValue = new object(); + + TextLocation startLocation; + public override TextLocation StartLocation { + get { + return startLocation; + } + } + + internal void SetStartLocation(TextLocation value) + { + ThrowIfFrozen(); + this.startLocation = value; + this.endLocation = null; + } + + string literalValue; + TextLocation? endLocation; + public override TextLocation EndLocation { + get { + if (!endLocation.HasValue) { + endLocation = value is string ? AdvanceLocation (StartLocation, literalValue ?? "") : + new TextLocation (StartLocation.Line, StartLocation.Column + (literalValue ?? "").Length); + } + return endLocation.Value; + } + } + + object value; + + public object Value { + get { return this.value; } + set { + ThrowIfFrozen(); + this.value = value; + literalValue = null; + } + } + + /// Never returns null. + public string LiteralValue { + get { return literalValue ?? ""; } + } + + /// Can be null. + public string UnsafeLiteralValue { + get { return literalValue; } + } + + public void SetValue(object value, string literalValue) + { + if (value == null) + throw new ArgumentNullException(); + ThrowIfFrozen(); + this.value = value; + this.literalValue = literalValue; + } + + public PrimitiveExpression (object value) + { + this.Value = value; + this.literalValue = null; + } + + public PrimitiveExpression (object value, string literalValue) + { + this.Value = value; + this.literalValue = literalValue; + } + + public PrimitiveExpression (object value, TextLocation startLocation, string literalValue) + { + this.Value = value; + this.startLocation = startLocation; + this.literalValue = literalValue; + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPrimitiveExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPrimitiveExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitPrimitiveExpression (this, data); + } + + unsafe static TextLocation AdvanceLocation(TextLocation startLocation, string str) + { + int line = startLocation.Line; + int col = startLocation.Column; + fixed (char* start = str) { + char* p = start; + char* endPtr = start + str.Length; + while (p < endPtr) { + var nl = NewLine.GetDelimiterLength(*p, () => { + char* nextp = p + 1; + if (nextp < endPtr) + return *nextp; + return '\0'; + }); + if (nl > 0) { + line++; + col = 1; + if (nl == 2) + p++; + } else { + col++; + } + p++; + } + } + return new TextLocation (line, col); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + PrimitiveExpression o = other as PrimitiveExpression; + return o != null && (this.Value == AnyValue || object.Equals(this.Value, o.Value)); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/QueryExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/QueryExpression.cs new file mode 100644 index 000000000..b52f50a47 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/QueryExpression.cs @@ -0,0 +1,655 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; +namespace ICSharpCode.NRefactory.CSharp +{ + public class QueryExpression : Expression + { + public static readonly Role ClauseRole = new Role("Clause"); + + #region Null + public new static readonly QueryExpression Null = new NullQueryExpression (); + + sealed class NullQueryExpression : QueryExpression + { + public override bool IsNull { + get { + return true; + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullNode(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return other == null || other.IsNull; + } + } + #endregion + + public AstNodeCollection Clauses { + get { return GetChildrenByRole(ClauseRole); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitQueryExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitQueryExpression (this); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitQueryExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + QueryExpression o = other as QueryExpression; + return o != null && !o.IsNull && this.Clauses.DoMatch(o.Clauses, match); + } + + #region Builder methods + public override MemberReferenceExpression Member(string memberName) + { + return new MemberReferenceExpression { Target = this, MemberName = memberName }; + } + + public override IndexerExpression Indexer(IEnumerable arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = new ParenthesizedExpression(this); + expr.Arguments.AddRange(arguments); + return expr; + } + + public override IndexerExpression Indexer(params Expression[] arguments) + { + IndexerExpression expr = new IndexerExpression(); + expr.Target = new ParenthesizedExpression(this); + expr.Arguments.AddRange(arguments); + return expr; + } + + public override InvocationExpression Invoke(string methodName, IEnumerable typeArguments, IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + MemberReferenceExpression mre = new MemberReferenceExpression(); + mre.Target = new ParenthesizedExpression(this); + mre.MemberName = methodName; + mre.TypeArguments.AddRange(typeArguments); + ie.Target = mre; + ie.Arguments.AddRange(arguments); + return ie; + } + + public override InvocationExpression Invoke(IEnumerable arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = new ParenthesizedExpression(this); + ie.Arguments.AddRange(arguments); + return ie; + } + + public override InvocationExpression Invoke(params Expression[] arguments) + { + InvocationExpression ie = new InvocationExpression(); + ie.Target = new ParenthesizedExpression(this); + ie.Arguments.AddRange(arguments); + return ie; + } + + public override CastExpression CastTo(AstType type) + { + return new CastExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + + public override AsExpression CastAs(AstType type) + { + return new AsExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + + public override IsExpression IsType(AstType type) + { + return new IsExpression { Type = type, Expression = new ParenthesizedExpression(this) }; + } + #endregion + } + + public abstract class QueryClause : AstNode + { + public override NodeType NodeType { + get { return NodeType.QueryClause; } + } + } + + /// + /// Represents a query continuation. + /// "(from .. select ..) into Identifier" or "(from .. group .. by ..) into Identifier" + /// Note that "join .. into .." is not a query continuation! + /// + /// This is always the first(!!) clause in a query expression. + /// The tree for "from a in b select c into d select e" looks like this: + /// new QueryExpression { + /// new QueryContinuationClause { + /// PrecedingQuery = new QueryExpression { + /// new QueryFromClause(a in b), + /// new QuerySelectClause(c) + /// }, + /// Identifier = d + /// }, + /// new QuerySelectClause(e) + /// } + /// + public class QueryContinuationClause : QueryClause + { + public static readonly Role PrecedingQueryRole = new Role("PrecedingQuery", QueryExpression.Null); + public static readonly TokenRole IntoKeywordRole = new TokenRole ("into"); + + public QueryExpression PrecedingQuery { + get { return GetChildByRole(PrecedingQueryRole); } + set { SetChildByRole(PrecedingQueryRole, value); } + } + + public CSharpTokenNode IntoKeyword { + get { return GetChildByRole (IntoKeywordRole); } + } + + public string Identifier { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole(Roles.Identifier, CSharp.Identifier.Create (value)); + } + } + + public Identifier IdentifierToken { + get { return GetChildByRole (Roles.Identifier); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitQueryContinuationClause (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitQueryContinuationClause (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitQueryContinuationClause (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + QueryContinuationClause o = other as QueryContinuationClause; + return o != null && MatchString(this.Identifier, o.Identifier) && this.PrecedingQuery.DoMatch(o.PrecedingQuery, match); + } + } + + public class QueryFromClause : QueryClause + { + public static readonly TokenRole FromKeywordRole = new TokenRole ("from"); + public static readonly TokenRole InKeywordRole = new TokenRole ("in"); + + public CSharpTokenNode FromKeyword { + get { return GetChildByRole (FromKeywordRole); } + } + + public AstType Type { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole (Roles.Type, value); } + } + + public string Identifier { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole(Roles.Identifier, CSharp.Identifier.Create (value)); + } + } + + public Identifier IdentifierToken { + get { return GetChildByRole(Roles.Identifier); } + } + + public CSharpTokenNode InKeyword { + get { return GetChildByRole (InKeywordRole); } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitQueryFromClause (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitQueryFromClause (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitQueryFromClause (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + QueryFromClause o = other as QueryFromClause; + return o != null && this.Type.DoMatch(o.Type, match) && MatchString(this.Identifier, o.Identifier) + && this.Expression.DoMatch(o.Expression, match); + } + } + + public class QueryLetClause : QueryClause + { + public readonly static TokenRole LetKeywordRole = new TokenRole ("let"); + + public CSharpTokenNode LetKeyword { + get { return GetChildByRole(LetKeywordRole); } + } + + public string Identifier { + get { + return GetChildByRole(Roles.Identifier).Name; + } + set { + SetChildByRole(Roles.Identifier, CSharp.Identifier.Create (value)); + } + } + + public Identifier IdentifierToken { + get { return GetChildByRole(Roles.Identifier); } + } + + public CSharpTokenNode AssignToken { + get { return GetChildByRole(Roles.Assign); } + } + + public Expression Expression { + get { return GetChildByRole(Roles.Expression); } + set { SetChildByRole(Roles.Expression, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitQueryLetClause (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitQueryLetClause (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitQueryLetClause (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + QueryLetClause o = other as QueryLetClause; + return o != null && MatchString(this.Identifier, o.Identifier) && this.Expression.DoMatch(o.Expression, match); + } + } + + + public class QueryWhereClause : QueryClause + { + public readonly static TokenRole WhereKeywordRole = new TokenRole ("where"); + + public CSharpTokenNode WhereKeyword { + get { return GetChildByRole (WhereKeywordRole); } + } + + public Expression Condition { + get { return GetChildByRole (Roles.Condition); } + set { SetChildByRole (Roles.Condition, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitQueryWhereClause (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitQueryWhereClause (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitQueryWhereClause (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + QueryWhereClause o = other as QueryWhereClause; + return o != null && this.Condition.DoMatch(o.Condition, match); + } + } + + /// + /// Represents a join or group join clause. + /// + public class QueryJoinClause : QueryClause + { + public static readonly TokenRole JoinKeywordRole = new TokenRole ("join"); + public static readonly Role TypeRole = Roles.Type; + public static readonly Role JoinIdentifierRole = Roles.Identifier; + public static readonly TokenRole InKeywordRole = new TokenRole ("in"); + public static readonly Role InExpressionRole = Roles.Expression; + public static readonly TokenRole OnKeywordRole = new TokenRole ("on"); + public static readonly Role OnExpressionRole = new Role("OnExpression", Expression.Null); + public static readonly TokenRole EqualsKeywordRole = new TokenRole ("equals"); + public static readonly Role EqualsExpressionRole = new Role("EqualsExpression", Expression.Null); + public static readonly TokenRole IntoKeywordRole = new TokenRole ("into"); + public static readonly Role IntoIdentifierRole = new Role("IntoIdentifier", Identifier.Null); + + public bool IsGroupJoin { + get { return !string.IsNullOrEmpty(this.IntoIdentifier); } + } + + public CSharpTokenNode JoinKeyword { + get { return GetChildByRole (JoinKeywordRole); } + } + + public AstType Type { + get { return GetChildByRole (TypeRole); } + set { SetChildByRole (TypeRole, value); } + } + + public string JoinIdentifier { + get { + return GetChildByRole(JoinIdentifierRole).Name; + } + set { + SetChildByRole(JoinIdentifierRole, Identifier.Create (value)); + } + } + + public Identifier JoinIdentifierToken { + get { return GetChildByRole(JoinIdentifierRole); } + } + + public CSharpTokenNode InKeyword { + get { return GetChildByRole (InKeywordRole); } + } + + public Expression InExpression { + get { return GetChildByRole (InExpressionRole); } + set { SetChildByRole (InExpressionRole, value); } + } + + public CSharpTokenNode OnKeyword { + get { return GetChildByRole (OnKeywordRole); } + } + + public Expression OnExpression { + get { return GetChildByRole (OnExpressionRole); } + set { SetChildByRole (OnExpressionRole, value); } + } + + public CSharpTokenNode EqualsKeyword { + get { return GetChildByRole (EqualsKeywordRole); } + } + + public Expression EqualsExpression { + get { return GetChildByRole (EqualsExpressionRole); } + set { SetChildByRole (EqualsExpressionRole, value); } + } + + public CSharpTokenNode IntoKeyword { + get { return GetChildByRole (IntoKeywordRole); } + } + + public string IntoIdentifier { + get { + return GetChildByRole (IntoIdentifierRole).Name; + } + set { + SetChildByRole(IntoIdentifierRole, Identifier.Create (value)); + } + } + + public Identifier IntoIdentifierToken { + get { return GetChildByRole(IntoIdentifierRole); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitQueryJoinClause (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitQueryJoinClause (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitQueryJoinClause (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + QueryJoinClause o = other as QueryJoinClause; + return o != null && this.IsGroupJoin == o.IsGroupJoin + && this.Type.DoMatch(o.Type, match) && MatchString(this.JoinIdentifier, o.JoinIdentifier) + && this.InExpression.DoMatch(o.InExpression, match) && this.OnExpression.DoMatch(o.OnExpression, match) + && this.EqualsExpression.DoMatch(o.EqualsExpression, match) + && MatchString(this.IntoIdentifier, o.IntoIdentifier); + } + } + + public class QueryOrderClause : QueryClause + { + public static readonly TokenRole OrderbyKeywordRole = new TokenRole ("orderby"); + public static readonly Role OrderingRole = new Role("Ordering"); + + public CSharpTokenNode OrderbyToken { + get { return GetChildByRole (OrderbyKeywordRole); } + } + + public AstNodeCollection Orderings { + get { return GetChildrenByRole (OrderingRole); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitQueryOrderClause (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitQueryOrderClause (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitQueryOrderClause (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + QueryOrderClause o = other as QueryOrderClause; + return o != null && this.Orderings.DoMatch(o.Orderings, match); + } + } + + public class QueryOrdering : AstNode + { + public readonly static TokenRole AscendingKeywordRole = new TokenRole ("ascending"); + public readonly static TokenRole DescendingKeywordRole = new TokenRole ("descending"); + + public override NodeType NodeType { + get { return NodeType.Unknown; } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public QueryOrderingDirection Direction { + get; + set; + } + + public CSharpTokenNode DirectionToken { + get { return Direction == QueryOrderingDirection.Ascending ? GetChildByRole (AscendingKeywordRole) : GetChildByRole (DescendingKeywordRole); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitQueryOrdering (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitQueryOrdering (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitQueryOrdering (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + QueryOrdering o = other as QueryOrdering; + return o != null && this.Direction == o.Direction && this.Expression.DoMatch(o.Expression, match); + } + } + + public enum QueryOrderingDirection + { + None, + Ascending, + Descending + } + + public class QuerySelectClause : QueryClause + { + public readonly static TokenRole SelectKeywordRole = new TokenRole ("select"); + + public CSharpTokenNode SelectKeyword { + get { return GetChildByRole (SelectKeywordRole); } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitQuerySelectClause (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitQuerySelectClause (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitQuerySelectClause (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + QuerySelectClause o = other as QuerySelectClause; + return o != null && this.Expression.DoMatch(o.Expression, match); + } + } + + public class QueryGroupClause : QueryClause + { + public static readonly TokenRole GroupKeywordRole = new TokenRole ("group"); + public static readonly Role ProjectionRole = new Role("Projection", Expression.Null); + public static readonly TokenRole ByKeywordRole = new TokenRole ("by"); + public static readonly Role KeyRole = new Role("Key", Expression.Null); + + public CSharpTokenNode GroupKeyword { + get { return GetChildByRole (GroupKeywordRole); } + } + + public Expression Projection { + get { return GetChildByRole (ProjectionRole); } + set { SetChildByRole (ProjectionRole, value); } + } + + public CSharpTokenNode ByKeyword { + get { return GetChildByRole (ByKeywordRole); } + } + + public Expression Key { + get { return GetChildByRole (KeyRole); } + set { SetChildByRole (KeyRole, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitQueryGroupClause (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitQueryGroupClause (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitQueryGroupClause (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + QueryGroupClause o = other as QueryGroupClause; + return o != null && this.Projection.DoMatch(o.Projection, match) && this.Key.DoMatch(o.Key, match); + } + } +} \ No newline at end of file diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/SizeOfExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/SizeOfExpression.cs new file mode 100644 index 000000000..8a794960c --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/SizeOfExpression.cs @@ -0,0 +1,83 @@ +// +// SizeOfExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// sizeof(Type) + /// + public class SizeOfExpression : Expression + { + public readonly static TokenRole SizeofKeywordRole = new TokenRole ("sizeof"); + + public CSharpTokenNode SizeOfToken { + get { return GetChildByRole (SizeofKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstType Type { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole(Roles.Type, value); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public SizeOfExpression () + { + } + + public SizeOfExpression (AstType type) + { + AddChild (type, Roles.Type); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitSizeOfExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitSizeOfExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitSizeOfExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + SizeOfExpression o = other as SizeOfExpression; + return o != null && this.Type.DoMatch(o.Type, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/StackAllocExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/StackAllocExpression.cs new file mode 100644 index 000000000..ad9f58c1b --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/StackAllocExpression.cs @@ -0,0 +1,79 @@ +// +// StackAllocExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// stackalloc Type[Count] + /// + public class StackAllocExpression : Expression + { + public readonly static TokenRole StackallocKeywordRole = new TokenRole ("stackalloc"); + + public CSharpTokenNode StackAllocToken { + get { return GetChildByRole (StackallocKeywordRole); } + } + + public AstType Type { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole(Roles.Type, value); } + } + + public CSharpTokenNode LBracketToken { + get { return GetChildByRole (Roles.LBracket); } + } + + public Expression CountExpression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public CSharpTokenNode RBracketToken { + get { return GetChildByRole (Roles.RBracket); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitStackAllocExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitStackAllocExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitStackAllocExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + StackAllocExpression o = other as StackAllocExpression; + return o != null && this.Type.DoMatch(o.Type, match) && this.CountExpression.DoMatch(o.CountExpression, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ThisReferenceExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ThisReferenceExpression.cs new file mode 100644 index 000000000..481ef6658 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ThisReferenceExpression.cs @@ -0,0 +1,71 @@ +// +// ThisReferenceExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// this + /// + public class ThisReferenceExpression : Expression + { + public TextLocation Location { + get; + set; + } + + public override TextLocation StartLocation { + get { + return Location; + } + } + public override TextLocation EndLocation { + get { + return new TextLocation (Location.Line, Location.Column + "this".Length); + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitThisReferenceExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitThisReferenceExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitThisReferenceExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ThisReferenceExpression o = other as ThisReferenceExpression; + return o != null; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/TypeOfExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/TypeOfExpression.cs new file mode 100644 index 000000000..fd2e93ab8 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/TypeOfExpression.cs @@ -0,0 +1,84 @@ +// +// TypeOfExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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. + + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// typeof(Type) + /// + public class TypeOfExpression : Expression + { + public readonly static TokenRole TypeofKeywordRole = new TokenRole ("typeof"); + + public CSharpTokenNode TypeOfToken { + get { return GetChildByRole (TypeofKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstType Type { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole(Roles.Type, value); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public TypeOfExpression () + { + } + + public TypeOfExpression (AstType type) + { + AddChild (type, Roles.Type); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitTypeOfExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitTypeOfExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitTypeOfExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + TypeOfExpression o = other as TypeOfExpression; + return o != null && this.Type.DoMatch(o.Type, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/TypeReferenceExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/TypeReferenceExpression.cs new file mode 100644 index 000000000..84b2d60dc --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/TypeReferenceExpression.cs @@ -0,0 +1,64 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Represents an AstType as an expression. + /// This is used when calling a method on a primitive type: "int.Parse()" + /// + public class TypeReferenceExpression : Expression + { + public AstType Type { + get { return GetChildByRole(Roles.Type); } + set { SetChildByRole(Roles.Type, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitTypeReferenceExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitTypeReferenceExpression (this); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitTypeReferenceExpression(this, data); + } + + public TypeReferenceExpression () + { + } + + public TypeReferenceExpression (AstType type) + { + AddChild (type, Roles.Type); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + TypeReferenceExpression o = other as TypeReferenceExpression; + return o != null && this.Type.DoMatch(o.Type, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/UnaryOperatorExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/UnaryOperatorExpression.cs new file mode 100644 index 000000000..878d6132f --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/UnaryOperatorExpression.cs @@ -0,0 +1,181 @@ +// +// UnaryOperatorExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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.Linq.Expressions; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Operator Expression + /// + public class UnaryOperatorExpression : Expression + { + public readonly static TokenRole NotRole = new TokenRole ("!"); + public readonly static TokenRole BitNotRole = new TokenRole ("~"); + public readonly static TokenRole MinusRole = new TokenRole ("-"); + public readonly static TokenRole PlusRole = new TokenRole ("+"); + public readonly static TokenRole IncrementRole = new TokenRole ("++"); + public readonly static TokenRole DecrementRole = new TokenRole ("--"); + public readonly static TokenRole DereferenceRole = new TokenRole ("*"); + public readonly static TokenRole AddressOfRole = new TokenRole ("&"); + public readonly static TokenRole AwaitRole = new TokenRole ("await"); + + public UnaryOperatorExpression() + { + } + + public UnaryOperatorExpression(UnaryOperatorType op, Expression expression) + { + this.Operator = op; + this.Expression = expression; + } + + public UnaryOperatorType Operator { + get; + set; + } + + public CSharpTokenNode OperatorToken { + get { return GetChildByRole (GetOperatorRole (Operator)); } + } + + static Expression NoUnaryExpressionError = new ErrorExpression ("No unary expression"); + public Expression Expression { + get { return GetChildByRole (Roles.Expression) ?? NoUnaryExpressionError; } + set { SetChildByRole (Roles.Expression, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitUnaryOperatorExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitUnaryOperatorExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitUnaryOperatorExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + UnaryOperatorExpression o = other as UnaryOperatorExpression; + return o != null && (this.Operator == UnaryOperatorType.Any || this.Operator == o.Operator) + && this.Expression.DoMatch(o.Expression, match); + } + + public static TokenRole GetOperatorRole(UnaryOperatorType op) + { + switch (op) { + case UnaryOperatorType.Not: + return NotRole; + case UnaryOperatorType.BitNot: + return BitNotRole; + case UnaryOperatorType.Minus: + return MinusRole; + case UnaryOperatorType.Plus: + return PlusRole; + case UnaryOperatorType.Increment: + case UnaryOperatorType.PostIncrement: + return IncrementRole; + case UnaryOperatorType.PostDecrement: + case UnaryOperatorType.Decrement: + return DecrementRole; + case UnaryOperatorType.Dereference: + return DereferenceRole; + case UnaryOperatorType.AddressOf: + return AddressOfRole; + case UnaryOperatorType.Await: + return AwaitRole; + default: + throw new NotSupportedException("Invalid value for UnaryOperatorType"); + } + } + + public static ExpressionType GetLinqNodeType(UnaryOperatorType op, bool checkForOverflow) + { + switch (op) { + case UnaryOperatorType.Not: + return ExpressionType.Not; + case UnaryOperatorType.BitNot: + return ExpressionType.OnesComplement; + case UnaryOperatorType.Minus: + return checkForOverflow ? ExpressionType.NegateChecked : ExpressionType.Negate; + case UnaryOperatorType.Plus: + return ExpressionType.UnaryPlus; + case UnaryOperatorType.Increment: + return ExpressionType.PreIncrementAssign; + case UnaryOperatorType.Decrement: + return ExpressionType.PreDecrementAssign; + case UnaryOperatorType.PostIncrement: + return ExpressionType.PostIncrementAssign; + case UnaryOperatorType.PostDecrement: + return ExpressionType.PostDecrementAssign; + case UnaryOperatorType.Dereference: + case UnaryOperatorType.AddressOf: + case UnaryOperatorType.Await: + return ExpressionType.Extension; + default: + throw new NotSupportedException("Invalid value for UnaryOperatorType"); + } + } + } + + public enum UnaryOperatorType + { + /// + /// Any unary operator (used in pattern matching) + /// + Any, + + /// Logical not (!a) + Not, + /// Bitwise not (~a) + BitNot, + /// Unary minus (-a) + Minus, + /// Unary plus (+a) + Plus, + /// Pre increment (++a) + Increment, + /// Pre decrement (--a) + Decrement, + /// Post increment (a++) + PostIncrement, + /// Post decrement (a--) + PostDecrement, + /// Dereferencing (*a) + Dereference, + /// Get address (&a) + AddressOf, + /// C# 5.0 await + Await + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/UncheckedExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/UncheckedExpression.cs new file mode 100644 index 000000000..5b8686a26 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/UncheckedExpression.cs @@ -0,0 +1,83 @@ +// +// UncheckedExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// unchecked(Expression) + /// + public class UncheckedExpression : Expression + { + public readonly static TokenRole UncheckedKeywordRole = new TokenRole ("unchecked"); + + public CSharpTokenNode UncheckedToken { + get { return GetChildByRole (UncheckedKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole(Roles.Expression, value); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public UncheckedExpression () + { + } + + public UncheckedExpression (Expression expression) + { + AddChild (expression, Roles.Expression); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitUncheckedExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitUncheckedExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitUncheckedExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + UncheckedExpression o = other as UncheckedExpression; + return o != null && this.Expression.DoMatch(o.Expression, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/UndocumentedExpression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/UndocumentedExpression.cs new file mode 100644 index 000000000..0efc0d70f --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Expressions/UndocumentedExpression.cs @@ -0,0 +1,105 @@ +// +// UndocumentedExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + public enum UndocumentedExpressionType + { + ArgListAccess, // __arglist + ArgList, // __arglist (a1, a2, ..., an) + RefValue, // __refvalue (expr , type) + RefType, // __reftype (expr) + MakeRef // __makeref (expr) + } + + /// + /// Represents undocumented expressions. + /// + public class UndocumentedExpression : Expression + { + public readonly static TokenRole ArglistKeywordRole = new TokenRole ("__arglist"); + public readonly static TokenRole RefvalueKeywordRole = new TokenRole ("__refvalue"); + public readonly static TokenRole ReftypeKeywordRole = new TokenRole ("__reftype"); + public readonly static TokenRole MakerefKeywordRole = new TokenRole ("__makeref"); + + public UndocumentedExpressionType UndocumentedExpressionType { + get; set; + } + + public CSharpTokenNode UndocumentedToken { + get { + switch (UndocumentedExpressionType) { + case ICSharpCode.NRefactory.CSharp.UndocumentedExpressionType.ArgListAccess: + case ICSharpCode.NRefactory.CSharp.UndocumentedExpressionType.ArgList: + return GetChildByRole (ArglistKeywordRole); + case ICSharpCode.NRefactory.CSharp.UndocumentedExpressionType.RefValue: + return GetChildByRole (RefvalueKeywordRole); + case ICSharpCode.NRefactory.CSharp.UndocumentedExpressionType.RefType: + return GetChildByRole (ReftypeKeywordRole); + case ICSharpCode.NRefactory.CSharp.UndocumentedExpressionType.MakeRef: + return GetChildByRole (MakerefKeywordRole); + } + return CSharpTokenNode.Null; + } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstNodeCollection Arguments { + get { return GetChildrenByRole(Roles.Argument); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitUndocumentedExpression (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitUndocumentedExpression (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitUndocumentedExpression (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + UndocumentedExpression o = other as UndocumentedExpression; + return o != null && this.UndocumentedExpressionType == o.UndocumentedExpressionType && this.Arguments.DoMatch(o.Arguments, match); + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/Attribute.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/Attribute.cs new file mode 100644 index 000000000..cc99936e5 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/Attribute.cs @@ -0,0 +1,93 @@ +// +// Attribute.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Attribute(Arguments) + /// + public class Attribute : AstNode + { + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public AstType Type { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole (Roles.Type, value); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstNodeCollection Arguments { + get { return base.GetChildrenByRole (Roles.Argument); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + // HasArgumentList == false: [Empty] + public bool HasArgumentList { + get; + set; + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitAttribute (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitAttribute (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitAttribute (this, data); + } + + protected internal override bool DoMatch (AstNode other, PatternMatching.Match match) + { + Attribute o = other as Attribute; + return o != null && this.Type.DoMatch (o.Type, match) && this.Arguments.DoMatch (o.Arguments, match); + } + + public override string ToString(CSharpFormattingOptions formattingOptions) + { + if (IsNull) + return "Null"; + return base.ToString(formattingOptions); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/AttributeSection.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/AttributeSection.cs new file mode 100644 index 000000000..67b04412b --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/AttributeSection.cs @@ -0,0 +1,174 @@ +// +// AttributeSection.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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 System.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// [AttributeTarget: Attributes] + /// + public class AttributeSection : AstNode + { + #region PatternPlaceholder + public static implicit operator AttributeSection(PatternMatching.Pattern pattern) + { + return pattern != null ? new PatternPlaceholder(pattern) : null; + } + + sealed class PatternPlaceholder : AttributeSection, PatternMatching.INode + { + readonly PatternMatching.Pattern child; + + public PatternPlaceholder(PatternMatching.Pattern child) + { + this.child = child; + } + + public override NodeType NodeType { + get { return NodeType.Pattern; } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPatternPlaceholder (this, child); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPatternPlaceholder (this, child); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitPatternPlaceholder(this, child, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return child.DoMatch(other, match); + } + + bool PatternMatching.INode.DoMatchCollection(Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) + { + return child.DoMatchCollection(role, pos, match, backtrackingInfo); + } + } + #endregion + + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public CSharpTokenNode LBracketToken { + get { return GetChildByRole (Roles.LBracket); } + } + + public string AttributeTarget { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole (Roles.Identifier, CSharp.Identifier.Create (value)); + } + } + + public Identifier AttributeTargetToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole (Roles.Identifier, value); + } + } + + public AstNodeCollection Attributes { + get { return base.GetChildrenByRole (Roles.Attribute); } + } + + public CSharpTokenNode RBracketToken { + get { return GetChildByRole (Roles.RBracket); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitAttributeSection (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitAttributeSection (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitAttributeSection (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + AttributeSection o = other as AttributeSection; + return o != null && MatchString(this.AttributeTarget, o.AttributeTarget) && this.Attributes.DoMatch(o.Attributes, match); + } + + public AttributeSection() + { + } + + public AttributeSection(Attribute attr) + { + this.Attributes.Add(attr); + } + +// public static string GetAttributeTargetName(AttributeTarget attributeTarget) +// { +// switch (attributeTarget) { +// case AttributeTarget.None: +// return null; +// case AttributeTarget.Assembly: +// return "assembly"; +// case AttributeTarget.Module: +// return "module"; +// case AttributeTarget.Type: +// return "type"; +// case AttributeTarget.Param: +// return "param"; +// case AttributeTarget.Field: +// return "field"; +// case AttributeTarget.Return: +// return "return"; +// case AttributeTarget.Method: +// return "method"; +// default: +// throw new NotSupportedException("Invalid value for AttributeTarget"); +// } +// } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/Comment.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/Comment.cs new file mode 100644 index 000000000..9d53f6e66 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/Comment.cs @@ -0,0 +1,140 @@ +// +// Comment.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.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. + +namespace ICSharpCode.NRefactory.CSharp +{ + public enum CommentType + { + /// + /// "//" comment + /// + SingleLine, + /// + /// "/* */" comment + /// + MultiLine, + /// + /// "///" comment + /// + Documentation, + /// + /// Inactive code (code in non-taken "#if") + /// + InactiveCode, + /// + /// "/** */" comment + /// + MultiLineDocumentation + } + + public class Comment : AstNode + { + public override NodeType NodeType { + get { + return NodeType.Whitespace; + } + } + + CommentType commentType; + + public CommentType CommentType { + get { return commentType; } + set { ThrowIfFrozen(); commentType = value; } + } + + /// + /// Returns true if the is Documentation or MultiLineDocumentation. + /// + public bool IsDocumentation { + get { + return commentType == CommentType.Documentation || commentType == CommentType.MultiLineDocumentation; + } + } + + bool startsLine; + + public bool StartsLine { + get { return startsLine; } + set { ThrowIfFrozen(); startsLine = value; } + } + + string content; + + public string Content { + get { return content; } + set { ThrowIfFrozen(); content = value; } + } + + TextLocation startLocation; + public override TextLocation StartLocation { + get { + return startLocation; + } + } + + TextLocation endLocation; + public override TextLocation EndLocation { + get { + return endLocation; + } + } + + public Comment (string content, CommentType type = CommentType.SingleLine) + { + this.CommentType = type; + this.Content = content; + } + + public Comment (CommentType commentType, TextLocation startLocation, TextLocation endLocation) + { + this.CommentType = commentType; + this.startLocation = startLocation; + this.endLocation = endLocation; + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitComment (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitComment (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitComment (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + Comment o = other as Comment; + return o != null && this.CommentType == o.CommentType && MatchString(this.Content, o.Content); + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/Constraint.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/Constraint.cs new file mode 100644 index 000000000..c49930427 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/Constraint.cs @@ -0,0 +1,85 @@ +// +// Constraint.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// where TypeParameter : BaseTypes + /// + /// + /// new(), struct and class constraints are represented using a PrimitiveType "new", "struct" or "class" + /// + public class Constraint : AstNode + { + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public CSharpTokenNode WhereKeyword { + get { return GetChildByRole (Roles.WhereKeyword); } + } + + public SimpleType TypeParameter { + get { + return GetChildByRole (Roles.ConstraintTypeParameter); + } + set { + SetChildByRole(Roles.ConstraintTypeParameter, value); + } + } + + public AstNodeCollection BaseTypes { + get { + return GetChildrenByRole(Roles.BaseType); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitConstraint (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitConstraint (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitConstraint (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + Constraint o = other as Constraint; + return o != null && this.TypeParameter.DoMatch (o.TypeParameter, match) && this.BaseTypes.DoMatch(o.BaseTypes, match); + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/DelegateDeclaration.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/DelegateDeclaration.cs new file mode 100644 index 000000000..68489bc7f --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/DelegateDeclaration.cs @@ -0,0 +1,92 @@ +// +// DelegateDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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 ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// delegate ReturnType Name<TypeParameters>(Parameters) where Constraints; + /// + public class DelegateDeclaration : EntityDeclaration + { + public override NodeType NodeType { + get { return NodeType.TypeDeclaration; } + } + + public override SymbolKind SymbolKind { + get { return SymbolKind.TypeDefinition; } + } + + public CSharpTokenNode DelegateToken { + get { return GetChildByRole(Roles.DelegateKeyword); } + } + + public AstNodeCollection TypeParameters { + get { return GetChildrenByRole (Roles.TypeParameter); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstNodeCollection Parameters { + get { return GetChildrenByRole (Roles.Parameter); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public AstNodeCollection Constraints { + get { return GetChildrenByRole (Roles.Constraint); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitDelegateDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitDelegateDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitDelegateDeclaration (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + DelegateDeclaration o = other as DelegateDeclaration; + return o != null && MatchString(this.Name, o.Name) + && this.MatchAttributesAndModifiers(o, match) && this.ReturnType.DoMatch(o.ReturnType, match) + && this.TypeParameters.DoMatch(o.TypeParameters, match) && this.Parameters.DoMatch(o.Parameters, match) + && this.Constraints.DoMatch(o.Constraints, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/ExternAliasDeclaration.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/ExternAliasDeclaration.cs new file mode 100644 index 000000000..9404d417f --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/ExternAliasDeclaration.cs @@ -0,0 +1,91 @@ +// +// ExternAliasDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// extern alias ; + /// + public class ExternAliasDeclaration : AstNode + { + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public CSharpTokenNode ExternToken { + get { return GetChildByRole (Roles.ExternKeyword); } + } + + public CSharpTokenNode AliasToken { + get { return GetChildByRole (Roles.AliasKeyword); } + } + + public string Name { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole (Roles.Identifier, Identifier.Create (value)); + } + } + + public Identifier NameToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole (Roles.Identifier, value); + } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitExternAliasDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitExternAliasDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitExternAliasDeclaration (this, data); + } + + protected internal override bool DoMatch (AstNode other, PatternMatching.Match match) + { + var o = other as ExternAliasDeclaration; + return o != null && MatchString (this.Name, o.Name); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/NamespaceDeclaration.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/NamespaceDeclaration.cs new file mode 100644 index 000000000..dbcf0192d --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/NamespaceDeclaration.cs @@ -0,0 +1,158 @@ +// +// NamespaceDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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.Collections.Generic; +using System.Linq; +using System.Text; +using System; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// namespace Name { Members } + /// + public class NamespaceDeclaration : AstNode + { + public static readonly Role MemberRole = SyntaxTree.MemberRole; + public static readonly Role NamespaceNameRole = new Role("NamespaceName", AstType.Null); + + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public CSharpTokenNode NamespaceToken { + get { return GetChildByRole(Roles.NamespaceKeyword); } + } + + public AstType NamespaceName { + get { return GetChildByRole(NamespaceNameRole) ?? AstType.Null; } + set { SetChildByRole(NamespaceNameRole, value); } + } + + public string Name { + get { + return UsingDeclaration.ConstructNamespace(NamespaceName); + } + set { + var arr = value.Split('.'); + NamespaceName = ConstructType(arr, arr.Length - 1); + } + } + + static AstType ConstructType(string[] arr, int i) + { + if (i < 0 || i >= arr.Length) + throw new ArgumentOutOfRangeException("i"); + if (i == 0) + return new SimpleType(arr[i]); + return new MemberType(ConstructType(arr, i - 1), arr[i]); + } + + /// + /// Gets the full namespace name (including any parent namespaces) + /// + public string FullName { + get { + NamespaceDeclaration parentNamespace = Parent as NamespaceDeclaration; + if (parentNamespace != null) + return BuildQualifiedName(parentNamespace.FullName, Name); + return Name; + } + } + + public IEnumerable Identifiers { + get { + var result = new Stack(); + AstType type = NamespaceName; + while (type is MemberType) { + var mt = (MemberType)type; + result.Push(mt.MemberName); + type = mt.Target; + } + if (type is SimpleType) + result.Push(((SimpleType)type).Identifier); + return result; + } + } + + public CSharpTokenNode LBraceToken { + get { return GetChildByRole(Roles.LBrace); } + } + + public AstNodeCollection Members { + get { return GetChildrenByRole(MemberRole); } + } + + public CSharpTokenNode RBraceToken { + get { return GetChildByRole(Roles.RBrace); } + } + + public NamespaceDeclaration() + { + } + + public NamespaceDeclaration(string name) + { + this.Name = name; + } + + public static string BuildQualifiedName(string name1, string name2) + { + if (string.IsNullOrEmpty(name1)) + return name2; + if (string.IsNullOrEmpty(name2)) + return name1; + return name1 + "." + name2; + } + + public void AddMember(AstNode child) + { + AddChild(child, MemberRole); + } + + public override void AcceptVisitor(IAstVisitor visitor) + { + visitor.VisitNamespaceDeclaration(this); + } + + public override T AcceptVisitor(IAstVisitor visitor) + { + return visitor.VisitNamespaceDeclaration(this); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitNamespaceDeclaration(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + NamespaceDeclaration o = other as NamespaceDeclaration; + return o != null && MatchString(this.Name, o.Name) && this.Members.DoMatch(o.Members, match); + } + } +} ; diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/NewLineNode.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/NewLineNode.cs new file mode 100644 index 000000000..12b004420 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/NewLineNode.cs @@ -0,0 +1,91 @@ +using System; +namespace ICSharpCode.NRefactory.CSharp +{ + + /// + /// A New line node represents a line break in the text. + /// + public sealed class NewLineNode : AstNode + { + public override NodeType NodeType { + get { + return NodeType.Whitespace; + } + } + + const uint newLineMask = 0xfu << AstNodeFlagsUsedBits; + static readonly UnicodeNewline[] newLineTypes = { + UnicodeNewline.Unknown, + UnicodeNewline.LF, + UnicodeNewline.CRLF, + UnicodeNewline.CR, + UnicodeNewline.NEL, + UnicodeNewline.VT, + UnicodeNewline.FF, + UnicodeNewline.LS, + UnicodeNewline.PS + }; + + public UnicodeNewline NewLineType { + get { + return newLineTypes[(flags & newLineMask) >> AstNodeFlagsUsedBits]; + } + set { + ThrowIfFrozen(); + int pos = Array.IndexOf(newLineTypes, value); + if (pos < 0) + pos = 0; + flags &= ~newLineMask; // clear old newline type + flags |= (uint)pos << AstNodeFlagsUsedBits; + } + } + + TextLocation startLocation; + public override TextLocation StartLocation { + get { + return startLocation; + } + } + + public override TextLocation EndLocation { + get { + return new TextLocation (startLocation.Line + 1, 1); + } + } + + public NewLineNode() : this (TextLocation.Empty) + { + } + + public NewLineNode(TextLocation startLocation) + { + this.startLocation = startLocation; + } + + public sealed override string ToString(CSharpFormattingOptions formattingOptions) + { + return NewLine.GetString (NewLineType); + } + + public override void AcceptVisitor(IAstVisitor visitor) + { + visitor.VisitNewLine (this); + } + + public override T AcceptVisitor(IAstVisitor visitor) + { + return visitor.VisitNewLine (this); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitNewLine (this, data); + } + + protected internal override bool DoMatch(AstNode other, ICSharpCode.NRefactory.PatternMatching.Match match) + { + return other is NewLineNode; + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/PreProcessorDirective.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/PreProcessorDirective.cs new file mode 100644 index 000000000..631f35e98 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/PreProcessorDirective.cs @@ -0,0 +1,205 @@ +// +// PreProcessorDirective.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Xamarin Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + public enum PreProcessorDirectiveType : byte + { + Invalid = 0, + Region = 1, + Endregion = 2, + + If = 3, + Endif = 4, + Elif = 5, + Else = 6, + + Define = 7, + Undef = 8, + Error = 9, + Warning = 10, + Pragma = 11, + Line = 12 + } + + public class LinePreprocessorDirective : PreProcessorDirective + { + public int LineNumber { + get; + set; + } + + public string FileName { + get; + set; + } + + public LinePreprocessorDirective(TextLocation startLocation, TextLocation endLocation) : base (PreProcessorDirectiveType.Line, startLocation, endLocation) + { + } + + public LinePreprocessorDirective(string argument = null) : base (PreProcessorDirectiveType.Line, argument) + { + } + } + + public class PragmaWarningPreprocessorDirective : PreProcessorDirective + { + public static readonly Role WarningRole = new Role ("Warning"); + + public static readonly TokenRole PragmaKeywordRole = new TokenRole ("#pragma"); + public static readonly TokenRole WarningKeywordRole = new TokenRole ("warning"); + public static readonly TokenRole DisableKeywordRole = new TokenRole ("disable"); + public static readonly TokenRole RestoreKeywordRole = new TokenRole ("restore"); + + public bool Disable { + get { + return !DisableToken.IsNull; + } + } + + public CSharpTokenNode PragmaToken { + get { return GetChildByRole (PragmaKeywordRole); } + } + + public CSharpTokenNode WarningToken { + get { return GetChildByRole (WarningKeywordRole); } + } + + public CSharpTokenNode DisableToken { + get { return GetChildByRole (DisableKeywordRole); } + } + + public CSharpTokenNode RestoreToken { + get { return GetChildByRole (RestoreKeywordRole); } + } + + public AstNodeCollection Warnings { + get { return GetChildrenByRole(WarningRole); } + } + + public override TextLocation EndLocation { + get { + var child = LastChild; + if (child == null) + return base.EndLocation; + return child.EndLocation; + } + } + + public PragmaWarningPreprocessorDirective(TextLocation startLocation, TextLocation endLocation) : base (PreProcessorDirectiveType.Pragma, startLocation, endLocation) + { + } + + public PragmaWarningPreprocessorDirective(string argument = null) : base (PreProcessorDirectiveType.Pragma, argument) + { + } + + public bool IsDefined(int pragmaWarning) + { + return Warnings.Select(w => (int)w.Value).Any(n => n == pragmaWarning); + } + } + + public class PreProcessorDirective : AstNode + { + public override NodeType NodeType { + get { + return NodeType.Whitespace; + } + } + + public PreProcessorDirectiveType Type { + get; + set; + } + + public string Argument { + get; + set; + } + + /// + /// For an '#if' directive, specifies whether the condition evaluated to true. + /// + public bool Take { + get; + set; + } + + TextLocation startLocation; + public override TextLocation StartLocation { + get { + return startLocation; + } + } + + TextLocation endLocation; + public override TextLocation EndLocation { + get { + return endLocation; + } + } + + public PreProcessorDirective(PreProcessorDirectiveType type, TextLocation startLocation, TextLocation endLocation) + { + this.Type = type; + this.startLocation = startLocation; + this.endLocation = endLocation; + } + + public PreProcessorDirective(PreProcessorDirectiveType type, string argument = null) + { + this.Type = type; + this.Argument = argument; + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPreProcessorDirective (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPreProcessorDirective (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitPreProcessorDirective (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + PreProcessorDirective o = other as PreProcessorDirective; + return o != null && Type == o.Type && MatchString(Argument, o.Argument); + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/TextNode.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/TextNode.cs new file mode 100644 index 000000000..4c7f9b942 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/TextNode.cs @@ -0,0 +1,94 @@ +// +// TextNode.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; +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// A text node contains text without syntactic or semantic information. + /// (non parseable part of a text) + /// + public class TextNode : AstNode + { + public override NodeType NodeType { + get { + return NodeType.Whitespace; + } + } + + public string Text { + get; + set; + } + + TextLocation startLocation; + public override TextLocation StartLocation { + get { + return startLocation; + } + } + + TextLocation endLocation; + public override TextLocation EndLocation { + get { + return endLocation; + } + } + + public TextNode(string text) : this (text, TextLocation.Empty, TextLocation.Empty) + { + } + + public TextNode(string text, TextLocation startLocation, TextLocation endLocation) + { + this.Text = text; + this.startLocation = startLocation; + this.endLocation = endLocation; + } + + public override void AcceptVisitor(IAstVisitor visitor) + { + visitor.VisitText (this); + } + + public override T AcceptVisitor(IAstVisitor visitor) + { + return visitor.VisitText (this); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitText (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + var o = other as TextNode; + return o != null && o.Text == Text; + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/TypeDeclaration.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/TypeDeclaration.cs new file mode 100644 index 000000000..f2dedfa16 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/TypeDeclaration.cs @@ -0,0 +1,145 @@ +// +// TypeDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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.Collections.Generic; +using System.Linq; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public enum ClassType + { + Class, + Struct, + Interface, + Enum + } + + /// + /// class Name<TypeParameters> : BaseTypes where Constraints; + /// + public class TypeDeclaration : EntityDeclaration + { + public override NodeType NodeType { + get { return NodeType.TypeDeclaration; } + } + + public override SymbolKind SymbolKind { + get { return SymbolKind.TypeDefinition; } + } + + ClassType classType; + + public CSharpTokenNode TypeKeyword { + get { + switch (classType) { + case ClassType.Class: + return GetChildByRole(Roles.ClassKeyword); + case ClassType.Struct: + return GetChildByRole(Roles.StructKeyword); + case ClassType.Interface: + return GetChildByRole(Roles.InterfaceKeyword); + case ClassType.Enum: + return GetChildByRole(Roles.EnumKeyword); + default: + return CSharpTokenNode.Null; + } + } + } + + public ClassType ClassType { + get { return classType; } + set { + ThrowIfFrozen(); + classType = value; + } + } + + public CSharpTokenNode LChevronToken { + get { return GetChildByRole (Roles.LChevron); } + } + + public AstNodeCollection TypeParameters { + get { return GetChildrenByRole (Roles.TypeParameter); } + } + + public CSharpTokenNode RChevronToken { + get { return GetChildByRole (Roles.RChevron); } + } + + + + public CSharpTokenNode ColonToken { + get { + return GetChildByRole(Roles.Colon); + } + } + + public AstNodeCollection BaseTypes { + get { return GetChildrenByRole(Roles.BaseType); } + } + + public AstNodeCollection Constraints { + get { return GetChildrenByRole(Roles.Constraint); } + } + + public CSharpTokenNode LBraceToken { + get { return GetChildByRole (Roles.LBrace); } + } + + public AstNodeCollection Members { + get { return GetChildrenByRole (Roles.TypeMemberRole); } + } + + public CSharpTokenNode RBraceToken { + get { return GetChildByRole (Roles.RBrace); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitTypeDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitTypeDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitTypeDeclaration (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + TypeDeclaration o = other as TypeDeclaration; + return o != null && this.ClassType == o.ClassType && MatchString(this.Name, o.Name) + && this.MatchAttributesAndModifiers(o, match) && this.TypeParameters.DoMatch(o.TypeParameters, match) + && this.BaseTypes.DoMatch(o.BaseTypes, match) && this.Constraints.DoMatch(o.Constraints, match) + && this.Members.DoMatch(o.Members, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/TypeParameterDeclaration.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/TypeParameterDeclaration.cs new file mode 100644 index 000000000..c992b629a --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/TypeParameterDeclaration.cs @@ -0,0 +1,113 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// [in|out] Name + /// + /// Represents a type parameter. + /// Note: mirroring the C# syntax, constraints are not part of the type parameter declaration, but belong + /// to the parent type or method. + /// + public class TypeParameterDeclaration : AstNode + { + public static readonly Role AttributeRole = EntityDeclaration.AttributeRole; + public static readonly TokenRole OutVarianceKeywordRole = new TokenRole ("out"); + public static readonly TokenRole InVarianceKeywordRole = new TokenRole ("in"); + + public override NodeType NodeType { + get { return NodeType.Unknown; } + } + + public AstNodeCollection Attributes { + get { return GetChildrenByRole (AttributeRole); } + } + + VarianceModifier variance; + + public VarianceModifier Variance { + get { return variance; } + set { ThrowIfFrozen(); variance = value; } + } + + public CSharpTokenNode VarianceToken { + get { + switch (Variance) { + case VarianceModifier.Covariant: + return GetChildByRole(OutVarianceKeywordRole); + case VarianceModifier.Contravariant: + return GetChildByRole(InVarianceKeywordRole); + default: + return CSharpTokenNode.Null; + } + } + } + + public string Name { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole(Roles.Identifier, Identifier.Create (value)); + } + } + + public Identifier NameToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole (Roles.Identifier, value); + } + } + + public TypeParameterDeclaration () + { + } + + public TypeParameterDeclaration (string name) + { + Name = name; + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitTypeParameterDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitTypeParameterDeclaration (this); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitTypeParameterDeclaration(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + TypeParameterDeclaration o = other as TypeParameterDeclaration; + return o != null && this.Variance == o.Variance && MatchString(this.Name, o.Name) && this.Attributes.DoMatch(o.Attributes, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/UsingAliasDeclaration.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/UsingAliasDeclaration.cs new file mode 100644 index 000000000..9924132d3 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/UsingAliasDeclaration.cs @@ -0,0 +1,107 @@ +// +// UsingAliasDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// using Alias = Import; + /// + public class UsingAliasDeclaration : AstNode + { + public static readonly TokenRole UsingKeywordRole = new TokenRole ("using"); + public static readonly Role AliasRole = new Role("Alias", Identifier.Null); + public static readonly Role ImportRole = UsingDeclaration.ImportRole; + + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public CSharpTokenNode UsingToken { + get { return GetChildByRole (UsingKeywordRole); } + } + + public string Alias { + get { + return GetChildByRole (AliasRole).Name; + } + set { + SetChildByRole(AliasRole, Identifier.Create (value)); + } + } + + public CSharpTokenNode AssignToken { + get { return GetChildByRole (Roles.Assign); } + } + + public AstType Import { + get { return GetChildByRole (ImportRole); } + set { SetChildByRole (ImportRole, value); } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public UsingAliasDeclaration () + { + } + + public UsingAliasDeclaration (string alias, string nameSpace) + { + AddChild (Identifier.Create (alias), AliasRole); + AddChild (new SimpleType (nameSpace), ImportRole); + } + + public UsingAliasDeclaration (string alias, AstType import) + { + AddChild (Identifier.Create (alias), AliasRole); + AddChild (import, ImportRole); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitUsingAliasDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitUsingAliasDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitUsingAliasDeclaration (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + UsingAliasDeclaration o = other as UsingAliasDeclaration; + return o != null && MatchString(this.Alias, o.Alias) && this.Import.DoMatch(o.Import, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/UsingDeclaration.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/UsingDeclaration.cs new file mode 100644 index 000000000..9e0c35a89 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/UsingDeclaration.cs @@ -0,0 +1,122 @@ +// +// UsingDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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.Linq; +using System.Text; +using System.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// using Import; + /// + public class UsingDeclaration : AstNode + { + public static readonly TokenRole UsingKeywordRole = new TokenRole ("using"); + public static readonly Role ImportRole = new Role("Import", AstType.Null); + + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public CSharpTokenNode UsingToken { + get { return GetChildByRole (UsingKeywordRole); } + } + + public AstType Import { + get { return GetChildByRole (ImportRole); } + set { SetChildByRole (ImportRole, value); } + } + + public string Namespace { + get { return ConstructNamespace (Import); } + } + + internal static string ConstructNamespace (AstType type) + { + var stack = new Stack(); + while (type is MemberType) { + var mt = (MemberType)type; + stack.Push(mt.MemberName); + type = mt.Target; + if (mt.IsDoubleColon) { + stack.Push("::"); + } else { + stack.Push("."); + } + } + if (type is SimpleType) + stack.Push(((SimpleType)type).Identifier); + + var result = new StringBuilder(); + while (stack.Count > 0) + result.Append(stack.Pop()); + return result.ToString(); + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public UsingDeclaration () + { + } + + public UsingDeclaration (string nameSpace) + { + AddChild (AstType.Create (nameSpace), ImportRole); + } + + public UsingDeclaration (AstType import) + { + AddChild (import, ImportRole); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitUsingDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitUsingDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitUsingDeclaration (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + UsingDeclaration o = other as UsingDeclaration; + return o != null && this.Import.DoMatch(o.Import, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/WhitespaceNode.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/WhitespaceNode.cs new file mode 100644 index 000000000..c7e37f704 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/WhitespaceNode.cs @@ -0,0 +1,91 @@ +// +// WhitespaceNode.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; +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// A Whitespace node contains only whitespaces. + /// + public class WhitespaceNode : AstNode + { + public override NodeType NodeType { + get { + return NodeType.Whitespace; + } + } + + public string WhiteSpaceText { + get; + set; + } + + TextLocation startLocation; + public override TextLocation StartLocation { + get { + return startLocation; + } + } + + public override TextLocation EndLocation { + get { + return new TextLocation (startLocation.Line, startLocation.Column + WhiteSpaceText.Length); + } + } + + public WhitespaceNode(string whiteSpaceText) : this (whiteSpaceText, TextLocation.Empty) + { + } + + public WhitespaceNode(string whiteSpaceText, TextLocation startLocation) + { + this.WhiteSpaceText = WhiteSpaceText; + this.startLocation = startLocation; + } + + public override void AcceptVisitor(IAstVisitor visitor) + { + visitor.VisitWhitespace (this); + } + + public override T AcceptVisitor(IAstVisitor visitor) + { + return visitor.VisitWhitespace (this); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitWhitespace (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + var o = other as WhitespaceNode; + return o != null && o.WhiteSpaceText == WhiteSpaceText; + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/IAstVisitor.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/IAstVisitor.cs new file mode 100644 index 000000000..4aadddbdc --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/IAstVisitor.cs @@ -0,0 +1,418 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// AST visitor. + /// + public interface IAstVisitor + { + void VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression); + void VisitUndocumentedExpression(UndocumentedExpression undocumentedExpression); + void VisitArrayCreateExpression(ArrayCreateExpression arrayCreateExpression); + void VisitArrayInitializerExpression(ArrayInitializerExpression arrayInitializerExpression); + void VisitAsExpression(AsExpression asExpression); + void VisitAssignmentExpression(AssignmentExpression assignmentExpression); + void VisitBaseReferenceExpression(BaseReferenceExpression baseReferenceExpression); + void VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression); + void VisitCastExpression(CastExpression castExpression); + void VisitCheckedExpression(CheckedExpression checkedExpression); + void VisitConditionalExpression(ConditionalExpression conditionalExpression); + void VisitDefaultValueExpression(DefaultValueExpression defaultValueExpression); + void VisitDirectionExpression(DirectionExpression directionExpression); + void VisitIdentifierExpression(IdentifierExpression identifierExpression); + void VisitIndexerExpression(IndexerExpression indexerExpression); + void VisitInvocationExpression(InvocationExpression invocationExpression); + void VisitIsExpression(IsExpression isExpression); + void VisitLambdaExpression(LambdaExpression lambdaExpression); + void VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression); + void VisitNamedArgumentExpression(NamedArgumentExpression namedArgumentExpression); + void VisitNamedExpression(NamedExpression namedExpression); + void VisitNullReferenceExpression(NullReferenceExpression nullReferenceExpression); + void VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression); + void VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression); + void VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression); + void VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression); + void VisitPrimitiveExpression(PrimitiveExpression primitiveExpression); + void VisitSizeOfExpression(SizeOfExpression sizeOfExpression); + void VisitStackAllocExpression(StackAllocExpression stackAllocExpression); + void VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression); + void VisitTypeOfExpression(TypeOfExpression typeOfExpression); + void VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression); + void VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression); + void VisitUncheckedExpression(UncheckedExpression uncheckedExpression); + + void VisitQueryExpression(QueryExpression queryExpression); + void VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause); + void VisitQueryFromClause(QueryFromClause queryFromClause); + void VisitQueryLetClause(QueryLetClause queryLetClause); + void VisitQueryWhereClause(QueryWhereClause queryWhereClause); + void VisitQueryJoinClause(QueryJoinClause queryJoinClause); + void VisitQueryOrderClause(QueryOrderClause queryOrderClause); + void VisitQueryOrdering(QueryOrdering queryOrdering); + void VisitQuerySelectClause(QuerySelectClause querySelectClause); + void VisitQueryGroupClause(QueryGroupClause queryGroupClause); + + void VisitAttribute(Attribute attribute); + void VisitAttributeSection(AttributeSection attributeSection); + void VisitDelegateDeclaration(DelegateDeclaration delegateDeclaration); + void VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration); + void VisitTypeDeclaration(TypeDeclaration typeDeclaration); + void VisitUsingAliasDeclaration(UsingAliasDeclaration usingAliasDeclaration); + void VisitUsingDeclaration(UsingDeclaration usingDeclaration); + void VisitExternAliasDeclaration(ExternAliasDeclaration externAliasDeclaration); + + void VisitBlockStatement(BlockStatement blockStatement); + void VisitBreakStatement(BreakStatement breakStatement); + void VisitCheckedStatement(CheckedStatement checkedStatement); + void VisitContinueStatement(ContinueStatement continueStatement); + void VisitDoWhileStatement(DoWhileStatement doWhileStatement); + void VisitEmptyStatement(EmptyStatement emptyStatement); + void VisitExpressionStatement(ExpressionStatement expressionStatement); + void VisitFixedStatement(FixedStatement fixedStatement); + void VisitForeachStatement(ForeachStatement foreachStatement); + void VisitForStatement(ForStatement forStatement); + void VisitGotoCaseStatement(GotoCaseStatement gotoCaseStatement); + void VisitGotoDefaultStatement(GotoDefaultStatement gotoDefaultStatement); + void VisitGotoStatement(GotoStatement gotoStatement); + void VisitIfElseStatement(IfElseStatement ifElseStatement); + void VisitLabelStatement(LabelStatement labelStatement); + void VisitLockStatement(LockStatement lockStatement); + void VisitReturnStatement(ReturnStatement returnStatement); + void VisitSwitchStatement(SwitchStatement switchStatement); + void VisitSwitchSection(SwitchSection switchSection); + void VisitCaseLabel(CaseLabel caseLabel); + void VisitThrowStatement(ThrowStatement throwStatement); + void VisitTryCatchStatement(TryCatchStatement tryCatchStatement); + void VisitCatchClause(CatchClause catchClause); + void VisitUncheckedStatement(UncheckedStatement uncheckedStatement); + void VisitUnsafeStatement(UnsafeStatement unsafeStatement); + void VisitUsingStatement(UsingStatement usingStatement); + void VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement); + void VisitWhileStatement(WhileStatement whileStatement); + void VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement); + void VisitYieldReturnStatement(YieldReturnStatement yieldReturnStatement); + + void VisitAccessor(Accessor accessor); + void VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration); + void VisitConstructorInitializer(ConstructorInitializer constructorInitializer); + void VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration); + void VisitEnumMemberDeclaration(EnumMemberDeclaration enumMemberDeclaration); + void VisitEventDeclaration(EventDeclaration eventDeclaration); + void VisitCustomEventDeclaration(CustomEventDeclaration customEventDeclaration); + void VisitFieldDeclaration(FieldDeclaration fieldDeclaration); + void VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration); + void VisitMethodDeclaration(MethodDeclaration methodDeclaration); + void VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration); + void VisitParameterDeclaration(ParameterDeclaration parameterDeclaration); + void VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration); + void VisitVariableInitializer(VariableInitializer variableInitializer); + void VisitFixedFieldDeclaration(FixedFieldDeclaration fixedFieldDeclaration); + void VisitFixedVariableInitializer(FixedVariableInitializer fixedVariableInitializer); + + void VisitSyntaxTree(SyntaxTree syntaxTree); + void VisitSimpleType(SimpleType simpleType); + void VisitMemberType(MemberType memberType); + void VisitComposedType(ComposedType composedType); + void VisitArraySpecifier(ArraySpecifier arraySpecifier); + void VisitPrimitiveType(PrimitiveType primitiveType); + + void VisitComment(Comment comment); + void VisitNewLine(NewLineNode newLineNode); + void VisitWhitespace(WhitespaceNode whitespaceNode); + void VisitText(TextNode textNode); + void VisitPreProcessorDirective(PreProcessorDirective preProcessorDirective); + void VisitDocumentationReference(DocumentationReference documentationReference); + + void VisitTypeParameterDeclaration(TypeParameterDeclaration typeParameterDeclaration); + void VisitConstraint(Constraint constraint); + void VisitCSharpTokenNode(CSharpTokenNode cSharpTokenNode); + void VisitIdentifier(Identifier identifier); + + void VisitNullNode(AstNode nullNode); + void VisitErrorNode(AstNode errorNode); + void VisitPatternPlaceholder(AstNode placeholder, PatternMatching.Pattern pattern); + } + + /// + /// AST visitor. + /// + public interface IAstVisitor + { + S VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression); + S VisitUndocumentedExpression(UndocumentedExpression undocumentedExpression); + S VisitArrayCreateExpression(ArrayCreateExpression arrayCreateExpression); + S VisitArrayInitializerExpression(ArrayInitializerExpression arrayInitializerExpression); + S VisitAsExpression(AsExpression asExpression); + S VisitAssignmentExpression(AssignmentExpression assignmentExpression); + S VisitBaseReferenceExpression(BaseReferenceExpression baseReferenceExpression); + S VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression); + S VisitCastExpression(CastExpression castExpression); + S VisitCheckedExpression(CheckedExpression checkedExpression); + S VisitConditionalExpression(ConditionalExpression conditionalExpression); + S VisitDefaultValueExpression(DefaultValueExpression defaultValueExpression); + S VisitDirectionExpression(DirectionExpression directionExpression); + S VisitIdentifierExpression(IdentifierExpression identifierExpression); + S VisitIndexerExpression(IndexerExpression indexerExpression); + S VisitInvocationExpression(InvocationExpression invocationExpression); + S VisitIsExpression(IsExpression isExpression); + S VisitLambdaExpression(LambdaExpression lambdaExpression); + S VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression); + S VisitNamedArgumentExpression(NamedArgumentExpression namedArgumentExpression); + S VisitNamedExpression(NamedExpression namedExpression); + S VisitNullReferenceExpression(NullReferenceExpression nullReferenceExpression); + S VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression); + S VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression); + S VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression); + S VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression); + S VisitPrimitiveExpression(PrimitiveExpression primitiveExpression); + S VisitSizeOfExpression(SizeOfExpression sizeOfExpression); + S VisitStackAllocExpression(StackAllocExpression stackAllocExpression); + S VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression); + S VisitTypeOfExpression(TypeOfExpression typeOfExpression); + S VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression); + S VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression); + S VisitUncheckedExpression(UncheckedExpression uncheckedExpression); + + S VisitQueryExpression(QueryExpression queryExpression); + S VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause); + S VisitQueryFromClause(QueryFromClause queryFromClause); + S VisitQueryLetClause(QueryLetClause queryLetClause); + S VisitQueryWhereClause(QueryWhereClause queryWhereClause); + S VisitQueryJoinClause(QueryJoinClause queryJoinClause); + S VisitQueryOrderClause(QueryOrderClause queryOrderClause); + S VisitQueryOrdering(QueryOrdering queryOrdering); + S VisitQuerySelectClause(QuerySelectClause querySelectClause); + S VisitQueryGroupClause(QueryGroupClause queryGroupClause); + + S VisitAttribute(Attribute attribute); + S VisitAttributeSection(AttributeSection attributeSection); + S VisitDelegateDeclaration(DelegateDeclaration delegateDeclaration); + S VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration); + S VisitTypeDeclaration(TypeDeclaration typeDeclaration); + S VisitUsingAliasDeclaration(UsingAliasDeclaration usingAliasDeclaration); + S VisitUsingDeclaration(UsingDeclaration usingDeclaration); + S VisitExternAliasDeclaration(ExternAliasDeclaration externAliasDeclaration); + + S VisitBlockStatement(BlockStatement blockStatement); + S VisitBreakStatement(BreakStatement breakStatement); + S VisitCheckedStatement(CheckedStatement checkedStatement); + S VisitContinueStatement(ContinueStatement continueStatement); + S VisitDoWhileStatement(DoWhileStatement doWhileStatement); + S VisitEmptyStatement(EmptyStatement emptyStatement); + S VisitExpressionStatement(ExpressionStatement expressionStatement); + S VisitFixedStatement(FixedStatement fixedStatement); + S VisitForeachStatement(ForeachStatement foreachStatement); + S VisitForStatement(ForStatement forStatement); + S VisitGotoCaseStatement(GotoCaseStatement gotoCaseStatement); + S VisitGotoDefaultStatement(GotoDefaultStatement gotoDefaultStatement); + S VisitGotoStatement(GotoStatement gotoStatement); + S VisitIfElseStatement(IfElseStatement ifElseStatement); + S VisitLabelStatement(LabelStatement labelStatement); + S VisitLockStatement(LockStatement lockStatement); + S VisitReturnStatement(ReturnStatement returnStatement); + S VisitSwitchStatement(SwitchStatement switchStatement); + S VisitSwitchSection(SwitchSection switchSection); + S VisitCaseLabel(CaseLabel caseLabel); + S VisitThrowStatement(ThrowStatement throwStatement); + S VisitTryCatchStatement(TryCatchStatement tryCatchStatement); + S VisitCatchClause(CatchClause catchClause); + S VisitUncheckedStatement(UncheckedStatement uncheckedStatement); + S VisitUnsafeStatement(UnsafeStatement unsafeStatement); + S VisitUsingStatement(UsingStatement usingStatement); + S VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement); + S VisitWhileStatement(WhileStatement whileStatement); + S VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement); + S VisitYieldReturnStatement(YieldReturnStatement yieldReturnStatement); + + S VisitAccessor(Accessor accessor); + S VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration); + S VisitConstructorInitializer(ConstructorInitializer constructorInitializer); + S VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration); + S VisitEnumMemberDeclaration(EnumMemberDeclaration enumMemberDeclaration); + S VisitEventDeclaration(EventDeclaration eventDeclaration); + S VisitCustomEventDeclaration(CustomEventDeclaration customEventDeclaration); + S VisitFieldDeclaration(FieldDeclaration fieldDeclaration); + S VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration); + S VisitMethodDeclaration(MethodDeclaration methodDeclaration); + S VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration); + S VisitParameterDeclaration(ParameterDeclaration parameterDeclaration); + S VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration); + S VisitVariableInitializer(VariableInitializer variableInitializer); + S VisitFixedFieldDeclaration(FixedFieldDeclaration fixedFieldDeclaration); + S VisitFixedVariableInitializer(FixedVariableInitializer fixedVariableInitializer); + + S VisitSyntaxTree(SyntaxTree syntaxTree); + S VisitSimpleType(SimpleType simpleType); + S VisitMemberType(MemberType memberType); + S VisitComposedType(ComposedType composedType); + S VisitArraySpecifier(ArraySpecifier arraySpecifier); + S VisitPrimitiveType(PrimitiveType primitiveType); + + S VisitComment(Comment comment); + S VisitWhitespace(WhitespaceNode whitespaceNode); + S VisitText(TextNode textNode); + S VisitNewLine(NewLineNode newLineNode); + S VisitPreProcessorDirective(PreProcessorDirective preProcessorDirective); + S VisitDocumentationReference(DocumentationReference documentationReference); + + S VisitTypeParameterDeclaration(TypeParameterDeclaration typeParameterDeclaration); + S VisitConstraint(Constraint constraint); + S VisitCSharpTokenNode(CSharpTokenNode cSharpTokenNode); + S VisitIdentifier(Identifier identifier); + + S VisitNullNode(AstNode nullNode); + S VisitErrorNode(AstNode errorNode); + S VisitPatternPlaceholder(AstNode placeholder, PatternMatching.Pattern pattern); + } + + /// + /// AST visitor. + /// + public interface IAstVisitor + { + S VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression, T data); + S VisitUndocumentedExpression(UndocumentedExpression undocumentedExpression, T data); + S VisitArrayCreateExpression(ArrayCreateExpression arrayCreateExpression, T data); + S VisitArrayInitializerExpression(ArrayInitializerExpression arrayInitializerExpression, T data); + S VisitAsExpression(AsExpression asExpression, T data); + S VisitAssignmentExpression(AssignmentExpression assignmentExpression, T data); + S VisitBaseReferenceExpression(BaseReferenceExpression baseReferenceExpression, T data); + S VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression, T data); + S VisitCastExpression(CastExpression castExpression, T data); + S VisitCheckedExpression(CheckedExpression checkedExpression, T data); + S VisitConditionalExpression(ConditionalExpression conditionalExpression, T data); + S VisitDefaultValueExpression(DefaultValueExpression defaultValueExpression, T data); + S VisitDirectionExpression(DirectionExpression directionExpression, T data); + S VisitIdentifierExpression(IdentifierExpression identifierExpression, T data); + S VisitIndexerExpression(IndexerExpression indexerExpression, T data); + S VisitInvocationExpression(InvocationExpression invocationExpression, T data); + S VisitIsExpression(IsExpression isExpression, T data); + S VisitLambdaExpression(LambdaExpression lambdaExpression, T data); + S VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression, T data); + S VisitNamedArgumentExpression(NamedArgumentExpression namedArgumentExpression, T data); + S VisitNamedExpression(NamedExpression namedExpression, T data); + S VisitNullReferenceExpression(NullReferenceExpression nullReferenceExpression, T data); + S VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression, T data); + S VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression, T data); + S VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression, T data); + S VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression, T data); + S VisitPrimitiveExpression(PrimitiveExpression primitiveExpression, T data); + S VisitSizeOfExpression(SizeOfExpression sizeOfExpression, T data); + S VisitStackAllocExpression(StackAllocExpression stackAllocExpression, T data); + S VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression, T data); + S VisitTypeOfExpression(TypeOfExpression typeOfExpression, T data); + S VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression, T data); + S VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression, T data); + S VisitUncheckedExpression(UncheckedExpression uncheckedExpression, T data); + + S VisitQueryExpression(QueryExpression queryExpression, T data); + S VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause, T data); + S VisitQueryFromClause(QueryFromClause queryFromClause, T data); + S VisitQueryLetClause(QueryLetClause queryLetClause, T data); + S VisitQueryWhereClause(QueryWhereClause queryWhereClause, T data); + S VisitQueryJoinClause(QueryJoinClause queryJoinClause, T data); + S VisitQueryOrderClause(QueryOrderClause queryOrderClause, T data); + S VisitQueryOrdering(QueryOrdering queryOrdering, T data); + S VisitQuerySelectClause(QuerySelectClause querySelectClause, T data); + S VisitQueryGroupClause(QueryGroupClause queryGroupClause, T data); + + S VisitAttribute(Attribute attribute, T data); + S VisitAttributeSection(AttributeSection attributeSection, T data); + S VisitDelegateDeclaration(DelegateDeclaration delegateDeclaration, T data); + S VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration, T data); + S VisitTypeDeclaration(TypeDeclaration typeDeclaration, T data); + S VisitUsingAliasDeclaration(UsingAliasDeclaration usingAliasDeclaration, T data); + S VisitUsingDeclaration(UsingDeclaration usingDeclaration, T data); + S VisitExternAliasDeclaration(ExternAliasDeclaration externAliasDeclaration, T data); + + S VisitBlockStatement(BlockStatement blockStatement, T data); + S VisitBreakStatement(BreakStatement breakStatement, T data); + S VisitCheckedStatement(CheckedStatement checkedStatement, T data); + S VisitContinueStatement(ContinueStatement continueStatement, T data); + S VisitDoWhileStatement(DoWhileStatement doWhileStatement, T data); + S VisitEmptyStatement(EmptyStatement emptyStatement, T data); + S VisitExpressionStatement(ExpressionStatement expressionStatement, T data); + S VisitFixedStatement(FixedStatement fixedStatement, T data); + S VisitForeachStatement(ForeachStatement foreachStatement, T data); + S VisitForStatement(ForStatement forStatement, T data); + S VisitGotoCaseStatement(GotoCaseStatement gotoCaseStatement, T data); + S VisitGotoDefaultStatement(GotoDefaultStatement gotoDefaultStatement, T data); + S VisitGotoStatement(GotoStatement gotoStatement, T data); + S VisitIfElseStatement(IfElseStatement ifElseStatement, T data); + S VisitLabelStatement(LabelStatement labelStatement, T data); + S VisitLockStatement(LockStatement lockStatement, T data); + S VisitReturnStatement(ReturnStatement returnStatement, T data); + S VisitSwitchStatement(SwitchStatement switchStatement, T data); + S VisitSwitchSection(SwitchSection switchSection, T data); + S VisitCaseLabel(CaseLabel caseLabel, T data); + S VisitThrowStatement(ThrowStatement throwStatement, T data); + S VisitTryCatchStatement(TryCatchStatement tryCatchStatement, T data); + S VisitCatchClause(CatchClause catchClause, T data); + S VisitUncheckedStatement(UncheckedStatement uncheckedStatement, T data); + S VisitUnsafeStatement(UnsafeStatement unsafeStatement, T data); + S VisitUsingStatement(UsingStatement usingStatement, T data); + S VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement, T data); + S VisitWhileStatement(WhileStatement whileStatement, T data); + S VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement, T data); + S VisitYieldReturnStatement(YieldReturnStatement yieldReturnStatement, T data); + + S VisitAccessor(Accessor accessor, T data); + S VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration, T data); + S VisitConstructorInitializer(ConstructorInitializer constructorInitializer, T data); + S VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration, T data); + S VisitEnumMemberDeclaration(EnumMemberDeclaration enumMemberDeclaration, T data); + S VisitEventDeclaration(EventDeclaration eventDeclaration, T data); + S VisitCustomEventDeclaration(CustomEventDeclaration customEventDeclaration, T data); + S VisitFieldDeclaration(FieldDeclaration fieldDeclaration, T data); + S VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration, T data); + S VisitMethodDeclaration(MethodDeclaration methodDeclaration, T data); + S VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration, T data); + S VisitParameterDeclaration(ParameterDeclaration parameterDeclaration, T data); + S VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration, T data); + S VisitVariableInitializer(VariableInitializer variableInitializer, T data); + S VisitFixedFieldDeclaration(FixedFieldDeclaration fixedFieldDeclaration, T data); + S VisitFixedVariableInitializer(FixedVariableInitializer fixedVariableInitializer, T data); + + S VisitSyntaxTree(SyntaxTree syntaxTree, T data); + S VisitSimpleType(SimpleType simpleType, T data); + S VisitMemberType(MemberType memberType, T data); + S VisitComposedType(ComposedType composedType, T data); + S VisitArraySpecifier(ArraySpecifier arraySpecifier, T data); + S VisitPrimitiveType(PrimitiveType primitiveType, T data); + + S VisitComment(Comment comment, T data); + S VisitNewLine(NewLineNode newLineNode, T data); + S VisitWhitespace(WhitespaceNode whitespaceNode, T data); + S VisitText(TextNode textNode, T data); + S VisitPreProcessorDirective(PreProcessorDirective preProcessorDirective, T data); + S VisitDocumentationReference(DocumentationReference documentationReference, T data); + + S VisitTypeParameterDeclaration(TypeParameterDeclaration typeParameterDeclaration, T data); + S VisitConstraint(Constraint constraint, T data); + S VisitCSharpTokenNode(CSharpTokenNode cSharpTokenNode, T data); + S VisitIdentifier(Identifier identifier, T data); + + S VisitNullNode(AstNode nullNode, T data); + S VisitErrorNode(AstNode errorNode, T data); + S VisitPatternPlaceholder(AstNode placeholder, PatternMatching.Pattern pattern, T data); + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Identifier.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Identifier.cs new file mode 100644 index 000000000..cf403afbd --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Identifier.cs @@ -0,0 +1,173 @@ +// +// Identifier.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class Identifier : AstNode + { + public new static readonly Identifier Null = new NullIdentifier (); + sealed class NullIdentifier : Identifier + { + public override bool IsNull { + get { + return true; + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullNode(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return other == null || other.IsNull; + } + } + + public override NodeType NodeType { + get { + return NodeType.Token; + } + } + + string name; + public string Name { + get { return this.name; } + set { + if (value == null) + throw new ArgumentNullException("value"); + ThrowIfFrozen(); + this.name = value; + } + } + + TextLocation startLocation; + public override TextLocation StartLocation { + get { + return startLocation; + } + } + + internal void SetStartLocation(TextLocation value) + { + ThrowIfFrozen(); + this.startLocation = value; + } + + const uint verbatimBit = 1u << AstNodeFlagsUsedBits; + + public bool IsVerbatim { + get { + return (flags & verbatimBit) != 0; + } + set { + ThrowIfFrozen(); + if (value) + flags |= verbatimBit; + else + flags &= ~verbatimBit; + } + } + + public override TextLocation EndLocation { + get { + return new TextLocation (StartLocation.Line, StartLocation.Column + (Name ?? "").Length + (IsVerbatim ? 1 : 0)); + } + } + + Identifier () + { + this.name = string.Empty; + } + + protected Identifier (string name, TextLocation location) + { + if (name == null) + throw new ArgumentNullException("name"); + this.Name = name; + this.startLocation = location; + } + + public static Identifier Create (string name) + { + return Create (name, TextLocation.Empty); + } + + public static Identifier Create (string name, TextLocation location) + { + if (string.IsNullOrEmpty(name)) + return Identifier.Null; + if (name[0] == '@') + return new Identifier (name.Substring (1), new TextLocation (location.Line, location.Column + 1)) { IsVerbatim = true }; + else + return new Identifier (name, location); + } + + public static Identifier Create (string name, TextLocation location, bool isVerbatim) + { + if (string.IsNullOrEmpty (name)) + return Identifier.Null; + + if (isVerbatim) + return new Identifier (name, location) { IsVerbatim = true }; + return new Identifier (name, location); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitIdentifier (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitIdentifier (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitIdentifier (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + Identifier o = other as Identifier; + return o != null && !o.IsNull && MatchString(this.Name, o.Name); + } + } +} \ No newline at end of file diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/IdentifierExpressionBackreference.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/IdentifierExpressionBackreference.cs new file mode 100644 index 000000000..7bfacd990 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/IdentifierExpressionBackreference.cs @@ -0,0 +1,54 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// 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.Linq; +using ICSharpCode.NRefactory.PatternMatching; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Matches identifier expressions that have the same identifier as the referenced variable/type definition/method definition. + /// + public class IdentifierExpressionBackreference : Pattern + { + readonly string referencedGroupName; + + public string ReferencedGroupName { + get { return referencedGroupName; } + } + + public IdentifierExpressionBackreference(string referencedGroupName) + { + if (referencedGroupName == null) + throw new ArgumentNullException("referencedGroupName"); + this.referencedGroupName = referencedGroupName; + } + + public override bool DoMatch(INode other, Match match) + { + CSharp.IdentifierExpression ident = other as CSharp.IdentifierExpression; + if (ident == null || ident.TypeArguments.Any()) + return false; + CSharp.AstNode referenced = (CSharp.AstNode)match.Get(referencedGroupName).Last(); + if (referenced == null) + return false; + return ident.Identifier == referenced.GetChildByRole(CSharp.Roles.Identifier).Name; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/MemberType.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/MemberType.cs new file mode 100644 index 000000000..2045d00ed --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/MemberType.cs @@ -0,0 +1,150 @@ +// +// FullTypeName.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.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 System.Linq; +using System.Text; +using ICSharpCode.NRefactory.CSharp.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class MemberType : AstType + { + public static readonly Role TargetRole = new Role("Target", AstType.Null); + + bool isDoubleColon; + + public bool IsDoubleColon { + get { return isDoubleColon; } + set { + ThrowIfFrozen(); + isDoubleColon = value; + } + } + + public AstType Target { + get { return GetChildByRole(TargetRole); } + set { SetChildByRole(TargetRole, value); } + } + + public string MemberName { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole (Roles.Identifier, Identifier.Create (value)); + } + } + + public Identifier MemberNameToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole (Roles.Identifier, value); + } + } + + public AstNodeCollection TypeArguments { + get { return GetChildrenByRole (Roles.TypeArgument); } + } + + public MemberType () + { + } + + public MemberType (AstType target, string memberName) + { + this.Target = target; + this.MemberName = memberName; + } + + public MemberType (AstType target, string memberName, IEnumerable typeArguments) + { + this.Target = target; + this.MemberName = memberName; + foreach (var arg in typeArguments) { + AddChild (arg, Roles.TypeArgument); + } + } + + public MemberType (AstType target, string memberName, params AstType[] typeArguments) : this (target, memberName, (IEnumerable)typeArguments) + { + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitMemberType (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitMemberType (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitMemberType (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + MemberType o = other as MemberType; + return o != null && this.IsDoubleColon == o.IsDoubleColon + && MatchString(this.MemberName, o.MemberName) && this.Target.DoMatch(o.Target, match) + && this.TypeArguments.DoMatch(o.TypeArguments, match); + } + + public override ITypeReference ToTypeReference(NameLookupMode lookupMode, InterningProvider interningProvider = null) + { + if (interningProvider == null) + interningProvider = InterningProvider.Dummy; + + TypeOrNamespaceReference t; + if (this.IsDoubleColon) { + SimpleType st = this.Target as SimpleType; + if (st != null) { + t = interningProvider.Intern(new AliasNamespaceReference(interningProvider.Intern(st.Identifier))); + } else { + t = null; + } + } else { + t = this.Target.ToTypeReference(lookupMode, interningProvider) as TypeOrNamespaceReference; + } + if (t == null) + return SpecialType.UnknownType; + var typeArguments = new List(); + foreach (var ta in this.TypeArguments) { + typeArguments.Add(ta.ToTypeReference(lookupMode, interningProvider)); + } + string memberName = interningProvider.Intern(this.MemberName); + return interningProvider.Intern(new MemberTypeOrNamespaceReference(t, memberName, interningProvider.InternList(typeArguments), lookupMode)); + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Modifiers.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Modifiers.cs new file mode 100644 index 000000000..eb320495c --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Modifiers.cs @@ -0,0 +1,65 @@ +// +// Modifiers.cs +// +// Author: +// Mike Krüger +// +// Copyright (C) 2008 Novell, Inc (http://www.novell.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; + +namespace ICSharpCode.NRefactory.CSharp +{ + [Flags] + public enum Modifiers + { + None = 0, + + Private = 0x0001, + Internal = 0x0002, + Protected = 0x0004, + Public = 0x0008, + + Abstract = 0x0010, + Virtual = 0x0020, + Sealed = 0x0040, + Static = 0x0080, + Override = 0x0100, + Readonly = 0x0200, + Const = 0x0400, + New = 0x0800, + Partial = 0x1000, + + Extern = 0x2000, + Volatile = 0x4000, + Unsafe = 0x8000, + Async = 0x10000, + + VisibilityMask = Private | Internal | Protected | Public, + + /// + /// Special value used to match any modifiers during pattern matching. + /// + Any = unchecked((int)0x80000000) + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/NodeType.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/NodeType.cs new file mode 100644 index 000000000..cf96a60c8 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/NodeType.cs @@ -0,0 +1,56 @@ +// +// NodeType.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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; + +namespace ICSharpCode.NRefactory.CSharp +{ + public enum NodeType + { + Unknown, + /// + /// AstType + /// + TypeReference, + /// + /// Type or delegate declaration + /// + TypeDeclaration, + Member, + Statement, + Expression, + Token, + QueryClause, + /// + /// Comment or whitespace or pre-processor directive + /// + Whitespace, + /// + /// Placeholder for a pattern + /// + Pattern + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/ObservableAstVisitor.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/ObservableAstVisitor.cs new file mode 100644 index 000000000..898d51864 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/ObservableAstVisitor.cs @@ -0,0 +1,857 @@ +// +// ObservableAstVisitor.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.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; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class ObservableAstVisitor : IAstVisitor + { + void Visit(Action enter, Action leave, T node) where T : AstNode + { + if (enter != null) + enter(node); + AstNode next; + for (var child = node.FirstChild; child != null; child = next) { + // Store next to allow the loop to continue + // if the visitor removes/replaces children. + next = child.NextSibling; + child.AcceptVisitor (this); + } + if (leave != null) + leave(node); + } + + void IAstVisitor.VisitNullNode(AstNode nullNode) + { + } + + void IAstVisitor.VisitErrorNode(AstNode nullNode) + { + } + + public event Action EnterSyntaxTree, LeaveSyntaxTree; + + void IAstVisitor.VisitSyntaxTree(SyntaxTree unit) + { + Visit(EnterSyntaxTree, LeaveSyntaxTree, unit); + } + + public event Action EnterComment, LeaveComment; + + void IAstVisitor.VisitComment(Comment comment) + { + Visit(EnterComment, LeaveComment, comment); + } + + public event Action EnterNewLine, LeaveNewLine; + + void IAstVisitor.VisitNewLine(NewLineNode newLineNode) + { + Visit(EnterNewLine, LeaveNewLine, newLineNode); + } + + public event Action EnterWhitespace, LeaveWhitespace; + + void IAstVisitor.VisitWhitespace(WhitespaceNode whitespace) + { + Visit(EnterWhitespace, LeaveWhitespace, whitespace); + } + + public event Action EnterText, LeaveText; + + void IAstVisitor.VisitText(TextNode textNode) + { + Visit(EnterText, LeaveText, textNode); + } + + public event Action EnterPreProcessorDirective, LeavePreProcessorDirective; + void IAstVisitor.VisitPreProcessorDirective(PreProcessorDirective preProcessorDirective) + { + Visit(EnterPreProcessorDirective, LeavePreProcessorDirective, preProcessorDirective); + } + + public event Action EnterDocumentationReference, LeaveDocumentationReference; + + void IAstVisitor.VisitDocumentationReference(DocumentationReference documentationReference) + { + Visit(EnterDocumentationReference, LeaveDocumentationReference, documentationReference); + } + + public event Action EnterIdentifier, LeaveIdentifier; + + void IAstVisitor.VisitIdentifier(Identifier identifier) + { + Visit(EnterIdentifier, LeaveIdentifier, identifier); + } + + public event Action EnterCSharpTokenNode, LeaveCSharpTokenNode; + + void IAstVisitor.VisitCSharpTokenNode(CSharpTokenNode token) + { + Visit(EnterCSharpTokenNode, LeaveCSharpTokenNode, token); + } + + public event Action EnterPrimitiveType, LeavePrimitiveType; + + void IAstVisitor.VisitPrimitiveType(PrimitiveType primitiveType) + { + Visit(EnterPrimitiveType, LeavePrimitiveType, primitiveType); + } + + public event Action EnterComposedType, LeaveComposedType; + + void IAstVisitor.VisitComposedType(ComposedType composedType) + { + Visit(EnterComposedType, LeaveComposedType, composedType); + } + + public event Action EnterSimpleType, LeaveSimpleType; + + void IAstVisitor.VisitSimpleType(SimpleType simpleType) + { + Visit(EnterSimpleType, LeaveSimpleType, simpleType); + } + + public event Action EnterMemberType, LeaveMemberType; + + void IAstVisitor.VisitMemberType(MemberType memberType) + { + Visit(EnterMemberType, LeaveMemberType, memberType); + } + + public event Action EnterAttribute, LeaveAttribute; + + void IAstVisitor.VisitAttribute(Attribute attribute) + { + Visit(EnterAttribute, LeaveAttribute, attribute); + } + + public event Action EnterAttributeSection, LeaveAttributeSection; + + void IAstVisitor.VisitAttributeSection(AttributeSection attributeSection) + { + Visit(EnterAttributeSection, LeaveAttributeSection, attributeSection); + } + + public event Action EnterDelegateDeclaration, LeaveDelegateDeclaration; + + void IAstVisitor.VisitDelegateDeclaration(DelegateDeclaration delegateDeclaration) + { + Visit(EnterDelegateDeclaration, LeaveDelegateDeclaration, delegateDeclaration); + } + + public event Action EnterNamespaceDeclaration, LeaveNamespaceDeclaration; + + void IAstVisitor.VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration) + { + Visit(EnterNamespaceDeclaration, LeaveNamespaceDeclaration, namespaceDeclaration); + } + + public event Action EnterTypeDeclaration, LeaveTypeDeclaration; + + void IAstVisitor.VisitTypeDeclaration(TypeDeclaration typeDeclaration) + { + Visit(EnterTypeDeclaration, LeaveTypeDeclaration, typeDeclaration); + } + + public event Action EnterTypeParameterDeclaration, LeaveTypeParameterDeclaration; + + void IAstVisitor.VisitTypeParameterDeclaration(TypeParameterDeclaration typeParameterDeclaration) + { + Visit(EnterTypeParameterDeclaration, LeaveTypeParameterDeclaration, typeParameterDeclaration); + } + + public event Action EnterEnumMemberDeclaration, LeaveEnumMemberDeclaration; + + void IAstVisitor.VisitEnumMemberDeclaration(EnumMemberDeclaration enumMemberDeclaration) + { + Visit(EnterEnumMemberDeclaration, LeaveEnumMemberDeclaration, enumMemberDeclaration); + } + + public event Action EnterUsingDeclaration, LeaveUsingDeclaration; + + void IAstVisitor.VisitUsingDeclaration(UsingDeclaration usingDeclaration) + { + Visit(EnterUsingDeclaration, LeaveUsingDeclaration, usingDeclaration); + } + + public event Action EnterUsingAliasDeclaration, LeaveUsingAliasDeclaration; + + void IAstVisitor.VisitUsingAliasDeclaration(UsingAliasDeclaration usingDeclaration) + { + Visit(EnterUsingAliasDeclaration, LeaveUsingAliasDeclaration, usingDeclaration); + } + + public event Action EnterExternAliasDeclaration, LeaveExternAliasDeclaration; + + void IAstVisitor.VisitExternAliasDeclaration(ExternAliasDeclaration externAliasDeclaration) + { + Visit(EnterExternAliasDeclaration, LeaveExternAliasDeclaration, externAliasDeclaration); + } + + public event Action EnterConstructorDeclaration, LeaveConstructorDeclaration; + + void IAstVisitor.VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration) + { + Visit(EnterConstructorDeclaration, LeaveConstructorDeclaration, constructorDeclaration); + } + + public event Action EnterConstructorInitializer, LeaveConstructorInitializer; + + void IAstVisitor.VisitConstructorInitializer(ConstructorInitializer constructorInitializer) + { + Visit(EnterConstructorInitializer, LeaveConstructorInitializer, constructorInitializer); + } + + public event Action EnterDestructorDeclaration, LeaveDestructorDeclaration; + + void IAstVisitor.VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration) + { + Visit(EnterDestructorDeclaration, LeaveDestructorDeclaration, destructorDeclaration); + } + + public event Action EnterEventDeclaration, LeaveEventDeclaration; + + void IAstVisitor.VisitEventDeclaration(EventDeclaration eventDeclaration) + { + Visit(EnterEventDeclaration, LeaveEventDeclaration, eventDeclaration); + } + + public event Action EnterCustomEventDeclaration, LeaveCustomEventDeclaration; + + void IAstVisitor.VisitCustomEventDeclaration(CustomEventDeclaration eventDeclaration) + { + Visit(EnterCustomEventDeclaration, LeaveCustomEventDeclaration, eventDeclaration); + } + + public event Action EnterFieldDeclaration, LeaveFieldDeclaration; + + void IAstVisitor.VisitFieldDeclaration(FieldDeclaration fieldDeclaration) + { + Visit(EnterFieldDeclaration, LeaveFieldDeclaration, fieldDeclaration); + } + + public event Action EnterFixedFieldDeclaration, LeaveFixedFieldDeclaration; + + void IAstVisitor.VisitFixedFieldDeclaration(FixedFieldDeclaration fixedFieldDeclaration) + { + Visit(EnterFixedFieldDeclaration, LeaveFixedFieldDeclaration, fixedFieldDeclaration); + } + + public event Action EnterFixedVariableInitializer, LeaveFixedVariableInitializer; + + void IAstVisitor.VisitFixedVariableInitializer(FixedVariableInitializer fixedVariableInitializer) + { + Visit(EnterFixedVariableInitializer, LeaveFixedVariableInitializer, fixedVariableInitializer); + } + + public event Action EnterIndexerDeclaration, LeaveIndexerDeclaration; + + void IAstVisitor.VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration) + { + Visit(EnterIndexerDeclaration, LeaveIndexerDeclaration, indexerDeclaration); + } + + public event Action EnterMethodDeclaration, LeaveMethodDeclaration; + + void IAstVisitor.VisitMethodDeclaration(MethodDeclaration methodDeclaration) + { + Visit(EnterMethodDeclaration, LeaveMethodDeclaration, methodDeclaration); + } + + public event Action EnterOperatorDeclaration, LeaveOperatorDeclaration; + + void IAstVisitor.VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration) + { + Visit(EnterOperatorDeclaration, LeaveOperatorDeclaration, operatorDeclaration); + } + + public event Action EnterPropertyDeclaration, LeavePropertyDeclaration; + + void IAstVisitor.VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration) + { + Visit(EnterPropertyDeclaration, LeavePropertyDeclaration, propertyDeclaration); + } + + public event Action EnterAccessor, LeaveAccessor; + + void IAstVisitor.VisitAccessor(Accessor accessor) + { + Visit(EnterAccessor, LeaveAccessor, accessor); + } + + public event Action EnterVariableInitializer, LeaveVariableInitializer; + + void IAstVisitor.VisitVariableInitializer(VariableInitializer variableInitializer) + { + Visit(EnterVariableInitializer, LeaveVariableInitializer, variableInitializer); + } + + public event Action EnterParameterDeclaration, LeaveParameterDeclaration; + + void IAstVisitor.VisitParameterDeclaration(ParameterDeclaration parameterDeclaration) + { + Visit(EnterParameterDeclaration, LeaveParameterDeclaration, parameterDeclaration); + } + + public event Action EnterConstraint, LeaveConstraint; + + void IAstVisitor.VisitConstraint(Constraint constraint) + { + Visit(EnterConstraint, LeaveConstraint, constraint); + } + + public event Action EnterBlockStatement, LeaveBlockStatement; + + void IAstVisitor.VisitBlockStatement(BlockStatement blockStatement) + { + Visit(EnterBlockStatement, LeaveBlockStatement, blockStatement); + } + + public event Action EnterExpressionStatement, LeaveExpressionStatement; + + void IAstVisitor.VisitExpressionStatement(ExpressionStatement expressionStatement) + { + Visit(EnterExpressionStatement, LeaveExpressionStatement, expressionStatement); + } + + public event Action EnterBreakStatement, LeaveBreakStatement; + + void IAstVisitor.VisitBreakStatement(BreakStatement breakStatement) + { + Visit(EnterBreakStatement, LeaveBreakStatement, breakStatement); + } + + public event Action EnterCheckedStatement, LeaveCheckedStatement; + + void IAstVisitor.VisitCheckedStatement(CheckedStatement checkedStatement) + { + Visit(EnterCheckedStatement, LeaveCheckedStatement, checkedStatement); + } + + public event Action EnterContinueStatement, LeaveContinueStatement; + + void IAstVisitor.VisitContinueStatement(ContinueStatement continueStatement) + { + Visit(EnterContinueStatement, LeaveContinueStatement, continueStatement); + } + + public event Action EnterDoWhileStatement, LeaveDoWhileStatement; + + void IAstVisitor.VisitDoWhileStatement(DoWhileStatement doWhileStatement) + { + Visit(EnterDoWhileStatement, LeaveDoWhileStatement, doWhileStatement); + } + + public event Action EnterEmptyStatement, LeaveEmptyStatement; + + void IAstVisitor.VisitEmptyStatement(EmptyStatement emptyStatement) + { + Visit(EnterEmptyStatement, LeaveEmptyStatement, emptyStatement); + } + + public event Action EnterFixedStatement, LeaveFixedStatement; + + void IAstVisitor.VisitFixedStatement(FixedStatement fixedStatement) + { + Visit(EnterFixedStatement, LeaveFixedStatement, fixedStatement); + } + + public event Action EnterForeachStatement, LeaveForeachStatement; + + void IAstVisitor.VisitForeachStatement(ForeachStatement foreachStatement) + { + Visit(EnterForeachStatement, LeaveForeachStatement, foreachStatement); + } + + public event Action EnterForStatement, LeaveForStatement; + + void IAstVisitor.VisitForStatement(ForStatement forStatement) + { + Visit(EnterForStatement, LeaveForStatement, forStatement); + } + + public event Action EnterGotoCaseStatement, LeaveGotoCaseStatement; + + void IAstVisitor.VisitGotoCaseStatement(GotoCaseStatement gotoCaseStatement) + { + Visit(EnterGotoCaseStatement, LeaveGotoCaseStatement, gotoCaseStatement); + } + + public event Action EnterGotoDefaultStatement, LeaveGotoDefaultStatement; + + void IAstVisitor.VisitGotoDefaultStatement(GotoDefaultStatement gotoDefaultStatement) + { + Visit(EnterGotoDefaultStatement, LeaveGotoDefaultStatement, gotoDefaultStatement); + } + + public event Action EnterGotoStatement, LeaveGotoStatement; + + void IAstVisitor.VisitGotoStatement(GotoStatement gotoStatement) + { + Visit(EnterGotoStatement, LeaveGotoStatement, gotoStatement); + } + + public event Action EnterIfElseStatement, LeaveIfElseStatement; + + void IAstVisitor.VisitIfElseStatement(IfElseStatement ifElseStatement) + { + Visit(EnterIfElseStatement, LeaveIfElseStatement, ifElseStatement); + } + + public event Action EnterLabelStatement, LeaveLabelStatement; + + void IAstVisitor.VisitLabelStatement(LabelStatement labelStatement) + { + Visit(EnterLabelStatement, LeaveLabelStatement, labelStatement); + } + + public event Action EnterLockStatement, LeaveLockStatement; + + void IAstVisitor.VisitLockStatement(LockStatement lockStatement) + { + Visit(EnterLockStatement, LeaveLockStatement, lockStatement); + } + + public event Action EnterReturnStatement, LeaveReturnStatement; + + void IAstVisitor.VisitReturnStatement(ReturnStatement returnStatement) + { + Visit(EnterReturnStatement, LeaveReturnStatement, returnStatement); + } + + public event Action EnterSwitchStatement, LeaveSwitchStatement; + + void IAstVisitor.VisitSwitchStatement(SwitchStatement switchStatement) + { + Visit(EnterSwitchStatement, LeaveSwitchStatement, switchStatement); + } + + public event Action EnterSwitchSection, LeaveSwitchSection; + + void IAstVisitor.VisitSwitchSection(SwitchSection switchSection) + { + Visit(EnterSwitchSection, LeaveSwitchSection, switchSection); + } + + public event Action EnterCaseLabel, LeaveCaseLabel; + + void IAstVisitor.VisitCaseLabel(CaseLabel caseLabel) + { + Visit(EnterCaseLabel, LeaveCaseLabel, caseLabel); + } + + public event Action EnterThrowStatement, LeaveThrowStatement; + + void IAstVisitor.VisitThrowStatement(ThrowStatement throwStatement) + { + Visit(EnterThrowStatement, LeaveThrowStatement, throwStatement); + } + + public event Action EnterTryCatchStatement, LeaveTryCatchStatement; + + void IAstVisitor.VisitTryCatchStatement(TryCatchStatement tryCatchStatement) + { + Visit(EnterTryCatchStatement, LeaveTryCatchStatement, tryCatchStatement); + } + + public event Action EnterCatchClause, LeaveCatchClause; + + void IAstVisitor.VisitCatchClause(CatchClause catchClause) + { + Visit(EnterCatchClause, LeaveCatchClause, catchClause); + } + + public event Action EnterUncheckedStatement, LeaveUncheckedStatement; + + void IAstVisitor.VisitUncheckedStatement(UncheckedStatement uncheckedStatement) + { + Visit(EnterUncheckedStatement, LeaveUncheckedStatement, uncheckedStatement); + } + + public event Action EnterUnsafeStatement, LeaveUnsafeStatement; + + void IAstVisitor.VisitUnsafeStatement(UnsafeStatement unsafeStatement) + { + Visit(EnterUnsafeStatement, LeaveUnsafeStatement, unsafeStatement); + } + + public event Action EnterUsingStatement, LeaveUsingStatement; + + void IAstVisitor.VisitUsingStatement(UsingStatement usingStatement) + { + Visit(EnterUsingStatement, LeaveUsingStatement, usingStatement); + } + + public event Action EnterVariableDeclarationStatement, LeaveVariableDeclarationStatement; + + void IAstVisitor.VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement) + { + Visit(EnterVariableDeclarationStatement, LeaveVariableDeclarationStatement, variableDeclarationStatement); + } + + public event Action EnterWhileStatement, LeaveWhileStatement; + + void IAstVisitor.VisitWhileStatement(WhileStatement whileStatement) + { + Visit(EnterWhileStatement, LeaveWhileStatement, whileStatement); + } + + public event Action EnterYieldBreakStatement, LeaveYieldBreakStatement; + + void IAstVisitor.VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement) + { + Visit(EnterYieldBreakStatement, LeaveYieldBreakStatement, yieldBreakStatement); + } + + public event Action EnterYieldReturnStatement, LeaveYieldReturnStatement; + + void IAstVisitor.VisitYieldReturnStatement(YieldReturnStatement yieldStatement) + { + Visit(EnterYieldReturnStatement, LeaveYieldReturnStatement, yieldStatement); + } + + public event Action EnterAnonymousMethodExpression, LeaveAnonymousMethodExpression; + + void IAstVisitor.VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression) + { + Visit(EnterAnonymousMethodExpression, LeaveAnonymousMethodExpression, anonymousMethodExpression); + } + + public event Action EnterLambdaExpression, LeaveLambdaExpression; + + void IAstVisitor.VisitLambdaExpression(LambdaExpression lambdaExpression) + { + Visit(EnterLambdaExpression, LeaveLambdaExpression, lambdaExpression); + } + + public event Action EnterAssignmentExpression, LeaveAssignmentExpression; + + void IAstVisitor.VisitAssignmentExpression(AssignmentExpression assignmentExpression) + { + Visit(EnterAssignmentExpression, LeaveAssignmentExpression, assignmentExpression); + } + + public event Action EnterBaseReferenceExpression, LeaveBaseReferenceExpression; + + void IAstVisitor.VisitBaseReferenceExpression(BaseReferenceExpression baseReferenceExpression) + { + Visit(EnterBaseReferenceExpression, LeaveBaseReferenceExpression, baseReferenceExpression); + } + + public event Action EnterBinaryOperatorExpression, LeaveBinaryOperatorExpression; + + void IAstVisitor.VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression) + { + Visit(EnterBinaryOperatorExpression, LeaveBinaryOperatorExpression, binaryOperatorExpression); + } + + public event Action EnterCastExpression, LeaveCastExpression; + + void IAstVisitor.VisitCastExpression(CastExpression castExpression) + { + Visit(EnterCastExpression, LeaveCastExpression, castExpression); + } + + public event Action EnterCheckedExpression, LeaveCheckedExpression; + + void IAstVisitor.VisitCheckedExpression(CheckedExpression checkedExpression) + { + Visit(EnterCheckedExpression, LeaveCheckedExpression, checkedExpression); + } + + public event Action EnterConditionalExpression, LeaveConditionalExpression; + + void IAstVisitor.VisitConditionalExpression(ConditionalExpression conditionalExpression) + { + Visit(EnterConditionalExpression, LeaveConditionalExpression, conditionalExpression); + } + + public event Action EnterIdentifierExpression, LeaveIdentifierExpression; + + void IAstVisitor.VisitIdentifierExpression(IdentifierExpression identifierExpression) + { + Visit(EnterIdentifierExpression, LeaveIdentifierExpression, identifierExpression); + } + + public event Action EnterIndexerExpression, LeaveIndexerExpression; + + void IAstVisitor.VisitIndexerExpression(IndexerExpression indexerExpression) + { + Visit(EnterIndexerExpression, LeaveIndexerExpression, indexerExpression); + } + + public event Action EnterInvocationExpression, LeaveInvocationExpression; + + void IAstVisitor.VisitInvocationExpression(InvocationExpression invocationExpression) + { + Visit(EnterInvocationExpression, LeaveInvocationExpression, invocationExpression); + } + + public event Action EnterDirectionExpression, LeaveDirectionExpression; + + void IAstVisitor.VisitDirectionExpression(DirectionExpression directionExpression) + { + Visit(EnterDirectionExpression, LeaveDirectionExpression, directionExpression); + } + + public event Action EnterMemberReferenceExpression, LeaveMemberReferenceExpression; + + void IAstVisitor.VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression) + { + Visit(EnterMemberReferenceExpression, LeaveMemberReferenceExpression, memberReferenceExpression); + } + + public event Action EnterNullReferenceExpression, LeaveNullReferenceExpression; + + void IAstVisitor.VisitNullReferenceExpression(NullReferenceExpression nullReferenceExpression) + { + Visit(EnterNullReferenceExpression, LeaveNullReferenceExpression, nullReferenceExpression); + } + + public event Action EnterObjectCreateExpression, LeaveObjectCreateExpression; + + void IAstVisitor.VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression) + { + Visit(EnterObjectCreateExpression, LeaveObjectCreateExpression, objectCreateExpression); + } + + public event Action EnterAnonymousTypeCreateExpression, LeaveAnonymousTypeCreateExpression; + + void IAstVisitor.VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression) + { + Visit(EnterAnonymousTypeCreateExpression, LeaveAnonymousTypeCreateExpression, anonymousTypeCreateExpression); + } + + public event Action EnterArrayCreateExpression, LeaveArrayCreateExpression; + + void IAstVisitor.VisitArrayCreateExpression(ArrayCreateExpression arraySCreateExpression) + { + Visit(EnterArrayCreateExpression, LeaveArrayCreateExpression, arraySCreateExpression); + } + + public event Action EnterParenthesizedExpression, LeaveParenthesizedExpression; + + void IAstVisitor.VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression) + { + Visit(EnterParenthesizedExpression, LeaveParenthesizedExpression, parenthesizedExpression); + } + + public event Action EnterPointerReferenceExpression, LeavePointerReferenceExpression; + + void IAstVisitor.VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression) + { + Visit(EnterPointerReferenceExpression, LeavePointerReferenceExpression, pointerReferenceExpression); + } + + public event Action EnterPrimitiveExpression, LeavePrimitiveExpression; + + void IAstVisitor.VisitPrimitiveExpression(PrimitiveExpression primitiveExpression) + { + Visit(EnterPrimitiveExpression, LeavePrimitiveExpression, primitiveExpression); + } + + public event Action EnterSizeOfExpression, LeaveSizeOfExpression; + + void IAstVisitor.VisitSizeOfExpression(SizeOfExpression sizeOfExpression) + { + Visit(EnterSizeOfExpression, LeaveSizeOfExpression, sizeOfExpression); + } + + public event Action EnterStackAllocExpression, LeaveStackAllocExpression; + + void IAstVisitor.VisitStackAllocExpression(StackAllocExpression stackAllocExpression) + { + Visit(EnterStackAllocExpression, LeaveStackAllocExpression, stackAllocExpression); + } + + public event Action EnterThisReferenceExpression, LeaveThisReferenceExpression; + + void IAstVisitor.VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression) + { + Visit(EnterThisReferenceExpression, LeaveThisReferenceExpression, thisReferenceExpression); + } + + public event Action EnterTypeOfExpression, LeaveTypeOfExpression; + + void IAstVisitor.VisitTypeOfExpression(TypeOfExpression typeOfExpression) + { + Visit(EnterTypeOfExpression, LeaveTypeOfExpression, typeOfExpression); + } + + public event Action EnterTypeReferenceExpression, LeaveTypeReferenceExpression; + + void IAstVisitor.VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression) + { + Visit(EnterTypeReferenceExpression, LeaveTypeReferenceExpression, typeReferenceExpression); + } + + public event Action EnterUnaryOperatorExpression, LeaveUnaryOperatorExpression; + + void IAstVisitor.VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression) + { + Visit(EnterUnaryOperatorExpression, LeaveUnaryOperatorExpression, unaryOperatorExpression); + } + + public event Action EnterUncheckedExpression, LeaveUncheckedExpression; + + void IAstVisitor.VisitUncheckedExpression(UncheckedExpression uncheckedExpression) + { + Visit(EnterUncheckedExpression, LeaveUncheckedExpression, uncheckedExpression); + } + + public event Action EnterQueryExpression, LeaveQueryExpression; + + void IAstVisitor.VisitQueryExpression(QueryExpression queryExpression) + { + Visit(EnterQueryExpression, LeaveQueryExpression, queryExpression); + } + + public event Action EnterQueryContinuationClause, LeaveQueryContinuationClause; + + void IAstVisitor.VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause) + { + Visit(EnterQueryContinuationClause, LeaveQueryContinuationClause, queryContinuationClause); + } + + public event Action EnterQueryFromClause, LeaveQueryFromClause; + + void IAstVisitor.VisitQueryFromClause(QueryFromClause queryFromClause) + { + Visit(EnterQueryFromClause, LeaveQueryFromClause, queryFromClause); + } + + public event Action EnterQueryLetClause, LeaveQueryLetClause; + + void IAstVisitor.VisitQueryLetClause(QueryLetClause queryLetClause) + { + Visit(EnterQueryLetClause, LeaveQueryLetClause, queryLetClause); + } + + public event Action EnterQueryWhereClause, LeaveQueryWhereClause; + + void IAstVisitor.VisitQueryWhereClause(QueryWhereClause queryWhereClause) + { + Visit(EnterQueryWhereClause, LeaveQueryWhereClause, queryWhereClause); + } + + public event Action EnterQueryJoinClause, LeaveQueryJoinClause; + + void IAstVisitor.VisitQueryJoinClause(QueryJoinClause queryJoinClause) + { + Visit(EnterQueryJoinClause, LeaveQueryJoinClause, queryJoinClause); + } + + public event Action EnterQueryOrderClause, LeaveQueryOrderClause; + + void IAstVisitor.VisitQueryOrderClause(QueryOrderClause queryOrderClause) + { + Visit(EnterQueryOrderClause, LeaveQueryOrderClause, queryOrderClause); + } + + public event Action EnterQueryOrdering, LeaveQueryOrdering; + + void IAstVisitor.VisitQueryOrdering(QueryOrdering queryOrdering) + { + Visit(EnterQueryOrdering, LeaveQueryOrdering, queryOrdering); + } + + public event Action EnterQuerySelectClause, LeaveQuerySelectClause; + + void IAstVisitor.VisitQuerySelectClause(QuerySelectClause querySelectClause) + { + Visit(EnterQuerySelectClause, LeaveQuerySelectClause, querySelectClause); + } + + public event Action EnterQueryGroupClause, LeaveQueryGroupClause; + + void IAstVisitor.VisitQueryGroupClause(QueryGroupClause queryGroupClause) + { + Visit(EnterQueryGroupClause, LeaveQueryGroupClause, queryGroupClause); + } + + public event Action EnterAsExpression, LeaveAsExpression; + + void IAstVisitor.VisitAsExpression(AsExpression asExpression) + { + Visit(EnterAsExpression, LeaveAsExpression, asExpression); + } + + public event Action EnterIsExpression, LeaveIsExpression; + + void IAstVisitor.VisitIsExpression(IsExpression isExpression) + { + Visit(EnterIsExpression, LeaveIsExpression, isExpression); + } + + public event Action EnterDefaultValueExpression, LeaveDefaultValueExpression; + + void IAstVisitor.VisitDefaultValueExpression(DefaultValueExpression defaultValueExpression) + { + Visit(EnterDefaultValueExpression, LeaveDefaultValueExpression, defaultValueExpression); + } + + public event Action EnterUndocumentedExpression, LeaveUndocumentedExpression; + + void IAstVisitor.VisitUndocumentedExpression(UndocumentedExpression undocumentedExpression) + { + Visit(EnterUndocumentedExpression, LeaveUndocumentedExpression, undocumentedExpression); + } + + public event Action EnterArrayInitializerExpression, LeaveArrayInitializerExpression; + + void IAstVisitor.VisitArrayInitializerExpression(ArrayInitializerExpression arrayInitializerExpression) + { + Visit(EnterArrayInitializerExpression, LeaveArrayInitializerExpression, arrayInitializerExpression); + } + + public event Action EnterArraySpecifier, LeaveArraySpecifier; + + void IAstVisitor.VisitArraySpecifier(ArraySpecifier arraySpecifier) + { + Visit(EnterArraySpecifier, LeaveArraySpecifier, arraySpecifier); + } + + public event Action EnterNamedArgumentExpression, LeaveNamedArgumentExpression; + + void IAstVisitor.VisitNamedArgumentExpression(NamedArgumentExpression namedArgumentExpression) + { + Visit(EnterNamedArgumentExpression, LeaveNamedArgumentExpression, namedArgumentExpression); + } + + public event Action EnterNamedExpression, LeaveNamedExpression; + + void IAstVisitor.VisitNamedExpression(NamedExpression namedExpression) + { + Visit(EnterNamedExpression, LeaveNamedExpression, namedExpression); + } + + void IAstVisitor.VisitPatternPlaceholder(AstNode placeholder, PatternMatching.Pattern pattern) + { + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/PrimitiveType.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/PrimitiveType.cs new file mode 100644 index 000000000..5b52a37ac --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/PrimitiveType.cs @@ -0,0 +1,163 @@ +// +// FullTypeName.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.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 System.Linq; +using ICSharpCode.NRefactory.CSharp.Resolver; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem.Implementation; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class PrimitiveType : AstType + { + TextLocation location; + string keyword = string.Empty; + + public string Keyword { + get { return keyword; } + set { + if (value == null) + throw new ArgumentNullException(); + ThrowIfFrozen(); + keyword = value; + } + } + + public KnownTypeCode KnownTypeCode { + get { return GetTypeCodeForPrimitiveType(this.Keyword); } + } + + public PrimitiveType() + { + } + + public PrimitiveType(string keyword) + { + this.Keyword = keyword; + } + + public PrimitiveType(string keyword, TextLocation location) + { + this.Keyword = keyword; + this.location = location; + } + + public override TextLocation StartLocation { + get { + return location; + } + } + + internal void SetStartLocation(TextLocation value) + { + ThrowIfFrozen(); + this.location = value; + } + + public override TextLocation EndLocation { + get { + return new TextLocation (location.Line, location.Column + keyword.Length); + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPrimitiveType (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPrimitiveType (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitPrimitiveType (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + PrimitiveType o = other as PrimitiveType; + return o != null && MatchString(this.Keyword, o.Keyword); + } + + public override string ToString(CSharpFormattingOptions formattingOptions) + { + return Keyword; + } + + public override ITypeReference ToTypeReference(NameLookupMode lookupMode, InterningProvider interningProvider = null) + { + KnownTypeCode typeCode = GetTypeCodeForPrimitiveType(this.Keyword); + if (typeCode == KnownTypeCode.None) + return new UnknownType(null, this.Keyword); + else + return KnownTypeReference.Get(typeCode); + } + + public static KnownTypeCode GetTypeCodeForPrimitiveType(string keyword) + { + switch (keyword) { + case "string": + return KnownTypeCode.String; + case "int": + return KnownTypeCode.Int32; + case "uint": + return KnownTypeCode.UInt32; + case "object": + return KnownTypeCode.Object; + case "bool": + return KnownTypeCode.Boolean; + case "sbyte": + return KnownTypeCode.SByte; + case "byte": + return KnownTypeCode.Byte; + case "short": + return KnownTypeCode.Int16; + case "ushort": + return KnownTypeCode.UInt16; + case "long": + return KnownTypeCode.Int64; + case "ulong": + return KnownTypeCode.UInt64; + case "float": + return KnownTypeCode.Single; + case "double": + return KnownTypeCode.Double; + case "decimal": + return KnownTypeCode.Decimal; + case "char": + return KnownTypeCode.Char; + case "void": + return KnownTypeCode.Void; + default: + return KnownTypeCode.None; + } + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Roles.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Roles.cs new file mode 100644 index 000000000..a7408c91d --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Roles.cs @@ -0,0 +1,96 @@ +// +// Roles.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2012 Xamarin +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; + +namespace ICSharpCode.NRefactory.CSharp +{ + public static class Roles + { + public static readonly Role Root = AstNode.RootRole; + + // some pre defined constants for common roles + public static readonly Role Identifier = new Role ("Identifier", CSharp.Identifier.Null); + public static readonly Role Body = new Role ("Body", CSharp.BlockStatement.Null); + public static readonly Role Parameter = new Role ("Parameter"); + public static readonly Role Argument = new Role ("Argument", CSharp.Expression.Null); + public static readonly Role Type = new Role ("Type", CSharp.AstType.Null); + public static readonly Role Expression = new Role ("Expression", CSharp.Expression.Null); + public static readonly Role TargetExpression = new Role ("Target", CSharp.Expression.Null); + public readonly static Role Condition = new Role ("Condition", CSharp.Expression.Null); + public static readonly Role TypeParameter = new Role ("TypeParameter"); + public static readonly Role TypeArgument = new Role ("TypeArgument", CSharp.AstType.Null); + public readonly static Role Constraint = new Role ("Constraint"); + public static readonly Role Variable = new Role ("Variable", VariableInitializer.Null); + public static readonly Role EmbeddedStatement = new Role ("EmbeddedStatement", CSharp.Statement.Null); + public readonly static Role TypeMemberRole = new Role ("TypeMember"); + + + // public static readonly TokenRole Keyword = new TokenRole ("Keyword", CSharpTokenNode.Null); +// public static readonly TokenRole InKeyword = new TokenRole ("InKeyword", CSharpTokenNode.Null); + + // some pre defined constants for most used punctuation + public static readonly TokenRole LPar = new TokenRole ("("); + public static readonly TokenRole RPar = new TokenRole (")"); + public static readonly TokenRole LBracket = new TokenRole ("["); + public static readonly TokenRole RBracket = new TokenRole ("]"); + public static readonly TokenRole LBrace = new TokenRole ("{"); + public static readonly TokenRole RBrace = new TokenRole ("}"); + public static readonly TokenRole LChevron = new TokenRole ("<"); + public static readonly TokenRole RChevron = new TokenRole (">"); + public static readonly TokenRole Comma = new TokenRole (","); + public static readonly TokenRole Dot = new TokenRole ("."); + public static readonly TokenRole Semicolon = new TokenRole (";"); + public static readonly TokenRole Assign = new TokenRole ("="); + public static readonly TokenRole Colon = new TokenRole (":"); + public static readonly TokenRole DoubleColon = new TokenRole ("::"); + public static readonly Role Comment = new Role ("Comment"); + public static readonly Role NewLine = new Role ("NewLine"); + public static readonly Role Whitespace = new Role ("Whitespace"); + public static readonly Role Text = new Role ("Text"); + public static readonly Role PreProcessorDirective = new Role ("PreProcessorDirective"); + public static readonly Role Error = new Role ("Error"); + + public readonly static Role BaseType = new Role ("BaseType", AstType.Null); + + public static readonly Role Attribute = new Role ("Attribute"); + public static readonly Role AttributeTargetRole = new Role ("AttributeTarget", CSharpTokenNode.Null); + + public readonly static TokenRole WhereKeyword = new TokenRole ("where"); + public readonly static Role ConstraintTypeParameter = new Role ("TypeParameter", SimpleType.Null); + public readonly static TokenRole DelegateKeyword = new TokenRole ("delegate"); + public static readonly TokenRole ExternKeyword = new TokenRole ("extern"); + public static readonly TokenRole AliasKeyword = new TokenRole ("alias"); + public static readonly TokenRole NamespaceKeyword = new TokenRole ("namespace"); + + public static readonly TokenRole EnumKeyword = new TokenRole ("enum"); + public static readonly TokenRole InterfaceKeyword = new TokenRole ("interface"); + public static readonly TokenRole StructKeyword = new TokenRole ("struct"); + public static readonly TokenRole ClassKeyword = new TokenRole ("class"); + + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/SimpleType.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/SimpleType.cs new file mode 100644 index 000000000..529a62fbe --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/SimpleType.cs @@ -0,0 +1,169 @@ +// +// FullTypeName.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.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 System.Linq; +using System.Text; +using ICSharpCode.NRefactory.CSharp.Resolver; +using ICSharpCode.NRefactory.CSharp.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class SimpleType : AstType + { + #region Null + public new static readonly SimpleType Null = new NullSimpleType (); + + sealed class NullSimpleType : SimpleType + { + public override bool IsNull { + get { + return true; + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullNode(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return other == null || other.IsNull; + } + + public override ITypeReference ToTypeReference(NameLookupMode lookupMode, InterningProvider interningProvider) + { + return SpecialType.UnknownType; + } + } + #endregion + + public SimpleType() + { + } + + public SimpleType(string identifier) + { + this.Identifier = identifier; + } + + public SimpleType (Identifier identifier) + { + this.IdentifierToken = identifier; + } + + public SimpleType(string identifier, TextLocation location) + { + SetChildByRole (Roles.Identifier, CSharp.Identifier.Create (identifier, location)); + } + + public SimpleType (string identifier, IEnumerable typeArguments) + { + this.Identifier = identifier; + foreach (var arg in typeArguments) { + AddChild (arg, Roles.TypeArgument); + } + } + + public SimpleType (string identifier, params AstType[] typeArguments) : this (identifier, (IEnumerable)typeArguments) + { + } + + public string Identifier { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole (Roles.Identifier, CSharp.Identifier.Create (value)); + } + } + + public Identifier IdentifierToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole (Roles.Identifier, value); + } + } + + public AstNodeCollection TypeArguments { + get { return GetChildrenByRole (Roles.TypeArgument); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitSimpleType (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitSimpleType (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitSimpleType (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + SimpleType o = other as SimpleType; + return o != null && MatchString(this.Identifier, o.Identifier) && this.TypeArguments.DoMatch(o.TypeArguments, match); + } + + public override ITypeReference ToTypeReference(NameLookupMode lookupMode, InterningProvider interningProvider = null) + { + if (interningProvider == null) + interningProvider = InterningProvider.Dummy; + var typeArguments = new List(); + foreach (var ta in this.TypeArguments) { + typeArguments.Add(ta.ToTypeReference(lookupMode, interningProvider)); + } + string identifier = interningProvider.Intern(this.Identifier); + if (typeArguments.Count == 0 && string.IsNullOrEmpty(identifier)) { + // empty SimpleType is used for typeof(List<>). + return SpecialType.UnboundTypeArgument; + } + var t = new SimpleTypeOrNamespaceReference(identifier, interningProvider.InternList(typeArguments), lookupMode); + return interningProvider.Intern(t); + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/BlockStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/BlockStatement.cs new file mode 100644 index 000000000..24b9cd106 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/BlockStatement.cs @@ -0,0 +1,164 @@ +// +// BlockStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// { Statements } + /// + public class BlockStatement : Statement, IEnumerable + { + public static readonly Role StatementRole = new Role("Statement", Statement.Null); + + #region Null + public static readonly new BlockStatement Null = new NullBlockStatement (); + sealed class NullBlockStatement : BlockStatement + { + public override bool IsNull { + get { + return true; + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullNode(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return other == null || other.IsNull; + } + } + #endregion + + #region PatternPlaceholder + public static implicit operator BlockStatement(PatternMatching.Pattern pattern) + { + return pattern != null ? new PatternPlaceholder(pattern) : null; + } + + sealed class PatternPlaceholder : BlockStatement, PatternMatching.INode + { + readonly PatternMatching.Pattern child; + + public PatternPlaceholder(PatternMatching.Pattern child) + { + this.child = child; + } + + public override NodeType NodeType { + get { return NodeType.Pattern; } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPatternPlaceholder(this, child); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPatternPlaceholder(this, child); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitPatternPlaceholder(this, child, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return child.DoMatch(other, match); + } + + bool PatternMatching.INode.DoMatchCollection(Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) + { + return child.DoMatchCollection(role, pos, match, backtrackingInfo); + } + } + #endregion + + public CSharpTokenNode LBraceToken { + get { return GetChildByRole (Roles.LBrace); } + } + + public AstNodeCollection Statements { + get { return GetChildrenByRole (StatementRole); } + } + + public CSharpTokenNode RBraceToken { + get { return GetChildByRole (Roles.RBrace); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitBlockStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitBlockStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitBlockStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + BlockStatement o = other as BlockStatement; + return o != null && !o.IsNull && this.Statements.DoMatch(o.Statements, match); + } + + public void Add(Statement statement) + { + AddChild(statement, StatementRole); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return this.Statements.GetEnumerator(); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return this.Statements.GetEnumerator(); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/BreakStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/BreakStatement.cs new file mode 100644 index 000000000..4bb4e39ef --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/BreakStatement.cs @@ -0,0 +1,65 @@ +// +// BreakStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// break; + /// + public class BreakStatement : Statement + { + public static readonly TokenRole BreakKeywordRole = new TokenRole ("break"); + + public CSharpTokenNode BreakToken { + get { return GetChildByRole (BreakKeywordRole); } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitBreakStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitBreakStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitBreakStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + BreakStatement o = other as BreakStatement; + return o != null; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/CheckedStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/CheckedStatement.cs new file mode 100644 index 000000000..803067aff --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/CheckedStatement.cs @@ -0,0 +1,75 @@ +// +// CheckedStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// checked BodyBlock + /// + public class CheckedStatement : Statement + { + public static readonly TokenRole CheckedKeywordRole = new TokenRole ("checked"); + + public CSharpTokenNode CheckedToken { + get { return GetChildByRole (CheckedKeywordRole); } + } + + public BlockStatement Body { + get { return GetChildByRole (Roles.Body); } + set { SetChildByRole (Roles.Body, value); } + } + + public CheckedStatement () + { + } + + public CheckedStatement (BlockStatement body) + { + AddChild (body, Roles.Body); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitCheckedStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitCheckedStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitCheckedStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + CheckedStatement o = other as CheckedStatement; + return o != null && this.Body.DoMatch(o.Body, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/ContinueStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/ContinueStatement.cs new file mode 100644 index 000000000..aac1690b2 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/ContinueStatement.cs @@ -0,0 +1,65 @@ +// +// ContinueStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// continue; + /// + public class ContinueStatement : Statement + { + public static readonly TokenRole ContinueKeywordRole = new TokenRole ("continue"); + + public CSharpTokenNode ContinueToken { + get { return GetChildByRole (ContinueKeywordRole); } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitContinueStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitContinueStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitContinueStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ContinueStatement o = other as ContinueStatement; + return o != null; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/DoWhileStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/DoWhileStatement.cs new file mode 100644 index 000000000..280ca7cea --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/DoWhileStatement.cs @@ -0,0 +1,99 @@ +// +// DoWhileStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.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; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// "do EmbeddedStatement while(Condition);" + /// + public class DoWhileStatement : Statement + { + public static readonly TokenRole DoKeywordRole = new TokenRole ("do"); + public static readonly TokenRole WhileKeywordRole = new TokenRole ("while"); + + public CSharpTokenNode DoToken { + get { return GetChildByRole (DoKeywordRole); } + } + + public Statement EmbeddedStatement { + get { return GetChildByRole (Roles.EmbeddedStatement); } + set { SetChildByRole (Roles.EmbeddedStatement, value); } + } + + public CSharpTokenNode WhileToken { + get { return GetChildByRole (WhileKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public Expression Condition { + get { return GetChildByRole (Roles.Condition); } + set { SetChildByRole (Roles.Condition, value); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitDoWhileStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitDoWhileStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitDoWhileStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + DoWhileStatement o = other as DoWhileStatement; + return o != null && this.EmbeddedStatement.DoMatch(o.EmbeddedStatement, match) && this.Condition.DoMatch(o.Condition, match); + } + + public DoWhileStatement() + { + } + + public DoWhileStatement(Expression condition, Statement embeddedStatement) + { + this.Condition = condition; + this.EmbeddedStatement = embeddedStatement; + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/EmptyStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/EmptyStatement.cs new file mode 100644 index 000000000..deaa3a9c4 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/EmptyStatement.cs @@ -0,0 +1,72 @@ +// +// EmptyStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// ; + /// + public class EmptyStatement : Statement + { + public TextLocation Location { + get; + set; + } + + public override TextLocation StartLocation { + get { + return Location; + } + } + + public override TextLocation EndLocation { + get { + return new TextLocation (Location.Line, Location.Column + 1); + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitEmptyStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitEmptyStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitEmptyStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + EmptyStatement o = other as EmptyStatement; + return o != null; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/ExpressionStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/ExpressionStatement.cs new file mode 100644 index 000000000..1fdc4ddc4 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/ExpressionStatement.cs @@ -0,0 +1,73 @@ +// +// ExpressionStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Expression; + /// + public class ExpressionStatement : Statement + { + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitExpressionStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitExpressionStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitExpressionStatement (this, data); + } + + public ExpressionStatement() + { + } + + public ExpressionStatement(Expression expression) + { + this.Expression = expression; + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ExpressionStatement o = other as ExpressionStatement; + return o != null && this.Expression.DoMatch(o.Expression, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/FixedStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/FixedStatement.cs new file mode 100644 index 000000000..d44366504 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/FixedStatement.cs @@ -0,0 +1,85 @@ +// +// FixedStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// fixed (Type Variables) EmbeddedStatement + /// + public class FixedStatement : Statement + { + public static readonly TokenRole FixedKeywordRole = new TokenRole ("fixed"); + + public CSharpTokenNode FixedToken { + get { return GetChildByRole (FixedKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstType Type { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole (Roles.Type, value); } + } + + public AstNodeCollection Variables { + get { return GetChildrenByRole (Roles.Variable); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public Statement EmbeddedStatement { + get { return GetChildByRole (Roles.EmbeddedStatement); } + set { SetChildByRole (Roles.EmbeddedStatement, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitFixedStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitFixedStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitFixedStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + FixedStatement o = other as FixedStatement; + return o != null && this.Type.DoMatch(o.Type, match) && this.Variables.DoMatch(o.Variables, match) && this.EmbeddedStatement.DoMatch(o.EmbeddedStatement, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/ForStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/ForStatement.cs new file mode 100644 index 000000000..d369536d0 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/ForStatement.cs @@ -0,0 +1,97 @@ +// +// ForStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// for (Initializers; Condition; Iterators) EmbeddedStatement + /// + public class ForStatement : Statement + { + public static readonly TokenRole ForKeywordRole = new TokenRole ("for"); + public readonly static Role InitializerRole = new Role("Initializer", Statement.Null); + public readonly static Role IteratorRole = new Role("Iterator", Statement.Null); + + public CSharpTokenNode ForToken { + get { return GetChildByRole (ForKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + /// + /// Gets the list of initializer statements. + /// Note: this contains multiple statements for "for (a = 2, b = 1; a > b; a--)", but contains + /// only a single statement for "for (int a = 2, b = 1; a > b; a--)" (a single VariableDeclarationStatement with two variables) + /// + public AstNodeCollection Initializers { + get { return GetChildrenByRole (InitializerRole); } + } + + public Expression Condition { + get { return GetChildByRole (Roles.Condition); } + set { SetChildByRole (Roles.Condition, value); } + } + + public AstNodeCollection Iterators { + get { return GetChildrenByRole (IteratorRole); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public Statement EmbeddedStatement { + get { return GetChildByRole (Roles.EmbeddedStatement); } + set { SetChildByRole (Roles.EmbeddedStatement, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitForStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitForStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitForStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ForStatement o = other as ForStatement; + return o != null && this.Initializers.DoMatch(o.Initializers, match) && this.Condition.DoMatch(o.Condition, match) + && this.Iterators.DoMatch(o.Iterators, match) && this.EmbeddedStatement.DoMatch(o.EmbeddedStatement, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/ForeachStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/ForeachStatement.cs new file mode 100644 index 000000000..b3a9c5f78 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/ForeachStatement.cs @@ -0,0 +1,108 @@ +// +// ForeachStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// foreach (Type VariableName in InExpression) EmbeddedStatement + /// + public class ForeachStatement : Statement + { + public static readonly TokenRole ForeachKeywordRole = new TokenRole ("foreach"); + public static readonly TokenRole InKeywordRole = new TokenRole ("in"); + + public CSharpTokenNode ForeachToken { + get { return GetChildByRole (ForeachKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstType VariableType { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole (Roles.Type, value); } + } + + public string VariableName { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole(Roles.Identifier, Identifier.Create (value)); + } + } + + public Identifier VariableNameToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole(Roles.Identifier, value); + } + } + + public CSharpTokenNode InToken { + get { return GetChildByRole (InKeywordRole); } + } + + public Expression InExpression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public Statement EmbeddedStatement { + get { return GetChildByRole (Roles.EmbeddedStatement); } + set { SetChildByRole (Roles.EmbeddedStatement, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitForeachStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitForeachStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitForeachStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ForeachStatement o = other as ForeachStatement; + return o != null && this.VariableType.DoMatch(o.VariableType, match) && MatchString(this.VariableName, o.VariableName) + && this.InExpression.DoMatch(o.InExpression, match) && this.EmbeddedStatement.DoMatch(o.EmbeddedStatement, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/GotoStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/GotoStatement.cs new file mode 100644 index 000000000..7aff7a82f --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/GotoStatement.cs @@ -0,0 +1,178 @@ +// +// GotoStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// "goto Label;" + /// + public class GotoStatement : Statement + { + public static readonly TokenRole GotoKeywordRole = new TokenRole ("goto"); + + public GotoStatement () + { + } + + public GotoStatement (string label) + { + this.Label = label; + } + + public CSharpTokenNode GotoToken { + get { return GetChildByRole (GotoKeywordRole); } + } + + public string Label { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + if (string.IsNullOrEmpty(value)) + SetChildByRole(Roles.Identifier, null); + else + SetChildByRole(Roles.Identifier, Identifier.Create (value)); + } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitGotoStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitGotoStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitGotoStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + GotoStatement o = other as GotoStatement; + return o != null && MatchString(this.Label, o.Label); + } + } + + /// + /// or "goto case LabelExpression;" + /// + public class GotoCaseStatement : Statement + { + public static readonly TokenRole GotoKeywordRole = new TokenRole ("goto"); + public static readonly TokenRole CaseKeywordRole = new TokenRole ("case"); + + public CSharpTokenNode GotoToken { + get { return GetChildByRole (GotoKeywordRole); } + } + + public CSharpTokenNode CaseToken { + get { return GetChildByRole (CaseKeywordRole); } + } + + /// + /// Used for "goto case LabelExpression;" + /// + public Expression LabelExpression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitGotoCaseStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitGotoCaseStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitGotoCaseStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + GotoCaseStatement o = other as GotoCaseStatement; + return o != null && this.LabelExpression.DoMatch(o.LabelExpression, match); + } + } + + /// + /// or "goto default;" + /// + public class GotoDefaultStatement : Statement + { + public static readonly TokenRole GotoKeywordRole = new TokenRole ("goto"); + public static readonly TokenRole DefaultKeywordRole = new TokenRole ("default"); + + public CSharpTokenNode GotoToken { + get { return GetChildByRole (GotoKeywordRole); } + } + + public CSharpTokenNode DefaultToken { + get { return GetChildByRole (DefaultKeywordRole); } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitGotoDefaultStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitGotoDefaultStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitGotoDefaultStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + GotoDefaultStatement o = other as GotoDefaultStatement; + return o != null; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/IfElseStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/IfElseStatement.cs new file mode 100644 index 000000000..70ece3fd5 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/IfElseStatement.cs @@ -0,0 +1,103 @@ +// +// IfElseStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// if (Condition) TrueStatement else FalseStatement + /// + public class IfElseStatement : Statement + { + public readonly static TokenRole IfKeywordRole = new TokenRole ("if"); + public readonly static Role ConditionRole = Roles.Condition; + public readonly static Role TrueRole = new Role("True", Statement.Null); + public readonly static TokenRole ElseKeywordRole = new TokenRole ("else"); + public readonly static Role FalseRole = new Role("False", Statement.Null); + + public CSharpTokenNode IfToken { + get { return GetChildByRole (IfKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public Expression Condition { + get { return GetChildByRole (ConditionRole); } + set { SetChildByRole (ConditionRole, value); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public Statement TrueStatement { + get { return GetChildByRole (TrueRole); } + set { SetChildByRole (TrueRole, value); } + } + + public CSharpTokenNode ElseToken { + get { return GetChildByRole (ElseKeywordRole); } + } + + public Statement FalseStatement { + get { return GetChildByRole (FalseRole); } + set { SetChildByRole (FalseRole, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitIfElseStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitIfElseStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitIfElseStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + IfElseStatement o = other as IfElseStatement; + return o != null && this.Condition.DoMatch(o.Condition, match) && this.TrueStatement.DoMatch(o.TrueStatement, match) && this.FalseStatement.DoMatch(o.FalseStatement, match); + } + + public IfElseStatement() + { + } + + public IfElseStatement(Expression condition, Statement trueStatement, Statement falseStatement = null) + { + this.Condition = condition; + this.TrueStatement = trueStatement; + this.FalseStatement = falseStatement; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/LabelStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/LabelStatement.cs new file mode 100644 index 000000000..43d22cea7 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/LabelStatement.cs @@ -0,0 +1,73 @@ +// +// LabelStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Label: + /// + public class LabelStatement : Statement + { + public string Label { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole(Roles.Identifier, Identifier.Create (value)); + } + } + + public Identifier LabelToken { + get { return GetChildByRole (Roles.Identifier); } + set { SetChildByRole (Roles.Identifier, value); } + } + + public CSharpTokenNode ColonToken { + get { return GetChildByRole (Roles.Colon); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitLabelStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitLabelStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitLabelStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + LabelStatement o = other as LabelStatement; + return o != null && MatchString(this.Label, o.Label); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/LockStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/LockStatement.cs new file mode 100644 index 000000000..e59f99308 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/LockStatement.cs @@ -0,0 +1,79 @@ +// +// LockStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// lock (Expression) EmbeddedStatement; + /// + public class LockStatement : Statement + { + public static readonly TokenRole LockKeywordRole = new TokenRole ("lock"); + + public CSharpTokenNode LockToken { + get { return GetChildByRole (LockKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public Statement EmbeddedStatement { + get { return GetChildByRole (Roles.EmbeddedStatement); } + set { SetChildByRole (Roles.EmbeddedStatement, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitLockStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitLockStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitLockStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + LockStatement o = other as LockStatement; + return o != null && this.Expression.DoMatch(o.Expression, match) && this.EmbeddedStatement.DoMatch(o.EmbeddedStatement, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/ReturnStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/ReturnStatement.cs new file mode 100644 index 000000000..0970bce43 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/ReturnStatement.cs @@ -0,0 +1,79 @@ +// +// ReturnStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// return Expression; + /// + public class ReturnStatement : Statement + { + public static readonly TokenRole ReturnKeywordRole = new TokenRole ("return"); + + public CSharpTokenNode ReturnToken { + get { return GetChildByRole (ReturnKeywordRole); } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public ReturnStatement () + { + } + + public ReturnStatement (Expression returnExpression) + { + AddChild (returnExpression, Roles.Expression); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitReturnStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitReturnStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitReturnStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ReturnStatement o = other as ReturnStatement; + return o != null && this.Expression.DoMatch(o.Expression, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/Statement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/Statement.cs new file mode 100644 index 000000000..24d3ede92 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/Statement.cs @@ -0,0 +1,132 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Base class for statements. + /// + /// + /// This class is useful even though it doesn't provide any additional functionality: + /// It can be used to communicate more information in APIs, e.g. "this subnode will always be a statement" + /// + public abstract class Statement : AstNode + { + #region Null + public new static readonly Statement Null = new NullStatement (); + + sealed class NullStatement : Statement + { + public override bool IsNull { + get { + return true; + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullNode(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return other == null || other.IsNull; + } + } + #endregion + + #region PatternPlaceholder + public static implicit operator Statement(PatternMatching.Pattern pattern) + { + return pattern != null ? new PatternPlaceholder(pattern) : null; + } + + sealed class PatternPlaceholder : Statement, PatternMatching.INode + { + readonly PatternMatching.Pattern child; + + public PatternPlaceholder(PatternMatching.Pattern child) + { + this.child = child; + } + + public override NodeType NodeType { + get { return NodeType.Pattern; } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPatternPlaceholder(this, child); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPatternPlaceholder(this, child); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitPatternPlaceholder(this, child, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return child.DoMatch(other, match); + } + + bool PatternMatching.INode.DoMatchCollection(Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) + { + return child.DoMatchCollection(role, pos, match, backtrackingInfo); + } + } + #endregion + + public new Statement Clone() + { + return (Statement)base.Clone(); + } + + public Statement ReplaceWith(Func replaceFunction) + { + if (replaceFunction == null) + throw new ArgumentNullException("replaceFunction"); + return (Statement)base.ReplaceWith(node => replaceFunction((Statement)node)); + } + + public override NodeType NodeType { + get { return NodeType.Statement; } + } + + public static implicit operator Statement (Expression type) + { + return new ExpressionStatement(type); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/SwitchStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/SwitchStatement.cs new file mode 100644 index 000000000..fa8e80cd5 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/SwitchStatement.cs @@ -0,0 +1,230 @@ +// +// SwitchStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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.Collections.Generic; +using System.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// switch (Expression) { SwitchSections } + /// + public class SwitchStatement : Statement + { + public static readonly TokenRole SwitchKeywordRole = new TokenRole ("switch"); + public static readonly Role SwitchSectionRole = new Role("SwitchSection"); + + public CSharpTokenNode SwitchToken { + get { return GetChildByRole (SwitchKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public CSharpTokenNode LBraceToken { + get { return GetChildByRole (Roles.LBrace); } + } + + public AstNodeCollection SwitchSections { + get { return GetChildrenByRole (SwitchSectionRole); } + } + + public CSharpTokenNode RBraceToken { + get { return GetChildByRole (Roles.RBrace); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitSwitchStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitSwitchStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitSwitchStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + SwitchStatement o = other as SwitchStatement; + return o != null && this.Expression.DoMatch(o.Expression, match) && this.SwitchSections.DoMatch(o.SwitchSections, match); + } + } + + public class SwitchSection : AstNode + { + #region PatternPlaceholder + public static implicit operator SwitchSection(PatternMatching.Pattern pattern) + { + return pattern != null ? new PatternPlaceholder(pattern) : null; + } + + sealed class PatternPlaceholder : SwitchSection, PatternMatching.INode + { + readonly PatternMatching.Pattern child; + + public PatternPlaceholder(PatternMatching.Pattern child) + { + this.child = child; + } + + public override NodeType NodeType { + get { return NodeType.Pattern; } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPatternPlaceholder(this, child); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPatternPlaceholder(this, child); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitPatternPlaceholder(this, child, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return child.DoMatch(other, match); + } + + bool PatternMatching.INode.DoMatchCollection(Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) + { + return child.DoMatchCollection(role, pos, match, backtrackingInfo); + } + } + #endregion + + public static readonly Role CaseLabelRole = new Role("CaseLabel"); + + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public AstNodeCollection CaseLabels { + get { return GetChildrenByRole (CaseLabelRole); } + } + + public AstNodeCollection Statements { + get { return GetChildrenByRole (Roles.EmbeddedStatement); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitSwitchSection (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitSwitchSection (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitSwitchSection (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + SwitchSection o = other as SwitchSection; + return o != null && this.CaseLabels.DoMatch(o.CaseLabels, match) && this.Statements.DoMatch(o.Statements, match); + } + } + + public class CaseLabel : AstNode + { + public static readonly TokenRole CaseKeywordRole = new TokenRole ("case"); + public static readonly TokenRole DefaultKeywordRole = new TokenRole ("default"); + + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + /// + /// Gets or sets the expression. The expression can be null - if the expression is null, it's the default switch section. + /// + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public CSharpTokenNode ColonToken { + get { return GetChildByRole (Roles.Colon); } + } + + public CaseLabel () + { + } + + public CaseLabel (Expression expression) + { + this.Expression = expression; + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitCaseLabel (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitCaseLabel (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitCaseLabel (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + CaseLabel o = other as CaseLabel; + return o != null && this.Expression.DoMatch(o.Expression, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/ThrowStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/ThrowStatement.cs new file mode 100644 index 000000000..98e27d1e7 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/ThrowStatement.cs @@ -0,0 +1,79 @@ +// +// ThrowStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// throw Expression; + /// + public class ThrowStatement : Statement + { + public static readonly TokenRole ThrowKeywordRole = new TokenRole ("throw"); + + public CSharpTokenNode ThrowToken { + get { return GetChildByRole (ThrowKeywordRole); } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public ThrowStatement () + { + } + + public ThrowStatement (Expression expression) + { + AddChild (expression, Roles.Expression); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitThrowStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitThrowStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitThrowStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ThrowStatement o = other as ThrowStatement; + return o != null && this.Expression.DoMatch(o.Expression, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/TryCatchStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/TryCatchStatement.cs new file mode 100644 index 000000000..3611574b7 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/TryCatchStatement.cs @@ -0,0 +1,241 @@ +// +// TryCatchStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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.Collections.Generic; +using System.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// try TryBlock CatchClauses finally FinallyBlock + /// + public class TryCatchStatement : Statement + { + public static readonly TokenRole TryKeywordRole = new TokenRole ("try"); + public static readonly Role TryBlockRole = new Role("TryBlock", BlockStatement.Null); + public static readonly Role CatchClauseRole = new Role("CatchClause", CatchClause.Null); + public static readonly TokenRole FinallyKeywordRole = new TokenRole ("finally"); + public static readonly Role FinallyBlockRole = new Role("FinallyBlock", BlockStatement.Null); + + public CSharpTokenNode TryToken { + get { return GetChildByRole (TryKeywordRole); } + } + + public BlockStatement TryBlock { + get { return GetChildByRole (TryBlockRole); } + set { SetChildByRole (TryBlockRole, value); } + } + + public AstNodeCollection CatchClauses { + get { return GetChildrenByRole (CatchClauseRole); } + } + + public CSharpTokenNode FinallyToken { + get { return GetChildByRole (FinallyKeywordRole); } + } + + public BlockStatement FinallyBlock { + get { return GetChildByRole (FinallyBlockRole); } + set { SetChildByRole (FinallyBlockRole, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitTryCatchStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitTryCatchStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitTryCatchStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + TryCatchStatement o = other as TryCatchStatement; + return o != null && this.TryBlock.DoMatch(o.TryBlock, match) && this.CatchClauses.DoMatch(o.CatchClauses, match) && this.FinallyBlock.DoMatch(o.FinallyBlock, match); + } + } + + /// + /// catch (Type VariableName) { Body } + /// + public class CatchClause : AstNode + { + public static readonly TokenRole CatchKeywordRole = new TokenRole ("catch"); + + #region Null + public new static readonly CatchClause Null = new NullCatchClause (); + + sealed class NullCatchClause : CatchClause + { + public override bool IsNull { + get { + return true; + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullNode(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return other == null || other.IsNull; + } + } + #endregion + + #region PatternPlaceholder + public static implicit operator CatchClause(PatternMatching.Pattern pattern) + { + return pattern != null ? new PatternPlaceholder(pattern) : null; + } + + sealed class PatternPlaceholder : CatchClause, PatternMatching.INode + { + readonly PatternMatching.Pattern child; + + public PatternPlaceholder(PatternMatching.Pattern child) + { + this.child = child; + } + + public override NodeType NodeType { + get { return NodeType.Pattern; } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPatternPlaceholder(this, child); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPatternPlaceholder(this, child); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitPatternPlaceholder(this, child, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return child.DoMatch(other, match); + } + + bool PatternMatching.INode.DoMatchCollection(Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) + { + return child.DoMatchCollection(role, pos, match, backtrackingInfo); + } + } + #endregion + + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public CSharpTokenNode CatchToken { + get { return GetChildByRole (CatchKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstType Type { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole (Roles.Type, value); } + } + + public string VariableName { + get { return GetChildByRole (Roles.Identifier).Name; } + set { + if (string.IsNullOrEmpty(value)) + SetChildByRole (Roles.Identifier, null); + else + SetChildByRole (Roles.Identifier, Identifier.Create (value)); + } + } + + public Identifier VariableNameToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole(Roles.Identifier, value); + } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public BlockStatement Body { + get { return GetChildByRole (Roles.Body); } + set { SetChildByRole (Roles.Body, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitCatchClause (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitCatchClause (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitCatchClause (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + CatchClause o = other as CatchClause; + return o != null && this.Type.DoMatch(o.Type, match) && MatchString(this.VariableName, o.VariableName) && this.Body.DoMatch(o.Body, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/UncheckedStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/UncheckedStatement.cs new file mode 100644 index 000000000..765cd9ab3 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/UncheckedStatement.cs @@ -0,0 +1,75 @@ +// +// UncheckedStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// unchecked BodyBlock + /// + public class UncheckedStatement : Statement + { + public static readonly TokenRole UncheckedKeywordRole = new TokenRole ("unchecked"); + + public CSharpTokenNode UncheckedToken { + get { return GetChildByRole (UncheckedKeywordRole); } + } + + public BlockStatement Body { + get { return GetChildByRole (Roles.Body); } + set { SetChildByRole (Roles.Body, value); } + } + + public UncheckedStatement () + { + } + + public UncheckedStatement (BlockStatement body) + { + AddChild (body, Roles.Body); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitUncheckedStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitUncheckedStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitUncheckedStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + UncheckedStatement o = other as UncheckedStatement; + return o != null && this.Body.DoMatch(o.Body, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/UnsafeStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/UnsafeStatement.cs new file mode 100644 index 000000000..fa6421ae6 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/UnsafeStatement.cs @@ -0,0 +1,66 @@ +// +// UnsafeStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// unsafe { Body } + /// + public class UnsafeStatement : Statement + { + public static readonly TokenRole UnsafeKeywordRole = new TokenRole ("unsafe"); + + public CSharpTokenNode UnsafeToken { + get { return GetChildByRole (UnsafeKeywordRole); } + } + + public BlockStatement Body { + get { return GetChildByRole (Roles.Body); } + set { SetChildByRole (Roles.Body, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitUnsafeStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitUnsafeStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitUnsafeStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + UnsafeStatement o = other as UnsafeStatement; + return o != null && this.Body.DoMatch(o.Body, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/UsingStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/UsingStatement.cs new file mode 100644 index 000000000..c87304675 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/UsingStatement.cs @@ -0,0 +1,83 @@ +// +// UsingStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// using (ResourceAcquisition) EmbeddedStatement + /// + public class UsingStatement : Statement + { + public static readonly TokenRole UsingKeywordRole = new TokenRole ("using"); + public static readonly Role ResourceAcquisitionRole = new Role("ResourceAcquisition", AstNode.Null); + + public CSharpTokenNode UsingToken { + get { return GetChildByRole (UsingKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + /// + /// Either a VariableDeclarationStatement, or an Expression. + /// + public AstNode ResourceAcquisition { + get { return GetChildByRole (ResourceAcquisitionRole); } + set { SetChildByRole (ResourceAcquisitionRole, value); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public Statement EmbeddedStatement { + get { return GetChildByRole (Roles.EmbeddedStatement); } + set { SetChildByRole (Roles.EmbeddedStatement, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitUsingStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitUsingStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitUsingStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + UsingStatement o = other as UsingStatement; + return o != null && this.ResourceAcquisition.DoMatch(o.ResourceAcquisition, match) && this.EmbeddedStatement.DoMatch(o.EmbeddedStatement, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/VariableDeclarationStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/VariableDeclarationStatement.cs new file mode 100644 index 000000000..32c141d96 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/VariableDeclarationStatement.cs @@ -0,0 +1,90 @@ +// +// VariableDeclarationStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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.Collections.Generic; +using System.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class VariableDeclarationStatement : Statement + { + public static readonly Role ModifierRole = EntityDeclaration.ModifierRole; + + public VariableDeclarationStatement() + { + } + + public VariableDeclarationStatement(AstType type, string name, Expression initializer = null) + { + this.Type = type; + this.Variables.Add(new VariableInitializer(name, initializer)); + } + + public Modifiers Modifiers { + get { return EntityDeclaration.GetModifiers(this); } + set { EntityDeclaration.SetModifiers(this, value); } + } + + public AstType Type { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole (Roles.Type, value); } + } + + public AstNodeCollection Variables { + get { return GetChildrenByRole (Roles.Variable); } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public VariableInitializer GetVariable (string name) + { + return Variables.FirstOrNullObject (vi => vi.Name == name); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitVariableDeclarationStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitVariableDeclarationStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitVariableDeclarationStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + VariableDeclarationStatement o = other as VariableDeclarationStatement; + return o != null && this.Modifiers == o.Modifiers && this.Type.DoMatch(o.Type, match) && this.Variables.DoMatch(o.Variables, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/WhileStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/WhileStatement.cs new file mode 100644 index 000000000..e38daa144 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/WhileStatement.cs @@ -0,0 +1,89 @@ +// +// WhileStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// "while (Condition) EmbeddedStatement" + /// + public class WhileStatement : Statement + { + public static readonly TokenRole WhileKeywordRole = new TokenRole ("while"); + + public CSharpTokenNode WhileToken { + get { return GetChildByRole (WhileKeywordRole); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public Expression Condition { + get { return GetChildByRole (Roles.Condition); } + set { SetChildByRole (Roles.Condition, value); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public Statement EmbeddedStatement { + get { return GetChildByRole (Roles.EmbeddedStatement); } + set { SetChildByRole (Roles.EmbeddedStatement, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitWhileStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitWhileStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitWhileStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + WhileStatement o = other as WhileStatement; + return o != null && this.Condition.DoMatch(o.Condition, match) && this.EmbeddedStatement.DoMatch(o.EmbeddedStatement, match); + } + + public WhileStatement() + { + } + + public WhileStatement(Expression condition, Statement embeddedStatement) + { + this.Condition = condition; + this.EmbeddedStatement = embeddedStatement; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/YieldBreakStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/YieldBreakStatement.cs new file mode 100644 index 000000000..ea5cac4a6 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/YieldBreakStatement.cs @@ -0,0 +1,70 @@ +// +// YieldBreakStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// yield break; + /// + public class YieldBreakStatement : Statement + { + public static readonly TokenRole YieldKeywordRole = new TokenRole ("yield"); + public static readonly TokenRole BreakKeywordRole = new TokenRole ("break"); + + public CSharpTokenNode YieldToken { + get { return GetChildByRole (YieldKeywordRole); } + } + + public CSharpTokenNode BreakToken { + get { return GetChildByRole (BreakKeywordRole); } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitYieldBreakStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitYieldBreakStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitYieldBreakStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + YieldBreakStatement o = other as YieldBreakStatement; + return o != null; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/YieldReturnStatement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/YieldReturnStatement.cs new file mode 100644 index 000000000..6539bf0c0 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/Statements/YieldReturnStatement.cs @@ -0,0 +1,75 @@ +// +// YieldStatement.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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. + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// yield return Expression; + /// + public class YieldReturnStatement : Statement + { + public static readonly TokenRole YieldKeywordRole = new TokenRole ("yield"); + public static readonly TokenRole ReturnKeywordRole = new TokenRole ("return"); + + public CSharpTokenNode YieldToken { + get { return GetChildByRole (YieldKeywordRole); } + } + + public CSharpTokenNode ReturnToken { + get { return GetChildByRole (ReturnKeywordRole); } + } + + public Expression Expression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitYieldReturnStatement (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitYieldReturnStatement (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitYieldReturnStatement (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + YieldReturnStatement o = other as YieldReturnStatement; + return o != null && this.Expression.DoMatch(o.Expression, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/SyntaxExtensions.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/SyntaxExtensions.cs new file mode 100644 index 000000000..bac28bb76 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/SyntaxExtensions.cs @@ -0,0 +1,45 @@ +// Copyright (c) 2013 Daniel Grunwald +// +// 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Extension methods for the syntax tree. + /// + public static class SyntaxExtensions + { + public static bool IsComparisonOperator(this OperatorType operatorType) + { + switch (operatorType) { + case OperatorType.Equality: + case OperatorType.Inequality: + case OperatorType.GreaterThan: + case OperatorType.LessThan: + case OperatorType.GreaterThanOrEqual: + case OperatorType.LessThanOrEqual: + return true; + default: + return false; + } + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/SyntaxTree.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/SyntaxTree.cs new file mode 100644 index 000000000..6ac536159 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/SyntaxTree.cs @@ -0,0 +1,193 @@ +// +// SyntaxTree.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.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.CSharp.Resolver; +using ICSharpCode.NRefactory.CSharp.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem; +using System.Threading; +using Mono.CSharp; +using System.IO; +using ICSharpCode.NRefactory.Editor; + +namespace ICSharpCode.NRefactory.CSharp +{ + [Obsolete("CompilationUnit was renamed to SyntaxTree", true)] + public class CompilationUnit {} + + public class SyntaxTree : AstNode + { + public static readonly Role MemberRole = new Role("Member", AstNode.Null); + + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + string fileName; + + /// + /// Gets/Sets the file name of this syntax tree. + /// + public string FileName { + get { return fileName; } + set { + ThrowIfFrozen(); + fileName = value; + } + } + + 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. + /// (eg. when an error follows an expression). + /// + /// This is used for code completion to 'get the expression before a token - like ., <, ('. + /// + public AstNode TopExpression { + get; + internal set; + } + + public SyntaxTree () + { + } + + /// + /// Gets all defined types in this syntax tree. + /// + /// + /// A list containing or nodes. + /// + public IEnumerable GetTypes(bool includeInnerTypes = false) + { + Stack nodeStack = new Stack (); + nodeStack.Push(this); + while (nodeStack.Count > 0) { + var curNode = nodeStack.Pop(); + if (curNode is TypeDeclaration || curNode is DelegateDeclaration) { + yield return (EntityDeclaration)curNode; + } + foreach (var child in curNode.Children) { + if (!(child is Statement || child is Expression) && + (child.Role != Roles.TypeMemberRole || ((child is TypeDeclaration || child is DelegateDeclaration) && includeInnerTypes))) + nodeStack.Push (child); + } + } + } + + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + SyntaxTree o = other as SyntaxTree; + return o != null && this.Members.DoMatch(o.Members, match); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitSyntaxTree (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitSyntaxTree (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitSyntaxTree (this, data); + } + + /// + /// Converts this syntax tree into a parsed file that can be stored in the type system. + /// + public CSharpUnresolvedFile ToTypeSystem () + { + if (string.IsNullOrEmpty (this.FileName)) + throw new InvalidOperationException ("Cannot use ToTypeSystem() on a syntax tree without file name."); + var v = new TypeSystemConvertVisitor (this.FileName); + v.VisitSyntaxTree (this); + return v.UnresolvedFile; + } + + public static SyntaxTree Parse (string program, string fileName = "", CompilerSettings settings = null, CancellationToken cancellationToken = default (CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + var parser = new CSharpParser (settings); + return parser.Parse (program, fileName); + } + + public static SyntaxTree Parse (TextReader reader, string fileName = "", CompilerSettings settings = null, CancellationToken cancellationToken = default (CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + var parser = new CSharpParser (settings); + return parser.Parse (reader, fileName); + } + + public static SyntaxTree Parse (Stream stream, string fileName = "", CompilerSettings settings = null, CancellationToken cancellationToken = default (CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + var parser = new CSharpParser (settings); + return parser.Parse (stream, fileName); + } + + public static SyntaxTree Parse (ITextSource textSource, string fileName = "", CompilerSettings settings = null, CancellationToken cancellationToken = default (CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + var parser = new CSharpParser (settings); + return parser.Parse (textSource, fileName); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TokenRole.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TokenRole.cs new file mode 100644 index 000000000..8c9c7392a --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TokenRole.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// A specific role only used for C# tokens + /// + public sealed class TokenRole : Role + { + internal readonly static List Tokens = new List (); + internal readonly static List TokenLengths = new List (); + internal readonly uint TokenIndex; + + static TokenRole () + { + // null token + Tokens.Add (""); + TokenLengths.Add (0); + } + + /// + /// Gets the token as string. Note that the token Name and Token value may differ. + /// + public string Token { + get; + private set; + } + + /// + /// Gets the char length of the token. + /// + public int Length { + get; + private set; + } + + + public TokenRole(string token) : base (token, CSharpTokenNode.Null) + { + this.Token = token; + this.Length = token.Length; + + bool found = false; + for (int i = 0; i < Tokens.Count; i++) { + var existingToken = Tokens [i]; + if (existingToken == token) { + TokenIndex = (uint)i; + found = true; + break; + } + } + if (!found) { + TokenIndex = (uint)Tokens.Count; + Tokens.Add (token); + TokenLengths.Add (this.Length); + } + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/Accessor.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/Accessor.cs new file mode 100644 index 000000000..8bd18c477 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/Accessor.cs @@ -0,0 +1,117 @@ +// +// PropertyDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.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 ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// get/set/add/remove + /// + public class Accessor : EntityDeclaration + { + public static readonly new Accessor Null = new NullAccessor (); + sealed class NullAccessor : Accessor + { + public override bool IsNull { + get { + return true; + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullNode(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return other == null || other.IsNull; + } + } + + public override NodeType NodeType { + get { return NodeType.Unknown; } + } + + public override SymbolKind SymbolKind { + get { return SymbolKind.Method; } + } + + /// + /// Gets the 'get'/'set'/'add'/'remove' keyword + /// + public CSharpTokenNode Keyword { + get { + for (AstNode child = this.FirstChild; child != null; child = child.NextSibling) { + if (child.Role == PropertyDeclaration.GetKeywordRole || child.Role == PropertyDeclaration.SetKeywordRole + || child.Role == CustomEventDeclaration.AddKeywordRole || child.Role == CustomEventDeclaration.RemoveKeywordRole) + { + return (CSharpTokenNode)child; + } + } + return CSharpTokenNode.Null; + } + } + + public BlockStatement Body { + get { return GetChildByRole (Roles.Body); } + set { SetChildByRole (Roles.Body, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitAccessor (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitAccessor (this); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitAccessor (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + Accessor o = other as Accessor; + return o != null && !o.IsNull && this.MatchAttributesAndModifiers(o, match) && this.Body.DoMatch(o.Body, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/ConstructorDeclaration.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/ConstructorDeclaration.cs new file mode 100644 index 000000000..23a973a5c --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/ConstructorDeclaration.cs @@ -0,0 +1,190 @@ +// +// ConstructorDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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 ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class ConstructorDeclaration : EntityDeclaration + { + public static readonly Role InitializerRole = new Role("Initializer", ConstructorInitializer.Null); + + public override SymbolKind SymbolKind { + get { return SymbolKind.Constructor; } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstNodeCollection Parameters { + get { return GetChildrenByRole (Roles.Parameter); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public CSharpTokenNode ColonToken { + get { return GetChildByRole (Roles.Colon); } + } + + public ConstructorInitializer Initializer { + get { return GetChildByRole (InitializerRole); } + set { SetChildByRole( InitializerRole, value); } + } + + public BlockStatement Body { + get { return GetChildByRole (Roles.Body); } + set { SetChildByRole (Roles.Body, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitConstructorDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitConstructorDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitConstructorDeclaration (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ConstructorDeclaration o = other as ConstructorDeclaration; + return o != null && this.MatchAttributesAndModifiers(o, match) && this.Parameters.DoMatch(o.Parameters, match) + && this.Initializer.DoMatch(o.Initializer, match) && this.Body.DoMatch(o.Body, match); + } + } + + public enum ConstructorInitializerType { + Any, + Base, + This + } + + public class ConstructorInitializer : AstNode + { + public static readonly TokenRole BaseKeywordRole = new TokenRole ("base"); + public static readonly TokenRole ThisKeywordRole = new TokenRole ("this"); + + public static readonly new ConstructorInitializer Null = new NullConstructorInitializer (); + class NullConstructorInitializer : ConstructorInitializer + { + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public override bool IsNull { + get { + return true; + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullNode(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return other == null || other.IsNull; + } + } + + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public ConstructorInitializerType ConstructorInitializerType { + get; + set; + } + + public CSharpTokenNode Keyword { + get { + if (ConstructorInitializerType == ConstructorInitializerType.Base) + return GetChildByRole(BaseKeywordRole); + else + return GetChildByRole(ThisKeywordRole); + } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstNodeCollection Arguments { + get { return GetChildrenByRole (Roles.Argument); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitConstructorInitializer (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitConstructorInitializer (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitConstructorInitializer (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ConstructorInitializer o = other as ConstructorInitializer; + return o != null && !o.IsNull + && (this.ConstructorInitializerType == ConstructorInitializerType.Any || this.ConstructorInitializerType == o.ConstructorInitializerType) + && this.Arguments.DoMatch(o.Arguments, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/DestructorDeclaration.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/DestructorDeclaration.cs new file mode 100644 index 000000000..0609e5dc6 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/DestructorDeclaration.cs @@ -0,0 +1,76 @@ +// +// DestructorDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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 ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class DestructorDeclaration : EntityDeclaration + { + public static readonly TokenRole TildeRole = new TokenRole ("~"); + + public CSharpTokenNode TildeToken { + get { return GetChildByRole (TildeRole); } + } + + public override SymbolKind SymbolKind { + get { return SymbolKind.Destructor; } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + public BlockStatement Body { + get { return GetChildByRole (Roles.Body); } + set { SetChildByRole (Roles.Body, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitDestructorDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitDestructorDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitDestructorDeclaration (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + DestructorDeclaration o = other as DestructorDeclaration; + return o != null && this.MatchAttributesAndModifiers(o, match) && this.Body.DoMatch(o.Body, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/EntityDeclaration.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/EntityDeclaration.cs new file mode 100644 index 000000000..c02ff21b6 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/EntityDeclaration.cs @@ -0,0 +1,117 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + public abstract class EntityDeclaration : AstNode + { + public static readonly Role AttributeRole = new Role("Attribute"); + public static readonly Role UnattachedAttributeRole = new Role("UnattachedAttribute"); + public static readonly Role ModifierRole = new Role("Modifier"); + public static readonly Role PrivateImplementationTypeRole = new Role("PrivateImplementationType", AstType.Null); + + public override NodeType NodeType { + get { return NodeType.Member; } + } + + public abstract NRefactory.TypeSystem.SymbolKind SymbolKind { get; } + + public AstNodeCollection Attributes { + get { return base.GetChildrenByRole (AttributeRole); } + } + + public Modifiers Modifiers { + get { return GetModifiers(this); } + set { SetModifiers(this, value); } + } + + public bool HasModifier (Modifiers mod) + { + return (Modifiers & mod) == mod; + } + + public IEnumerable ModifierTokens { + get { return GetChildrenByRole (ModifierRole); } + } + + public virtual string Name { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole (Roles.Identifier, Identifier.Create (value, TextLocation.Empty)); + } + } + + public virtual Identifier NameToken { + get { return GetChildByRole (Roles.Identifier); } + set { SetChildByRole (Roles.Identifier, value); } + } + + public virtual AstType ReturnType { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole(Roles.Type, value); } + } + + public CSharpTokenNode SemicolonToken { + get { return GetChildByRole (Roles.Semicolon); } + } + + internal static Modifiers GetModifiers(AstNode node) + { + Modifiers m = 0; + foreach (CSharpModifierToken t in node.GetChildrenByRole (ModifierRole)) { + m |= t.Modifier; + } + return m; + } + + internal static void SetModifiers(AstNode node, Modifiers newValue) + { + Modifiers oldValue = GetModifiers(node); + AstNode insertionPos = node.GetChildrenByRole(AttributeRole).LastOrDefault(); + foreach (Modifiers m in CSharpModifierToken.AllModifiers) { + if ((m & newValue) != 0) { + if ((m & oldValue) == 0) { + // Modifier was added + var newToken = new CSharpModifierToken(TextLocation.Empty, m); + node.InsertChildAfter(insertionPos, newToken, ModifierRole); + insertionPos = newToken; + } else { + // Modifier already exists + insertionPos = node.GetChildrenByRole(ModifierRole).First(t => t.Modifier == m); + } + } else { + if ((m & oldValue) != 0) { + // Modifier was removed + node.GetChildrenByRole (ModifierRole).First(t => t.Modifier == m).Remove(); + } + } + } + } + + protected bool MatchAttributesAndModifiers (EntityDeclaration o, PatternMatching.Match match) + { + return (this.Modifiers == Modifiers.Any || this.Modifiers == o.Modifiers) && this.Attributes.DoMatch (o.Attributes, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/EnumMemberDeclaration.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/EnumMemberDeclaration.cs new file mode 100644 index 000000000..b7c924ab9 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/EnumMemberDeclaration.cs @@ -0,0 +1,72 @@ +// +// EnumMemberDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.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 ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class EnumMemberDeclaration : EntityDeclaration + { + public static readonly Role InitializerRole = new Role("Initializer", Expression.Null); + + public override SymbolKind SymbolKind { + get { return SymbolKind.Field; } + } + + public CSharpTokenNode AssignToken { + get { return GetChildByRole (Roles.Assign); } + } + + public Expression Initializer { + get { return GetChildByRole (InitializerRole); } + set { SetChildByRole (InitializerRole, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitEnumMemberDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitEnumMemberDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitEnumMemberDeclaration (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + EnumMemberDeclaration o = other as EnumMemberDeclaration; + return o != null && this.MatchAttributesAndModifiers(o, match) + && MatchString(this.Name, o.Name) && this.Initializer.DoMatch(o.Initializer, match); + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/EventDeclaration.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/EventDeclaration.cs new file mode 100644 index 000000000..d543f9ea7 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/EventDeclaration.cs @@ -0,0 +1,152 @@ +// +// EventDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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 System.ComponentModel; + +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class EventDeclaration : EntityDeclaration + { + public static readonly TokenRole EventKeywordRole = new TokenRole ("event"); + + public override SymbolKind SymbolKind { + get { return SymbolKind.Event; } + } + + public CSharpTokenNode EventToken { + get { return GetChildByRole (EventKeywordRole); } + } + + public AstNodeCollection Variables { + get { return GetChildrenByRole (Roles.Variable); } + } + + // Hide .Name and .NameToken from users; the actual field names + // are stored in the VariableInitializer. + [EditorBrowsable(EditorBrowsableState.Never)] + public override string Name { + get { return string.Empty; } + set { throw new NotSupportedException(); } + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public override Identifier NameToken { + get { return Identifier.Null; } + set { throw new NotSupportedException(); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitEventDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitEventDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitEventDeclaration (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + EventDeclaration o = other as EventDeclaration; + return o != null && this.MatchAttributesAndModifiers(o, match) + && this.ReturnType.DoMatch(o.ReturnType, match) && this.Variables.DoMatch(o.Variables, match); + } + } + + public class CustomEventDeclaration : EntityDeclaration + { + public static readonly TokenRole EventKeywordRole = new TokenRole ("event"); + public static readonly TokenRole AddKeywordRole = new TokenRole ("add"); + public static readonly TokenRole RemoveKeywordRole = new TokenRole ("remove"); + + public static readonly Role AddAccessorRole = new Role("AddAccessor", Accessor.Null); + public static readonly Role RemoveAccessorRole = new Role("RemoveAccessor", Accessor.Null); + + public override SymbolKind SymbolKind { + get { return SymbolKind.Event; } + } + + /// + /// Gets/Sets the type reference of the interface that is explicitly implemented. + /// Null node if this member is not an explicit interface implementation. + /// + public AstType PrivateImplementationType { + get { return GetChildByRole (PrivateImplementationTypeRole); } + set { SetChildByRole (PrivateImplementationTypeRole, value); } + } + + public CSharpTokenNode LBraceToken { + get { return GetChildByRole (Roles.LBrace); } + } + + public Accessor AddAccessor { + get { return GetChildByRole (AddAccessorRole); } + set { SetChildByRole (AddAccessorRole, value); } + } + + public Accessor RemoveAccessor { + get { return GetChildByRole (RemoveAccessorRole); } + set { SetChildByRole (RemoveAccessorRole, value); } + } + + public CSharpTokenNode RBraceToken { + get { return GetChildByRole (Roles.RBrace); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitCustomEventDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitCustomEventDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitCustomEventDeclaration (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + CustomEventDeclaration o = other as CustomEventDeclaration; + return o != null && MatchString(this.Name, o.Name) + && this.MatchAttributesAndModifiers(o, match) && this.ReturnType.DoMatch(o.ReturnType, match) + && this.PrivateImplementationType.DoMatch(o.PrivateImplementationType, match) + && this.AddAccessor.DoMatch(o.AddAccessor, match) && this.RemoveAccessor.DoMatch(o.RemoveAccessor, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/FieldDeclaration.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/FieldDeclaration.cs new file mode 100644 index 000000000..de220ecd7 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/FieldDeclaration.cs @@ -0,0 +1,79 @@ +// +// FieldDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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.ComponentModel; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class FieldDeclaration : EntityDeclaration + { + public override SymbolKind SymbolKind { + get { return SymbolKind.Field; } + } + + public AstNodeCollection Variables { + get { return GetChildrenByRole (Roles.Variable); } + } + + // Hide .Name and .NameToken from users; the actual field names + // are stored in the VariableInitializer. + [EditorBrowsable(EditorBrowsableState.Never)] + public override string Name { + get { return string.Empty; } + set { throw new NotSupportedException(); } + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public override Identifier NameToken { + get { return Identifier.Null; } + set { throw new NotSupportedException(); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitFieldDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitFieldDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitFieldDeclaration (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + FieldDeclaration o = other as FieldDeclaration; + return o != null && this.MatchAttributesAndModifiers(o, match) + && this.ReturnType.DoMatch(o.ReturnType, match) && this.Variables.DoMatch(o.Variables, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/FixedFieldDeclaration.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/FixedFieldDeclaration.cs new file mode 100644 index 000000000..fea2a2af2 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/FixedFieldDeclaration.cs @@ -0,0 +1,71 @@ +// +// FixedFieldDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.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 ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class FixedFieldDeclaration : EntityDeclaration + { + public static readonly TokenRole FixedKeywordRole = new TokenRole ("fixed"); + public static readonly Role VariableRole = new Role ("FixedVariable"); + + public override SymbolKind SymbolKind { + get { return SymbolKind.Field; } + } + + public CSharpTokenNode FixedToken { + get { return GetChildByRole (FixedKeywordRole); } + } + + public AstNodeCollection Variables { + get { return GetChildrenByRole (VariableRole); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitFixedFieldDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitFixedFieldDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitFixedFieldDeclaration (this, data); + } + + protected internal override bool DoMatch (AstNode other, PatternMatching.Match match) + { + var o = other as FixedFieldDeclaration; + return o != null && this.MatchAttributesAndModifiers (o, match) + && this.ReturnType.DoMatch (o.ReturnType, match) && this.Variables.DoMatch (o.Variables, match); + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/FixedVariableInitializer.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/FixedVariableInitializer.cs new file mode 100644 index 000000000..2c320a826 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/FixedVariableInitializer.cs @@ -0,0 +1,105 @@ +// +// FixedFieldDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.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; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Name [ CountExpression ] + /// + public class FixedVariableInitializer : AstNode + { + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public FixedVariableInitializer() + { + } + + public FixedVariableInitializer (string name, Expression initializer = null) + { + this.Name = name; + this.CountExpression = initializer; + } + + public string Name { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole (Roles.Identifier, Identifier.Create (value)); + } + } + + public Identifier NameToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole (Roles.Identifier, value); + } + } + + public CSharpTokenNode LBracketToken { + get { return GetChildByRole (Roles.LBracket); } + } + + public Expression CountExpression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public CSharpTokenNode RBracketToken { + get { return GetChildByRole (Roles.RBracket); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitFixedVariableInitializer (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitFixedVariableInitializer (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitFixedVariableInitializer (this, data); + } + + protected internal override bool DoMatch (AstNode other, ICSharpCode.NRefactory.PatternMatching.Match match) + { + var o = other as FixedVariableInitializer; + return o != null && MatchString (this.Name, o.Name) && this.CountExpression.DoMatch (o.CountExpression, match); + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/IndexerDeclaration.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/IndexerDeclaration.cs new file mode 100644 index 000000000..56156dd19 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/IndexerDeclaration.cs @@ -0,0 +1,122 @@ +// +// IndexerDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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.ComponentModel; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class IndexerDeclaration : EntityDeclaration + { + public static readonly TokenRole ThisKeywordRole = new TokenRole ("this"); + public static readonly Role GetterRole = PropertyDeclaration.GetterRole; + public static readonly Role SetterRole = PropertyDeclaration.SetterRole; + + public override SymbolKind SymbolKind { + get { return SymbolKind.Indexer; } + } + + /// + /// Gets/Sets the type reference of the interface that is explicitly implemented. + /// Null node if this member is not an explicit interface implementation. + /// + public AstType PrivateImplementationType { + get { return GetChildByRole (PrivateImplementationTypeRole); } + set { SetChildByRole (PrivateImplementationTypeRole, value); } + } + + public override string Name { + get { return "Item"; } + set { throw new NotSupportedException(); } + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public override Identifier NameToken { + get { return Identifier.Null; } + set { throw new NotSupportedException(); } + } + + public CSharpTokenNode LBracketToken { + get { return GetChildByRole (Roles.LBracket); } + } + + public CSharpTokenNode ThisToken { + get { return GetChildByRole (ThisKeywordRole); } + } + + public AstNodeCollection Parameters { + get { return GetChildrenByRole (Roles.Parameter); } + } + + public CSharpTokenNode RBracketToken { + get { return GetChildByRole (Roles.RBracket); } + } + + public CSharpTokenNode LBraceToken { + get { return GetChildByRole (Roles.LBrace); } + } + + public Accessor Getter { + get { return GetChildByRole(GetterRole); } + set { SetChildByRole(GetterRole, value); } + } + + public Accessor Setter { + get { return GetChildByRole(SetterRole); } + set { SetChildByRole(SetterRole, value); } + } + + public CSharpTokenNode RBraceToken { + get { return GetChildByRole (Roles.RBrace); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitIndexerDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitIndexerDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitIndexerDeclaration (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + IndexerDeclaration o = other as IndexerDeclaration; + return o != null + && this.MatchAttributesAndModifiers(o, match) && this.ReturnType.DoMatch(o.ReturnType, match) + && this.PrivateImplementationType.DoMatch(o.PrivateImplementationType, match) + && this.Parameters.DoMatch(o.Parameters, match) + && this.Getter.DoMatch(o.Getter, match) && this.Setter.DoMatch(o.Setter, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/MethodDeclaration.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/MethodDeclaration.cs new file mode 100644 index 000000000..90aaa3047 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/MethodDeclaration.cs @@ -0,0 +1,104 @@ +// +// MethodDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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 ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class MethodDeclaration : EntityDeclaration + { + public override SymbolKind SymbolKind { + get { return SymbolKind.Method; } + } + + /// + /// Gets/Sets the type reference of the interface that is explicitly implemented. + /// Null node if this member is not an explicit interface implementation. + /// + public AstType PrivateImplementationType { + get { return GetChildByRole (PrivateImplementationTypeRole); } + set { SetChildByRole (PrivateImplementationTypeRole, value); } + } + + public AstNodeCollection TypeParameters { + get { return GetChildrenByRole (Roles.TypeParameter); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstNodeCollection Parameters { + get { return GetChildrenByRole (Roles.Parameter); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public AstNodeCollection Constraints { + get { return GetChildrenByRole (Roles.Constraint); } + } + + public BlockStatement Body { + get { return GetChildByRole (Roles.Body); } + set { SetChildByRole (Roles.Body, value); } + } + + public bool IsExtensionMethod { + get { + ParameterDeclaration pd = (ParameterDeclaration)GetChildByRole (Roles.Parameter); + return pd != null && pd.ParameterModifier == ParameterModifier.This; + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitMethodDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitMethodDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitMethodDeclaration (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + MethodDeclaration o = other as MethodDeclaration; + return o != null && MatchString(this.Name, o.Name) + && this.MatchAttributesAndModifiers(o, match) && this.ReturnType.DoMatch(o.ReturnType, match) + && this.PrivateImplementationType.DoMatch(o.PrivateImplementationType, match) + && this.TypeParameters.DoMatch(o.TypeParameters, match) + && this.Parameters.DoMatch(o.Parameters, match) && this.Constraints.DoMatch(o.Constraints, match) + && this.Body.DoMatch(o.Body, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/OperatorDeclaration.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/OperatorDeclaration.cs new file mode 100644 index 000000000..b4ae52a2a --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/OperatorDeclaration.cs @@ -0,0 +1,268 @@ +// +// OperatorDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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.ComponentModel; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public enum OperatorType + { + // Values must correspond to Mono.CSharp.Operator.OpType + // due to the casts used in OperatorDeclaration. + + // Unary operators + LogicalNot = Mono.CSharp.Operator.OpType.LogicalNot, + OnesComplement = Mono.CSharp.Operator.OpType.OnesComplement, + Increment = Mono.CSharp.Operator.OpType.Increment, + Decrement = Mono.CSharp.Operator.OpType.Decrement, + True = Mono.CSharp.Operator.OpType.True, + False = Mono.CSharp.Operator.OpType.False, + + // Unary and Binary operators + Addition = Mono.CSharp.Operator.OpType.Addition, + Subtraction = Mono.CSharp.Operator.OpType.Subtraction, + + UnaryPlus = Mono.CSharp.Operator.OpType.UnaryPlus, + UnaryNegation = Mono.CSharp.Operator.OpType.UnaryNegation, + + // Binary operators + Multiply = Mono.CSharp.Operator.OpType.Multiply, + Division = Mono.CSharp.Operator.OpType.Division, + Modulus = Mono.CSharp.Operator.OpType.Modulus, + BitwiseAnd = Mono.CSharp.Operator.OpType.BitwiseAnd, + BitwiseOr = Mono.CSharp.Operator.OpType.BitwiseOr, + ExclusiveOr = Mono.CSharp.Operator.OpType.ExclusiveOr, + LeftShift = Mono.CSharp.Operator.OpType.LeftShift, + RightShift = Mono.CSharp.Operator.OpType.RightShift, + Equality = Mono.CSharp.Operator.OpType.Equality, + Inequality = Mono.CSharp.Operator.OpType.Inequality, + GreaterThan = Mono.CSharp.Operator.OpType.GreaterThan, + LessThan = Mono.CSharp.Operator.OpType.LessThan, + GreaterThanOrEqual = Mono.CSharp.Operator.OpType.GreaterThanOrEqual, + LessThanOrEqual = Mono.CSharp.Operator.OpType.LessThanOrEqual, + + // Implicit and Explicit + Implicit = Mono.CSharp.Operator.OpType.Implicit, + Explicit = Mono.CSharp.Operator.OpType.Explicit + } + + public class OperatorDeclaration : EntityDeclaration + { + public static readonly TokenRole OperatorKeywordRole = new TokenRole ("operator"); + + // Unary operators + public static readonly TokenRole LogicalNotRole = new TokenRole ("!"); + public static readonly TokenRole OnesComplementRole = new TokenRole ("~"); + public static readonly TokenRole IncrementRole = new TokenRole ("++"); + public static readonly TokenRole DecrementRole = new TokenRole ("--"); + public static readonly TokenRole TrueRole = new TokenRole ("true"); + public static readonly TokenRole FalseRole = new TokenRole ("false"); + + // Unary and Binary operators + public static readonly TokenRole AdditionRole = new TokenRole ("+"); + public static readonly TokenRole SubtractionRole = new TokenRole ("-"); + + // Binary operators + public static readonly TokenRole MultiplyRole = new TokenRole ("*"); + public static readonly TokenRole DivisionRole = new TokenRole ("/"); + public static readonly TokenRole ModulusRole = new TokenRole ("%"); + public static readonly TokenRole BitwiseAndRole = new TokenRole ("&"); + public static readonly TokenRole BitwiseOrRole = new TokenRole ("|"); + public static readonly TokenRole ExclusiveOrRole = new TokenRole ("^"); + public static readonly TokenRole LeftShiftRole = new TokenRole ("<<"); + public static readonly TokenRole RightShiftRole = new TokenRole (">>"); + public static readonly TokenRole EqualityRole = new TokenRole ("=="); + public static readonly TokenRole InequalityRole = new TokenRole ("!="); + public static readonly TokenRole GreaterThanRole = new TokenRole (">"); + public static readonly TokenRole LessThanRole = new TokenRole ("<"); + public static readonly TokenRole GreaterThanOrEqualRole = new TokenRole (">="); + public static readonly TokenRole LessThanOrEqualRole = new TokenRole ("<="); + + public static readonly TokenRole ExplicitRole = new TokenRole ("explicit"); + public static readonly TokenRole ImplicitRole = new TokenRole ("implicit"); + + public override SymbolKind SymbolKind { + get { return SymbolKind.Operator; } + } + + OperatorType operatorType; + + public OperatorType OperatorType { + get { return operatorType; } + set { + ThrowIfFrozen(); + operatorType = value; + } + } + + public CSharpTokenNode OperatorToken { + get { return GetChildByRole (OperatorKeywordRole); } + } + + public CSharpTokenNode OperatorTypeToken { + get { return GetChildByRole (GetRole (OperatorType)); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole (Roles.LPar); } + } + + public AstNodeCollection Parameters { + get { return GetChildrenByRole (Roles.Parameter); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole (Roles.RPar); } + } + + public BlockStatement Body { + get { return GetChildByRole (Roles.Body); } + set { SetChildByRole (Roles.Body, value); } + } + + /// + /// Gets the operator type from the method name, or null, if the method does not represent one of the known operator types. + /// + public static OperatorType? GetOperatorType(string methodName) + { + return (OperatorType?)Mono.CSharp.Operator.GetType(methodName); + } + + public static TokenRole GetRole (OperatorType type) + { + switch (type) { + case OperatorType.LogicalNot: + return LogicalNotRole; + case OperatorType.OnesComplement: + return OnesComplementRole; + case OperatorType.Increment: + return IncrementRole; + case OperatorType.Decrement: + return DecrementRole; + case OperatorType.True: + return TrueRole; + case OperatorType.False: + return FalseRole; + + case OperatorType.Addition: + case OperatorType.UnaryPlus: + return AdditionRole; + case OperatorType.Subtraction: + case OperatorType.UnaryNegation: + return SubtractionRole; + + case OperatorType.Multiply: + return MultiplyRole; + case OperatorType.Division: + return DivisionRole; + case OperatorType.Modulus: + return ModulusRole; + case OperatorType.BitwiseAnd: + return BitwiseAndRole; + case OperatorType.BitwiseOr: + return BitwiseOrRole; + case OperatorType.ExclusiveOr: + return ExclusiveOrRole; + case OperatorType.LeftShift: + return LeftShiftRole; + case OperatorType.RightShift: + return RightShiftRole; + case OperatorType.Equality: + return EqualityRole; + case OperatorType.Inequality: + return InequalityRole; + case OperatorType.GreaterThan: + return GreaterThanRole; + case OperatorType.LessThan: + return LessThanRole; + case OperatorType.GreaterThanOrEqual: + return GreaterThanOrEqualRole; + case OperatorType.LessThanOrEqual: + return LessThanOrEqualRole; + + case OperatorType.Implicit: + return ImplicitRole; + case OperatorType.Explicit: + return ExplicitRole; + + default: + throw new System.ArgumentOutOfRangeException (); + } + } + + /// + /// Gets the method name for the operator type. ("op_Addition", "op_Implicit", etc.) + /// + public static string GetName (OperatorType type) + { + return Mono.CSharp.Operator.GetMetadataName ((Mono.CSharp.Operator.OpType)type); + } + + /// + /// Gets the token for the operator type ("+", "implicit", etc.) + /// + public static string GetToken (OperatorType type) + { + return Mono.CSharp.Operator.GetName ((Mono.CSharp.Operator.OpType)type); + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitOperatorDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitOperatorDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitOperatorDeclaration (this, data); + } + + public override string Name { + get { return GetName (this.OperatorType); } + set { throw new NotSupportedException(); } + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public override Identifier NameToken { + get { return Identifier.Null; } + set { throw new NotSupportedException(); } + } + + protected internal override bool DoMatch (AstNode other, PatternMatching.Match match) + { + OperatorDeclaration o = other as OperatorDeclaration; + return o != null && this.MatchAttributesAndModifiers (o, match) && this.OperatorType == o.OperatorType + && this.ReturnType.DoMatch (o.ReturnType, match) + && this.Parameters.DoMatch (o.Parameters, match) && this.Body.DoMatch (o.Body, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/ParameterDeclaration.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/ParameterDeclaration.cs new file mode 100644 index 000000000..cc69ff1fd --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/ParameterDeclaration.cs @@ -0,0 +1,147 @@ +// +// ParameterDeclarationExpression.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.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 System.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + public enum ParameterModifier { + None, + Ref, + Out, + Params, + This + } + + public class ParameterDeclaration : AstNode + { + public static readonly Role AttributeRole = EntityDeclaration.AttributeRole; + public static readonly TokenRole RefModifierRole = new TokenRole("ref"); + public static readonly TokenRole OutModifierRole = new TokenRole("out"); + public static readonly TokenRole ParamsModifierRole = new TokenRole("params"); + public static readonly TokenRole ThisModifierRole = new TokenRole("this"); + + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public AstNodeCollection Attributes { + get { return GetChildrenByRole (AttributeRole); } + } + + ParameterModifier parameterModifier; + + public ParameterModifier ParameterModifier { + get { return parameterModifier; } + set { + ThrowIfFrozen(); + parameterModifier = value; + } + } + + public AstType Type { + get { return GetChildByRole (Roles.Type); } + set { SetChildByRole (Roles.Type, value); } + } + + public string Name { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole (Roles.Identifier, Identifier.Create (value)); + } + } + + public Identifier NameToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole (Roles.Identifier, value); + } + } + + public CSharpTokenNode AssignToken { + get { return GetChildByRole (Roles.Assign); } + } + + public Expression DefaultExpression { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitParameterDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitParameterDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitParameterDeclaration (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + ParameterDeclaration o = other as ParameterDeclaration; + return o != null && this.Attributes.DoMatch(o.Attributes, match) && this.ParameterModifier == o.ParameterModifier + && this.Type.DoMatch(o.Type, match) && MatchString(this.Name, o.Name) + && this.DefaultExpression.DoMatch(o.DefaultExpression, match); + } + + public ParameterDeclaration() + { + } + + public ParameterDeclaration(AstType type, string name, ParameterModifier modifier = ParameterModifier.None) + { + Type = type; + Name = name; + ParameterModifier = modifier; + } + + public ParameterDeclaration(string name, ParameterModifier modifier = ParameterModifier.None) + { + Name = name; + ParameterModifier = modifier; + } + + public new ParameterDeclaration Clone() + { + return (ParameterDeclaration)base.Clone(); + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/PropertyDeclaration.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/PropertyDeclaration.cs new file mode 100644 index 000000000..1f137e0c9 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/PropertyDeclaration.cs @@ -0,0 +1,92 @@ +// +// PropertyDeclaration.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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 ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class PropertyDeclaration : EntityDeclaration + { + public static readonly TokenRole GetKeywordRole = new TokenRole ("get"); + public static readonly TokenRole SetKeywordRole = new TokenRole ("set"); + public static readonly Role GetterRole = new Role("Getter", Accessor.Null); + public static readonly Role SetterRole = new Role("Setter", Accessor.Null); + + public override SymbolKind SymbolKind { + get { return SymbolKind.Property; } + } + + /// + /// Gets/Sets the type reference of the interface that is explicitly implemented. + /// Null node if this member is not an explicit interface implementation. + /// + public AstType PrivateImplementationType { + get { return GetChildByRole (PrivateImplementationTypeRole); } + set { SetChildByRole (PrivateImplementationTypeRole, value); } + } + + public CSharpTokenNode LBraceToken { + get { return GetChildByRole (Roles.LBrace); } + } + + public Accessor Getter { + get { return GetChildByRole(GetterRole); } + set { SetChildByRole(GetterRole, value); } + } + + public Accessor Setter { + get { return GetChildByRole(SetterRole); } + set { SetChildByRole(SetterRole, value); } + } + + public CSharpTokenNode RBraceToken { + get { return GetChildByRole (Roles.RBrace); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPropertyDeclaration (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPropertyDeclaration (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitPropertyDeclaration (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + PropertyDeclaration o = other as PropertyDeclaration; + return o != null && MatchString(this.Name, o.Name) + && this.MatchAttributesAndModifiers(o, match) && this.ReturnType.DoMatch(o.ReturnType, match) + && this.PrivateImplementationType.DoMatch(o.PrivateImplementationType, match) + && this.Getter.DoMatch(o.Getter, match) && this.Setter.DoMatch(o.Setter, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/VariableInitializer.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/VariableInitializer.cs new file mode 100644 index 000000000..dbf4bbe3d --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/VariableInitializer.cs @@ -0,0 +1,174 @@ +// +// VariableInitializer.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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. + +namespace ICSharpCode.NRefactory.CSharp +{ + public class VariableInitializer : AstNode + { + #region Null + public new static readonly VariableInitializer Null = new NullVariableInitializer (); + + sealed class NullVariableInitializer : VariableInitializer + { + public override bool IsNull { + get { + return true; + } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitNullNode(this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitNullNode(this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitNullNode(this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return other == null || other.IsNull; + } + } + #endregion + + #region PatternPlaceholder + public static implicit operator VariableInitializer(PatternMatching.Pattern pattern) + { + return pattern != null ? new PatternPlaceholder(pattern) : null; + } + + sealed class PatternPlaceholder : VariableInitializer, PatternMatching.INode + { + readonly PatternMatching.Pattern child; + + public PatternPlaceholder(PatternMatching.Pattern child) + { + this.child = child; + } + + public override NodeType NodeType { + get { return NodeType.Pattern; } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitPatternPlaceholder (this, child); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitPatternPlaceholder (this, child); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitPatternPlaceholder(this, child, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + return child.DoMatch(other, match); + } + + bool PatternMatching.INode.DoMatchCollection(Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo) + { + return child.DoMatchCollection(role, pos, match, backtrackingInfo); + } + } + #endregion + + public override NodeType NodeType { + get { + return NodeType.Unknown; + } + } + + public VariableInitializer() + { + } + + public VariableInitializer(string name, Expression initializer = null) + { + this.Name = name; + this.Initializer = initializer; + } + + public string Name { + get { + return GetChildByRole (Roles.Identifier).Name; + } + set { + SetChildByRole (Roles.Identifier, Identifier.Create (value)); + } + } + + public Identifier NameToken { + get { + return GetChildByRole (Roles.Identifier); + } + set { + SetChildByRole (Roles.Identifier, value); + } + } + + public CSharpTokenNode AssignToken { + get { return GetChildByRole (Roles.Assign); } + } + + public Expression Initializer { + get { return GetChildByRole (Roles.Expression); } + set { SetChildByRole (Roles.Expression, value); } + } + + public override void AcceptVisitor (IAstVisitor visitor) + { + visitor.VisitVariableInitializer (this); + } + + public override T AcceptVisitor (IAstVisitor visitor) + { + return visitor.VisitVariableInitializer (this); + } + + public override S AcceptVisitor (IAstVisitor visitor, T data) + { + return visitor.VisitVariableInitializer (this, data); + } + + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) + { + VariableInitializer o = other as VariableInitializer; + return o != null && MatchString(this.Name, o.Name) && this.Initializer.DoMatch(o.Initializer, match); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/CSharpProjectContent.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/CSharpProjectContent.cs new file mode 100644 index 000000000..ae0ba716e --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/CSharpProjectContent.cs @@ -0,0 +1,289 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using ICSharpCode.NRefactory.CSharp.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem.Implementation; +using ICSharpCode.NRefactory.Utils; + +namespace ICSharpCode.NRefactory.CSharp +{ + [Serializable] + public class CSharpProjectContent : IProjectContent + { + string assemblyName; + string fullAssemblyName; + string projectFileName; + string location; + Dictionary unresolvedFiles; + List assemblyReferences; + CompilerSettings compilerSettings; + + public CSharpProjectContent() + { + this.unresolvedFiles = new Dictionary(Platform.FileNameComparer); + this.assemblyReferences = new List(); + this.compilerSettings = new CompilerSettings(); + compilerSettings.Freeze(); + } + + protected CSharpProjectContent(CSharpProjectContent pc) + { + this.assemblyName = pc.assemblyName; + this.fullAssemblyName = pc.fullAssemblyName; + this.projectFileName = pc.projectFileName; + this.location = pc.location; + this.unresolvedFiles = new Dictionary(pc.unresolvedFiles, Platform.FileNameComparer); + this.assemblyReferences = new List(pc.assemblyReferences); + this.compilerSettings = pc.compilerSettings; + } + + public IEnumerable Files { + get { return unresolvedFiles.Values; } + } + + public IEnumerable AssemblyReferences { + get { return assemblyReferences; } + } + + public string ProjectFileName { + get { return projectFileName; } + } + + public string AssemblyName { + get { return assemblyName; } + } + + public string FullAssemblyName { + get { return fullAssemblyName; } + } + + public string Location { + get { return location; } + } + + public CompilerSettings CompilerSettings { + get { return compilerSettings; } + } + + object IProjectContent.CompilerSettings { + get { return compilerSettings; } + } + + public IEnumerable AssemblyAttributes { + get { + return this.Files.SelectMany(f => f.AssemblyAttributes); + } + } + + public IEnumerable ModuleAttributes { + get { + return this.Files.SelectMany(f => f.ModuleAttributes); + } + } + + public IEnumerable TopLevelTypeDefinitions { + get { + return this.Files.SelectMany(f => f.TopLevelTypeDefinitions); + } + } + + public IUnresolvedFile GetFile(string fileName) + { + IUnresolvedFile file; + if (unresolvedFiles.TryGetValue(fileName, out file)) + return file; + else + return null; + } + + public virtual ICompilation CreateCompilation() + { + var solutionSnapshot = new DefaultSolutionSnapshot(); + ICompilation compilation = new SimpleCompilation(solutionSnapshot, this, assemblyReferences); + solutionSnapshot.AddCompilation(this, compilation); + return compilation; + } + + public virtual ICompilation CreateCompilation(ISolutionSnapshot solutionSnapshot) + { + return new SimpleCompilation(solutionSnapshot, this, assemblyReferences); + } + + protected virtual CSharpProjectContent Clone() + { + return new CSharpProjectContent(this); + } + + /// + /// Sets both the short and the full assembly names. + /// + /// New full assembly name. + public IProjectContent SetAssemblyName(string newAssemblyName) + { + CSharpProjectContent pc = Clone(); + pc.fullAssemblyName = newAssemblyName; + int pos = newAssemblyName != null ? newAssemblyName.IndexOf(',') : -1; + pc.assemblyName = pos < 0 ? newAssemblyName : newAssemblyName.Substring(0, pos); + return pc; + } + + public IProjectContent SetProjectFileName(string newProjectFileName) + { + CSharpProjectContent pc = Clone(); + pc.projectFileName = newProjectFileName; + return pc; + } + + public IProjectContent SetLocation(string newLocation) + { + CSharpProjectContent pc = Clone(); + pc.location = newLocation; + return pc; + } + + public IProjectContent SetCompilerSettings(object compilerSettings) + { + if (!(compilerSettings is CompilerSettings)) + throw new ArgumentException("Settings must be an instance of " + typeof(CompilerSettings).FullName, "compilerSettings"); + CSharpProjectContent pc = Clone(); + pc.compilerSettings = (CompilerSettings)compilerSettings; + pc.compilerSettings.Freeze(); + return pc; + } + + public IProjectContent AddAssemblyReferences(IEnumerable references) + { + return AddAssemblyReferences(references.ToArray()); + } + + public IProjectContent AddAssemblyReferences(params IAssemblyReference[] references) + { + CSharpProjectContent pc = Clone(); + pc.assemblyReferences.AddRange(references); + return pc; + } + + public IProjectContent RemoveAssemblyReferences(IEnumerable references) + { + return RemoveAssemblyReferences(references.ToArray()); + } + + public IProjectContent RemoveAssemblyReferences(params IAssemblyReference[] references) + { + CSharpProjectContent pc = Clone(); + foreach (var r in references) + pc.assemblyReferences.Remove(r); + return pc; + } + + /// + /// Adds the specified files to the project content. + /// If a file with the same name already exists, updated the existing file. + /// + public IProjectContent AddOrUpdateFiles(IEnumerable newFiles) + { + CSharpProjectContent pc = Clone(); + foreach (var file in newFiles) { + pc.unresolvedFiles[file.FileName] = file; + } + return pc; + } + + /// + /// Adds the specified files to the project content. + /// If a file with the same name already exists, this method updates the existing file. + /// + public IProjectContent AddOrUpdateFiles(params IUnresolvedFile[] newFiles) + { + return AddOrUpdateFiles((IEnumerable)newFiles); + } + + /// + /// Removes the files with the specified names. + /// + public IProjectContent RemoveFiles(IEnumerable fileNames) + { + CSharpProjectContent pc = Clone(); + foreach (var fileName in fileNames) { + pc.unresolvedFiles.Remove(fileName); + } + return pc; + } + + /// + /// Removes the files with the specified names. + /// + public IProjectContent RemoveFiles(params string[] fileNames) + { + return RemoveFiles((IEnumerable)fileNames); + } + + [Obsolete("Use RemoveFiles/AddOrUpdateFiles instead")] + public IProjectContent UpdateProjectContent(IUnresolvedFile oldFile, IUnresolvedFile newFile) + { + if (oldFile == null && newFile == null) + return this; + if (oldFile != null && newFile != null) { + if (!Platform.FileNameComparer.Equals(oldFile.FileName, newFile.FileName)) + throw new ArgumentException("When both oldFile and newFile are specified, they must use the same file name."); + } + CSharpProjectContent pc = Clone(); + if (newFile == null) + pc.unresolvedFiles.Remove(oldFile.FileName); + else + pc.unresolvedFiles[newFile.FileName] = newFile; + return pc; + } + + [Obsolete("Use RemoveFiles/AddOrUpdateFiles instead")] + public IProjectContent UpdateProjectContent(IEnumerable oldFiles, IEnumerable newFiles) + { + CSharpProjectContent pc = Clone(); + if (oldFiles != null) { + foreach (var oldFile in oldFiles) { + pc.unresolvedFiles.Remove(oldFile.FileName); + } + } + if (newFiles != null) { + foreach (var newFile in newFiles) { + pc.unresolvedFiles.Add(newFile.FileName, newFile); + } + } + return pc; + } + + IAssembly IAssemblyReference.Resolve(ITypeResolveContext context) + { + if (context == null) + throw new ArgumentNullException("context"); + var cache = context.Compilation.CacheManager; + IAssembly asm = (IAssembly)cache.GetShared(this); + if (asm != null) { + return asm; + } else { + asm = new CSharpAssembly(context.Compilation, this); + return (IAssembly)cache.GetOrAddShared(this, asm); + } + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/CombineQueryExpressions.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/CombineQueryExpressions.cs new file mode 100644 index 000000000..5311d9a24 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/CombineQueryExpressions.cs @@ -0,0 +1,216 @@ +// +// CombineQueryExpressions.cs +// +// Modified by Luís Reis (Copyright (C) 2013) +// +// Copyright header of the original version follows: +// +// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team +// +// 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.Linq; +using ICSharpCode.NRefactory.CSharp; +using ICSharpCode.NRefactory.PatternMatching; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Combines query expressions and removes transparent identifiers. + /// + public class CombineQueryExpressions + { + static readonly InvocationExpression castPattern = new InvocationExpression { + Target = new MemberReferenceExpression { + Target = new AnyNode("inExpr"), + MemberName = "Cast", + TypeArguments = { new AnyNode("targetType") } + }}; + + public string CombineQuery(AstNode node, AstNode rootQuery = null) + { + if (rootQuery == null) { + rootQuery = node; + } + + QueryExpression query = node as QueryExpression; + if (query != null) { + string continuationIdentifier = null; + + foreach (var clause in query.Clauses) { + var continuation = clause as QueryContinuationClause; + if (continuation != null) { + CombineQuery(continuation.PrecedingQuery); + } + + var from = clause as QueryFromClause; + if (from != null) { + continuationIdentifier = CombineQuery(from.Expression, rootQuery); + } + } + + QueryFromClause fromClause = (QueryFromClause)query.Clauses.First(); + QueryExpression innerQuery = fromClause.Expression as QueryExpression; + if (innerQuery != null) { + continuationIdentifier = continuationIdentifier ?? ((QueryFromClause)innerQuery.Clauses.First()).Identifier; + + string transparentIdentifier; + if (TryRemoveTransparentIdentifier(query, fromClause, innerQuery, continuationIdentifier, out transparentIdentifier)) { + RemoveTransparentIdentifierReferences(rootQuery, transparentIdentifier); + } else if (fromClause.Type.IsNull) { + QueryContinuationClause continuation = new QueryContinuationClause(); + continuation.PrecedingQuery = innerQuery.Detach(); + continuation.Identifier = fromClause.Identifier; + fromClause.ReplaceWith(continuation); + } + + return transparentIdentifier; + } else { + Match m = castPattern.Match(fromClause.Expression); + if (m.Success) { + fromClause.Type = m.Get("targetType").Single().Detach(); + fromClause.Expression = m.Get("inExpr").Single().Detach(); + } + } + } + + return null; + } + + static readonly QuerySelectClause selectTransparentIdentifierPattern = new QuerySelectClause { + Expression = new AnonymousTypeCreateExpression { + Initializers = { + new AnyNode("nae1"), + new AnyNode("nae2") + } + } + }; + + bool TryRemoveTransparentIdentifier(QueryExpression query, QueryFromClause fromClause, QueryExpression innerQuery, string continuationIdentifier, out string transparentIdentifier) + { + transparentIdentifier = fromClause.Identifier; + + Match match = selectTransparentIdentifierPattern.Match(innerQuery.Clauses.Last()); + if (!match.Success) + return false; + QuerySelectClause selectClause = (QuerySelectClause)innerQuery.Clauses.Last(); + Expression nae1 = match.Get("nae1").SingleOrDefault(); + string nae1Name = ExtractExpressionName(ref nae1); + if (nae1Name == null) + return false; + + Expression nae2 = match.Get("nae2").SingleOrDefault(); + string nae2Name = ExtractExpressionName(ref nae2); + if (nae1Name == null) + return false; + + bool introduceLetClause = true; + var nae1Identifier = nae1 as IdentifierExpression; + var nae2Identifier = nae2 as IdentifierExpression; + if (nae1Identifier != null && nae2Identifier != null && nae1Identifier.Identifier == nae1Name && nae2Identifier.Identifier == nae2Name) { + introduceLetClause = false; + } + + if (nae1Name != continuationIdentifier) { + if (nae2Name == continuationIdentifier) { + //Members are in reversed order + string tempName = nae1Name; + Expression tempNae = nae1; + + nae1Name = nae2Name; + nae1 = nae2; + nae2Name = tempName; + nae2 = tempNae; + } else { + return false; + } + } + + if (introduceLetClause && innerQuery.Clauses.OfType().Any(from => from.Identifier == nae2Name)) { + return false; + } + if (introduceLetClause && innerQuery.Clauses.OfType().Any(join => join.JoinIdentifier == nae2Name)) { + return false; + } + + // from * in (from x in ... select new { x = x, y = expr }) ... + // => + // from x in ... let y = expr ... + fromClause.Remove(); + selectClause.Remove(); + // Move clauses from innerQuery to query + QueryClause insertionPos = null; + foreach (var clause in innerQuery.Clauses) { + query.Clauses.InsertAfter(insertionPos, insertionPos = clause.Detach()); + } + if (introduceLetClause) { + query.Clauses.InsertAfter(insertionPos, new QueryLetClause { Identifier = nae2Name, Expression = nae2.Detach() }); + } + return true; + } + + /// + /// Removes all occurrences of transparent identifiers + /// + void RemoveTransparentIdentifierReferences(AstNode node, string transparentIdentifier) + { + foreach (AstNode child in node.Children) { + RemoveTransparentIdentifierReferences(child, transparentIdentifier); + } + MemberReferenceExpression mre = node as MemberReferenceExpression; + if (mre != null) { + IdentifierExpression ident = mre.Target as IdentifierExpression; + if (ident != null && ident.Identifier == transparentIdentifier) { + IdentifierExpression newIdent = new IdentifierExpression(mre.MemberName); + mre.TypeArguments.MoveTo(newIdent.TypeArguments); + newIdent.CopyAnnotationsFrom(mre); + newIdent.RemoveAnnotations(); // remove the reference to the property of the anonymous type + mre.ReplaceWith(newIdent); + return; + } else if (mre.MemberName == transparentIdentifier) { + var newVar = mre.Target.Detach(); + newVar.CopyAnnotationsFrom(mre); + newVar.RemoveAnnotations(); // remove the reference to the property of the anonymous type + mre.ReplaceWith(newVar); + return; + } + } + } + + string ExtractExpressionName(ref Expression expr) + { + NamedExpression namedExpr = expr as NamedExpression; + if (namedExpr != null) { + expr = namedExpr.Expression; + return namedExpr.Name; + } + + IdentifierExpression identifier = expr as IdentifierExpression; + if (identifier != null) { + return identifier.Identifier; + } + + MemberReferenceExpression memberRef = expr as MemberReferenceExpression; + if (memberRef != null) { + return memberRef.MemberName; + } + + return null; + } + } +} \ No newline at end of file diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs new file mode 100644 index 000000000..28c9ea02a --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs @@ -0,0 +1,3807 @@ +// +// CSharpCompletionEngine.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 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 System.Linq; +using System.Text; +using ICSharpCode.NRefactory.Completion; +using ICSharpCode.NRefactory.CSharp.Refactoring; +using ICSharpCode.NRefactory.CSharp.Resolver; +using ICSharpCode.NRefactory.Editor; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.CSharp.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp.Completion +{ + public enum EditorBrowsableBehavior + { + Ignore, + Normal, + IncludeAdvanced + } + + public class CompletionEngineCache + { + public List namespaces; + public ICompletionData[] importCompletion; + } + + public class CSharpCompletionEngine : CSharpCompletionEngineBase + { + internal ICompletionDataFactory factory; + + #region Additional input properties + + public CSharpFormattingOptions FormattingPolicy { get; set; } + + public string EolMarker { get; set; } + + public string IndentString { get; set; } + + public bool AutomaticallyAddImports { get; set; } + + public bool IncludeKeywordsInCompletionList { get; set; } + + public EditorBrowsableBehavior EditorBrowsableBehavior { get; set; } + + public CompletionEngineCache CompletionEngineCache { get; set; } + + #endregion + + #region Result properties + + public bool AutoCompleteEmptyMatch; + /// + /// The auto complete empty match on curly bracket. (only taken into account when AutoCompleteEmptyMatch is true ) + /// + public bool AutoCompleteEmptyMatchOnCurlyBracket = true; + public bool AutoSelect; + public string DefaultCompletionString; + public bool CloseOnSquareBrackets; + public readonly List PossibleDelegates = new List(); + + #endregion + + public CSharpCompletionEngine(IDocument document, ICompletionContextProvider completionContextProvider, ICompletionDataFactory factory, IProjectContent content, CSharpTypeResolveContext ctx) : base(content, completionContextProvider, ctx) + { + if (document == null) { + throw new ArgumentNullException("document"); + } + if (factory == null) { + throw new ArgumentNullException("factory"); + } + this.document = document; + this.factory = factory; + // Set defaults for additional input properties + this.FormattingPolicy = FormattingOptionsFactory.CreateMono(); + this.EolMarker = Environment.NewLine; + this.IncludeKeywordsInCompletionList = true; + EditorBrowsableBehavior = EditorBrowsableBehavior.IncludeAdvanced; + this.IndentString = "\t"; + } + + public bool TryGetCompletionWord(int offset, out int startPos, out int wordLength) + { + startPos = wordLength = 0; + int pos = offset - 1; + while (pos >= 0) { + char c = document.GetCharAt(pos); + if (!char.IsLetterOrDigit(c) && c != '_') + break; + pos--; + } + if (pos == -1) + return false; + + pos++; + startPos = pos; + + while (pos < document.TextLength) { + char c = document.GetCharAt(pos); + if (!char.IsLetterOrDigit(c) && c != '_') + break; + pos++; + } + wordLength = pos - startPos; + return true; + } + + public IEnumerable GetCompletionData(int offset, bool controlSpace) + { + this.AutoCompleteEmptyMatch = true; + this.AutoSelect = true; + this.DefaultCompletionString = null; + SetOffset(offset); + if (offset > 0) { + char lastChar = document.GetCharAt(offset - 1); + bool isComplete = false; + var result = MagicKeyCompletion(lastChar, controlSpace, out isComplete) ?? Enumerable.Empty(); + if (!isComplete && controlSpace && char.IsWhiteSpace(lastChar)) { + offset -= 2; + while (offset >= 0 && char.IsWhiteSpace(document.GetCharAt(offset))) { + offset--; + } + if (offset > 0) { + var nonWsResult = MagicKeyCompletion( + document.GetCharAt(offset), + controlSpace, + out isComplete + ); + if (nonWsResult != null) { + var text = new HashSet(result.Select(r => r.CompletionText)); + result = result.Concat(nonWsResult.Where(r => !text.Contains(r.CompletionText))); + } + } + } + + return result; + } + return Enumerable.Empty(); + } + + /// + /// Gets the types that needs to be imported via using or full type name. + /// + public IEnumerable GetImportCompletionData(int offset) + { + var generalLookup = new MemberLookup(null, Compilation.MainAssembly); + SetOffset(offset); + + // flatten usings + var namespaces = new List(); + for (var n = ctx.CurrentUsingScope; n != null; n = n.Parent) { + namespaces.Add(n.Namespace); + foreach (var u in n.Usings) + namespaces.Add(u); + } + + foreach (var type in Compilation.GetAllTypeDefinitions ()) { + if (!generalLookup.IsAccessible(type, false)) + continue; + if (namespaces.Any(n => n.FullName == type.Namespace)) + continue; + bool useFullName = false; + foreach (var ns in namespaces) { + if (ns.GetTypeDefinition(type.Name, type.TypeParameterCount) != null) { + useFullName = true; + break; + } + } + yield return factory.CreateImportCompletionData(type, useFullName, false); + } + } + + IEnumerable GenerateNameProposals(AstType type) + { + if (type is PrimitiveType) { + var pt = (PrimitiveType)type; + switch (pt.Keyword) { + case "object": + yield return "o"; + yield return "obj"; + break; + case "bool": + yield return "b"; + yield return "pred"; + break; + case "double": + case "float": + case "decimal": + yield return "d"; + yield return "f"; + yield return "m"; + break; + default: + yield return "i"; + yield return "j"; + yield return "k"; + break; + } + yield break; + } + string name; + if (type is SimpleType) { + name = ((SimpleType)type).Identifier; + } else if (type is MemberType) { + name = ((MemberType)type).MemberName; + } else { + yield break; + } + + var names = WordParser.BreakWords(name); + + var possibleName = new StringBuilder(); + for (int i = 0; i < names.Count; i++) { + possibleName.Length = 0; + for (int j = i; j < names.Count; j++) { + if (string.IsNullOrEmpty(names [j])) { + continue; + } + if (j == i) { + names [j] = Char.ToLower(names [j] [0]) + names [j].Substring(1); + } + possibleName.Append(names [j]); + } + yield return possibleName.ToString(); + } + } + + IEnumerable HandleMemberReferenceCompletion(ExpressionResult expr) + { + if (expr == null) + return null; + + // do not auto select . (but ..) (0.ToString() is valid) + if (expr.Node is PrimitiveExpression) { + var pexpr = (PrimitiveExpression)expr.Node; + if (!(pexpr.Value is string || pexpr.Value is char) && !pexpr.LiteralValue.Contains('.')) { + AutoSelect = false; + } + } + var resolveResult = ResolveExpression(expr); + + if (resolveResult == null) { + return null; + } + if (expr.Node is AstType) { + + // check for namespace names + if (expr.Node.AncestorsAndSelf + .TakeWhile(n => n is AstType) + .Any(m => m.Role == NamespaceDeclaration.NamespaceNameRole)) + return null; + + // need to look at paren.parent because of "catch (.A" expression + if (expr.Node.Parent != null && expr.Node.Parent.Parent is CatchClause) + return HandleCatchClauseType(expr); + return CreateTypeAndNamespaceCompletionData( + location, + resolveResult.Result, + expr.Node, + resolveResult.Resolver + ); + } + + + return CreateCompletionData( + location, + resolveResult.Result, + expr.Node, + resolveResult.Resolver + ); + } + + bool IsInPreprocessorDirective() + { + var text = GetMemberTextToCaret().Item1; + var miniLexer = new MiniLexer(text); + miniLexer.Parse(); + return miniLexer.IsInPreprocessorDirective; + } + + IEnumerable HandleObjectInitializer(SyntaxTree unit, AstNode n) + { + var p = n.Parent; + while (p != null && !(p is ObjectCreateExpression)) { + p = p.Parent; + } + var parent = n.Parent as ArrayInitializerExpression; + if (parent == null) + return null; + if (parent.IsSingleElement) + parent = (ArrayInitializerExpression)parent.Parent; + if (p != null) { + var contextList = new CompletionDataWrapper(this); + var initializerResult = ResolveExpression(p); + IType initializerType = null; + + if (initializerResult.Result is DynamicInvocationResolveResult) { + var dr = (DynamicInvocationResolveResult)initializerResult.Result; + var constructor = (dr.Target as MethodGroupResolveResult).Methods.FirstOrDefault(); + if (constructor != null) + initializerType = constructor.DeclaringType; + } else { + initializerType = initializerResult != null ? initializerResult.Result.Type : null; + } + + + if (initializerType != null && initializerType.Kind != TypeKind.Unknown) { + // check 3 cases: + // 1) New initalizer { xpr + // 2) Object initializer { prop = val1, field = val2, xpr + // 3) Array initializer { new Foo (), a, xpr + // in case 1 all object/array initializer options should be given - in the others not. + + AstNode prev = null; + if (parent.Elements.Count > 1) { + prev = parent.Elements.First(); + if (prev is ArrayInitializerExpression && ((ArrayInitializerExpression)prev).IsSingleElement) + prev = ((ArrayInitializerExpression)prev).Elements.FirstOrDefault(); + } + + if (prev != null && !(prev is NamedExpression)) { + AddContextCompletion(contextList, GetState(), n); + // case 3) + return contextList.Result; + } + var lookup = new MemberLookup(ctx.CurrentTypeDefinition, Compilation.MainAssembly); + var list = typeof(System.Collections.IList).ToTypeReference().Resolve(Compilation); + var list1 = typeof(System.Collections.Generic.IList<>).ToTypeReference().Resolve(Compilation); + bool isProtectedAllowed = ctx.CurrentTypeDefinition != null && initializerType.GetDefinition() != null ? + ctx.CurrentTypeDefinition.IsDerivedFrom(initializerType.GetDefinition()) : + false; + foreach (var m in initializerType.GetMembers (m => m.SymbolKind == SymbolKind.Field)) { + var f = m as IField; + if (f != null && (f.IsReadOnly || f.IsConst)) + continue; + if (lookup.IsAccessible(m, isProtectedAllowed)) { + var data = contextList.AddMember(m); + if (data != null) + data.DisplayFlags |= DisplayFlags.NamedArgument; + } + } + + foreach (IProperty m in initializerType.GetMembers (m => m.SymbolKind == SymbolKind.Property)) { + if (m.CanSet && lookup.IsAccessible(m.Setter, isProtectedAllowed) || + m.CanGet && lookup.IsAccessible(m.Getter, isProtectedAllowed) && m.ReturnType.GetDefinition() != null && + (m.ReturnType.GetDefinition().IsDerivedFrom(list.GetDefinition()) || m.ReturnType.GetDefinition().IsDerivedFrom(list1.GetDefinition()))) { + var data = contextList.AddMember(m); + if (data != null) + data.DisplayFlags |= DisplayFlags.NamedArgument; + } + } + + if (prev != null && (prev is NamedExpression)) { + // case 2) + return contextList.Result; + } + + // case 1) + + // check if the object is a list, if not only provide object initalizers + if (initializerType.Kind != TypeKind.Array && list != null) { + var def = initializerType.GetDefinition(); + if (def != null && !def.IsDerivedFrom(list.GetDefinition()) && !def.IsDerivedFrom(list1.GetDefinition())) + return contextList.Result; + } + + AddContextCompletion(contextList, GetState(), n); + return contextList.Result; + } + } + return null; + } + + static readonly DateTime curDate = DateTime.Now; + + IEnumerable GenerateNumberFormatitems(bool isFloatingPoint) + { + yield return factory.CreateFormatItemCompletionData("D", "decimal", 123); + yield return factory.CreateFormatItemCompletionData("D5", "decimal", 123); + yield return factory.CreateFormatItemCompletionData("C", "currency", 123); + yield return factory.CreateFormatItemCompletionData("C0", "currency", 123); + yield return factory.CreateFormatItemCompletionData("E", "exponential", 1.23E4); + yield return factory.CreateFormatItemCompletionData("E2", "exponential", 1.234); + yield return factory.CreateFormatItemCompletionData("e2", "exponential", 1.234); + yield return factory.CreateFormatItemCompletionData("F", "fixed-point", 123.45); + yield return factory.CreateFormatItemCompletionData("F1", "fixed-point", 123.45); + yield return factory.CreateFormatItemCompletionData("G", "general", 1.23E+56); + yield return factory.CreateFormatItemCompletionData("g2", "general", 1.23E+56); + yield return factory.CreateFormatItemCompletionData("N", "number", 12345.68); + yield return factory.CreateFormatItemCompletionData("N1", "number", 12345.68); + yield return factory.CreateFormatItemCompletionData("P", "percent", 12.34); + yield return factory.CreateFormatItemCompletionData("P1", "percent", 12.34); + yield return factory.CreateFormatItemCompletionData("R", "round-trip", 0.1230000001); + yield return factory.CreateFormatItemCompletionData("X", "hexadecimal", 1234); + yield return factory.CreateFormatItemCompletionData("x8", "hexadecimal", 1234); + yield return factory.CreateFormatItemCompletionData("0000", "custom", 123); + yield return factory.CreateFormatItemCompletionData("####", "custom", 123); + yield return factory.CreateFormatItemCompletionData("##.###", "custom", 1.23); + yield return factory.CreateFormatItemCompletionData("##.000", "custom", 1.23); + yield return factory.CreateFormatItemCompletionData("## 'items'", "custom", 12); + } + + IEnumerable GenerateDateTimeFormatitems() + { + yield return factory.CreateFormatItemCompletionData("D", "long date", curDate); + yield return factory.CreateFormatItemCompletionData("d", "short date", curDate); + yield return factory.CreateFormatItemCompletionData("F", "full date long", curDate); + yield return factory.CreateFormatItemCompletionData("f", "full date short", curDate); + yield return factory.CreateFormatItemCompletionData("G", "general long", curDate); + yield return factory.CreateFormatItemCompletionData("g", "general short", curDate); + yield return factory.CreateFormatItemCompletionData("M", "month", curDate); + yield return factory.CreateFormatItemCompletionData("O", "ISO 8601", curDate); + yield return factory.CreateFormatItemCompletionData("R", "RFC 1123", curDate); + yield return factory.CreateFormatItemCompletionData("s", "sortable", curDate); + yield return factory.CreateFormatItemCompletionData("T", "long time", curDate); + yield return factory.CreateFormatItemCompletionData("t", "short time", curDate); + yield return factory.CreateFormatItemCompletionData("U", "universal full", curDate); + yield return factory.CreateFormatItemCompletionData("u", "universal sortable", curDate); + yield return factory.CreateFormatItemCompletionData("Y", "year month", curDate); + yield return factory.CreateFormatItemCompletionData("yy-MM-dd", "custom", curDate); + yield return factory.CreateFormatItemCompletionData("yyyy MMMMM dd", "custom", curDate); + yield return factory.CreateFormatItemCompletionData("yy-MMM-dd ddd", "custom", curDate); + yield return factory.CreateFormatItemCompletionData("yyyy-M-d dddd", "custom", curDate); + yield return factory.CreateFormatItemCompletionData("hh:mm:ss t z", "custom", curDate); + yield return factory.CreateFormatItemCompletionData("hh:mm:ss tt zz", "custom", curDate); + yield return factory.CreateFormatItemCompletionData("HH:mm:ss tt zz", "custom", curDate); + yield return factory.CreateFormatItemCompletionData("HH:m:s tt zz", "custom", curDate); + + } + + [Flags] + enum TestEnum + { + EnumCaseName = 0, + Flag1 = 1, + Flag2 = 2, + Flags + + } + + IEnumerable GenerateEnumFormatitems() + { + yield return factory.CreateFormatItemCompletionData("G", "string value", TestEnum.EnumCaseName); + yield return factory.CreateFormatItemCompletionData("F", "flags value", TestEnum.Flags); + yield return factory.CreateFormatItemCompletionData("D", "integer value", TestEnum.Flags); + yield return factory.CreateFormatItemCompletionData("X", "hexadecimal", TestEnum.Flags); + } + + IEnumerable GenerateTimeSpanFormatitems() + { + yield return factory.CreateFormatItemCompletionData("c", "invariant", new TimeSpan(0, 1, 23, 456)); + yield return factory.CreateFormatItemCompletionData("G", "general long", new TimeSpan(0, 1, 23, 456)); + yield return factory.CreateFormatItemCompletionData("g", "general short", new TimeSpan(0, 1, 23, 456)); + } + + static Guid defaultGuid = Guid.NewGuid(); + + IEnumerable GenerateGuidFormatitems() + { + yield return factory.CreateFormatItemCompletionData("N", "digits", defaultGuid); + yield return factory.CreateFormatItemCompletionData("D", "hypens", defaultGuid); + yield return factory.CreateFormatItemCompletionData("B", "braces", defaultGuid); + yield return factory.CreateFormatItemCompletionData("P", "parentheses", defaultGuid); + } + + int GetFormatItemNumber() + { + int number = 0; + var o = offset - 2; + while (o > 0) { + char ch = document.GetCharAt(o); + if (ch == '{') + return number; + if (!char.IsDigit(ch)) + break; + number = number * 10 + ch - '0'; + o--; + } + return -1; + } + + IEnumerable HandleStringFormatItems() + { + var formatArgument = GetFormatItemNumber(); + if (formatArgument < 0) + return Enumerable.Empty(); + var followUp = new StringBuilder(); + + var o = offset; + while (o < document.TextLength) { + char ch = document.GetCharAt(o); + followUp.Append(ch); + o++; + if (ch == ';') + break; + } + var unit = ParseStub(followUp.ToString(), false); + + var invoke = unit.GetNodeAt(location); + + if (invoke != null) { + var resolveResult = ResolveExpression(new ExpressionResult(invoke, unit)); + var invokeResult = resolveResult.Result as InvocationResolveResult; + if (invokeResult != null) { + var arg = formatArgument + 1; // First argument is the format string + if (arg < invoke.Arguments.Count) { + var invokeArgument = ResolveExpression(new ExpressionResult(invoke.Arguments.ElementAt(arg), unit)); + if (invokeArgument != null) { + var provider = GetFormatCompletionData(invokeArgument.Result.Type); + if (provider != null) + return provider; + if (!invokeArgument.Result.Type.IsKnownType(KnownTypeCode.Object)) + return Enumerable.Empty(); + } + } + } + } + return HandleStringFormatItemsFallback(); + } + + IEnumerable HandleStringFormatItemsFallback() + { + var unit = ParseStub("a}\");", false); + + var invoke = unit.GetNodeAt(location); + + if (invoke == null) + return Enumerable.Empty(); + + var resolveResult = ResolveExpression(new ExpressionResult(invoke, unit)); + var invokeResult = resolveResult.Result as CSharpInvocationResolveResult; + if (invokeResult == null) + return Enumerable.Empty(); + + Expression fmtArgumets; + IList args; + if (FormatStringHelper.TryGetFormattingParameters(invokeResult, invoke, out fmtArgumets, out args, null)) { + return GenerateNumberFormatitems(false) + .Concat(GenerateDateTimeFormatitems()) + .Concat(GenerateTimeSpanFormatitems()) + .Concat(GenerateEnumFormatitems()) + .Concat(GenerateGuidFormatitems()); + } + return Enumerable.Empty(); + + } + + IEnumerable GetFormatCompletionData(IType type) + { + if (type.Namespace != "System") + return null; + switch (type.Name) { + case "Int64": + case "UInt64": + case "Int32": + case "UInt32": + case "Int16": + case "UInt16": + case "Byte": + case "SByte": + return GenerateNumberFormatitems(false); + case "Single": + case "Double": + case "Decimal": + return GenerateNumberFormatitems(true); + case "Enum": + return GenerateEnumFormatitems(); + case "DateTime": + return GenerateDateTimeFormatitems(); + case "TimeSpan": + return GenerateTimeSpanFormatitems(); + case "Guid": + return GenerateGuidFormatitems(); + } + return null; + } + + IEnumerable HandleToStringFormatItems() + { + var unit = ParseStub("\");", false); + + var invoke = unit.GetNodeAt(location); + if (invoke == null) + return Enumerable.Empty(); + + var resolveResult = ResolveExpression(new ExpressionResult(invoke, unit)); + var invokeResult = resolveResult.Result as InvocationResolveResult; + if (invokeResult == null) + return Enumerable.Empty(); + if (invokeResult.Member.Name == "ToString") + return GetFormatCompletionData(invokeResult.Member.DeclaringType) ?? Enumerable.Empty(); + return Enumerable.Empty(); + } + + IEnumerable MagicKeyCompletion(char completionChar, bool controlSpace, out bool isComplete) + { + isComplete = false; + ExpressionResolveResult resolveResult; + switch (completionChar) { + // Magic key completion + case ':': + var text = GetMemberTextToCaret(); + var lexer = new MiniLexer(text.Item1); + lexer.Parse(); + if (lexer.IsInSingleComment || + lexer.IsInChar || + lexer.IsInMultiLineComment || + lexer.IsInPreprocessorDirective) { + return Enumerable.Empty(); + } + + if (lexer.IsInString || lexer.IsInVerbatimString) + return HandleStringFormatItems(); + return HandleMemberReferenceCompletion(GetExpressionBeforeCursor()); + case '"': + text = GetMemberTextToCaret(); + lexer = new MiniLexer(text.Item1); + lexer.Parse(); + if (lexer.IsInSingleComment || + lexer.IsInChar || + lexer.IsInMultiLineComment || + lexer.IsInPreprocessorDirective) { + return Enumerable.Empty(); + } + + if (lexer.IsInString || lexer.IsInVerbatimString) + return HandleToStringFormatItems(); + return Enumerable.Empty(); + case '.': + if (IsInsideCommentStringOrDirective()) { + return Enumerable.Empty(); + } + return HandleMemberReferenceCompletion(GetExpressionBeforeCursor()); + case '#': + if (!IsInPreprocessorDirective()) + return null; + return GetDirectiveCompletionData(); + // XML doc completion + case '<': + if (IsInsideDocComment()) { + return GetXmlDocumentationCompletionData(); + } + if (controlSpace) { + return DefaultControlSpaceItems(ref isComplete); + } + return null; + case '>': + if (!IsInsideDocComment()) { + if (offset > 2 && document.GetCharAt(offset - 2) == '-' && !IsInsideCommentStringOrDirective()) { + return HandleMemberReferenceCompletion(GetExpressionBeforeCursor()); + } + return null; + } + return null; + + // Parameter completion + case '(': + if (IsInsideCommentStringOrDirective()) { + return null; + } + var invoke = GetInvocationBeforeCursor(true); + if (invoke == null) { + if (controlSpace) + return DefaultControlSpaceItems(ref isComplete, invoke); + return null; + } + if (invoke.Node is TypeOfExpression) { + return CreateTypeList(); + } + var invocationResult = ResolveExpression(invoke); + if (invocationResult == null) { + return null; + } + var methodGroup = invocationResult.Result as MethodGroupResolveResult; + if (methodGroup != null) { + return CreateParameterCompletion( + methodGroup, + invocationResult.Resolver, + invoke.Node, + invoke.Unit, + 0, + controlSpace + ); + } + + if (controlSpace) { + return DefaultControlSpaceItems(ref isComplete, invoke); + } + return null; + case '=': + return controlSpace ? DefaultControlSpaceItems(ref isComplete) : null; + case ',': + int cpos2; + if (!GetParameterCompletionCommandOffset(out cpos2)) { + return null; + } + // completionContext = CompletionWidget.CreateCodeCompletionContext (cpos2); + // int currentParameter2 = MethodParameterDataProvider.GetCurrentParameterIndex (CompletionWidget, completionContext) - 1; + // return CreateParameterCompletion (CreateResolver (), location, ExpressionContext.MethodBody, provider.Methods, currentParameter); + break; + + // Completion on space: + case ' ': + int tokenIndex = offset; + string token = GetPreviousToken(ref tokenIndex, false); + if (IsInsideCommentStringOrDirective()) { + return null; + } + // check propose name, for context (but only in control space context) + //IType isAsType = null; + var isAsExpression = GetExpressionAt(offset); + if (controlSpace && isAsExpression != null && isAsExpression.Node is VariableDeclarationStatement && token != "new") { + var parent = isAsExpression.Node as VariableDeclarationStatement; + var proposeNameList = new CompletionDataWrapper(this); + if (parent.Variables.Count != 1) + return DefaultControlSpaceItems(ref isComplete, isAsExpression, controlSpace); + + foreach (var possibleName in GenerateNameProposals (parent.Type)) { + if (possibleName.Length > 0) { + proposeNameList.Result.Add(factory.CreateLiteralCompletionData(possibleName.ToString())); + } + } + + AutoSelect = false; + AutoCompleteEmptyMatch = false; + isComplete = true; + return proposeNameList.Result; + } + // int tokenIndex = offset; + // string token = GetPreviousToken (ref tokenIndex, false); + // if (result.ExpressionContext == ExpressionContext.ObjectInitializer) { + // resolver = CreateResolver (); + // ExpressionContext exactContext = new NewCSharpExpressionFinder (dom).FindExactContextForObjectInitializer (document, resolver.Unit, Document.FileName, resolver.CallingType); + // IReturnType objectInitializer = ((ExpressionContext.TypeExpressionContext)exactContext).UnresolvedType; + // if (objectInitializer != null && objectInitializer.ArrayDimensions == 0 && objectInitializer.PointerNestingLevel == 0 && (token == "{" || token == ",")) + // return CreateCtrlSpaceCompletionData (completionContext, result); + // } + if (token == "=") { + int j = tokenIndex; + string prevToken = GetPreviousToken(ref j, false); + if (prevToken == "=" || prevToken == "+" || prevToken == "-" || prevToken == "!") { + token = prevToken + token; + tokenIndex = j; + } + } + switch (token) { + case "(": + case ",": + int cpos; + if (!GetParameterCompletionCommandOffset(out cpos)) { + break; + } + int currentParameter = GetCurrentParameterIndex(cpos - 1, this.offset) - 1; + if (currentParameter < 0) { + return null; + } + invoke = GetInvocationBeforeCursor(token == "("); + if (invoke == null) { + return null; + } + invocationResult = ResolveExpression(invoke); + if (invocationResult == null) { + return null; + } + methodGroup = invocationResult.Result as MethodGroupResolveResult; + if (methodGroup != null) { + return CreateParameterCompletion( + methodGroup, + invocationResult.Resolver, + invoke.Node, + invoke.Unit, + currentParameter, + controlSpace); + } + return null; + case "=": + case "==": + case "!=": + GetPreviousToken(ref tokenIndex, false); + var expressionOrVariableDeclaration = GetExpressionAt(tokenIndex); + if (expressionOrVariableDeclaration == null) { + return null; + } + resolveResult = ResolveExpression(expressionOrVariableDeclaration); + if (resolveResult == null) { + return null; + } + if (resolveResult.Result.Type.Kind == TypeKind.Enum) { + var wrapper = new CompletionDataWrapper(this); + AddContextCompletion( + wrapper, + resolveResult.Resolver, + expressionOrVariableDeclaration.Node); + AddEnumMembers(wrapper, resolveResult.Result.Type, resolveResult.Resolver); + AutoCompleteEmptyMatch = false; + return wrapper.Result; + } + // + // if (resolvedType.FullName == DomReturnType.Bool.FullName) { + // CompletionDataList completionList = new ProjectDomCompletionDataList (); + // CompletionDataCollector cdc = new CompletionDataCollector (this, dom, completionList, Document.CompilationUnit, resolver.CallingType, location); + // completionList.AutoCompleteEmptyMatch = false; + // cdc.Add ("true", "md-keyword"); + // cdc.Add ("false", "md-keyword"); + // resolver.AddAccessibleCodeCompletionData (result.ExpressionContext, cdc); + // return completionList; + // } + // if (resolvedType.ClassType == ClassType.Delegate && token == "=") { + // CompletionDataList completionList = new ProjectDomCompletionDataList (); + // string parameterDefinition = AddDelegateHandlers (completionList, resolvedType); + // string varName = GetPreviousMemberReferenceExpression (tokenIndex); + // completionList.Add (new EventCreationCompletionData (document, varName, resolvedType, null, parameterDefinition, resolver.CallingMember, resolvedType)); + // + // CompletionDataCollector cdc = new CompletionDataCollector (this, dom, completionList, Document.CompilationUnit, resolver.CallingType, location); + // resolver.AddAccessibleCodeCompletionData (result.ExpressionContext, cdc); + // foreach (var data in completionList) { + // if (data is MemberCompletionData) + // ((MemberCompletionData)data).IsDelegateExpected = true; + // } + // return completionList; + // } + return null; + case "+=": + case "-=": + var curTokenIndex = tokenIndex; + GetPreviousToken(ref tokenIndex, false); + + expressionOrVariableDeclaration = GetExpressionAt(tokenIndex); + if (expressionOrVariableDeclaration == null) { + return null; + } + + resolveResult = ResolveExpression(expressionOrVariableDeclaration); + if (resolveResult == null) { + return null; + } + + + var mrr = resolveResult.Result as MemberResolveResult; + if (mrr != null) { + var evt = mrr.Member as IEvent; + if (evt == null) { + return null; + } + var delegateType = evt.ReturnType; + if (delegateType.Kind != TypeKind.Delegate) { + return null; + } + + var wrapper = new CompletionDataWrapper(this); + if (currentType != null) { + // bool includeProtected = DomType.IncludeProtected (dom, typeFromDatabase, resolver.CallingType); + foreach (var method in ctx.CurrentTypeDefinition.Methods) { + if (MatchDelegate(delegateType, method) /* && method.IsAccessibleFrom (dom, resolver.CallingType, resolver.CallingMember, includeProtected) &&*/) { + wrapper.AddMember(method); + // data.SetText (data.CompletionText + ";"); + } + } + } + if (token == "+=") { + string parameterDefinition = AddDelegateHandlers( + wrapper, + delegateType, + optDelegateName: GuessEventHandlerMethodName(curTokenIndex, (currentType == null) ? null : currentType.Name) + ); + } + + return wrapper.Result; + } + return null; + case ":": + if (currentMember == null) { + token = GetPreviousToken(ref tokenIndex, false); + token = GetPreviousToken(ref tokenIndex, false); + if (token == "enum") + return HandleEnumContext(); + var wrapper = new CompletionDataWrapper(this); + AddTypesAndNamespaces( + wrapper, + GetState(), + null, + t => { + if (currentType != null && currentType.ReflectionName.Equals(t.ReflectionName)) + return null; + var def = t.GetDefinition(); + if (def != null && t.Kind != TypeKind.Interface && (def.IsSealed ||def.IsStatic)) + return null; + return t; + } + ); + return wrapper.Result; + } + return null; + } + + var keywordCompletion = HandleKeywordCompletion(tokenIndex, token); + if (keywordCompletion == null && controlSpace) { + goto default; + } + return keywordCompletion; + // Automatic completion + default: + if (IsInsideCommentStringOrDirective()) { + tokenIndex = offset; + token = GetPreviousToken(ref tokenIndex, false); + if (IsInPreprocessorDirective() && (token.Length == 1 && char.IsLetter(completionChar) || controlSpace)) { + while (token != null && document.GetCharAt(tokenIndex - 1) != '#') { + token = GetPreviousToken(ref tokenIndex, false); + } + if (token != null) + return HandleKeywordCompletion(tokenIndex, token); + } + return null; + } + char prevCh = offset > 2 ? document.GetCharAt(offset - 2) : ';'; + char nextCh = offset < document.TextLength ? document.GetCharAt(offset) : ' '; + const string allowedChars = ";,.[](){}+-*/%^?:&|~!<>="; + + if ((!Char.IsWhiteSpace(nextCh) && allowedChars.IndexOf(nextCh) < 0) || !(Char.IsWhiteSpace(prevCh) || allowedChars.IndexOf(prevCh) >= 0)) { + if (!controlSpace) + return null; + } + + if (IsInLinqContext(offset)) { + if (!controlSpace && !(char.IsLetter(completionChar) || completionChar == '_')) { + return null; + } + tokenIndex = offset; + token = GetPreviousToken(ref tokenIndex, false); + // token last typed + if (!char.IsWhiteSpace(completionChar) && !linqKeywords.Contains(token)) { + token = GetPreviousToken(ref tokenIndex, false); + } + // token last typed + + if (linqKeywords.Contains(token)) { + if (token == "from") { + // after from no auto code completion. + return null; + } + return DefaultControlSpaceItems(ref isComplete); + } + var dataList = new CompletionDataWrapper(this); + AddKeywords(dataList, linqKeywords); + return dataList.Result; + } + if (currentType != null && currentType.Kind == TypeKind.Enum) { + if (!char.IsLetter(completionChar)) + return null; + return HandleEnumContext(); + } + var contextList = new CompletionDataWrapper(this); + var identifierStart = GetExpressionAtCursor(); + if (!(char.IsLetter(completionChar) || completionChar == '_') && (!controlSpace || identifierStart == null)) { + return controlSpace ? HandleAccessorContext() ?? DefaultControlSpaceItems(ref isComplete, identifierStart) : null; + } + + if (identifierStart != null) { + if (identifierStart.Node is TypeParameterDeclaration) { + return null; + } + + if (identifierStart.Node is MemberReferenceExpression) { + return HandleMemberReferenceCompletion( + new ExpressionResult( + ((MemberReferenceExpression)identifierStart.Node).Target, + identifierStart.Unit + ) + ); + } + + if (identifierStart.Node is Identifier) { + if (identifierStart.Node.Parent is GotoStatement) + return null; + + // May happen in variable names + return controlSpace ? DefaultControlSpaceItems(ref isComplete, identifierStart) : null; + } + if (identifierStart.Node is VariableInitializer && location <= ((VariableInitializer)identifierStart.Node).NameToken.EndLocation) { + return controlSpace ? HandleAccessorContext() ?? DefaultControlSpaceItems(ref isComplete, identifierStart) : null; + } + if (identifierStart.Node is CatchClause) { + if (((CatchClause)identifierStart.Node).VariableNameToken.IsInside(location)) { + return null; + } + } + if (identifierStart.Node is AstType && identifierStart.Node.Parent is CatchClause) { + return HandleCatchClauseType(identifierStart); + } + + var pDecl = identifierStart.Node as ParameterDeclaration; + if (pDecl != null && pDecl.Parent is LambdaExpression) { + return null; + } + } + + + // Do not pop up completion on identifier identifier (should be handled by keyword completion). + tokenIndex = offset - 1; + token = GetPreviousToken(ref tokenIndex, false); + if (token == "class" || token == "interface" || token == "struct" || token == "enum" || token == "namespace") { + // after these always follows a name + return null; + } + var keywordresult = HandleKeywordCompletion(tokenIndex, token); + if (keywordresult != null) { + return keywordresult; + } + + if ((!Char.IsWhiteSpace(nextCh) && allowedChars.IndexOf(nextCh) < 0) || !(Char.IsWhiteSpace(prevCh) || allowedChars.IndexOf(prevCh) >= 0)) { + if (controlSpace) + return DefaultControlSpaceItems(ref isComplete, identifierStart); + } + + int prevTokenIndex = tokenIndex; + var prevToken2 = GetPreviousToken(ref prevTokenIndex, false); + if (prevToken2 == "delegate") { + // after these always follows a name + return null; + } + + if (identifierStart == null && !string.IsNullOrEmpty(token) && !IsInsideCommentStringOrDirective() && (prevToken2 == ";" || prevToken2 == "{" || prevToken2 == "}")) { + char last = token [token.Length - 1]; + if (char.IsLetterOrDigit(last) || last == '_' || token == ">") { + return HandleKeywordCompletion(tokenIndex, token); + } + } + if (identifierStart == null) { + var accCtx = HandleAccessorContext(); + if (accCtx != null) { + return accCtx; + } + return DefaultControlSpaceItems(ref isComplete, null, controlSpace); + } + CSharpResolver csResolver; + AstNode n = identifierStart.Node; + if (n.Parent is NamedArgumentExpression) + n = n.Parent; + + if (n != null && n.Parent is AnonymousTypeCreateExpression) { + AutoSelect = false; + } + + // new { b$ } + if (n is IdentifierExpression && n.Parent is AnonymousTypeCreateExpression) + return null; + + // Handle foreach (type name _ + if (n is IdentifierExpression) { + var prev = n.GetPrevNode() as ForeachStatement; + while (prev != null && prev.EmbeddedStatement is ForeachStatement) + prev = (ForeachStatement)prev.EmbeddedStatement; + if (prev != null && prev.InExpression.IsNull) { + if (IncludeKeywordsInCompletionList) + contextList.AddCustom("in"); + return contextList.Result; + } + } + // Handle object/enumerable initialzer expressions: "new O () { P$" + if (n is IdentifierExpression && n.Parent is ArrayInitializerExpression && !(n.Parent.Parent is ArrayCreateExpression)) { + var result = HandleObjectInitializer(identifierStart.Unit, n); + if (result != null) + return result; + } + + if (n != null && n.Parent is InvocationExpression || + n.Parent is ParenthesizedExpression && n.Parent.Parent is InvocationExpression) { + if (n.Parent is ParenthesizedExpression) + n = n.Parent; + var invokeParent = (InvocationExpression)n.Parent; + var invokeResult = ResolveExpression( + invokeParent.Target + ); + var mgr = invokeResult != null ? invokeResult.Result as MethodGroupResolveResult : null; + if (mgr != null) { + int idx = 0; + foreach (var arg in invokeParent.Arguments) { + if (arg == n) { + break; + } + idx++; + } + + foreach (var method in mgr.Methods) { + if (idx < method.Parameters.Count && method.Parameters [idx].Type.Kind == TypeKind.Delegate) { + AutoSelect = false; + AutoCompleteEmptyMatch = false; + } + foreach (var p in method.Parameters) { + contextList.AddNamedParameterVariable(p); + } + } + idx++; + 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; + AutoCompleteEmptyMatch = false; + } + } + } + } + } + + if (n != null && n.Parent is ObjectCreateExpression) { + var invokeResult = ResolveExpression(n.Parent); + var mgr = invokeResult != null ? invokeResult.Result as ResolveResult : null; + if (mgr != null) { + foreach (var constructor in mgr.Type.GetConstructors ()) { + foreach (var p in constructor.Parameters) { + contextList.AddVariable(p); + } + } + } + } + + if (n is IdentifierExpression) { + var bop = n.Parent as BinaryOperatorExpression; + Expression evaluationExpr = null; + + if (bop != null && bop.Right == n && (bop.Operator == BinaryOperatorType.Equality || bop.Operator == BinaryOperatorType.InEquality)) { + evaluationExpr = bop.Left; + } + // check for compare to enum case + if (evaluationExpr != null) { + resolveResult = ResolveExpression(evaluationExpr); + if (resolveResult != null && resolveResult.Result.Type.Kind == TypeKind.Enum) { + var wrapper = new CompletionDataWrapper(this); + AddContextCompletion( + wrapper, + resolveResult.Resolver, + evaluationExpr + ); + AddEnumMembers(wrapper, resolveResult.Result.Type, resolveResult.Resolver); + AutoCompleteEmptyMatch = false; + return wrapper.Result; + } + } + } + + if (n is Identifier && n.Parent is ForeachStatement) { + if (controlSpace) { + return DefaultControlSpaceItems(ref isComplete); + } + return null; + } + + if (n is ArrayInitializerExpression) { + // check for new [] {...} expression -> no need to resolve the type there + var parent = n.Parent as ArrayCreateExpression; + if (parent != null && parent.Type.IsNull) { + return DefaultControlSpaceItems(ref isComplete); + } + + var initalizerResult = ResolveExpression(n.Parent); + + var concreteNode = identifierStart.Unit.GetNodeAt(location); + // check if we're on the right side of an initializer expression + if (concreteNode != null && concreteNode.Parent != null && concreteNode.Parent.Parent != null && concreteNode.Identifier != "a" && concreteNode.Parent.Parent is NamedExpression) { + return DefaultControlSpaceItems(ref isComplete); + } + if (initalizerResult != null && initalizerResult.Result.Type.Kind != TypeKind.Unknown) { + + foreach (var property in initalizerResult.Result.Type.GetProperties ()) { + if (!property.IsPublic) { + continue; + } + var data = contextList.AddMember(property); + if (data != null) + data.DisplayFlags |= DisplayFlags.NamedArgument; + } + foreach (var field in initalizerResult.Result.Type.GetFields ()) { + if (!field.IsPublic) { + continue; + } + var data = contextList.AddMember(field); + if (data != null) + data.DisplayFlags |= DisplayFlags.NamedArgument; + } + return contextList.Result; + } + return DefaultControlSpaceItems(ref isComplete); + } + + if (IsAttributeContext(n)) { + // add attribute targets + if (currentType == null) { + contextList.AddCustom("assembly"); + contextList.AddCustom("module"); + contextList.AddCustom("type"); + } else { + contextList.AddCustom("param"); + contextList.AddCustom("field"); + contextList.AddCustom("property"); + contextList.AddCustom("method"); + contextList.AddCustom("event"); + } + contextList.AddCustom("return"); + } + if (n is MemberType) { + resolveResult = ResolveExpression( + ((MemberType)n).Target + ); + return CreateTypeAndNamespaceCompletionData( + location, + resolveResult.Result, + ((MemberType)n).Target, + resolveResult.Resolver + ); + } + if (n != null/* && !(identifierStart.Item2 is TypeDeclaration)*/) { + csResolver = new CSharpResolver(ctx); + var nodes = new List(); + nodes.Add(n); + if (n.Parent is ICSharpCode.NRefactory.CSharp.Attribute) { + nodes.Add(n.Parent); + } + var astResolver = CompletionContextProvider.GetResolver(csResolver, identifierStart.Unit); + astResolver.ApplyNavigator(new NodeListResolveVisitorNavigator(nodes)); + try { + csResolver = astResolver.GetResolverStateBefore(n); + } catch (Exception) { + csResolver = GetState(); + } + // add attribute properties. + if (n.Parent is ICSharpCode.NRefactory.CSharp.Attribute) { + var rr = ResolveExpression(n.Parent); + if (rr != null) + AddAttributeProperties(contextList, rr.Result); + } + } else { + csResolver = GetState(); + } + // identifier has already started with the first letter + offset--; + AddContextCompletion( + contextList, + csResolver, + identifierStart.Node + ); + return contextList.Result; + // if (stub.Parent is BlockStatement) + + // result = FindExpression (dom, completionContext, -1); + // if (result == null) + // return null; + // else if (result.ExpressionContext != ExpressionContext.IdentifierExpected) { + // triggerWordLength = 1; + // bool autoSelect = true; + // IType returnType = null; + // if ((prevCh == ',' || prevCh == '(') && GetParameterCompletionCommandOffset (out cpos)) { + // ctx = CompletionWidget.CreateCodeCompletionContext (cpos); + // NRefactoryParameterDataProvider dataProvider = ParameterCompletionCommand (ctx) as NRefactoryParameterDataProvider; + // if (dataProvider != null) { + // int i = dataProvider.GetCurrentParameterIndex (CompletionWidget, ctx) - 1; + // foreach (var method in dataProvider.Methods) { + // if (i < method.Parameters.Count) { + // returnType = dom.GetType (method.Parameters [i].ReturnType); + // autoSelect = returnType == null || returnType.ClassType != ClassType.Delegate; + // break; + // } + // } + // } + // } + // // Bug 677531 - Auto-complete doesn't always highlight generic parameter in method signature + // //if (result.ExpressionContext == ExpressionContext.TypeName) + // // autoSelect = false; + // CompletionDataList dataList = CreateCtrlSpaceCompletionData (completionContext, result); + // AddEnumMembers (dataList, returnType); + // dataList.AutoSelect = autoSelect; + // return dataList; + // } else { + // result = FindExpression (dom, completionContext, 0); + // tokenIndex = offset; + // + // // check foreach case, unfortunately the expression finder is too dumb to handle full type names + // // should be overworked if the expression finder is replaced with a mcs ast based analyzer. + // var possibleForeachToken = GetPreviousToken (ref tokenIndex, false); // starting letter + // possibleForeachToken = GetPreviousToken (ref tokenIndex, false); // varname + // + // // read return types to '(' token + // possibleForeachToken = GetPreviousToken (ref tokenIndex, false); // varType + // if (possibleForeachToken == ">") { + // while (possibleForeachToken != null && possibleForeachToken != "(") { + // possibleForeachToken = GetPreviousToken (ref tokenIndex, false); + // } + // } else { + // possibleForeachToken = GetPreviousToken (ref tokenIndex, false); // ( + // if (possibleForeachToken == ".") + // while (possibleForeachToken != null && possibleForeachToken != "(") + // possibleForeachToken = GetPreviousToken (ref tokenIndex, false); + // } + // possibleForeachToken = GetPreviousToken (ref tokenIndex, false); // foreach + // + // if (possibleForeachToken == "foreach") { + // result.ExpressionContext = ExpressionContext.ForeachInToken; + // } else { + // return null; + // // result.ExpressionContext = ExpressionContext.IdentifierExpected; + // } + // result.Expression = ""; + // result.Region = DomRegion.Empty; + // + // return CreateCtrlSpaceCompletionData (completionContext, result); + // } + // break; + } + return null; + + } + + IEnumerable HandleCatchClauseType(ExpressionResult identifierStart) + { + Func typePred = delegate (IType type) { + if (type.GetAllBaseTypes().Any(t => t.ReflectionName == "System.Exception")) + return type; + return null; + }; + if (identifierStart.Node.Parent is CatchClause) { + var wrapper = new CompletionDataWrapper(this); + AddTypesAndNamespaces( + wrapper, + GetState(), + identifierStart.Node, + typePred, + m => false + ); + return wrapper.Result; + } + + var resolveResult = ResolveExpression(identifierStart); + return CreateCompletionData( + location, + resolveResult.Result, + identifierStart.Node, + resolveResult.Resolver, + typePred + ); + } + + string[] validEnumBaseTypes = { + "byte", + "sbyte", + "short", + "int", + "long", + "ushort", + "uint", + "ulong" + }; + + IEnumerable HandleEnumContext() + { + var syntaxTree = ParseStub("a", false); + if (syntaxTree == null) { + return null; + } + + var curType = syntaxTree.GetNodeAt(location); + if (curType == null || curType.ClassType != ClassType.Enum) { + syntaxTree = ParseStub("a {}", false); + var node = syntaxTree.GetNodeAt(location); + if (node != null) { + var wrapper = new CompletionDataWrapper(this); + AddKeywords(wrapper, validEnumBaseTypes); + return wrapper.Result; + } + } + + var member = syntaxTree.GetNodeAt(location); + if (member != null && member.NameToken.EndLocation < location) { + if (currentMember == null && currentType != null) { + foreach (var a in currentType.Members) + if (a.Region.Begin < location && (currentMember == null || a.Region.Begin > currentMember.Region.Begin)) + currentMember = a; + } + bool isComplete = false; + return DefaultControlSpaceItems(ref isComplete); + } + + var attribute = syntaxTree.GetNodeAt(location); + if (attribute != null) { + var contextList = new CompletionDataWrapper(this); + var astResolver = CompletionContextProvider.GetResolver(GetState(), syntaxTree); + var csResolver = astResolver.GetResolverStateBefore(attribute); + AddContextCompletion( + contextList, + csResolver, + attribute + ); + return contextList.Result; + } + return null; + } + + bool IsInLinqContext(int offset) + { + string token; + while (null != (token = GetPreviousToken(ref offset, true)) && !IsInsideCommentStringOrDirective()) { + + if (token == "from") { + return !IsInsideCommentStringOrDirective(offset); + } + if (token == ";" || token == "{") { + return false; + } + } + return false; + } + + IEnumerable HandleAccessorContext() + { + var unit = ParseStub("get; }", false); + var node = unit.GetNodeAt(location, cn => !(cn is CSharpTokenNode)); + if (node is Accessor) { + node = node.Parent; + } + var contextList = new CompletionDataWrapper(this); + if (node is PropertyDeclaration || node is IndexerDeclaration) { + if (IncludeKeywordsInCompletionList) { + contextList.AddCustom("get"); + contextList.AddCustom("set"); + AddKeywords(contextList, accessorModifierKeywords); + } + } else if (node is CustomEventDeclaration) { + if (IncludeKeywordsInCompletionList) { + contextList.AddCustom("add"); + contextList.AddCustom("remove"); + } + } else { + return null; + } + + return contextList.Result; + } + + class IfVisitor :DepthFirstAstVisitor + { + TextLocation loc; + ICompletionContextProvider completionContextProvider; + public bool IsValid; + + public IfVisitor(TextLocation loc, ICompletionContextProvider completionContextProvider) + { + this.loc = loc; + this.completionContextProvider = completionContextProvider; + + this.IsValid = true; + } + + void Check(string argument) + { + // TODO: evaluate #if epressions + if (argument.Any(c => !(char.IsLetterOrDigit(c) || c == '_'))) + return; + IsValid &= completionContextProvider.ConditionalSymbols.Contains(argument); + } + + Stack ifStack = new Stack(); + + public override void VisitPreProcessorDirective(PreProcessorDirective preProcessorDirective) + { + if (preProcessorDirective.Type == PreProcessorDirectiveType.If) { + ifStack.Push(preProcessorDirective); + } else if (preProcessorDirective.Type == PreProcessorDirectiveType.Endif) { + if (ifStack.Count == 0) + return; + var ifDirective = ifStack.Pop(); + if (ifDirective.StartLocation < loc && loc < preProcessorDirective.EndLocation) { + Check(ifDirective.Argument); + } + + } + + base.VisitPreProcessorDirective(preProcessorDirective); + } + + public void End() + { + while (ifStack.Count > 0) { + Check(ifStack.Pop().Argument); + } + } + } + + IEnumerable DefaultControlSpaceItems(ref bool isComplete, ExpressionResult xp = null, bool controlSpace = true) + { + var wrapper = new CompletionDataWrapper(this); + if (offset >= document.TextLength) { + offset = document.TextLength - 1; + } + while (offset > 1 && char.IsWhiteSpace(document.GetCharAt(offset))) { + offset--; + } + location = document.GetLocation(offset); + + if (xp == null) { + xp = GetExpressionAtCursor(); + } + AstNode node; + SyntaxTree unit; + ExpressionResolveResult rr; + if (xp != null) { + node = xp.Node; + rr = ResolveExpression(node); + unit = xp.Unit; + } else { + unit = ParseStub("foo", false); + node = unit.GetNodeAt( + location.Line, + location.Column + 2, + n => n is Expression || n is AstType || n is NamespaceDeclaration || n is Attribute + ); + rr = ResolveExpression(node); + } + var ifvisitor = new IfVisitor(location, CompletionContextProvider); + unit.AcceptVisitor(ifvisitor); + ifvisitor.End(); + if (!ifvisitor.IsValid) + return null; + // namespace name case + var ns = node as NamespaceDeclaration; + if (ns != null) { + var last = ns.NamespaceName; + if (last != null && location < last.EndLocation) + return null; + } + if (node is Identifier && node.Parent is ForeachStatement) { + var foreachStmt = (ForeachStatement)node.Parent; + foreach (var possibleName in GenerateNameProposals (foreachStmt.VariableType)) { + if (possibleName.Length > 0) { + wrapper.Result.Add(factory.CreateLiteralCompletionData(possibleName.ToString())); + } + } + + AutoSelect = false; + AutoCompleteEmptyMatch = false; + isComplete = true; + return wrapper.Result; + } + + if (node is Identifier && node.Parent is ParameterDeclaration) { + if (!controlSpace) { + return null; + } + // Try Parameter name case + var param = node.Parent as ParameterDeclaration; + if (param != null) { + foreach (var possibleName in GenerateNameProposals (param.Type)) { + if (possibleName.Length > 0) { + wrapper.Result.Add(factory.CreateLiteralCompletionData(possibleName.ToString())); + } + } + AutoSelect = false; + AutoCompleteEmptyMatch = false; + isComplete = true; + return wrapper.Result; + } + } + var pDecl = node as ParameterDeclaration; + if (pDecl != null && pDecl.Parent is LambdaExpression) { + return null; + } + /* if (Unit != null && (node == null || node is TypeDeclaration)) { + var constructor = Unit.GetNodeAt( + location.Line, + location.Column - 3 + ); + if (constructor != null && !constructor.ColonToken.IsNull && constructor.Initializer.IsNull) { + wrapper.AddCustom("this"); + wrapper.AddCustom("base"); + return wrapper.Result; + } + }*/ + + var initializer = node != null ? node.Parent as ArrayInitializerExpression : null; + if (initializer != null) { + var result = HandleObjectInitializer(unit, initializer); + if (result != null) + return result; + } + CSharpResolver csResolver = null; + if (rr != null) { + csResolver = rr.Resolver; + } + + if (csResolver == null) { + if (node != null) { + csResolver = GetState(); + //var astResolver = new CSharpAstResolver (csResolver, node, xp != null ? xp.Item1 : CSharpUnresolvedFile); + + try { + //csResolver = astResolver.GetResolverStateBefore (node); + Console.WriteLine(csResolver.LocalVariables.Count()); + } catch (Exception e) { + Console.WriteLine("E!!!" + e); + } + + } else { + csResolver = GetState(); + } + } + + if (node is Attribute) { + // add attribute properties. + var astResolver = CompletionContextProvider.GetResolver(csResolver, unit); + var resolved = astResolver.Resolve(node); + AddAttributeProperties(wrapper, resolved); + } + + + if (node == null) { + // try lambda + unit = ParseStub("foo) => {}", true); + var pd = unit.GetNodeAt( + location.Line, + location.Column + ); + if (pd != null) { + var astResolver = unit != null ? CompletionContextProvider.GetResolver(GetState(), unit) : null; + var parameterType = astResolver.Resolve(pd.Type); + // Type is always a name context -> return null + if (parameterType != null && !parameterType.IsError) + return null; + } + } + + AddContextCompletion(wrapper, csResolver, node); + + return wrapper.Result; + } + + static void AddAttributeProperties(CompletionDataWrapper wrapper, ResolveResult resolved) + { + if (resolved == null || resolved.Type.Kind == TypeKind.Unknown) + return; + + foreach (var property in resolved.Type.GetProperties (p => p.Accessibility == Accessibility.Public)) { + var data = wrapper.AddMember(property); + if (data != null) + data.DisplayFlags |= DisplayFlags.NamedArgument; + } + foreach (var field in resolved.Type.GetFields (p => p.Accessibility == Accessibility.Public)) { + var data = wrapper.AddMember(field); + if (data != null) + data.DisplayFlags |= DisplayFlags.NamedArgument; + } + foreach (var constructor in resolved.Type.GetConstructors (p => p.Accessibility == Accessibility.Public)) { + foreach (var p in constructor.Parameters) { + wrapper.AddNamedParameterVariable(p); + } + } + } + + void AddContextCompletion(CompletionDataWrapper wrapper, CSharpResolver state, AstNode node) + { + int i = offset - 1; + var isInGlobalDelegate = node == null && state.CurrentTypeDefinition == null && GetPreviousToken(ref i, true) == "delegate"; + + if (state != null && !(node is AstType)) { + foreach (var variable in state.LocalVariables) { + if (variable.Region.IsInside(location.Line, location.Column - 1)) { + continue; + } + wrapper.AddVariable(variable); + } + } + + if (state.CurrentMember is IParameterizedMember && !(node is AstType)) { + var param = (IParameterizedMember)state.CurrentMember; + foreach (var p in param.Parameters) { + wrapper.AddVariable(p); + } + } + + if (state.CurrentMember is IMethod) { + var method = (IMethod)state.CurrentMember; + foreach (var p in method.TypeParameters) { + wrapper.AddTypeParameter(p); + } + } + + Func typePred = null; + if (IsAttributeContext(node)) { + var attribute = Compilation.FindType(KnownTypeCode.Attribute); + typePred = t => t.GetAllBaseTypeDefinitions().Any(bt => bt.Equals(attribute)) ? t : null; + } + if (node != null && node.Role == Roles.BaseType) { + typePred = t => { + var def = t.GetDefinition(); + if (def != null && t.Kind != TypeKind.Interface && (def.IsSealed || def.IsStatic)) + return null; + return t; + }; + } + + if (node != null && !(node is NamespaceDeclaration) || state.CurrentTypeDefinition != null || isInGlobalDelegate) { + AddTypesAndNamespaces(wrapper, state, node, typePred); + + wrapper.Result.Add(factory.CreateLiteralCompletionData("global")); + } + + if (!(node is AstType)) { + if (currentMember != null || node is Expression) { + AddKeywords(wrapper, statementStartKeywords); + if (LanguageVersion.Major >= 5) + AddKeywords(wrapper, new [] { "await" }); + AddKeywords(wrapper, expressionLevelKeywords); + if (node == null || node is TypeDeclaration) + AddKeywords(wrapper, typeLevelKeywords); + } else if (currentType != null) { + AddKeywords(wrapper, typeLevelKeywords); + } else { + if (!isInGlobalDelegate && !(node is Attribute)) + AddKeywords(wrapper, globalLevelKeywords); + } + var prop = currentMember as IUnresolvedProperty; + if (prop != null && prop.Setter != null && prop.Setter.Region.IsInside(location)) { + wrapper.AddCustom("value"); + } + if (currentMember is IUnresolvedEvent) { + wrapper.AddCustom("value"); + } + + if (IsInSwitchContext(node)) { + if (IncludeKeywordsInCompletionList) + wrapper.AddCustom("case"); + } + } else { + if (((AstType)node).Parent is ParameterDeclaration) { + AddKeywords(wrapper, parameterTypePredecessorKeywords); + } + } + + if (node != null || state.CurrentTypeDefinition != null || isInGlobalDelegate) + AddKeywords(wrapper, primitiveTypesKeywords); + if (currentMember != null && (node is IdentifierExpression || node is SimpleType) && (node.Parent is ExpressionStatement || node.Parent is ForeachStatement || node.Parent is UsingStatement)) { + if (IncludeKeywordsInCompletionList) { + wrapper.AddCustom("var"); + wrapper.AddCustom("dynamic"); + } + } + wrapper.Result.AddRange(factory.CreateCodeTemplateCompletionData()); + if (node != null && node.Role == Roles.Argument) { + var resolved = ResolveExpression(node.Parent); + var invokeResult = resolved != null ? resolved.Result as CSharpInvocationResolveResult : null; + if (invokeResult != null) { + int argNum = 0; + foreach (var arg in node.Parent.Children.Where (c => c.Role == Roles.Argument)) { + if (arg == node) { + break; + } + argNum++; + } + var param = argNum < invokeResult.Member.Parameters.Count ? invokeResult.Member.Parameters [argNum] : null; + if (param != null && param.Type.Kind == TypeKind.Enum) { + AddEnumMembers(wrapper, param.Type, state); + } + } + } + + if (node is Expression) { + var root = node; + while (root.Parent != null) + root = root.Parent; + var astResolver = CompletionContextProvider.GetResolver(state, root); + foreach (var type in TypeGuessing.GetValidTypes(astResolver, (Expression)node)) { + if (type.Kind == TypeKind.Enum) { + AddEnumMembers(wrapper, type, state); + } else if (type.Kind == TypeKind.Delegate) { + AddDelegateHandlers(wrapper, type, false, true); + AutoSelect = false; + AutoCompleteEmptyMatch = false; + } + } + } + + // Add 'this' keyword for first parameter (extension method case) + if (node != null && node.Parent is ParameterDeclaration && + node.Parent.PrevSibling != null && node.Parent.PrevSibling.Role == Roles.LPar && IncludeKeywordsInCompletionList) { + wrapper.AddCustom("this"); + } + } + + static bool IsInSwitchContext(AstNode node) + { + var n = node; + while (n != null && !(n is EntityDeclaration)) { + if (n is SwitchStatement) { + return true; + } + if (n is BlockStatement) { + return false; + } + n = n.Parent; + } + return false; + } + + static bool ListEquals(List curNamespaces, List oldNamespaces) + { + if (oldNamespaces == null || curNamespaces.Count != oldNamespaces.Count) + return false; + for (int i = 0; i < curNamespaces.Count; i++) { + if (curNamespaces [i].FullName != oldNamespaces [i].FullName) { + return false; + } + } + return true; + } + + void AddTypesAndNamespaces(CompletionDataWrapper wrapper, CSharpResolver state, AstNode node, Func typePred = null, Predicate memberPred = null, Action callback = null, bool onlyAddConstructors = false) + { + var lookup = new MemberLookup(ctx.CurrentTypeDefinition, Compilation.MainAssembly); + + if (currentType != null) { + for (var ct = ctx.CurrentTypeDefinition; ct != null; ct = ct.DeclaringTypeDefinition) { + foreach (var nestedType in ct.GetNestedTypes ()) { + if (!lookup.IsAccessible(nestedType.GetDefinition(), true)) + continue; + if (onlyAddConstructors) { + if (!nestedType.GetConstructors().Any(c => lookup.IsAccessible(c, true))) + continue; + } + + if (typePred == null) { + if (onlyAddConstructors) + wrapper.AddConstructors(nestedType, false, IsAttributeContext(node)); + else + wrapper.AddType(nestedType, false, IsAttributeContext(node)); + continue; + } + + var type = typePred(nestedType); + if (type != null) { + var a2 = onlyAddConstructors ? wrapper.AddConstructors(type, false, IsAttributeContext(node)) : wrapper.AddType(type, false, IsAttributeContext(node)); + if (a2 != null && callback != null) { + callback(a2, type); + } + } + continue; + } + } + + if (this.currentMember != null && !(node is AstType)) { + var def = ctx.CurrentTypeDefinition; + if (def == null && currentType != null) + def = Compilation.MainAssembly.GetTypeDefinition(currentType.FullTypeName); + if (def != null) { + bool isProtectedAllowed = true; + + foreach (var member in def.GetMembers (m => currentMember.IsStatic ? m.IsStatic : true)) { + if (member is IMethod && ((IMethod)member).FullName == "System.Object.Finalize") { + continue; + } + if (member.SymbolKind == SymbolKind.Operator) { + continue; + } + if (member.IsExplicitInterfaceImplementation) { + continue; + } + if (!lookup.IsAccessible(member, isProtectedAllowed)) { + continue; + } + if (memberPred == null || memberPred(member)) { + wrapper.AddMember(member); + } + } + var declaring = def.DeclaringTypeDefinition; + while (declaring != null) { + foreach (var member in declaring.GetMembers (m => m.IsStatic)) { + if (memberPred == null || memberPred(member)) { + wrapper.AddMember(member); + } + } + declaring = declaring.DeclaringTypeDefinition; + } + } + } + if (ctx.CurrentTypeDefinition != null) { + foreach (var p in ctx.CurrentTypeDefinition.TypeParameters) { + wrapper.AddTypeParameter(p); + } + } + } + var scope = ctx.CurrentUsingScope; + + for (var n = scope; n != null; n = n.Parent) { + foreach (var pair in n.UsingAliases) { + wrapper.AddAlias(pair.Key); + } + foreach (var alias in n.ExternAliases) { + wrapper.AddAlias(alias); + } + foreach (var u in n.Usings) { + foreach (var type in u.Types) { + if (!lookup.IsAccessible(type, false)) + continue; + + IType addType = typePred != null ? typePred(type) : type; + + if (onlyAddConstructors && addType != null) { + if (!addType.GetConstructors().Any(c => lookup.IsAccessible(c, true))) + continue; + } + + if (addType != null) { + var a = onlyAddConstructors ? wrapper.AddConstructors(addType, false, IsAttributeContext(node)) : wrapper.AddType(addType, false, IsAttributeContext(node)); + if (a != null && callback != null) { + callback(a, type); + } + } + } + } + + foreach (var type in n.Namespace.Types) { + if (!lookup.IsAccessible(type, false)) + continue; + IType addType = typePred != null ? typePred(type) : type; + if (onlyAddConstructors && addType != null) { + if (!addType.GetConstructors().Any(c => lookup.IsAccessible(c, true))) + continue; + } + + if (addType != null) { + var a2 = onlyAddConstructors ? wrapper.AddConstructors(addType, false, IsAttributeContext(node)) : wrapper.AddType(addType, false); + if (a2 != null && callback != null) { + callback(a2, type); + } + } + } + } + + for (var n = scope; n != null; n = n.Parent) { + foreach (var curNs in n.Namespace.ChildNamespaces) { + wrapper.AddNamespace(lookup, curNs); + } + } + + if (node is AstType && node.Parent is Constraint && IncludeKeywordsInCompletionList) { + wrapper.AddCustom("new()"); + } + + if (AutomaticallyAddImports) { + state = GetState(); + ICompletionData[] importData; + + var namespaces = new List(); + for (var n = ctx.CurrentUsingScope; n != null; n = n.Parent) { + namespaces.Add(n.Namespace); + foreach (var u in n.Usings) + namespaces.Add(u); + } + + if (this.CompletionEngineCache != null && ListEquals(namespaces, CompletionEngineCache.namespaces)) { + importData = CompletionEngineCache.importCompletion; + } else { + // flatten usings + var importList = new List(); + var dict = new Dictionary>(); + foreach (var type in Compilation.GetTopLevelTypeDefinitons ()) { + if (!lookup.IsAccessible(type, false)) + continue; + if (namespaces.Any(n => n.FullName == type.Namespace)) + continue; + bool useFullName = false; + foreach (var ns in namespaces) { + if (ns.GetTypeDefinition(type.Name, type.TypeParameterCount) != null) { + useFullName = true; + break; + } + } + + if (onlyAddConstructors) { + if (!type.GetConstructors().Any(c => lookup.IsAccessible(c, true))) + continue; + } + var data = factory.CreateImportCompletionData(type, useFullName, onlyAddConstructors); + Dictionary createdDict; + if (!dict.TryGetValue(type.Name, out createdDict)) { + createdDict = new Dictionary(); + dict.Add(type.Name, createdDict); + } + ICompletionData oldData; + if (!createdDict.TryGetValue(type.Namespace, out oldData)) { + importList.Add(data); + createdDict.Add(type.Namespace, data); + } else { + oldData.AddOverload(data); + } + } + + importData = importList.ToArray(); + if (CompletionEngineCache != null) { + CompletionEngineCache.namespaces = namespaces; + CompletionEngineCache.importCompletion = importData; + } + } + foreach (var data in importData) { + wrapper.Result.Add(data); + } + + + } + + } + + IEnumerable HandleKeywordCompletion(int wordStart, string word) + { + if (IsInsideCommentStringOrDirective()) { + if (IsInPreprocessorDirective()) { + if (word == "if" || word == "elif") { + if (wordStart > 0 && document.GetCharAt(wordStart - 1) == '#') { + return factory.CreatePreProcessorDefinesCompletionData(); + } + } + } + return null; + } + switch (word) { + case "namespace": + return null; + case "using": + if (currentType != null) { + return null; + } + var wrapper = new CompletionDataWrapper(this); + AddTypesAndNamespaces(wrapper, GetState(), null, t => null); + return wrapper.Result; + case "case": + return CreateCaseCompletionData(location); + // case ",": + // case ":": + // if (result.ExpressionContext == ExpressionContext.InheritableType) { + // IType cls = NRefactoryResolver.GetTypeAtCursor (Document.CompilationUnit, Document.FileName, new TextLocation (completionContext.TriggerLine, completionContext.TriggerLineOffset)); + // CompletionDataList completionList = new ProjectDomCompletionDataList (); + // List namespaceList = GetUsedNamespaces (); + // var col = new CSharpTextEditorCompletion.CompletionDataCollector (this, dom, completionList, Document.CompilationUnit, null, location); + // bool isInterface = false; + // HashSet baseTypeNames = new HashSet (); + // if (cls != null) { + // baseTypeNames.Add (cls.Name); + // if (cls.ClassType == ClassType.Struct) + // isInterface = true; + // } + // int tokenIndex = offset; + // + // // Search base types " : [Type1, ... ,TypeN,] " + // string token = null; + // do { + // token = GetPreviousToken (ref tokenIndex, false); + // if (string.IsNullOrEmpty (token)) + // break; + // token = token.Trim (); + // if (Char.IsLetterOrDigit (token [0]) || token [0] == '_') { + // IType baseType = dom.SearchType (Document.CompilationUnit, cls, result.Region.Start, token); + // if (baseType != null) { + // if (baseType.ClassType != ClassType.Interface) + // isInterface = true; + // baseTypeNames.Add (baseType.Name); + // } + // } + // } while (token != ":"); + // foreach (object o in dom.GetNamespaceContents (namespaceList, true, true)) { + // IType type = o as IType; + // if (type != null && (type.IsStatic || type.IsSealed || baseTypeNames.Contains (type.Name) || isInterface && type.ClassType != ClassType.Interface)) { + // continue; + // } + // if (o is Namespace && !namespaceList.Any (ns => ns.StartsWith (((Namespace)o).FullName))) + // continue; + // col.Add (o); + // } + // // Add inner classes + // Stack innerStack = new Stack (); + // innerStack.Push (cls); + // while (innerStack.Count > 0) { + // IType curType = innerStack.Pop (); + // if (curType == null) + // continue; + // foreach (IType innerType in curType.InnerTypes) { + // if (innerType != cls) + // // don't add the calling class as possible base type + // col.Add (innerType); + // } + // if (curType.DeclaringType != null) + // innerStack.Push (curType.DeclaringType); + // } + // return completionList; + // } + // break; + case "is": + case "as": + if (currentType == null) { + return null; + } + IType isAsType = null; + var isAsExpression = GetExpressionAt(wordStart); + if (isAsExpression != null) { + var parent = isAsExpression.Node.Parent; + if (parent is VariableInitializer) { + parent = parent.Parent; + } + var varDecl = parent as VariableDeclarationStatement; + if (varDecl != null) { + ExpressionResolveResult resolved; + if (varDecl.Type.IsVar()) { + resolved = null; + } else { + resolved = ResolveExpression(parent); + } + if (resolved != null) { + isAsType = resolved.Result.Type; + } + } + } + var isAsWrapper = new CompletionDataWrapper(this); + var def = isAsType != null ? isAsType.GetDefinition() : null; + AddTypesAndNamespaces( + isAsWrapper, + GetState(), + null, + t => t.GetDefinition() == null || def == null || t.GetDefinition().IsDerivedFrom(def) ? t : null, + m => false); + AddKeywords(isAsWrapper, primitiveTypesKeywords); + return isAsWrapper.Result; + // { + // CompletionDataList completionList = new ProjectDomCompletionDataList (); + // ExpressionResult expressionResult = FindExpression (dom, completionContext, wordStart - document.Caret.Offset); + // NRefactoryResolver resolver = CreateResolver (); + // ResolveResult resolveResult = resolver.Resolve (expressionResult, new TextLocation (completionContext.TriggerLine, completionContext.TriggerLineOffset)); + // if (resolveResult != null && resolveResult.ResolvedType != null) { + // CompletionDataCollector col = new CompletionDataCollector (this, dom, completionList, Document.CompilationUnit, resolver.CallingType, location); + // IType foundType = null; + // if (word == "as") { + // ExpressionContext exactContext = new NewCSharpExpressionFinder (dom).FindExactContextForAsCompletion (document, Document.CompilationUnit, Document.FileName, resolver.CallingType); + // if (exactContext is ExpressionContext.TypeExpressionContext) { + // foundType = resolver.SearchType (((ExpressionContext.TypeExpressionContext)exactContext).Type); + // AddAsCompletionData (col, foundType); + // } + // } + // + // if (foundType == null) + // foundType = resolver.SearchType (resolveResult.ResolvedType); + // + // if (foundType != null) { + // if (foundType.ClassType == ClassType.Interface) + // foundType = resolver.SearchType (DomReturnType.Object); + // + // foreach (IType type in dom.GetSubclasses (foundType)) { + // if (type.IsSpecialName || type.Name.StartsWith ("<")) + // continue; + // AddAsCompletionData (col, type); + // } + // } + // List namespaceList = GetUsedNamespaces (); + // foreach (object o in dom.GetNamespaceContents (namespaceList, true, true)) { + // if (o is IType) { + // IType type = (IType)o; + // if (type.ClassType != ClassType.Interface || type.IsSpecialName || type.Name.StartsWith ("<")) + // continue; + // // if (foundType != null && !dom.GetInheritanceTree (foundType).Any (x => x.FullName == type.FullName)) + // // continue; + // AddAsCompletionData (col, type); + // continue; + // } + // if (o is Namespace) + // continue; + // col.Add (o); + // } + // return completionList; + // } + // result.ExpressionContext = ExpressionContext.TypeName; + // return CreateCtrlSpaceCompletionData (completionContext, result); + // } + case "override": + // Look for modifiers, in order to find the beginning of the declaration + int firstMod = wordStart; + int i = wordStart; + for (int n = 0; n < 3; n++) { + string mod = GetPreviousToken(ref i, true); + if (mod == "public" || mod == "protected" || mod == "private" || mod == "internal" || mod == "sealed") { + firstMod = i; + } else if (mod == "static") { + // static methods are not overridable + return null; + } else { + break; + } + } + if (!IsLineEmptyUpToEol()) { + return null; + } + if (currentType != null && (currentType.Kind == TypeKind.Class || currentType.Kind == TypeKind.Struct)) { + string modifiers = document.GetText(firstMod, wordStart - firstMod); + return GetOverrideCompletionData(currentType, modifiers); + } + return null; + case "partial": + // Look for modifiers, in order to find the beginning of the declaration + firstMod = wordStart; + i = wordStart; + for (int n = 0; n < 3; n++) { + string mod = GetPreviousToken(ref i, true); + if (mod == "public" || mod == "protected" || mod == "private" || mod == "internal" || mod == "sealed") { + firstMod = i; + } else if (mod == "static") { + // static methods are not overridable + return null; + } else { + break; + } + } + if (!IsLineEmptyUpToEol()) { + return null; + } + var state = GetState(); + + if (state.CurrentTypeDefinition != null && (state.CurrentTypeDefinition.Kind == TypeKind.Class || state.CurrentTypeDefinition.Kind == TypeKind.Struct)) { + string modifiers = document.GetText(firstMod, wordStart - firstMod); + return GetPartialCompletionData(state.CurrentTypeDefinition, modifiers); + } + return null; + + case "public": + case "protected": + case "private": + case "internal": + case "sealed": + case "static": + var accessorContext = HandleAccessorContext(); + if (accessorContext != null) { + return accessorContext; + } + return null; + case "new": + int j = offset - 4; + // string token = GetPreviousToken (ref j, true); + + IType hintType = null; + var expressionOrVariableDeclaration = GetNewExpressionAt(j); + if (expressionOrVariableDeclaration == null) + return null; + var astResolver = CompletionContextProvider.GetResolver(GetState(), expressionOrVariableDeclaration.Node.Ancestors.FirstOrDefault(n => n is EntityDeclaration || n is SyntaxTree)); + hintType = TypeGuessing.GetValidTypes( + astResolver, + expressionOrVariableDeclaration.Node + ).FirstOrDefault(); + + return CreateConstructorCompletionData(hintType); + case "yield": + var yieldDataList = new CompletionDataWrapper(this); + DefaultCompletionString = "return"; + if (IncludeKeywordsInCompletionList) { + yieldDataList.AddCustom("break"); + yieldDataList.AddCustom("return"); + } + return yieldDataList.Result; + case "in": + var inList = new CompletionDataWrapper(this); + + var expr = GetExpressionAtCursor(); + if (expr == null) + return null; + var rr = ResolveExpression(expr); + + AddContextCompletion( + inList, + rr != null ? rr.Resolver : GetState(), + expr.Node + ); + return inList.Result; + } + return null; + } + + bool IsLineEmptyUpToEol() + { + var line = document.GetLineByNumber(location.Line); + for (int j = offset; j < line.EndOffset; j++) { + char ch = document.GetCharAt(j); + if (!char.IsWhiteSpace(ch)) { + return false; + } + } + return true; + } + + string GetLineIndent(int lineNr) + { + var line = document.GetLineByNumber(lineNr); + for (int j = line.Offset; j < line.EndOffset; j++) { + char ch = document.GetCharAt(j); + if (!char.IsWhiteSpace(ch)) { + return document.GetText(line.Offset, j - line.Offset); + } + } + return ""; + } + // static CSharpAmbience amb = new CSharpAmbience(); + class Category : CompletionCategory + { + public Category(string displayText, string icon) : base(displayText, icon) + { + } + + public override int CompareTo(CompletionCategory other) + { + return 0; + } + } + + IEnumerable CreateConstructorCompletionData(IType hintType) + { + var wrapper = new CompletionDataWrapper(this); + var state = GetState(); + Func pred = null; + Action typeCallback = null; + var inferredTypesCategory = new Category("Inferred Types", null); + var derivedTypesCategory = new Category("Derived Types", null); + + if (hintType != null && (hintType.Kind != TypeKind.TypeParameter || IsTypeParameterInScope(hintType))) { + if (hintType.Kind != TypeKind.Unknown) { + var lookup = new MemberLookup( + ctx.CurrentTypeDefinition, + Compilation.MainAssembly + ); + typeCallback = (data, t) => { + //check if type is in inheritance tree. + if (hintType.GetDefinition() != null && + t.GetDefinition() != null && + t.GetDefinition().IsDerivedFrom(hintType.GetDefinition())) { + data.CompletionCategory = derivedTypesCategory; + } + }; + pred = t => { + if (t.Kind == TypeKind.Interface && hintType.Kind != TypeKind.Array) { + return null; + } + // check for valid constructors + if (t.GetConstructors().Any()) { + bool isProtectedAllowed = currentType != null ? + currentType.Resolve(ctx).GetDefinition().IsDerivedFrom(t.GetDefinition()) : false; + if (!t.GetConstructors().Any(m => lookup.IsAccessible(m, isProtectedAllowed))) { + return null; + } + } + + // check derived types + var typeDef = t.GetDefinition(); + var hintDef = hintType.GetDefinition(); + if (typeDef != null && hintDef != null && typeDef.IsDerivedFrom(hintDef)) { + var newType = wrapper.AddType(t, true); + if (newType != null) { + newType.CompletionCategory = inferredTypesCategory; + } + } + + // check type inference + var typeInference = new TypeInference(Compilation); + typeInference.Algorithm = TypeInferenceAlgorithm.ImprovedReturnAllResults; + + var inferedType = typeInference.FindTypeInBounds(new [] { t }, new [] { hintType }); + if (inferedType != SpecialType.UnknownType) { + var newType = wrapper.AddType(inferedType, true); + if (newType != null) { + newType.CompletionCategory = inferredTypesCategory; + } + return null; + } + return t; + }; + if (!(hintType.Kind == TypeKind.Interface && hintType.Kind != TypeKind.Array)) { + var hint = wrapper.AddType(hintType, true); + if (hint != null) { + DefaultCompletionString = hint.DisplayText; + hint.CompletionCategory = derivedTypesCategory; + } + } + if (hintType is ParameterizedType && hintType.TypeParameterCount == 1 && hintType.FullName == "System.Collections.Generic.IEnumerable") { + var arg = ((ParameterizedType)hintType).TypeArguments.FirstOrDefault(); + if (arg.Kind != TypeKind.TypeParameter) { + var array = new ArrayType(ctx.Compilation, arg, 1); + wrapper.AddType(array, true); + } + } + } else { + var hint = wrapper.AddType(hintType, true); + if (hint != null) { + DefaultCompletionString = hint.DisplayText; + hint.CompletionCategory = derivedTypesCategory; + } + } + } + AddTypesAndNamespaces(wrapper, state, null, pred, m => false, typeCallback, true); + if (hintType == null || hintType == SpecialType.UnknownType) { + AddKeywords(wrapper, primitiveTypesKeywords.Where(k => k != "void")); + } + + CloseOnSquareBrackets = true; + AutoCompleteEmptyMatch = true; + AutoCompleteEmptyMatchOnCurlyBracket = false; + return wrapper.Result; + } + + bool IsTypeParameterInScope(IType hintType) + { + var tp = hintType as ITypeParameter; + var ownerName = tp.Owner.ReflectionName; + if (currentMember != null && ownerName == currentMember.ReflectionName) + return true; + var ot = currentType; + while (ot != null) { + if (ownerName == ot.ReflectionName) + return true; + ot = ot.DeclaringTypeDefinition; + } + return false; + } + + IEnumerable GetOverrideCompletionData(IUnresolvedTypeDefinition type, string modifiers) + { + var wrapper = new CompletionDataWrapper(this); + var alreadyInserted = new List(); + //bool addedVirtuals = false; + + int declarationBegin = offset; + int j = declarationBegin; + for (int i = 0; i < 3; i++) { + switch (GetPreviousToken(ref j, true)) { + case "public": + case "protected": + case "private": + case "internal": + case "sealed": + case "override": + case "partial": + case "async": + declarationBegin = j; + break; + case "static": + return null; // don't add override completion for static members + } + } + AddVirtuals( + alreadyInserted, + wrapper, + modifiers, + type.Resolve(ctx), + declarationBegin + ); + return wrapper.Result; + } + + IEnumerable GetPartialCompletionData(ITypeDefinition type, string modifiers) + { + var wrapper = new CompletionDataWrapper(this); + int declarationBegin = offset; + int j = declarationBegin; + for (int i = 0; i < 3; i++) { + switch (GetPreviousToken(ref j, true)) { + case "public": + case "protected": + case "private": + case "internal": + case "sealed": + case "override": + case "partial": + case "async": + declarationBegin = j; + break; + case "static": + return null; // don't add override completion for static members + } + } + + var methods = new List(); + + foreach (var part in type.Parts) { + foreach (var method in part.Methods) { + if (method.BodyRegion.IsEmpty) { + if (GetImplementation(type, method) != null) { + continue; + } + methods.Add(method); + } + } + } + + foreach (var method in methods) { + wrapper.Add(factory.CreateNewPartialCompletionData( + declarationBegin, + method.DeclaringTypeDefinition, + method + ) + ); + } + + return wrapper.Result; + } + + IMethod GetImplementation(ITypeDefinition type, IUnresolvedMethod method) + { + foreach (var cur in type.Methods) { + if (cur.Name == method.Name && cur.Parameters.Count == method.Parameters.Count && !cur.BodyRegion.IsEmpty) { + bool equal = true; + /* for (int i = 0; i < cur.Parameters.Count; i++) { + if (!cur.Parameters [i].Type.Equals (method.Parameters [i].Type)) { + equal = false; + break; + } + }*/ + if (equal) { + return cur; + } + } + } + return null; + } + + protected virtual void AddVirtuals(List alreadyInserted, CompletionDataWrapper col, string modifiers, IType curType, int declarationBegin) + { + if (curType == null) { + return; + } + foreach (var m in curType.GetMembers ().Reverse ()) { + if (curType.Kind != TypeKind.Interface && !m.IsOverridable) { + continue; + } + // filter out the "Finalize" methods, because finalizers should be done with destructors. + if (m is IMethod && m.Name == "Finalize") { + continue; + } + + var data = factory.CreateNewOverrideCompletionData( + declarationBegin, + currentType, + m + ); + // check if the member is already implemented + bool foundMember = curType.GetMembers().Any(cm => SignatureComparer.Ordinal.Equals( + cm, + m + ) && cm.DeclaringTypeDefinition == curType.GetDefinition() + ); + if (foundMember) { + continue; + } + if (alreadyInserted.Any(cm => SignatureComparer.Ordinal.Equals(cm, m))) + continue; + alreadyInserted.Add(m); + data.CompletionCategory = col.GetCompletionCategory(m.DeclaringTypeDefinition); + col.Add(data); + } + } + + void AddKeywords(CompletionDataWrapper wrapper, IEnumerable keywords) + { + if (!IncludeKeywordsInCompletionList) + return; + foreach (string keyword in keywords) { + if (wrapper.Result.Any(data => data.DisplayText == keyword)) + continue; + wrapper.AddCustom(keyword); + } + } + + public string GuessEventHandlerMethodName(int tokenIndex, string surroundingTypeName) + { + var names = new List(); + + string eventName = GetPreviousToken(ref tokenIndex, false); + string result = GetPreviousToken(ref tokenIndex, false); + if (result != ".") { + if (surroundingTypeName == null) { + eventName = "Handle" + eventName; + } else { + names.Add(surroundingTypeName); + } + } + while (result == ".") { + result = GetPreviousToken(ref tokenIndex, false); + if (result == "this") { + if (names.Count == 0) { + if (surroundingTypeName == null) { + eventName = "Handle" + eventName; + } else { + names.Add(surroundingTypeName); + } + } + } else if (result != null) { + string trimmedName = result.Trim(); + if (trimmedName.Length == 0) { + break; + } + names.Insert(0, trimmedName); + } + result = GetPreviousToken(ref tokenIndex, false); + } + if (!string.IsNullOrEmpty(eventName)) { + names.Add(eventName); + } + result = String.Join("_", names.ToArray()); + foreach (char ch in result) { + if (!char.IsLetterOrDigit(ch) && ch != '_') { + result = ""; + break; + } + } + return result; + } + + bool MatchDelegate(IType delegateType, IMethod method) + { + if (method.SymbolKind != SymbolKind.Method) + return false; + var delegateMethod = delegateType.GetDelegateInvokeMethod(); + if (delegateMethod == null || delegateMethod.Parameters.Count != method.Parameters.Count) { + return false; + } + + for (int i = 0; i < delegateMethod.Parameters.Count; i++) { + if (!delegateMethod.Parameters [i].Type.Equals(method.Parameters [i].Type)) { + return false; + } + } + return true; + } + + string AddDelegateHandlers(CompletionDataWrapper completionList, IType delegateType, bool addSemicolon = true, bool addDefault = true, string optDelegateName = null) + { + IMethod delegateMethod = delegateType.GetDelegateInvokeMethod(); + PossibleDelegates.Add(delegateMethod); + var thisLineIndent = GetLineIndent(location.Line); + string delegateEndString = EolMarker + thisLineIndent + "}" + (addSemicolon ? ";" : ""); + //bool containsDelegateData = completionList.Result.Any(d => d.DisplayText.StartsWith("delegate(")); + if (addDefault && !completionList.AnonymousDelegateAdded) { + completionList.AnonymousDelegateAdded = true; + var oldDelegate = completionList.Result.FirstOrDefault(cd => cd.DisplayText == "delegate"); + if (oldDelegate != null) + completionList.Result.Remove(oldDelegate); + completionList.AddCustom( + "delegate", + "Creates anonymous delegate.", + "delegate {" + EolMarker + thisLineIndent + IndentString + "|" + delegateEndString + ).DisplayFlags |= DisplayFlags.MarkedBold; + if (LanguageVersion.Major >= 5) { + completionList.AddCustom( + "async delegate", + "Creates anonymous async delegate.", + "async delegate {" + EolMarker + thisLineIndent + IndentString + "|" + delegateEndString + ).DisplayFlags |= DisplayFlags.MarkedBold; + } + } + var sb = new StringBuilder("("); + var sbWithoutTypes = new StringBuilder("("); + var state = GetState(); + var builder = new TypeSystemAstBuilder(state); + + for (int k = 0; k < delegateMethod.Parameters.Count; k++) { + + if (k > 0) { + sb.Append(", "); + sbWithoutTypes.Append(", "); + } + var convertedParameter = builder.ConvertParameter(delegateMethod.Parameters [k]); + if (convertedParameter.ParameterModifier == ParameterModifier.Params) + convertedParameter.ParameterModifier = ParameterModifier.None; + sb.Append(convertedParameter.ToString(FormattingPolicy)); + sbWithoutTypes.Append(delegateMethod.Parameters [k].Name); + } + + sb.Append(")"); + sbWithoutTypes.Append(")"); + var signature = sb.ToString(); + if (!completionList.HasAnonymousDelegateAdded(signature)) { + completionList.AddAnonymousDelegateAdded(signature); + + completionList.AddCustom( + "delegate" + signature, + "Creates anonymous delegate.", + "delegate" + signature + " {" + EolMarker + thisLineIndent + IndentString + "|" + delegateEndString + ).DisplayFlags |= DisplayFlags.MarkedBold; + if (LanguageVersion.Major >= 5) { + completionList.AddCustom( + "async delegate" + signature, + "Creates anonymous async delegate.", + "async delegate" + signature + " {" + EolMarker + thisLineIndent + IndentString + "|" + delegateEndString + ).DisplayFlags |= DisplayFlags.MarkedBold; + } + if (!completionList.Result.Any(data => data.DisplayText == sb.ToString())) { + completionList.AddCustom( + signature, + "Creates typed lambda expression.", + signature + " => |" + (addSemicolon ? ";" : "") + ).DisplayFlags |= DisplayFlags.MarkedBold; + if (LanguageVersion.Major >= 5) { + completionList.AddCustom( + "async " + signature, + "Creates typed async lambda expression.", + "async " + signature + " => |" + (addSemicolon ? ";" : "") + ).DisplayFlags |= DisplayFlags.MarkedBold; + } + + if (!delegateMethod.Parameters.Any(p => p.IsOut || p.IsRef) && !completionList.Result.Any(data => data.DisplayText == sbWithoutTypes.ToString())) { + completionList.AddCustom( + sbWithoutTypes.ToString(), + "Creates lambda expression.", + sbWithoutTypes + " => |" + (addSemicolon ? ";" : "") + ).DisplayFlags |= DisplayFlags.MarkedBold; + if (LanguageVersion.Major >= 5) { + completionList.AddCustom( + "async " + sbWithoutTypes, + "Creates async lambda expression.", + "async " + sbWithoutTypes + " => |" + (addSemicolon ? ";" : "") + ).DisplayFlags |= DisplayFlags.MarkedBold; + } + } + } + + } + + string varName = optDelegateName ?? "Handle" + delegateType.Name; + + var ecd = factory.CreateEventCreationCompletionData(varName, delegateType, null, signature, currentMember, currentType); + ecd.DisplayFlags |= DisplayFlags.MarkedBold; + completionList.Add(ecd); + + return sb.ToString(); + } + + bool IsAccessibleFrom(IEntity member, ITypeDefinition calledType, IMember currentMember, bool includeProtected) + { + if (currentMember == null) { + return member.IsStatic || member.IsPublic; + } + // if (currentMember is MonoDevelop.Projects.Dom.BaseResolveResult.BaseMemberDecorator) + // return member.IsPublic | member.IsProtected; + // if (member.IsStatic && !IsStatic) + // return false; + if (member.IsPublic || calledType != null && calledType.Kind == TypeKind.Interface && !member.IsProtected) { + return true; + } + if (member.DeclaringTypeDefinition != null) { + if (member.DeclaringTypeDefinition.Kind == TypeKind.Interface) { + return IsAccessibleFrom( + member.DeclaringTypeDefinition, + calledType, + currentMember, + includeProtected + ); + } + + if (member.IsProtected && !(member.DeclaringTypeDefinition.IsProtectedOrInternal && !includeProtected)) { + return includeProtected; + } + } + if (member.IsInternal || member.IsProtectedAndInternal || member.IsProtectedOrInternal) { + //var type1 = member is ITypeDefinition ? (ITypeDefinition)member : member.DeclaringTypeDefinition; + //var type2 = currentMember is ITypeDefinition ? (ITypeDefinition)currentMember : currentMember.DeclaringTypeDefinition; + bool result = true; + // easy case, projects are the same + /* // if (type1.ProjectContent == type2.ProjectContent) { + // result = true; + // } else + if (type1.ProjectContent != null) { + // maybe type2 hasn't project dom set (may occur in some cases), check if the file is in the project + //TODO !! + // result = type1.ProjectContent.Annotation ().GetProjectFile (type2.Region.FileName) != null; + result = false; + } else if (type2.ProjectContent != null) { + //TODO!! + // result = type2.ProjectContent.Annotation ().GetProjectFile (type1.Region.FileName) != null; + result = false; + } else { + // should never happen ! + result = true; + }*/ + return member.IsProtectedAndInternal ? includeProtected && result : result; + } + + if (!(currentMember is IType) && (currentMember.DeclaringTypeDefinition == null || member.DeclaringTypeDefinition == null)) { + return false; + } + + // inner class + var declaringType = currentMember.DeclaringTypeDefinition; + while (declaringType != null) { + if (declaringType.ReflectionName == currentMember.DeclaringType.ReflectionName) { + return true; + } + declaringType = declaringType.DeclaringTypeDefinition; + } + + + return currentMember.DeclaringTypeDefinition != null && member.DeclaringTypeDefinition.FullName == currentMember.DeclaringTypeDefinition.FullName; + } + + static bool IsAttributeContext(AstNode node) + { + AstNode n = node; + while (n is AstType) { + n = n.Parent; + } + return n is Attribute; + } + + IEnumerable CreateTypeAndNamespaceCompletionData(TextLocation location, ResolveResult resolveResult, AstNode resolvedNode, CSharpResolver state) + { + if (resolveResult == null || resolveResult.IsError) { + return null; + } + var exprParent = resolvedNode.GetParent(); + var unit = exprParent != null ? exprParent.GetParent() : null; + + var astResolver = unit != null ? CompletionContextProvider.GetResolver(state, unit) : null; + IType hintType = exprParent != null && astResolver != null ? + TypeGuessing.GetValidTypes(astResolver, exprParent).FirstOrDefault() : + null; + var result = new CompletionDataWrapper(this); + var lookup = new MemberLookup( + ctx.CurrentTypeDefinition, + Compilation.MainAssembly + ); + if (resolveResult is NamespaceResolveResult) { + var nr = (NamespaceResolveResult)resolveResult; + if (!(resolvedNode.Parent is UsingDeclaration || resolvedNode.Parent != null && resolvedNode.Parent.Parent is UsingDeclaration)) { + foreach (var cl in nr.Namespace.Types) { + if (hintType != null && hintType.Kind != TypeKind.Array && cl.Kind == TypeKind.Interface) { + continue; + } + if (!lookup.IsAccessible(cl, false)) + continue; + result.AddType(cl, false, IsAttributeContext(resolvedNode)); + } + } + foreach (var ns in nr.Namespace.ChildNamespaces) { + result.AddNamespace(lookup, ns); + } + } else if (resolveResult is TypeResolveResult) { + var type = resolveResult.Type; + foreach (var nested in type.GetNestedTypes ()) { + if (hintType != null && hintType.Kind != TypeKind.Array && nested.Kind == TypeKind.Interface) { + continue; + } + var def = nested.GetDefinition(); + if (def != null && !lookup.IsAccessible(def, false)) + continue; + result.AddType(nested, false); + } + } + return result.Result; + } + + IEnumerable CreateTypeList() + { + foreach (var cl in Compilation.RootNamespace.Types) { + yield return factory.CreateTypeCompletionData(cl, false, false, false); + } + + foreach (var ns in Compilation.RootNamespace.ChildNamespaces) { + yield return factory.CreateNamespaceCompletionData(ns); + } + } + + void CreateParameterForInvocation(CompletionDataWrapper result, IMethod method, CSharpResolver state, int parameter, HashSet addedEnums, HashSet addedDelegates) + { + if (method.Parameters.Count <= parameter) { + return; + } + var resolvedType = method.Parameters [parameter].Type; + if (resolvedType.Kind == TypeKind.Enum) { + if (addedEnums.Contains(resolvedType.ReflectionName)) { + return; + } + addedEnums.Add(resolvedType.ReflectionName); + AddEnumMembers(result, resolvedType, state); + return; + } + + if (resolvedType.Kind == TypeKind.Delegate) { + if (addedDelegates.Contains(resolvedType.ReflectionName)) + return; + AddDelegateHandlers(result, resolvedType, false, true, "Handle" + method.Parameters [parameter].Type.Name + method.Parameters [parameter].Name); + } + } + + IEnumerable CreateParameterCompletion(MethodGroupResolveResult resolveResult, CSharpResolver state, AstNode invocation, SyntaxTree unit, int parameter, bool controlSpace) + { + var result = new CompletionDataWrapper(this); + var addedEnums = new HashSet(); + var addedDelegates = new HashSet(); + + foreach (var method in resolveResult.Methods) { + CreateParameterForInvocation(result, method, state, parameter, addedEnums, addedDelegates); + } + foreach (var methods in resolveResult.GetEligibleExtensionMethods (true)) { + foreach (var method in methods) { + if (resolveResult.Methods.Contains(method)) + continue; + CreateParameterForInvocation(result, new ReducedExtensionMethod(method), state, parameter, addedEnums, addedDelegates); + } + } + + foreach (var method in resolveResult.Methods) { + if (parameter < method.Parameters.Count && method.Parameters [parameter].Type.Kind == TypeKind.Delegate) { + AutoSelect = false; + AutoCompleteEmptyMatch = false; + } + foreach (var p in method.Parameters) { + result.AddNamedParameterVariable(p); + } + } + + if (!controlSpace) { + if (addedEnums.Count + addedDelegates.Count == 0) { + return Enumerable.Empty(); + } + AutoCompleteEmptyMatch = false; + AutoSelect = false; + } + AddContextCompletion(result, state, invocation); + + // resolver.AddAccessibleCodeCompletionData (ExpressionContext.MethodBody, cdc); + // if (addedDelegates.Count > 0) { + // foreach (var data in result.Result) { + // if (data is MemberCompletionData) + // ((MemberCompletionData)data).IsDelegateExpected = true; + // } + // } + return result.Result; + } + + void AddEnumMembers(CompletionDataWrapper completionList, IType resolvedType, CSharpResolver state) + { + if (resolvedType.Kind != TypeKind.Enum) { + return; + } + var type = completionList.AddEnumMembers(resolvedType, state); + if (type != null) + DefaultCompletionString = type.DisplayText; + } + + IEnumerable CreateCompletionData(TextLocation location, ResolveResult resolveResult, AstNode resolvedNode, CSharpResolver state, Func typePred = null) + { + if (resolveResult == null /* || resolveResult.IsError*/) { + return null; + } + + var lookup = new MemberLookup( + ctx.CurrentTypeDefinition, + Compilation.MainAssembly + ); + + if (resolveResult is NamespaceResolveResult) { + var nr = (NamespaceResolveResult)resolveResult; + var namespaceContents = new CompletionDataWrapper(this); + + foreach (var cl in nr.Namespace.Types) { + if (!lookup.IsAccessible(cl, false)) + continue; + IType addType = typePred != null ? typePred(cl) : cl; + if (addType != null) + namespaceContents.AddType(addType, false); + } + + foreach (var ns in nr.Namespace.ChildNamespaces) { + namespaceContents.AddNamespace(lookup, ns); + } + return namespaceContents.Result; + } + IType type = resolveResult.Type; + + if (type.Namespace == "System" && type.Name == "Void") + return null; + + if (resolvedNode.Parent is PointerReferenceExpression && (type is PointerType)) { + resolveResult = new OperatorResolveResult(((PointerType)type).ElementType, System.Linq.Expressions.ExpressionType.Extension, resolveResult); + } + + //var typeDef = resolveResult.Type.GetDefinition(); + var result = new CompletionDataWrapper(this); + bool includeStaticMembers = false; + + if (resolveResult is LocalResolveResult) { + if (resolvedNode is IdentifierExpression) { + var mrr = (LocalResolveResult)resolveResult; + includeStaticMembers = mrr.Variable.Name == mrr.Type.Name; + } + } + if (resolveResult is TypeResolveResult && type.Kind == TypeKind.Enum) { + foreach (var field in type.GetFields ()) { + if (!lookup.IsAccessible(field, false)) + continue; + result.AddMember(field); + } + return result.Result; + } + + bool isProtectedAllowed = lookup.IsProtectedAccessAllowed(resolveResult); + bool skipNonStaticMembers = (resolveResult is TypeResolveResult); + + if (resolveResult is MemberResolveResult && resolvedNode is IdentifierExpression) { + var mrr = (MemberResolveResult)resolveResult; + includeStaticMembers = mrr.Member.Name == mrr.Type.Name; + + TypeResolveResult trr; + if (state.IsVariableReferenceWithSameType( + resolveResult, + ((IdentifierExpression)resolvedNode).Identifier, + out trr + )) { + if (currentMember != null && mrr.Member.IsStatic ^ currentMember.IsStatic) { + skipNonStaticMembers = true; + + if (trr.Type.Kind == TypeKind.Enum) { + foreach (var field in trr.Type.GetFields ()) { + if (lookup.IsAccessible(field, false)) + result.AddMember(field); + } + return result.Result; + } + } + } + // ADD Aliases + var scope = ctx.CurrentUsingScope; + + for (var n = scope; n != null; n = n.Parent) { + foreach (var pair in n.UsingAliases) { + if (pair.Key == mrr.Member.Name) { + foreach (var r in CreateCompletionData (location, pair.Value, resolvedNode, state)) { + if (r is IEntityCompletionData && ((IEntityCompletionData)r).Entity is IMember) { + result.AddMember((IMember)((IEntityCompletionData)r).Entity); + } else { + result.Add(r); + } + } + } + } + } + + + } + if (resolveResult is TypeResolveResult && (resolvedNode is IdentifierExpression || resolvedNode is MemberReferenceExpression)) { + includeStaticMembers = true; + } + + // Console.WriteLine ("type:" + type +"/"+type.GetType ()); + // Console.WriteLine ("current:" + ctx.CurrentTypeDefinition); + // Console.WriteLine ("IS PROT ALLOWED:" + isProtectedAllowed + " static: "+ includeStaticMembers); + // Console.WriteLine (resolveResult); + // Console.WriteLine ("node:" + resolvedNode); + // Console.WriteLine (currentMember != null ? currentMember.IsStatic : "currentMember == null"); + + if (resolvedNode.Annotation() == null) { + //tags the created expression as part of an object create expression. + /* + var filteredList = new List(); + foreach (var member in type.GetMembers ()) { + filteredList.Add(member); + } + + foreach (var member in filteredList) { + // Console.WriteLine ("add:" + member + "/" + member.IsStatic); + result.AddMember(member); + }*/ + foreach (var member in lookup.GetAccessibleMembers (resolveResult)) { + if (member.SymbolKind == SymbolKind.Indexer || member.SymbolKind == SymbolKind.Operator || member.SymbolKind == SymbolKind.Constructor || member.SymbolKind == SymbolKind.Destructor) { + continue; + } + if (resolvedNode is BaseReferenceExpression && member.IsAbstract) { + continue; + } + if (member is IType) { + if (resolveResult is TypeResolveResult || includeStaticMembers) { + if (!lookup.IsAccessible(member, isProtectedAllowed)) + continue; + result.AddType((IType)member, false); + continue; + } + } + bool memberIsStatic = member.IsStatic; + if (!includeStaticMembers && memberIsStatic && !(resolveResult is TypeResolveResult)) { + // Console.WriteLine ("skip static member: " + member.FullName); + continue; + } + + var field = member as IField; + if (field != null) { + memberIsStatic |= field.IsConst; + } + if (!memberIsStatic && skipNonStaticMembers) { + continue; + } + + if (member is IMethod && ((IMethod)member).FullName == "System.Object.Finalize") { + continue; + } + if (member.SymbolKind == SymbolKind.Operator) { + continue; + } + + if (member is IMember) { + result.AddMember((IMember)member); + } + } + } + + if (!(resolveResult is TypeResolveResult || includeStaticMembers)) { + foreach (var meths in state.GetExtensionMethods (type)) { + foreach (var m in meths) { + if (!lookup.IsAccessible(m, isProtectedAllowed)) + continue; + result.AddMember(new ReducedExtensionMethod(m)); + } + } + } + + // IEnumerable objects = resolveResult.CreateResolveResult (dom, resolver != null ? resolver.CallingMember : null); + // CompletionDataCollector col = new CompletionDataCollector (this, dom, result, Document.CompilationUnit, resolver != null ? resolver.CallingType : null, location); + // col.HideExtensionParameter = !resolveResult.StaticResolve; + // col.NamePrefix = expressionResult.Expression; + // bool showOnlyTypes = expressionResult.Contexts.Any (ctx => ctx == ExpressionContext.InheritableType || ctx == ExpressionContext.Constraints); + // if (objects != null) { + // foreach (object obj in objects) { + // if (expressionResult.ExpressionContext != null && expressionResult.ExpressionContext.FilterEntry (obj)) + // continue; + // if (expressionResult.ExpressionContext == ExpressionContext.NamespaceNameExcepted && !(obj is Namespace)) + // continue; + // if (showOnlyTypes && !(obj is IType)) + // continue; + // CompletionData data = col.Add (obj); + // if (data != null && expressionResult.ExpressionContext == ExpressionContext.Attribute && data.CompletionText != null && data.CompletionText.EndsWith ("Attribute")) { + // string newText = data.CompletionText.Substring (0, data.CompletionText.Length - "Attribute".Length); + // data.SetText (newText); + // } + // } + // } + + return result.Result; + } + + IEnumerable CreateCaseCompletionData(TextLocation location) + { + var unit = ParseStub("a: break;"); + if (unit == null) { + return null; + } + var s = unit.GetNodeAt(location); + if (s == null) { + return null; + } + + var offset = document.GetOffset(s.Expression.StartLocation); + var expr = GetExpressionAt(offset); + if (expr == null) { + return null; + } + + var resolveResult = ResolveExpression(expr); + if (resolveResult == null || resolveResult.Result.Type.Kind != TypeKind.Enum) { + return null; + } + var wrapper = new CompletionDataWrapper(this); + AddEnumMembers(wrapper, resolveResult.Result.Type, resolveResult.Resolver); + AutoCompleteEmptyMatch = false; + return wrapper.Result; + } + + #region Parsing methods + + ExpressionResult GetExpressionBeforeCursor() + { + SyntaxTree baseUnit; + if (currentMember == null) { + baseUnit = ParseStub("a", false); + var type = baseUnit.GetNodeAt(location); + if (type == null) { + baseUnit = ParseStub("a;", false); + type = baseUnit.GetNodeAt(location); + } + + if (type == null) { + baseUnit = ParseStub("A a;", false); + type = baseUnit.GetNodeAt(location); + } + if (type != null) { + return new ExpressionResult((AstNode)type.Target, baseUnit); + } + } + + baseUnit = ParseStub("ToString()", false); + var curNode = baseUnit.GetNodeAt(location); + // hack for local variable declaration missing ';' issue - remove that if it works. + if (curNode is EntityDeclaration || baseUnit.GetNodeAt(location) == null && baseUnit.GetNodeAt(location) == null) { + baseUnit = ParseStub("a"); + curNode = baseUnit.GetNodeAt(location); + } + + // Hack for handle object initializer continuation expressions + if (curNode is EntityDeclaration || baseUnit.GetNodeAt(location) == null && baseUnit.GetNodeAt(location) == null) { + baseUnit = ParseStub("a};"); + } + var mref = baseUnit.GetNodeAt(location); + if (currentMember == null && currentType == null) { + if (mref != null) { + return new ExpressionResult((AstNode)mref.Target, baseUnit); + } + return null; + } + + //var memberLocation = currentMember != null ? currentMember.Region.Begin : currentType.Region.Begin; + if (mref == null) { + var type = baseUnit.GetNodeAt(location); + if (type != null) { + return new ExpressionResult((AstNode)type.Target, baseUnit); + } + + var pref = baseUnit.GetNodeAt(location); + if (pref != null) { + return new ExpressionResult((AstNode)pref.Target, baseUnit); + } + } + + if (mref == null) { + baseUnit = ParseStub("A a;", false); + var type = baseUnit.GetNodeAt(location); + if (type != null) { + return new ExpressionResult((AstNode)type.Target, baseUnit); + } + } + + AstNode expr = null; + if (mref != null) { + expr = mref.Target; + } else { + Expression tref = baseUnit.GetNodeAt(location); + MemberType memberType = tref != null ? ((TypeReferenceExpression)tref).Type as MemberType : null; + if (memberType == null) { + memberType = baseUnit.GetNodeAt(location); + if (memberType != null) { + if (memberType.Parent is ObjectCreateExpression) { + var mt = memberType.Target.Clone(); + memberType.ReplaceWith(mt); + expr = mt; + goto exit; + } else { + tref = baseUnit.GetNodeAt(location); + if (tref == null) { + tref = new TypeReferenceExpression(memberType.Clone()); + memberType.Parent.AddChild(tref, Roles.Expression); + } + if (tref is ObjectCreateExpression) { + expr = memberType.Target.Clone(); + expr.AddAnnotation(new ObjectCreateExpression()); + } + } + } + } + + if (memberType == null) { + return null; + } + if (expr == null) { + expr = memberType.Target.Clone(); + } + tref.ReplaceWith(expr); + } + exit: + return new ExpressionResult((AstNode)expr, baseUnit); + } + + ExpressionResult GetExpressionAtCursor() + { + // TextLocation memberLocation; + // if (currentMember != null) { + // memberLocation = currentMember.Region.Begin; + // } else if (currentType != null) { + // memberLocation = currentType.Region.Begin; + // } else { + // memberLocation = location; + // } + var baseUnit = ParseStub("a"); + var tmpUnit = baseUnit; + AstNode expr = baseUnit.GetNodeAt( + location, + n => n is IdentifierExpression || n is MemberReferenceExpression + ); + + if (expr == null) { + expr = baseUnit.GetNodeAt(location.Line, location.Column - 1); + } + if (expr == null) + expr = baseUnit.GetNodeAt(location.Line, location.Column - 1); + // try insertStatement + if (expr == null && baseUnit.GetNodeAt(location.Line, location.Column) != null) { + tmpUnit = baseUnit = ParseStub("a();", false); + expr = baseUnit.GetNodeAt( + location.Line, + location.Column + 1 + ); + } + + if (expr == null) { + baseUnit = ParseStub("()"); + expr = baseUnit.GetNodeAt( + location.Line, + location.Column - 1 + ); + if (expr == null) { + expr = baseUnit.GetNodeAt(location.Line, location.Column - 1); + } + } + + if (expr == null) { + baseUnit = ParseStub("a", false); + expr = baseUnit.GetNodeAt( + location, + n => n is IdentifierExpression || n is MemberReferenceExpression || n is CatchClause + ); + } + + // try statement + if (expr == null) { + expr = tmpUnit.GetNodeAt( + location.Line, + location.Column - 1 + ); + baseUnit = tmpUnit; + } + + if (expr == null) { + var block = tmpUnit.GetNodeAt(location); + var node = block != null ? block.Statements.LastOrDefault() : null; + + var forStmt = node != null ? node.PrevSibling as ForStatement : null; + if (forStmt != null && forStmt.EmbeddedStatement.IsNull) { + expr = forStmt; + var id = new IdentifierExpression("stub"); + forStmt.EmbeddedStatement = new BlockStatement() { Statements = { new ExpressionStatement(id) } }; + expr = id; + baseUnit = tmpUnit; + } + } + + if (expr == null) { + var forStmt = tmpUnit.GetNodeAt( + location.Line, + location.Column - 3 + ); + if (forStmt != null && forStmt.EmbeddedStatement.IsNull) { + forStmt.VariableNameToken = Identifier.Create("stub"); + expr = forStmt.VariableNameToken; + baseUnit = tmpUnit; + } + } + if (expr == null) { + expr = tmpUnit.GetNodeAt( + location.Line, + location.Column - 1 + ); + baseUnit = tmpUnit; + } + + // try parameter declaration type + if (expr == null) { + baseUnit = ParseStub(">", false, "{}"); + expr = baseUnit.GetNodeAt( + location.Line, + location.Column - 1 + ); + } + + // try parameter declaration method + if (expr == null) { + baseUnit = ParseStub("> ()", false, "{}"); + expr = baseUnit.GetNodeAt( + location.Line, + location.Column - 1 + ); + } + + // try expression in anonymous type "new { sample = x$" case + if (expr == null) { + baseUnit = ParseStub("a", false); + expr = baseUnit.GetNodeAt( + location.Line, + location.Column + ); + if (expr != null) { + expr = baseUnit.GetNodeAt(location.Line, location.Column) ?? expr; + } + if (expr == null) { + expr = baseUnit.GetNodeAt(location.Line, location.Column); + } + } + + // try lambda + if (expr == null) { + baseUnit = ParseStub("foo) => {}", false); + expr = baseUnit.GetNodeAt( + location.Line, + location.Column + ); + } + + if (expr == null) + return null; + return new ExpressionResult(expr, baseUnit); + } + + ExpressionResult GetExpressionAt(int offset) + { + var parser = new CSharpParser(); + var text = GetMemberTextToCaret(); + + int closingBrackets = 0, generatedLines = 0; + var sb = CreateWrapper("a;", false, "", text.Item1, text.Item2, ref closingBrackets, ref generatedLines); + + var completionUnit = parser.Parse(sb.ToString()); + var offsetLocation = document.GetLocation(offset); + var loc = new TextLocation(offsetLocation.Line - text.Item2.Line + generatedLines + 1, offsetLocation.Column); + + var expr = completionUnit.GetNodeAt( + loc, + n => n is Expression || n is VariableDeclarationStatement + ); + if (expr == null) + return null; + return new ExpressionResult(expr, completionUnit); + } + + ExpressionResult GetNewExpressionAt(int offset) + { + var parser = new CSharpParser(); + var text = GetMemberTextToCaret(); + int closingBrackets = 0, generatedLines = 0; + var sb = CreateWrapper("a ();", false, "", text.Item1, text.Item2, ref closingBrackets, ref generatedLines); + + var completionUnit = parser.Parse(sb.ToString()); + var offsetLocation = document.GetLocation(offset); + var loc = new TextLocation(offsetLocation.Line - text.Item2.Line + generatedLines + 1, offsetLocation.Column); + + var expr = completionUnit.GetNodeAt(loc, n => n is Expression); + if (expr == null) { + // try without ";" + sb = CreateWrapper("a ()", false, "", text.Item1, text.Item2, ref closingBrackets, ref generatedLines); + completionUnit = parser.Parse(sb.ToString()); + + expr = completionUnit.GetNodeAt(loc, n => n is Expression); + if (expr == null) { + return null; + } + } + return new ExpressionResult(expr, completionUnit); + } + + #endregion + + #region Helper methods + + string GetPreviousToken(ref int i, bool allowLineChange) + { + char c; + if (i <= 0) { + return null; + } + + do { + c = document.GetCharAt(--i); + } while (i > 0 && char.IsWhiteSpace(c) && (allowLineChange ? true : c != '\n')); + + if (i == 0) { + return null; + } + + if (!char.IsLetterOrDigit(c)) { + return new string(c, 1); + } + + int endOffset = i + 1; + + do { + c = document.GetCharAt(i - 1); + if (!(char.IsLetterOrDigit(c) || c == '_')) { + break; + } + + i--; + } while (i > 0); + + return document.GetText(i, endOffset - i); + } + + #endregion + + #region Preprocessor + + IEnumerable GetDirectiveCompletionData() + { + yield return factory.CreateLiteralCompletionData("if"); + yield return factory.CreateLiteralCompletionData("else"); + yield return factory.CreateLiteralCompletionData("elif"); + yield return factory.CreateLiteralCompletionData("endif"); + yield return factory.CreateLiteralCompletionData("define"); + yield return factory.CreateLiteralCompletionData("undef"); + yield return factory.CreateLiteralCompletionData("warning"); + yield return factory.CreateLiteralCompletionData("error"); + yield return factory.CreateLiteralCompletionData("pragma"); + yield return factory.CreateLiteralCompletionData("line"); + yield return factory.CreateLiteralCompletionData("line hidden"); + yield return factory.CreateLiteralCompletionData("line default"); + yield return factory.CreateLiteralCompletionData("region"); + yield return factory.CreateLiteralCompletionData("endregion"); + } + + #endregion + + #region Xml Comments + + static readonly List commentTags = new List(new string[] { + "c", + "code", + "example", + "exception", + "include", + "list", + "listheader", + "item", + "term", + "description", + "para", + "param", + "paramref", + "permission", + "remarks", + "returns", + "see", + "seealso", + "summary", + "value" + } + ); + + public static IEnumerable CommentTags { + get { + return commentTags; + } + } + + string GetLastClosingXmlCommentTag() + { + var line = document.GetLineByNumber(location.Line); + + restart: + string lineText = document.GetText(line); + if (!lineText.Trim().StartsWith("///", StringComparison.Ordinal)) + return null; + int startIndex = Math.Min(location.Column - 1, lineText.Length - 1) - 1; + while (startIndex > 0 && lineText [startIndex] != '<') { + --startIndex; + if (lineText [startIndex] == '/') { + // already closed. + startIndex = -1; + break; + } + } + if (startIndex < 0 && line.PreviousLine != null) { + line = line.PreviousLine; + goto restart; + } + + if (startIndex >= 0) { + int endIndex = startIndex; + while (endIndex + 1 < lineText.Length && lineText [endIndex] != '>' && !char.IsWhiteSpace(lineText [endIndex])) { + endIndex++; + } + string tag = endIndex - startIndex - 1 > 0 ? lineText.Substring( + startIndex + 1, + endIndex - startIndex - 1 + ) : null; + if (!string.IsNullOrEmpty(tag) && commentTags.IndexOf(tag) >= 0) { + return tag; + } + } + return null; + } + + IEnumerable GetXmlDocumentationCompletionData() + { + var closingTag = GetLastClosingXmlCommentTag(); + if (closingTag != null) { + yield return factory.CreateLiteralCompletionData( + "/" + closingTag + ">" + ); + } + + yield return factory.CreateXmlDocCompletionData( + "c", + "Set text in a code-like font" + ); + yield return factory.CreateXmlDocCompletionData( + "code", + "Set one or more lines of source code or program output" + ); + yield return factory.CreateXmlDocCompletionData( + "example", + "Indicate an example" + ); + yield return factory.CreateXmlDocCompletionData( + "exception", + "Identifies the exceptions a method can throw", + "exception cref=\"|\"> +// +// Copyright (c) 2011 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 System.Linq; +using System.Text; + +using ICSharpCode.NRefactory.CSharp.Resolver; +using ICSharpCode.NRefactory.Editor; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem.Implementation; +using ICSharpCode.NRefactory.CSharp.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp.Completion +{ + /// + /// Acts as a common base between code completion and parameter completion. + /// + public class CSharpCompletionEngineBase + { + protected IDocument document; + protected int offset; + protected TextLocation location; + protected IUnresolvedTypeDefinition currentType; + protected IUnresolvedMember currentMember; + + #region Input properties + public CSharpTypeResolveContext ctx { get; private set; } + + public IProjectContent ProjectContent { get; private set; } + + ICompilation compilation; + + protected ICompilation Compilation { + get { + if (compilation == null) + compilation = ProjectContent.Resolve (ctx).Compilation; + return compilation; + } + } + + Version languageVersion = new Version (5, 0); + public Version LanguageVersion { + get { + return languageVersion; + } + set { + languageVersion = value; + } + } + #endregion + + protected CSharpCompletionEngineBase(IProjectContent content, ICompletionContextProvider completionContextProvider, CSharpTypeResolveContext ctx) + { + if (content == null) + throw new ArgumentNullException("content"); + if (ctx == null) + throw new ArgumentNullException("ctx"); + if (completionContextProvider == null) + throw new ArgumentNullException("completionContextProvider"); + + this.ProjectContent = content; + this.CompletionContextProvider = completionContextProvider; + this.ctx = ctx; + } + + + public ICompletionContextProvider CompletionContextProvider { + get; + private set; + } + + public void SetOffset (int offset) + { + Reset (); + + this.offset = offset; + this.location = document.GetLocation (offset); + CompletionContextProvider.GetCurrentMembers (offset, out currentType, out currentMember); + } + + public bool GetParameterCompletionCommandOffset (out int cpos) + { + // Start calculating the parameter offset from the beginning of the + // current member, instead of the beginning of the file. + cpos = offset - 1; + var mem = currentMember; + if (mem == null || (mem is IType) || IsInsideCommentStringOrDirective ()) { + return false; + } + int startPos = document.GetOffset (mem.Region.BeginLine, mem.Region.BeginColumn); + int parenDepth = 0; + int chevronDepth = 0; + Stack indexStack = new Stack (); + while (cpos > startPos) { + char c = document.GetCharAt (cpos); + if (c == ')') { + parenDepth++; + } + if (c == '>') { + chevronDepth++; + } + if (c == '}') { + if (indexStack.Count > 0) { + parenDepth = indexStack.Pop (); + } else { + parenDepth = 0; + } + chevronDepth = 0; + } + if (indexStack.Count == 0 && (parenDepth == 0 && c == '(' || chevronDepth == 0 && c == '<')) { + int p = GetCurrentParameterIndex (startPos, cpos + 1); + if (p != -1) { + cpos++; + return true; + } else { + return false; + } + } + if (c == '(') { + parenDepth--; + } + if (c == '<') { + chevronDepth--; + } + if (c == '{') { + indexStack.Push (parenDepth); + chevronDepth = 0; + } + cpos--; + } + return false; + } + + public int GetCurrentParameterIndex(int triggerOffset, int endOffset) + { + List list; + return GetCurrentParameterIndex (triggerOffset, endOffset, out list); + } + + public int GetCurrentParameterIndex (int triggerOffset, int endOffset, out List usedNamedParameters) + { + usedNamedParameters =new List (); + var parameter = new Stack (); + var bracketStack = new Stack> (); + bool inSingleComment = false, inString = false, inVerbatimString = false, inChar = false, inMultiLineComment = false; + var word = new StringBuilder (); + bool foundCharAfterOpenBracket = false; + for (int i = triggerOffset; i < endOffset; i++) { + char ch = document.GetCharAt (i); + char nextCh = i + 1 < document.TextLength ? document.GetCharAt (i + 1) : '\0'; + if (ch == ':') { + usedNamedParameters.Add (word.ToString ()); + word.Length = 0; + } else if (char.IsLetterOrDigit (ch) || ch =='_') { + word.Append (ch); + } else if (char.IsWhiteSpace (ch)) { + + } else { + word.Length = 0; + } + if (!char.IsWhiteSpace(ch) && parameter.Count > 0) + foundCharAfterOpenBracket = true; + + switch (ch) { + case '{': + if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) { + break; + } + bracketStack.Push (parameter); + parameter = new Stack (); + break; + case '[': + case '(': + if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) { + break; + } + parameter.Push (0); + break; + case '}': + if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) { + break; + } + if (bracketStack.Count > 0) { + parameter = bracketStack.Pop (); + } else { + return -1; + } + break; + case ']': + case ')': + if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) { + break; + } + if (parameter.Count > 0) { + parameter.Pop (); + } else { + return -1; + } + break; + case '<': + if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) { + break; + } + parameter.Push (0); + break; + case '=': + if (nextCh == '>') { + i++; + continue; + } + break; + case '>': + if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) { + break; + } + if (parameter.Count > 0) { + parameter.Pop (); + } + break; + case ',': + if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) { + break; + } + if (parameter.Count > 0) { + parameter.Push (parameter.Pop () + 1); + } + break; + case '/': + if (inString || inChar || inVerbatimString) { + break; + } + if (nextCh == '/') { + i++; + inSingleComment = true; + } + if (nextCh == '*') { + inMultiLineComment = true; + } + break; + case '*': + if (inString || inChar || inVerbatimString || inSingleComment) { + break; + } + if (nextCh == '/') { + i++; + inMultiLineComment = false; + } + break; + case '@': + if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) { + break; + } + if (nextCh == '"') { + i++; + inVerbatimString = true; + } + break; + case '\\': + if (inString || inChar) { + i++; + } + break; + case '"': + if (inSingleComment || inMultiLineComment || inChar) { + break; + } + if (inVerbatimString) { + if (nextCh == '"') { + i++; + break; + } + inVerbatimString = false; + break; + } + inString = !inString; + break; + case '\'': + if (inSingleComment || inMultiLineComment || inString || inVerbatimString) { + break; + } + inChar = !inChar; + break; + default: + if (NewLine.IsNewLine(ch)) { + inSingleComment = false; + inString = false; + inChar = false; + } + break; + } + } + if (parameter.Count != 1 || bracketStack.Count > 0) { + return -1; + } + if (!foundCharAfterOpenBracket) + return 0; + return parameter.Pop() + 1; + } + + #region Context helper methods + public class MiniLexer + { + readonly string text; + + public bool IsFistNonWs = true; + public bool IsInSingleComment = false; + public bool IsInString = false; + public bool IsInVerbatimString = false; + public bool IsInChar = false; + public bool IsInMultiLineComment = false; + public bool IsInPreprocessorDirective = false; + + public MiniLexer(string text) + { + this.text = text; + } + + /// + /// Parsing all text and calling act delegate on almost every character. + /// Skipping begining of comments, begining of verbatim strings and escaped characters. + /// + /// Return true to abort parsing. Integer argument represent offset in text. + /// True if aborted. + public bool Parse(Func act = null) + { + return Parse(0, text.Length, act); + } + + + /// + /// Parsing text from start to start+length and calling act delegate on almost every character. + /// Skipping begining of comments, begining of verbatim strings and escaped characters. + /// + /// Start offset. + /// Lenght to parse. + /// Return true to abort parsing. Integer argument represent offset in text. + /// True if aborted. + public bool Parse(int start, int length, Func act = null) + { + for (int i = start; i < length; i++) { + char ch = text [i]; + char nextCh = i + 1 < text.Length ? text [i + 1] : '\0'; + switch (ch) { + case '#': + if (IsFistNonWs) + IsInPreprocessorDirective = true; + break; + case '/': + if (IsInString || IsInChar || IsInVerbatimString || IsInSingleComment || IsInMultiLineComment) + break; + if (nextCh == '/') { + i++; + IsInSingleComment = true; + IsInPreprocessorDirective = false; + } + if (nextCh == '*' && !IsInPreprocessorDirective) { + IsInMultiLineComment = true; + i++; + } + break; + case '*': + if (IsInString || IsInChar || IsInVerbatimString || IsInSingleComment) + break; + if (nextCh == '/') { + i++; + IsInMultiLineComment = false; + } + break; + case '@': + if (IsInString || IsInChar || IsInVerbatimString || IsInSingleComment || IsInMultiLineComment) + break; + if (nextCh == '"') { + i++; + IsInVerbatimString = true; + } + break; + case '\n': + case '\r': + IsInSingleComment = false; + IsInString = false; + IsInChar = false; + IsFistNonWs = true; + IsInPreprocessorDirective = false; + break; + case '\\': + if (IsInString || IsInChar) + i++; + break; + case '"': + if (IsInSingleComment || IsInMultiLineComment || IsInChar) + break; + if (IsInVerbatimString) { + if (nextCh == '"') { + i++; + break; + } + IsInVerbatimString = false; + break; + } + IsInString = !IsInString; + break; + case '\'': + if (IsInSingleComment || IsInMultiLineComment || IsInString || IsInVerbatimString) + break; + IsInChar = !IsInChar; + break; + } + if (act != null) + if (act (ch, i)) + return true; + IsFistNonWs &= ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'; + } + return false; + } + } + + + protected bool IsInsideCommentStringOrDirective(int offset) + { + var lexer = new MiniLexer(document.Text); + lexer.Parse(0, offset); + return + lexer.IsInSingleComment || + lexer.IsInString || + lexer.IsInVerbatimString || + lexer.IsInChar || + lexer.IsInMultiLineComment || + lexer.IsInPreprocessorDirective; + } + + + protected bool IsInsideCommentStringOrDirective() + { + var text = GetMemberTextToCaret(); + var lexer = new MiniLexer(text.Item1); + lexer.Parse(); + return + lexer.IsInSingleComment || + lexer.IsInString || + lexer.IsInVerbatimString || + lexer.IsInChar || + lexer.IsInMultiLineComment || + lexer.IsInPreprocessorDirective; + } + + protected bool IsInsideDocComment () + { + var text = GetMemberTextToCaret (); + bool inSingleComment = false, inString = false, inVerbatimString = false, inChar = false, inMultiLineComment = false; + bool singleLineIsDoc = false; + + for (int i = 0; i < text.Item1.Length - 1; i++) { + char ch = text.Item1 [i]; + char nextCh = text.Item1 [i + 1]; + + switch (ch) { + case '/': + if (inString || inChar || inVerbatimString) + break; + if (nextCh == '/') { + i++; + inSingleComment = true; + singleLineIsDoc = i + 1 < text.Item1.Length && text.Item1 [i + 1] == '/'; + if (singleLineIsDoc) { + i++; + } + } + if (nextCh == '*') + inMultiLineComment = true; + break; + case '*': + if (inString || inChar || inVerbatimString || inSingleComment) + break; + if (nextCh == '/') { + i++; + inMultiLineComment = false; + } + break; + case '@': + if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) + break; + if (nextCh == '"') { + i++; + inVerbatimString = true; + } + break; + case '\n': + case '\r': + inSingleComment = false; + inString = false; + inChar = false; + break; + case '\\': + if (inString || inChar) + i++; + break; + case '"': + if (inSingleComment || inMultiLineComment || inChar) + break; + if (inVerbatimString) { + if (nextCh == '"') { + i++; + break; + } + inVerbatimString = false; + break; + } + inString = !inString; + break; + case '\'': + if (inSingleComment || inMultiLineComment || inString || inVerbatimString) + break; + inChar = !inChar; + break; + } + } + + return inSingleComment && singleLineIsDoc; + } + + protected CSharpResolver GetState () + { + return new CSharpResolver (ctx); + /*var state = new CSharpResolver (ctx); + + state.CurrentMember = currentMember; + state.CurrentTypeDefinition = currentType; + state.CurrentUsingScope = CSharpUnresolvedFile.GetUsingScope (location); + if (state.CurrentMember != null) { + var node = Unit.GetNodeAt (location); + if (node == null) + return state; + var navigator = new NodeListResolveVisitorNavigator (new[] { node }); + var visitor = new ResolveVisitor (state, CSharpUnresolvedFile, navigator); + Unit.AcceptVisitor (visitor, null); + try { + var newState = visitor.GetResolverStateBefore (node); + if (newState != null) + state = newState; + } catch (Exception) { + } + } + + return state;*/ + } + #endregion + + #region Basic parsing/resolving functions + static Stack> GetBracketStack (string memberText) + { + var bracketStack = new Stack> (); + + bool inSingleComment = false, inString = false, inVerbatimString = false, inChar = false, inMultiLineComment = false; + + for (int i = 0; i < memberText.Length; i++) { + char ch = memberText [i]; + char nextCh = i + 1 < memberText.Length ? memberText [i + 1] : '\0'; + switch (ch) { + case '(': + case '[': + case '{': + if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) + break; + bracketStack.Push (Tuple.Create (ch, i)); + break; + case ')': + case ']': + case '}': + if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) + break; + if (bracketStack.Count > 0) + bracketStack.Pop (); + break; + case '/': + if (inString || inChar || inVerbatimString) + break; + if (nextCh == '/') { + i++; + inSingleComment = true; + } + if (nextCh == '*') + inMultiLineComment = true; + break; + case '*': + if (inString || inChar || inVerbatimString || inSingleComment) + break; + if (nextCh == '/') { + i++; + inMultiLineComment = false; + } + break; + case '@': + if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) + break; + if (nextCh == '"') { + i++; + inVerbatimString = true; + } + break; + case '\\': + if (inString || inChar) + i++; + break; + case '"': + if (inSingleComment || inMultiLineComment || inChar) + break; + if (inVerbatimString) { + if (nextCh == '"') { + i++; + break; + } + inVerbatimString = false; + break; + } + inString = !inString; + break; + case '\'': + if (inSingleComment || inMultiLineComment || inString || inVerbatimString) + break; + inChar = !inChar; + break; + default : + if (NewLine.IsNewLine(ch)) { + inSingleComment = false; + inString = false; + inChar = false; + } + break; + } + } + return bracketStack; + } + + public static void AppendMissingClosingBrackets (StringBuilder wrapper, bool appendSemicolon) + { + var memberText = wrapper.ToString(); + var bracketStack = GetBracketStack(memberText); + bool didAppendSemicolon = !appendSemicolon; + //char lastBracket = '\0'; + while (bracketStack.Count > 0) { + var t = bracketStack.Pop (); + switch (t.Item1) { + case '(': + wrapper.Append (')'); + if (appendSemicolon) + didAppendSemicolon = false; + //lastBracket = ')'; + break; + case '[': + wrapper.Append (']'); + if (appendSemicolon) + didAppendSemicolon = false; + //lastBracket = ']'; + break; + case '<': + wrapper.Append ('>'); + if (appendSemicolon) + didAppendSemicolon = false; + //lastBracket = '>'; + break; + case '{': + int o = t.Item2 - 1; + if (!didAppendSemicolon) { + didAppendSemicolon = true; + wrapper.Append (';'); + } + + bool didAppendCatch = false; + while (o >= "try".Length) { + char ch = memberText [o]; + if (!char.IsWhiteSpace (ch)) { + if (ch == 'y' && memberText [o - 1] == 'r' && memberText [o - 2] == 't' && (o - 3 < 0 || !char.IsLetterOrDigit(memberText [o - 3]))) { + wrapper.Append ("} catch {}"); + didAppendCatch = true; + } + break; + } + o--; + } + if (!didAppendCatch) + wrapper.Append ('}'); + break; + } + } + if (!didAppendSemicolon) + wrapper.Append (';'); + } + + protected StringBuilder CreateWrapper(string continuation, bool appendSemicolon, string afterContinuation, string memberText, TextLocation memberLocation, ref int closingBrackets, ref int generatedLines) + { + var wrapper = new StringBuilder(); + bool wrapInClass = memberLocation != new TextLocation(1, 1); + if (wrapInClass) { + wrapper.Append("class Stub {"); + wrapper.AppendLine(); + closingBrackets++; + generatedLines++; + } + wrapper.Append(memberText); + wrapper.Append(continuation); + AppendMissingClosingBrackets(wrapper, appendSemicolon); + wrapper.Append(afterContinuation); + if (closingBrackets > 0) { + wrapper.Append(new string('}', closingBrackets)); + } + return wrapper; + } + + protected SyntaxTree ParseStub(string continuation, bool appendSemicolon = true, string afterContinuation = null) + { + var mt = GetMemberTextToCaret(); + if (mt == null) { + return null; + } + + string memberText = mt.Item1; + var memberLocation = mt.Item2; + int closingBrackets = 1; + int generatedLines = 0; + var wrapper = CreateWrapper(continuation, appendSemicolon, afterContinuation, memberText, memberLocation, ref closingBrackets, ref generatedLines); + var parser = new CSharpParser (); + foreach (var sym in CompletionContextProvider.ConditionalSymbols) + parser.CompilerSettings.ConditionalSymbols.Add (sym); + parser.InitialLocation = new TextLocation(memberLocation.Line - generatedLines, 1); + var result = parser.Parse(wrapper.ToString ()); + return result; + } + + protected virtual void Reset () + { + memberText = null; + } + + Tuple memberText; + protected Tuple GetMemberTextToCaret() + { + if (memberText == null) + memberText = CompletionContextProvider.GetMemberTextToCaret(offset, currentType, currentMember); + return memberText; + } + + protected ExpressionResult GetInvocationBeforeCursor(bool afterBracket) + { + SyntaxTree baseUnit; + baseUnit = ParseStub("a", false); + + var section = baseUnit.GetNodeAt(location.Line, location.Column - 2); + var attr = section != null ? section.Attributes.LastOrDefault() : null; + if (attr != null) { + return new ExpressionResult((AstNode)attr, baseUnit); + } + + //var memberLocation = currentMember != null ? currentMember.Region.Begin : currentType.Region.Begin; + var mref = baseUnit.GetNodeAt(location.Line, location.Column - 1, n => n is InvocationExpression || n is ObjectCreateExpression); + AstNode expr = null; + if (mref is InvocationExpression) { + expr = ((InvocationExpression)mref).Target; + } else if (mref is ObjectCreateExpression) { + expr = mref; + } else { + baseUnit = ParseStub(")};", false); + mref = baseUnit.GetNodeAt(location.Line, location.Column - 1, n => n is InvocationExpression || n is ObjectCreateExpression); + if (mref is InvocationExpression) { + expr = ((InvocationExpression)mref).Target; + } else if (mref is ObjectCreateExpression) { + expr = mref; + } + } + + if (expr == null) { + // work around for missing ';' bug in mcs: + baseUnit = ParseStub("a", true); + + section = baseUnit.GetNodeAt(location.Line, location.Column - 2); + attr = section != null ? section.Attributes.LastOrDefault() : null; + if (attr != null) { + return new ExpressionResult((AstNode)attr, baseUnit); + } + + //var memberLocation = currentMember != null ? currentMember.Region.Begin : currentType.Region.Begin; + mref = baseUnit.GetNodeAt(location.Line, location.Column - 1, n => n is InvocationExpression || n is ObjectCreateExpression); + expr = null; + if (mref is InvocationExpression) { + expr = ((InvocationExpression)mref).Target; + } else if (mref is ObjectCreateExpression) { + expr = mref; + } + } + + if (expr == null) { + return null; + } + return new ExpressionResult ((AstNode)expr, baseUnit); + } + + public class ExpressionResult + { + public AstNode Node { get; private set; } + public SyntaxTree Unit { get; private set; } + + + public ExpressionResult (AstNode item2, SyntaxTree item3) + { + this.Node = item2; + this.Unit = item3; + } + + public override string ToString () + { + return string.Format ("[ExpressionResult: Node={0}, Unit={1}]", Node, Unit); + } + } + + protected ExpressionResolveResult ResolveExpression (ExpressionResult tuple) + { + return ResolveExpression (tuple.Node); + } + + protected class ExpressionResolveResult + { + public ResolveResult Result { get; set; } + public CSharpResolver Resolver { get; set; } + public CSharpAstResolver AstResolver { get; set; } + + public ExpressionResolveResult(ResolveResult item1, CSharpResolver item2, CSharpAstResolver item3) + { + this.Result = item1; + this.Resolver = item2; + this.AstResolver = item3; + } + } + + protected ExpressionResolveResult ResolveExpression(AstNode expr) + { + if (expr == null) { + return null; + } + AstNode resolveNode; + if (expr is Expression || expr is AstType) { + resolveNode = expr; + } else if (expr is VariableDeclarationStatement) { + resolveNode = ((VariableDeclarationStatement)expr).Type; + } else { + resolveNode = expr; + } + try { + var root = expr.AncestorsAndSelf.FirstOrDefault(n => n is EntityDeclaration || n is SyntaxTree); + if (root == null) { + return null; + } + var curState = GetState(); + // current member needs to be in the setter because of the 'value' parameter + if (root is Accessor) { + var prop = curState.CurrentMember as IProperty; + if (prop != null && prop.CanSet && (root.Role == IndexerDeclaration.SetterRole || root.Role == PropertyDeclaration.SetterRole)) + curState = curState.WithCurrentMember(prop.Setter); + } + + // Rood should be the 'body' - otherwise the state -> current member isn't correct. + var body = root.Children.FirstOrDefault(r => r.Role == Roles.Body); + if (body != null && body.Contains(expr.StartLocation)) + root = body; + + var csResolver = CompletionContextProvider.GetResolver (curState, root); + var result = csResolver.Resolve(resolveNode); + var state = csResolver.GetResolverStateBefore(resolveNode); + if (state.CurrentMember == null) + state = state.WithCurrentMember(curState.CurrentMember); + if (state.CurrentTypeDefinition == null) + state = state.WithCurrentTypeDefinition(curState.CurrentTypeDefinition); + if (state.CurrentUsingScope == null) + state = state.WithCurrentUsingScope(curState.CurrentUsingScope); + return new ExpressionResolveResult(result, state, csResolver); + } catch (Exception e) { + Console.WriteLine(e); + return null; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/CSharpParameterCompletionEngine.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/CSharpParameterCompletionEngine.cs new file mode 100644 index 000000000..e5919834e --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/CSharpParameterCompletionEngine.cs @@ -0,0 +1,408 @@ +// +// CSharpParameterCompletionEngine.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 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 ICSharpCode.NRefactory.Editor; +using ICSharpCode.NRefactory.Completion; +using System.Collections.Generic; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.CSharp.Resolver; +using ICSharpCode.NRefactory.CSharp.TypeSystem; +using System.Linq; + +namespace ICSharpCode.NRefactory.CSharp.Completion +{ + public class CSharpParameterCompletionEngine : CSharpCompletionEngineBase + { + internal IParameterCompletionDataFactory factory; + + public CSharpParameterCompletionEngine(IDocument document, ICompletionContextProvider completionContextProvider, IParameterCompletionDataFactory factory, IProjectContent content, CSharpTypeResolveContext ctx) : base (content, completionContextProvider, ctx) + { + if (document == null) { + throw new ArgumentNullException("document"); + } + if (factory == null) { + throw new ArgumentNullException("factory"); + } + this.document = document; + this.factory = factory; + } + + public ExpressionResult GetIndexerBeforeCursor() + { + SyntaxTree baseUnit; + if (currentMember == null && currentType == null) { + return null; + } + baseUnit = ParseStub("x]"); + + //var memberLocation = currentMember != null ? currentMember.Region.Begin : currentType.Region.Begin; + var mref = baseUnit.GetNodeAt(location, n => n is IndexerExpression); + AstNode expr; + if (mref is IndexerExpression) { + expr = ((IndexerExpression)mref).Target; + } else { + return null; + } + + return new ExpressionResult((AstNode)expr, baseUnit); + } + + public ExpressionResult GetConstructorInitializerBeforeCursor() + { + SyntaxTree baseUnit; + if (currentMember == null && currentType == null) { + return null; + } + baseUnit = ParseStub("a) {}", false); + + var expr = baseUnit.GetNodeAt (location); + if (expr == null) { + return null; + } + return new ExpressionResult((AstNode)expr, baseUnit); + } + + public ExpressionResult GetTypeBeforeCursor() + { + SyntaxTree baseUnit; + if (currentMember == null && currentType == null) { + return null; + } + baseUnit = ParseStub("x> a"); + + //var memberLocation = currentMember != null ? currentMember.Region.Begin : currentType.Region.Begin; + var expr = baseUnit.GetNodeAt(location.Line, location.Column + 1); + if (expr == null) + return null; + // '>' position + return new ExpressionResult((AstNode)expr, baseUnit); + } + + public ExpressionResult GetMethodTypeArgumentInvocationBeforeCursor() + { + SyntaxTree baseUnit; + if (currentMember == null && currentType == null) { + return null; + } + baseUnit = ParseStub("x>.A ()"); + + //var memberLocation = currentMember != null ? currentMember.Region.Begin : currentType.Region.Begin; + var expr = baseUnit.GetNodeAt(location.Line, location.Column + 1); + if (expr == null) + return null; + return new ExpressionResult((AstNode)expr, baseUnit); + } + + + + IEnumerable CollectMethods(AstNode resolvedNode, MethodGroupResolveResult resolveResult) + { + var lookup = new MemberLookup(ctx.CurrentTypeDefinition, Compilation.MainAssembly); + bool onlyStatic = false; + if (resolvedNode is IdentifierExpression && currentMember != null && currentMember.IsStatic || resolveResult.TargetResult is TypeResolveResult) { + onlyStatic = true; + } + var methods = new List(); + foreach (var method in resolveResult.Methods) { + if (method.IsConstructor) { + continue; + } + if (!lookup.IsAccessible (method, true)) + continue; + if (onlyStatic && !method.IsStatic) { + continue; + } + if (method.IsShadowing) { + for (int j = 0; j < methods.Count; j++) { + if (ParameterListComparer.Instance.Equals(methods[j].Parameters, method.Parameters)) { + methods.RemoveAt (j); + j--; + } + } + } + methods.Add (method); + } + foreach (var m in methods) { + yield return m; + } + foreach (var extMethods in resolveResult.GetEligibleExtensionMethods (true)) { + foreach (var method in extMethods) { + if (methods.Contains (method)) + continue; + yield return new ReducedExtensionMethod (method); + } + } + } + + IEnumerable GetAccessibleIndexers(IType type) + { + var lookup = new MemberLookup(ctx.CurrentTypeDefinition, Compilation.MainAssembly); + var properties = new List(); + foreach (var property in type.GetProperties ()) { + if (!property.IsIndexer) + continue; + if (!lookup.IsAccessible (property, true)) + continue; + if (property.IsShadowing) { + for (int j = 0; j < properties.Count; j++) { + if (ParameterListComparer.Instance.Equals(properties[j].Parameters, property.Parameters)) { + properties.RemoveAt (j); + j--; + } + } + } + + properties.Add (property); + } + return properties; + } + + public IParameterDataProvider GetParameterDataProvider(int offset, char completionChar) + { + //Ignoring completionChar == '\0' because it usually means moving with arrow keys, tab or enter + //we don't want to trigger on those events but it probably should be handled somewhere else + //since our job is to resolve method and not to decide when to display tooltip or not + if (offset <= 0 || completionChar == '\0') { + return null; + } + SetOffset (offset); + int startOffset; + string text; + if (currentMember == null && currentType == null) { + //In case of attributes parse all file + startOffset = 0; + text = document.Text; + } else { + var memberText = GetMemberTextToCaret (); + text = memberText.Item1; + startOffset = document.GetOffset (memberText.Item2); + } + + var parenStack = new Stack (); + var chevronStack = new Stack (); + var squareStack = new Stack (); + var bracketStack = new Stack (); + + var lex = new MiniLexer (text); + bool failed = lex.Parse ((ch, off) => { + if (lex.IsInString || lex.IsInChar || lex.IsInVerbatimString || lex.IsInSingleComment || lex.IsInMultiLineComment || lex.IsInPreprocessorDirective) + return false; + switch (ch) { + case '(': + parenStack.Push (startOffset + off); + break; + case ')': + if (parenStack.Count == 0) { + return true; + } + parenStack.Pop (); + break; + case '<': + chevronStack.Push (startOffset + off); + break; + case '>': + //Don't abort if we don't have macthing '<' for '>' it could be if (i > 0) Foo($ + if (chevronStack.Count == 0) { + return false; + } + chevronStack.Pop (); + break; + case '[': + squareStack.Push (startOffset + off); + break; + case ']': + if (squareStack.Count == 0) { + return true; + } + squareStack.Pop (); + break; + case '{': + bracketStack.Push (startOffset + off); + break; + case '}': + if (bracketStack.Count == 0) { + return true; + } + bracketStack.Pop (); + break; + } + return false; + }); + if (failed) + return null; + int result = -1; + if (parenStack.Count > 0) + result = parenStack.Pop (); + if (squareStack.Count > 0) + result = Math.Max (result, squareStack.Pop ()); + if (chevronStack.Count > 0) + result = Math.Max (result, chevronStack.Pop ()); + + //If we are inside { bracket we don't want to display anything + if (bracketStack.Count > 0 && bracketStack.Pop () > result) + return null; + if (result == -1) + return null; + SetOffset (result + 1); + ResolveResult resolveResult; + switch (document.GetCharAt (result)) { + case '(': + var invoke = GetInvocationBeforeCursor(true) ?? GetConstructorInitializerBeforeCursor(); + if (invoke == null) { + return null; + } + if (invoke.Node is ConstructorInitializer) { + var init = (ConstructorInitializer)invoke.Node; + if (init.ConstructorInitializerType == ConstructorInitializerType.This) { + return factory.CreateConstructorProvider(document.GetOffset(invoke.Node.StartLocation), ctx.CurrentTypeDefinition, init); + } else { + var baseType = ctx.CurrentTypeDefinition.DirectBaseTypes.FirstOrDefault(bt => bt.Kind != TypeKind.Interface); + if (baseType == null) { + return null; + } + return factory.CreateConstructorProvider(document.GetOffset(invoke.Node.StartLocation), baseType); + } + } + if (invoke.Node is ObjectCreateExpression) { + var createType = ResolveExpression(((ObjectCreateExpression)invoke.Node).Type); + if (createType.Result.Type.Kind == TypeKind.Unknown) + return null; + return factory.CreateConstructorProvider(document.GetOffset(invoke.Node.StartLocation), createType.Result.Type); + } + + if (invoke.Node is ICSharpCode.NRefactory.CSharp.Attribute) { + var attribute = ResolveExpression(invoke); + if (attribute == null || attribute.Result == null) { + return null; + } + return factory.CreateConstructorProvider(document.GetOffset(invoke.Node.StartLocation), attribute.Result.Type); + } + var invocationExpression = ResolveExpression(invoke); + if (invocationExpression == null || invocationExpression.Result == null || invocationExpression.Result.IsError) { + return null; + } + resolveResult = invocationExpression.Result; + if (resolveResult is MethodGroupResolveResult) { + return factory.CreateMethodDataProvider(document.GetOffset(invoke.Node.StartLocation), CollectMethods(invoke.Node, resolveResult as MethodGroupResolveResult)); + } + if (resolveResult is MemberResolveResult) { + var mr = resolveResult as MemberResolveResult; + if (mr.Member is IMethod) { + return factory.CreateMethodDataProvider(document.GetOffset(invoke.Node.StartLocation), new [] { (IMethod)mr.Member }); + } + } + + if (resolveResult.Type.Kind == TypeKind.Delegate) { + return factory.CreateDelegateDataProvider(document.GetOffset(invoke.Node.StartLocation), resolveResult.Type); + } + + // + // if (result.ExpressionContext == ExpressionContext.BaseConstructorCall) { + // if (resolveResult is ThisResolveResult) + // return new NRefactoryParameterDataProvider (textEditorData, resolver, resolveResult as ThisResolveResult); + // if (resolveResult is BaseResolveResult) + // return new NRefactoryParameterDataProvider (textEditorData, resolver, resolveResult as BaseResolveResult); + // } + // IType resolvedType = resolver.SearchType (resolveResult.ResolvedType); + // if (resolvedType != null && resolvedType.ClassType == ClassType.Delegate) { + // return new NRefactoryParameterDataProvider (textEditorData, result.Expression, resolvedType); + // } + break; + case '<': + invoke = GetMethodTypeArgumentInvocationBeforeCursor(); + if (invoke != null) { + var tExpr2 = ResolveExpression(invoke); + if (tExpr2 != null && tExpr2.Result is MethodGroupResolveResult && !tExpr2.Result.IsError) { + return factory.CreateTypeParameterDataProvider(document.GetOffset(invoke.Node.StartLocation), CollectMethods(invoke.Node, tExpr2.Result as MethodGroupResolveResult)); + } + } + invoke = GetTypeBeforeCursor(); + if (invoke == null || invoke.Node.StartLocation.IsEmpty) { + return null; + } + var tExpr = ResolveExpression(invoke); + if (tExpr == null || tExpr.Result == null || tExpr.Result.IsError) { + return null; + } + + return factory.CreateTypeParameterDataProvider(document.GetOffset(invoke.Node.StartLocation), CollectAllTypes(tExpr.Result.Type)); + case '[': + invoke = GetIndexerBeforeCursor(); + if (invoke == null) { + return null; + } + if (invoke.Node is ArrayCreateExpression) { + return null; + } + var indexerExpression = ResolveExpression(invoke); + if (indexerExpression == null || indexerExpression.Result == null || indexerExpression.Result.IsError) { + return null; + } + return factory.CreateIndexerParameterDataProvider(document.GetOffset(invoke.Node.StartLocation), indexerExpression.Result.Type, GetAccessibleIndexers (indexerExpression.Result.Type), invoke.Node); + } + return null; + } + + IEnumerable CollectAllTypes(IType baseType) + { + var state = GetState(); + for (var n = state.CurrentUsingScope; n != null; n = n.Parent) { + foreach (var u in n.Usings) { + foreach (var type in u.Types) { + if (type.TypeParameterCount > 0 && type.Name == baseType.Name) { + yield return type; + } + } + } + + foreach (var type in n.Namespace.Types) { + if (type.TypeParameterCount > 0 && type.Name == baseType.Name) { + yield return type; + } + } + } + } + + List GetUsedNamespaces() + { + var scope = ctx.CurrentUsingScope; + var result = new List(); + while (scope != null) { + result.Add(scope.Namespace.FullName); + + foreach (var ns in scope.Usings) { + result.Add(ns.FullName); + } + scope = scope.Parent; + } + return result; + } + + + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/CompletionDataWrapper.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/CompletionDataWrapper.cs new file mode 100644 index 000000000..4bf10d937 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/CompletionDataWrapper.cs @@ -0,0 +1,328 @@ +// +// CompletionDataWrapper.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 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.Completion; +using ICSharpCode.NRefactory.TypeSystem; +using System.Linq; +using ICSharpCode.NRefactory.CSharp.Resolver; + +namespace ICSharpCode.NRefactory.CSharp.Completion +{ + public class CompletionDataWrapper + { + CSharpCompletionEngine completion; + List result = new List (); + + public List Result { + get { + return result; + } + } + + ICompletionDataFactory Factory { + get { + return completion.factory; + } + } + + internal bool AnonymousDelegateAdded { + get; + set; + } + + public CompletionDataWrapper (CSharpCompletionEngine completion) + { + this.completion = completion; + } + + public void Add (ICompletionData data) + { + result.Add (data); + } + + + public ICompletionData AddCustom (string displayText, string description = null, string completionText = null) + { + var literalCompletionData = Factory.CreateLiteralCompletionData(displayText, description, completionText); + result.Add(literalCompletionData); + return literalCompletionData; + } + + HashSet usedNamespaces = new HashSet (); + + bool IsAccessible(MemberLookup lookup, INamespace ns) + { + if (ns.Types.Any (t => lookup.IsAccessible (t, false))) + return true; + foreach (var child in ns.ChildNamespaces) + if (IsAccessible (lookup, child)) + return true; + return false; + } + + public void AddNamespace (MemberLookup lookup, INamespace ns) + { + if (usedNamespaces.Contains (ns.Name)) + return; + if (!IsAccessible (lookup, ns)) { + usedNamespaces.Add (ns.Name); + return; + } + usedNamespaces.Add (ns.Name); + result.Add (Factory.CreateNamespaceCompletionData (ns)); + } + + public void AddAlias(string alias) + { + result.Add (Factory.CreateLiteralCompletionData (alias)); + } + + Dictionary typeDisplayText = new Dictionary (); + Dictionary addedTypes = new Dictionary (); + + public ICompletionData AddConstructors(IType type, bool showFullName, bool isInAttributeContext = false) + { + return InternalAddType(type, showFullName, isInAttributeContext, true); + } + + public ICompletionData AddType(IType type, bool showFullName, bool isInAttributeContext = false) + { + return InternalAddType(type, showFullName, isInAttributeContext, false); + } + + ICompletionData InternalAddType(IType type, bool showFullName, bool isInAttributeContext, bool addConstrurs) + { + if (type == null) + throw new ArgumentNullException("type"); + if (type.Name == "Void" && type.Namespace == "System" || type.Kind == TypeKind.Unknown) + return null; + if (addedTypes.ContainsKey (type)) + return addedTypes[type]; + usedNamespaces.Add(type.Name); + var def = type.GetDefinition(); + if (def != null && def.ParentAssembly != completion.ctx.CurrentAssembly) { + switch (completion.EditorBrowsableBehavior) { + case EditorBrowsableBehavior.Ignore: + break; + case EditorBrowsableBehavior.Normal: + var state = def.GetEditorBrowsableState(); + if (state != System.ComponentModel.EditorBrowsableState.Always) + return null; + break; + case EditorBrowsableBehavior.IncludeAdvanced: + if (!def.IsBrowsable()) + return null; + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + ICompletionData usedType; + var data = Factory.CreateTypeCompletionData(type, showFullName, isInAttributeContext, addConstrurs); + var text = data.DisplayText; + if (typeDisplayText.TryGetValue(text, out usedType)) { + usedType.AddOverload(data); + return usedType; + } + typeDisplayText [text] = data; + result.Add(data); + addedTypes[type] = data; + return data; + } + + Dictionary> data = new Dictionary> (); + + public ICompletionData AddVariable(IVariable variable) + { + if (data.ContainsKey(variable.Name)) + return null; + data [variable.Name] = new List(); + var cd = Factory.CreateVariableCompletionData(variable); + 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 += ":"; + result.Add(cd); + return cd; + } + + public void AddTypeParameter (ITypeParameter variable) + { + if (data.ContainsKey (variable.Name)) + return; + data [variable.Name] = new List (); + result.Add (Factory.CreateVariableCompletionData (variable)); + } + + public void AddTypeImport(ITypeDefinition type, bool useFullName, bool addForTypeCreation) + { + result.Add(Factory.CreateImportCompletionData(type, useFullName, addForTypeCreation)); + } + + public ICompletionData AddMember (IMember member) + { + var newData = Factory.CreateEntityCompletionData (member); + + if (member.ParentAssembly != completion.ctx.CurrentAssembly) { + switch (completion.EditorBrowsableBehavior) { + case EditorBrowsableBehavior.Ignore: + break; + case EditorBrowsableBehavior.Normal: + var state = member.GetEditorBrowsableState(); + if (state != System.ComponentModel.EditorBrowsableState.Always) + return null; + break; + case EditorBrowsableBehavior.IncludeAdvanced: + if (!member.IsBrowsable()) + return null; + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + string memberKey = newData.DisplayText; + if (memberKey == null) + return null; + + newData.CompletionCategory = GetCompletionCategory (member.DeclaringTypeDefinition); + + List existingData; + data.TryGetValue (memberKey, out existingData); + if (existingData != null) { + if (member.SymbolKind == SymbolKind.Field || member.SymbolKind == SymbolKind.Property || member.SymbolKind == SymbolKind.Event) + return null; + var a = member as IEntity; + foreach (var d in existingData) { + if (!(d is IEntityCompletionData)) + continue; + var b = ((IEntityCompletionData)d).Entity; + if (a == null || b == null || a.SymbolKind == b.SymbolKind) { + d.AddOverload (newData); + return d; + } + } + if (newData != null) { + result.Add (newData); + data [memberKey].Add (newData); + } + } else { + result.Add (newData); + data [memberKey] = new List (); + data [memberKey].Add (newData); + } + return newData; + } + + internal CompletionCategory GetCompletionCategory (IType type) + { + if (type == null) + return null; + if (!completionCategories.ContainsKey (type)) + completionCategories [type] = new TypeCompletionCategory (type); + return completionCategories [type]; + } + + Dictionary completionCategories = new Dictionary (); + class TypeCompletionCategory : CompletionCategory + { + public IType Type { + get; + private set; + } + + public TypeCompletionCategory (IType type) : base (type.FullName, null) + { + this.Type = type; + } + + public override int CompareTo (CompletionCategory other) + { + var compareCategory = other as TypeCompletionCategory; + if (compareCategory == null) + return -1; + int result; + if (Type.ReflectionName == compareCategory.Type.ReflectionName) { + result = 0; + } else if (Type.GetAllBaseTypes().Any(t => t.ReflectionName == compareCategory.Type.ReflectionName)) { + result = -1; + } else if (compareCategory.Type.GetAllBaseTypes().Any(t => t.ReflectionName == Type.ReflectionName)) { + result = 1; + } else { + var d = Type.GetDefinition (); + var ct = compareCategory.Type.GetDefinition(); + if (ct.IsStatic && d.IsStatic) { + result = d.FullName.CompareTo (ct.FullName); + } else if (d.IsStatic) { + result = 1; + }else if (ct.IsStatic) { + result = -1; + } else { + result = 0; + } + } + return result; + } + } + HashSet addedEnums = new HashSet (); + public ICompletionData AddEnumMembers (IType resolvedType, CSharpResolver state) + { + if (addedEnums.Contains (resolvedType)) + return null; + addedEnums.Add (resolvedType); + var result = AddType(resolvedType, true); + foreach (var field in resolvedType.GetFields ()) { + if (field.IsPublic && (field.IsConst || field.IsStatic)) { + Result.Add(Factory.CreateMemberCompletionData(resolvedType, field)); + } + } + return result; + } + HashSet anonymousSignatures = new HashSet (); + + public bool HasAnonymousDelegateAdded(string signature) + { + return anonymousSignatures.Contains(signature); + } + + public void AddAnonymousDelegateAdded(string signature) + { + anonymousSignatures.Add(signature); + } + } +} + + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/ICompletionContextProvider.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/ICompletionContextProvider.cs new file mode 100644 index 000000000..b6e9822ee --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/ICompletionContextProvider.cs @@ -0,0 +1,214 @@ +// +// IMemberProvider.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2012 Xamarin Inc. +// +// 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 ICSharpCode.NRefactory.Editor; +using ICSharpCode.NRefactory.CSharp.TypeSystem; +using System.Linq; +using ICSharpCode.NRefactory.CSharp.Resolver; + +namespace ICSharpCode.NRefactory.CSharp.Completion +{ + public interface ICompletionContextProvider + { + IList ConditionalSymbols { + get; + } + + void GetCurrentMembers (int offset, out IUnresolvedTypeDefinition currentType, out IUnresolvedMember currentMember); + + Tuple GetMemberTextToCaret(int caretOffset, IUnresolvedTypeDefinition currentType, IUnresolvedMember currentMember); + + CSharpAstResolver GetResolver (CSharpResolver resolver, AstNode rootNode); + } + + public class DefaultCompletionContextProvider : ICompletionContextProvider + { + readonly IDocument document; + readonly CSharpUnresolvedFile unresolvedFile; + readonly List symbols = new List (); + + public IList ConditionalSymbols { + get { + return symbols; + } + } + + public DefaultCompletionContextProvider (IDocument document, CSharpUnresolvedFile unresolvedFile) + { + if (document == null) + throw new ArgumentNullException("document"); + if (unresolvedFile == null) + throw new ArgumentNullException("unresolvedFile"); + this.document = document; + this.unresolvedFile = unresolvedFile; + } + + public void AddSymbol (string sym) + { + symbols.Add (sym); + } + public void GetCurrentMembers(int offset, out IUnresolvedTypeDefinition currentType, out IUnresolvedMember currentMember) + { + //var document = engine.document; + var location = document.GetLocation(offset); + + currentType = null; + + foreach (var type in unresolvedFile.TopLevelTypeDefinitions) { + if (type.Region.Begin < location) + currentType = type; + } + currentType = FindInnerType (currentType, location); + + // location is beyond last reported end region, now we need to check, if the end region changed + if (currentType != null && currentType.Region.End < location) { + if (!IsInsideType (currentType, location)) + currentType = null; + } + currentMember = null; + if (currentType != null) { + foreach (var member in currentType.Members) { + if (member.Region.Begin < location && (currentMember == null || currentMember.Region.Begin < member.Region.Begin)) + currentMember = member; + } + } + + // location is beyond last reported end region, now we need to check, if the end region changed + // NOTE: Enums are a special case, there the "last" field needs to be treated as current member + if (currentMember != null && currentMember.Region.End < location && currentType.Kind != TypeKind.Enum) { + if (!IsInsideType (currentMember, location)) + currentMember = null; + }/* + var stack = GetBracketStack (engine.GetMemberTextToCaret ().Item1); + if (stack.Count == 0) + currentMember = null;*/ + } + + IUnresolvedTypeDefinition FindInnerType (IUnresolvedTypeDefinition parent, TextLocation location) + { + if (parent == null) + return null; + var currentType = parent; + foreach (var type in parent.NestedTypes) { + if (type.Region.Begin < location && location < type.Region.End) + currentType = FindInnerType (type, location); + } + + return currentType; + } + + bool IsInsideType (IUnresolvedEntity currentType, TextLocation location) + { + if (currentType.Region.IsEmpty) + return false; + int startOffset = document.GetOffset (currentType.Region.Begin); + int endOffset = document.GetOffset (location); + //bool foundEndBracket = false; + + var bracketStack = new Stack (); + + bool isInString = false, isInChar = false; + bool isInLineComment = false, isInBlockComment = false; + + for (int i = startOffset; i < endOffset; i++) { + char ch = document.GetCharAt (i); + switch (ch) { + case '(': + case '[': + case '{': + if (!isInString && !isInChar && !isInLineComment && !isInBlockComment) + bracketStack.Push (ch); + break; + case ')': + case ']': + case '}': + if (!isInString && !isInChar && !isInLineComment && !isInBlockComment) + if (bracketStack.Count > 0) + bracketStack.Pop (); + break; + case '/': + if (isInBlockComment) { + if (i > 0 && document.GetCharAt (i - 1) == '*') + isInBlockComment = false; + } else if (!isInString && !isInChar && i + 1 < document.TextLength) { + char nextChar = document.GetCharAt (i + 1); + if (nextChar == '/') + isInLineComment = true; + if (!isInLineComment && nextChar == '*') + isInBlockComment = true; + } + break; + case '"': + if (!(isInChar || isInLineComment || isInBlockComment)) + isInString = !isInString; + break; + case '\'': + if (!(isInString || isInLineComment || isInBlockComment)) + isInChar = !isInChar; + break; + default : + if (NewLine.IsNewLine(ch)) { + isInLineComment = false; + } + break; + } + } + return bracketStack.Any (t => t == '{'); + } + + public Tuple GetMemberTextToCaret(int caretOffset, IUnresolvedTypeDefinition currentType, IUnresolvedMember currentMember) + { + int startOffset; + if (currentMember != null && currentType != null && currentType.Kind != TypeKind.Enum) { + startOffset = document.GetOffset(currentMember.Region.Begin); + } else if (currentType != null) { + startOffset = document.GetOffset(currentType.Region.Begin); + } else { + startOffset = 0; + } + while (startOffset > 0) { + char ch = document.GetCharAt(startOffset - 1); + if (ch != ' ' && ch != '\t') { + break; + } + --startOffset; + } + + return Tuple.Create (document.GetText (startOffset, caretOffset - startOffset), document.GetLocation (startOffset)); + } + + + public CSharpAstResolver GetResolver (CSharpResolver resolver, AstNode rootNode) + { + return new CSharpAstResolver (resolver, rootNode, unresolvedFile); + } + + + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/ICompletionDataFactory.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/ICompletionDataFactory.cs new file mode 100644 index 000000000..da61f8907 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/ICompletionDataFactory.cs @@ -0,0 +1,89 @@ +// +// ICompletionDataFactory.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 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 ICSharpCode.NRefactory.Completion; + +namespace ICSharpCode.NRefactory.CSharp.Completion +{ + public interface ICompletionDataFactory + { + ICompletionData CreateEntityCompletionData (IEntity entity); + ICompletionData CreateEntityCompletionData (IEntity entity, string text); + + ICompletionData CreateTypeCompletionData (IType type, bool showFullName, bool isInAttributeContext, bool addForTypeCreation); + + /// + /// Creates the member completion data. + /// Form: Type.Member + /// Used for generating enum members Foo.A, Foo.B where the enum 'Foo' is valid. + /// + ICompletionData CreateMemberCompletionData(IType type, IEntity member); + + /// + /// Creates a generic completion data. + /// + /// + /// The title of the completion data + /// + /// + /// The description of the literal. + /// + /// + /// The insert text. If null, title is taken. + /// + ICompletionData CreateLiteralCompletionData (string title, string description = null, string insertText = null); + + ICompletionData CreateNamespaceCompletionData (INamespace name); + + ICompletionData CreateVariableCompletionData (IVariable variable); + + ICompletionData CreateVariableCompletionData (ITypeParameter parameter); + + ICompletionData CreateEventCreationCompletionData (string delegateMethodName, IType delegateType, IEvent evt, string parameterDefinition, IUnresolvedMember currentMember, IUnresolvedTypeDefinition currentType); + + ICompletionData CreateNewOverrideCompletionData (int declarationBegin, IUnresolvedTypeDefinition type, IMember m); + ICompletionData CreateNewPartialCompletionData (int declarationBegin, IUnresolvedTypeDefinition type, IUnresolvedMember m); + + IEnumerable CreateCodeTemplateCompletionData (); + + IEnumerable CreatePreProcessorDefinesCompletionData (); + + /// + /// Creates a completion data that adds the required using for the created type. + /// + /// The type to import + /// If set to true the full name of the type needs to be used. + /// If true the completion data is used in 'new' context. + ICompletionData CreateImportCompletionData(IType type, bool useFullName, bool addForTypeCreation); + + ICompletionData CreateFormatItemCompletionData(string format, string description, object example); + + ICompletionData CreateXmlDocCompletionData (string tag, string description = null, string tagInsertionText = null); + + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/IParameterCompletionDataFactory.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/IParameterCompletionDataFactory.cs new file mode 100644 index 000000000..5bbb64e40 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Completion/IParameterCompletionDataFactory.cs @@ -0,0 +1,54 @@ +// +// IParameterCopmletionFactory.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2011 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 ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.Completion; +using ICSharpCode.NRefactory.CSharp.Resolver; +using System.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp.Completion +{ + public interface IParameterCompletionDataFactory + { + IParameterDataProvider CreateConstructorProvider (int startOffset, IType type); + + /// + /// Creates a constructor provider skipping the parent of thisInitializer. + /// + IParameterDataProvider CreateConstructorProvider (int startOffset, IType type, AstNode thisInitializer); + + IParameterDataProvider CreateMethodDataProvider (int startOffset, IEnumerable methods); + + IParameterDataProvider CreateDelegateDataProvider (int startOffset, IType type); + + IParameterDataProvider CreateIndexerParameterDataProvider (int startOffset, IType type, IEnumerable accessibleIndexers, AstNode resolvedNode); + + IParameterDataProvider CreateTypeParameterDataProvider (int startOffset, IEnumerable types); + + IParameterDataProvider CreateTypeParameterDataProvider (int startOffset, IEnumerable methods); + } + +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/CSharpFormatter.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/CSharpFormatter.cs new file mode 100644 index 000000000..a6969fea7 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/CSharpFormatter.cs @@ -0,0 +1,159 @@ +// +// CSharpFormatter.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2013 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 ICSharpCode.NRefactory.Editor; +using System.Threading; +using System.Linq; +using ICSharpCode.NRefactory.CSharp.Refactoring; +using ICSharpCode.NRefactory.TypeSystem; +using System.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + public enum FormattingMode { + OnTheFly, + Intrusive + } + + /// + /// The C# Formatter generates a set of text replace actions to format a region in a C# document. + /// + public class CSharpFormatter + { + readonly CSharpFormattingOptions policy; + readonly TextEditorOptions options; + + /// + /// Gets the formatting policy the formatter uses. + /// + public CSharpFormattingOptions Policy { + get { + return policy; + } + } + + /// + /// Gets the text editor options the formatter uses. + /// Note: If none was specified TextEditorOptions.Default gets used. + /// + public TextEditorOptions TextEditorOptions { + get { + return options; + } + } + + List formattingRegions = new List (); + internal TextLocation lastFormattingLocation = new TextLocation(int.MaxValue, int.MaxValue); + + /// + /// Gets the formatting regions. NOTE: Will get changed to IReadOnlyList. + /// + public IList FormattingRegions { + get { + return formattingRegions; + } + } + + /// + /// Gets or sets the formatting mode. For on the fly formatting a lightweight formatting mode + /// gives better results. + /// + public FormattingMode FormattingMode { + get; + set; + } + + /// + /// Initializes a new instance of the class. + /// + /// The formatting policy to use. + /// The text editor options (optional). Default is: TextEditorOptions.Default + public CSharpFormatter(CSharpFormattingOptions policy, TextEditorOptions options = null) + { + if (policy == null) + throw new ArgumentNullException("policy"); + this.policy = policy; + this.options = options ?? TextEditorOptions.Default; + } + + /// + /// Format the specified document and gives back the formatted text as result. + /// + public string Format(IDocument document) + { + return InternalFormat (new StringBuilderDocument (document.Text)); + } + + /// + /// Format the specified text and gives back the formatted text as result. + /// + public string Format(string text) + { + return InternalFormat (new StringBuilderDocument (text)); + } + + string InternalFormat(IDocument document) + { + var syntaxTree = SyntaxTree.Parse (document, document.FileName); + var changes = AnalyzeFormatting(document, syntaxTree); + changes.ApplyChanges(); + return document.Text; + } + + /// + /// Analyzes the formatting of a given document and syntax tree. + /// + /// Document. + /// Syntax tree. + /// The cancellation token. + public FormattingChanges AnalyzeFormatting(IDocument document, SyntaxTree syntaxTree, CancellationToken token = default (CancellationToken)) + { + if (document == null) + throw new ArgumentNullException("document"); + if (syntaxTree == null) + throw new ArgumentNullException("syntaxTree"); + var result = new FormattingChanges(document); + var visitor = new FormattingVisitor(this, document, result, token); + syntaxTree.AcceptVisitor(visitor); + return result; + } + + /// + /// Adds a region in the document that should be formatted. + /// + public void AddFormattingRegion (DomRegion region) + { + formattingRegions.Add(region); + if (formattingRegions.Count == 1) { + lastFormattingLocation = region.End; + } else { + lastFormattingLocation = lastFormattingLocation < region.End ? region.End : lastFormattingLocation; + } + } + + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/CSharpFormattingOptions.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/CSharpFormattingOptions.cs new file mode 100644 index 000000000..0a97a1c0b --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/CSharpFormattingOptions.cs @@ -0,0 +1,1039 @@ +// +// CSharpFormattingOptions.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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.Reflection; +using System.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + public enum BraceStyle + { + DoNotChange, + EndOfLine, + EndOfLineWithoutSpace, + NextLine, + NextLineShifted, + NextLineShifted2, + BannerStyle + } + + public enum PropertyFormatting + { + AllowOneLine, + ForceOneLine, + ForceNewLine + } + + public enum Wrapping { + DoNotChange, + DoNotWrap, + WrapAlways, + WrapIfTooLong + } + + public enum NewLinePlacement { + DoNotCare, + NewLine, + SameLine + } + + public enum UsingPlacement { + TopOfFile, + InsideNamespace + } + + public enum EmptyLineFormatting { + DoNotChange, + Indent, + DoNotIndent + } + + public class CSharpFormattingOptions + { + public string Name { + get; + set; + } + + public bool IsBuiltIn { + get; + set; + } + + public CSharpFormattingOptions Clone () + { + return (CSharpFormattingOptions)MemberwiseClone (); + } + + #region Indentation + public bool IndentNamespaceBody { // tested + get; + set; + } + + public bool IndentClassBody { // tested + get; + set; + } + + public bool IndentInterfaceBody { // tested + get; + set; + } + + public bool IndentStructBody { // tested + get; + set; + } + + public bool IndentEnumBody { // tested + get; + set; + } + + public bool IndentMethodBody { // tested + get; + set; + } + + public bool IndentPropertyBody { // tested + get; + set; + } + + public bool IndentEventBody { // tested + get; + set; + } + + public bool IndentBlocks { // tested + get; + set; + } + + public bool IndentSwitchBody { // tested + get; + set; + } + + public bool IndentCaseBody { // tested + get; + set; + } + + public bool IndentBreakStatements { // tested + get; + set; + } + + public bool AlignEmbeddedStatements { // tested + get; + set; + } + + public bool AlignElseInIfStatements { + get; + set; + } + + + + public PropertyFormatting AutoPropertyFormatting { // tested + get; + set; + } + + public PropertyFormatting SimplePropertyFormatting { // tested + get; + set; + } + + public EmptyLineFormatting EmptyLineFormatting { + get; + set; + } + + public bool IndentPreprocessorDirectives { // tested + get; + set; + } + + public bool AlignToMemberReferenceDot { // TODO! + get; + set; + } + + public bool IndentBlocksInsideExpressions { + get; + set; + } + #endregion + + #region Braces + public BraceStyle NamespaceBraceStyle { // tested + get; + set; + } + + public BraceStyle ClassBraceStyle { // tested + get; + set; + } + + public BraceStyle InterfaceBraceStyle { // tested + get; + set; + } + + public BraceStyle StructBraceStyle { // tested + get; + set; + } + + public BraceStyle EnumBraceStyle { // tested + get; + set; + } + + public BraceStyle MethodBraceStyle { // tested + get; + set; + } + + public BraceStyle AnonymousMethodBraceStyle { + get; + set; + } + + public BraceStyle ConstructorBraceStyle { // tested + get; + set; + } + + public BraceStyle DestructorBraceStyle { // tested + get; + set; + } + + public BraceStyle PropertyBraceStyle { // tested + get; + set; + } + + public BraceStyle PropertyGetBraceStyle { // tested + get; + set; + } + + public BraceStyle PropertySetBraceStyle { // tested + get; + set; + } + + public PropertyFormatting SimpleGetBlockFormatting { // tested + get; + set; + } + + public PropertyFormatting SimpleSetBlockFormatting { // tested + get; + set; + } + + public BraceStyle EventBraceStyle { // tested + get; + set; + } + + public BraceStyle EventAddBraceStyle { // tested + get; + set; + } + + public BraceStyle EventRemoveBraceStyle { // tested + get; + set; + } + + public bool AllowEventAddBlockInline { // tested + get; + set; + } + + public bool AllowEventRemoveBlockInline { // tested + get; + set; + } + + public BraceStyle StatementBraceStyle { // tested + get; + set; + } + + public bool AllowIfBlockInline { + get; + set; + } + + bool allowOneLinedArrayInitialziers = true; + public bool AllowOneLinedArrayInitialziers { + get { + return allowOneLinedArrayInitialziers; + } + set { + allowOneLinedArrayInitialziers = value; + } + } + #endregion + + #region NewLines + public NewLinePlacement ElseNewLinePlacement { // tested + get; + set; + } + + public NewLinePlacement ElseIfNewLinePlacement { // tested + get; + set; + } + + public NewLinePlacement CatchNewLinePlacement { // tested + get; + set; + } + + public NewLinePlacement FinallyNewLinePlacement { // tested + get; + set; + } + + public NewLinePlacement WhileNewLinePlacement { // tested + get; + set; + } + + NewLinePlacement embeddedStatementPlacement = NewLinePlacement.NewLine; + public NewLinePlacement EmbeddedStatementPlacement { + get { + return embeddedStatementPlacement; + } + set { + embeddedStatementPlacement = value; + } + } + #endregion + + #region Spaces + // Methods + public bool SpaceBeforeMethodDeclarationParentheses { // tested + get; + set; + } + + public bool SpaceBetweenEmptyMethodDeclarationParentheses { + get; + set; + } + + public bool SpaceBeforeMethodDeclarationParameterComma { // tested + get; + set; + } + + public bool SpaceAfterMethodDeclarationParameterComma { // tested + get; + set; + } + + public bool SpaceWithinMethodDeclarationParentheses { // tested + get; + set; + } + + // Method calls + public bool SpaceBeforeMethodCallParentheses { // tested + get; + set; + } + + public bool SpaceBetweenEmptyMethodCallParentheses { // tested + get; + set; + } + + public bool SpaceBeforeMethodCallParameterComma { // tested + get; + set; + } + + public bool SpaceAfterMethodCallParameterComma { // tested + get; + set; + } + + public bool SpaceWithinMethodCallParentheses { // tested + get; + set; + } + + // fields + + public bool SpaceBeforeFieldDeclarationComma { // tested + get; + set; + } + + public bool SpaceAfterFieldDeclarationComma { // tested + get; + set; + } + + // local variables + + public bool SpaceBeforeLocalVariableDeclarationComma { // tested + get; + set; + } + + public bool SpaceAfterLocalVariableDeclarationComma { // tested + get; + set; + } + + // constructors + + public bool SpaceBeforeConstructorDeclarationParentheses { // tested + get; + set; + } + + public bool SpaceBetweenEmptyConstructorDeclarationParentheses { // tested + get; + set; + } + + public bool SpaceBeforeConstructorDeclarationParameterComma { // tested + get; + set; + } + + public bool SpaceAfterConstructorDeclarationParameterComma { // tested + get; + set; + } + + public bool SpaceWithinConstructorDeclarationParentheses { // tested + get; + set; + } + + public NewLinePlacement NewLineBeforeConstructorInitializerColon { + get; + set; + } + + public NewLinePlacement NewLineAfterConstructorInitializerColon { + get; + set; + } + + // indexer + public bool SpaceBeforeIndexerDeclarationBracket { // tested + get; + set; + } + + public bool SpaceWithinIndexerDeclarationBracket { // tested + get; + set; + } + + public bool SpaceBeforeIndexerDeclarationParameterComma { + get; + set; + } + + public bool SpaceAfterIndexerDeclarationParameterComma { + get; + set; + } + + // delegates + + public bool SpaceBeforeDelegateDeclarationParentheses { + get; + set; + } + + public bool SpaceBetweenEmptyDelegateDeclarationParentheses { + get; + set; + } + + public bool SpaceBeforeDelegateDeclarationParameterComma { + get; + set; + } + + public bool SpaceAfterDelegateDeclarationParameterComma { + get; + set; + } + + public bool SpaceWithinDelegateDeclarationParentheses { + get; + set; + } + + public bool SpaceBeforeNewParentheses { // tested + get; + set; + } + + public bool SpaceBeforeIfParentheses { // tested + get; + set; + } + + public bool SpaceBeforeWhileParentheses { // tested + get; + set; + } + + public bool SpaceBeforeForParentheses { // tested + get; + set; + } + + public bool SpaceBeforeForeachParentheses { // tested + get; + set; + } + + public bool SpaceBeforeCatchParentheses { // tested + get; + set; + } + + public bool SpaceBeforeSwitchParentheses { // tested + get; + set; + } + + public bool SpaceBeforeLockParentheses { // tested + get; + set; + } + + public bool SpaceBeforeUsingParentheses { // tested + get; + set; + } + + public bool SpaceAroundAssignment { // tested + get; + set; + } + + public bool SpaceAroundLogicalOperator { // tested + get; + set; + } + + public bool SpaceAroundEqualityOperator { // tested + get; + set; + } + + public bool SpaceAroundRelationalOperator { // tested + get; + set; + } + + public bool SpaceAroundBitwiseOperator { // tested + get; + set; + } + + public bool SpaceAroundAdditiveOperator { // tested + get; + set; + } + + public bool SpaceAroundMultiplicativeOperator { // tested + get; + set; + } + + public bool SpaceAroundShiftOperator { // tested + get; + set; + } + + public bool SpaceAroundNullCoalescingOperator { // Tested + get; + set; + } + + public bool SpaceAfterUnsafeAddressOfOperator { // Tested + get; + set; + } + + public bool SpaceAfterUnsafeAsteriskOfOperator { // Tested + get; + set; + } + + public bool SpaceAroundUnsafeArrowOperator { // Tested + get; + set; + } + + public bool SpacesWithinParentheses { // tested + get; + set; + } + + public bool SpacesWithinIfParentheses { // tested + get; + set; + } + + public bool SpacesWithinWhileParentheses { // tested + get; + set; + } + + public bool SpacesWithinForParentheses { // tested + get; + set; + } + + public bool SpacesWithinForeachParentheses { // tested + get; + set; + } + + public bool SpacesWithinCatchParentheses { // tested + get; + set; + } + + public bool SpacesWithinSwitchParentheses { // tested + get; + set; + } + + public bool SpacesWithinLockParentheses { // tested + get; + set; + } + + public bool SpacesWithinUsingParentheses { // tested + get; + set; + } + + public bool SpacesWithinCastParentheses { // tested + get; + set; + } + + public bool SpacesWithinSizeOfParentheses { // tested + get; + set; + } + + public bool SpaceBeforeSizeOfParentheses { // tested + get; + set; + } + + public bool SpacesWithinTypeOfParentheses { // tested + get; + set; + } + + public bool SpacesWithinNewParentheses { // tested + get; + set; + } + + public bool SpacesBetweenEmptyNewParentheses { // tested + get; + set; + } + + public bool SpaceBeforeNewParameterComma { // tested + get; + set; + } + + public bool SpaceAfterNewParameterComma { // tested + get; + set; + } + + public bool SpaceBeforeTypeOfParentheses { // tested + get; + set; + } + + public bool SpacesWithinCheckedExpressionParantheses { // tested + get; + set; + } + + public bool SpaceBeforeConditionalOperatorCondition { // tested + get; + set; + } + + public bool SpaceAfterConditionalOperatorCondition { // tested + get; + set; + } + + public bool SpaceBeforeConditionalOperatorSeparator { // tested + get; + set; + } + + public bool SpaceAfterConditionalOperatorSeparator { // tested + get; + set; + } + + // brackets + public bool SpacesWithinBrackets { // tested + get; + set; + } + + public bool SpacesBeforeBrackets { // tested + get; + set; + } + + public bool SpaceBeforeBracketComma { // tested + get; + set; + } + + public bool SpaceAfterBracketComma { // tested + get; + set; + } + + public bool SpaceBeforeForSemicolon { // tested + get; + set; + } + + public bool SpaceAfterForSemicolon { // tested + get; + set; + } + + public bool SpaceAfterTypecast { // tested + get; + set; + } + + public bool SpaceBeforeArrayDeclarationBrackets { // tested + get; + set; + } + + public bool SpaceInNamedArgumentAfterDoubleColon { + get; + set; + } + + public bool RemoveEndOfLineWhiteSpace { + get; + set; + } + + public bool SpaceBeforeSemicolon { + get; + set; + } + #endregion + + #region Blank Lines + public int MinimumBlankLinesBeforeUsings { + get; + set; + } + + public int MinimumBlankLinesAfterUsings { + get; + set; + } + + public int MinimumBlankLinesBeforeFirstDeclaration { + get; + set; + } + + public int MinimumBlankLinesBetweenTypes { + get; + set; + } + + public int MinimumBlankLinesBetweenFields { + get; + set; + } + + public int MinimumBlankLinesBetweenEventFields { + get; + set; + } + + public int MinimumBlankLinesBetweenMembers { + get; + set; + } + + public int MinimumBlankLinesAroundRegion { + get; + set; + } + + public int MinimumBlankLinesInsideRegion { + get; + set; + } + + #endregion + + + #region Keep formatting + public bool KeepCommentsAtFirstColumn { + get; + set; + } + #endregion + + #region Wrapping + + public Wrapping ArrayInitializerWrapping { + get; + set; + } + + public BraceStyle ArrayInitializerBraceStyle { + get; + set; + } + + public Wrapping ChainedMethodCallWrapping { + get; + set; + } + + public Wrapping MethodCallArgumentWrapping { + get; + set; + } + + public NewLinePlacement NewLineAferMethodCallOpenParentheses { + get; + set; + } + + public NewLinePlacement MethodCallClosingParenthesesOnNewLine { + get; + set; + } + + public Wrapping IndexerArgumentWrapping { + get; + set; + } + + public NewLinePlacement NewLineAferIndexerOpenBracket { + get; + set; + } + + public NewLinePlacement IndexerClosingBracketOnNewLine { + get; + set; + } + + public Wrapping MethodDeclarationParameterWrapping { + get; + set; + } + + public NewLinePlacement NewLineAferMethodDeclarationOpenParentheses { + get; + set; + } + + public NewLinePlacement MethodDeclarationClosingParenthesesOnNewLine { + get; + set; + } + + public Wrapping IndexerDeclarationParameterWrapping { + get; + set; + } + + public NewLinePlacement NewLineAferIndexerDeclarationOpenBracket { + get; + set; + } + + public NewLinePlacement IndexerDeclarationClosingBracketOnNewLine { + get; + set; + } + + public bool AlignToFirstIndexerArgument { + get; + set; + } + + public bool AlignToFirstIndexerDeclarationParameter { + get; + set; + } + + public bool AlignToFirstMethodCallArgument { + get; + set; + } + + public bool AlignToFirstMethodDeclarationParameter { + get; + set; + } + + public NewLinePlacement NewLineBeforeNewQueryClause { + get; + set; + } + + #endregion + + #region Using Declarations + public UsingPlacement UsingPlacement { + get; + set; + } + #endregion + + internal CSharpFormattingOptions() + { + } + + /*public static CSharpFormattingOptions Load (FilePath selectedFile) + { + using (var stream = System.IO.File.OpenRead (selectedFile)) { + return Load (stream); + } + } + + public static CSharpFormattingOptions Load (System.IO.Stream input) + { + CSharpFormattingOptions result = FormattingOptionsFactory.CreateMonoOptions (); + result.Name = "noname"; + using (XmlTextReader reader = new XmlTextReader (input)) { + while (reader.Read ()) { + if (reader.NodeType == XmlNodeType.Element) { + if (reader.LocalName == "Property") { + var info = typeof(CSharpFormattingOptions).GetProperty (reader.GetAttribute ("name")); + string valString = reader.GetAttribute ("value"); + object value; + if (info.PropertyType == typeof(bool)) { + value = Boolean.Parse (valString); + } else if (info.PropertyType == typeof(int)) { + value = Int32.Parse (valString); + } else { + value = Enum.Parse (info.PropertyType, valString); + } + info.SetValue (result, value, null); + } else if (reader.LocalName == "FormattingProfile") { + result.Name = reader.GetAttribute ("name"); + } + } else if (reader.NodeType == XmlNodeType.EndElement && reader.LocalName == "FormattingProfile") { + //Console.WriteLine ("result:" + result.Name); + return result; + } + } + } + return result; + } + + public void Save (string fileName) + { + using (var writer = new XmlTextWriter (fileName, Encoding.Default)) { + writer.Formatting = System.Xml.Formatting.Indented; + writer.Indentation = 1; + writer.IndentChar = '\t'; + writer.WriteStartElement ("FormattingProfile"); + writer.WriteAttributeString ("name", Name); + foreach (PropertyInfo info in typeof (CSharpFormattingOptions).GetProperties ()) { + if (info.GetCustomAttributes (false).Any (o => o.GetType () == typeof(ItemPropertyAttribute))) { + writer.WriteStartElement ("Property"); + writer.WriteAttributeString ("name", info.Name); + writer.WriteAttributeString ("value", info.GetValue (this, null).ToString ()); + writer.WriteEndElement (); + } + } + writer.WriteEndElement (); + } + } + + public bool Equals (CSharpFormattingOptions other) + { + foreach (PropertyInfo info in typeof (CSharpFormattingOptions).GetProperties ()) { + if (info.GetCustomAttributes (false).Any (o => o.GetType () == typeof(ItemPropertyAttribute))) { + object val = info.GetValue (this, null); + object otherVal = info.GetValue (other, null); + if (val == null) { + if (otherVal == null) + continue; + return false; + } + if (!val.Equals (otherVal)) { + //Console.WriteLine ("!equal"); + return false; + } + } + } + //Console.WriteLine ("== equal"); + return true; + }*/ + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/ConstructFixer.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/ConstructFixer.cs new file mode 100644 index 000000000..f2fd87547 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/ConstructFixer.cs @@ -0,0 +1,514 @@ +// +// ConstructFixer.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2014 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 ICSharpCode.NRefactory.Editor; +using System.Text; +using System.Reflection; +using System.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + abstract class ConstructCompleter + { + public abstract bool TryFix (ConstructFixer fixer, SyntaxTree syntaxTree, IDocument document, TextLocation location, ref int newOffset); + + protected AstNode GetLastNonErrorChild (AstNode node) + { + var lastNode = node.LastChild; + + while (lastNode is ErrorNode) { + lastNode = lastNode.GetPrevNode(FormattingVisitor.NoWhitespacePredicate); + } + return lastNode; + } + } + + class TypeDeclarationCompleter : ConstructCompleter + { + public override bool TryFix(ConstructFixer fixer, SyntaxTree syntaxTree, IDocument document, TextLocation location, ref int newOffset) + { + var typeDeclaration = syntaxTree.GetNodeAt(location); + if (typeDeclaration != null) { + if (typeDeclaration.LBraceToken.IsNull && typeDeclaration.RBraceToken.IsNull) { + if (typeDeclaration.Members.Any()) + return false; + var lastNode = GetLastNonErrorChild (typeDeclaration); + if (lastNode == null) + return false; + var insertionOffset = document.GetOffset(lastNode.EndLocation); + document.Insert(insertionOffset, fixer.GenerateBody (typeDeclaration, fixer.Options.ClassBraceStyle, false, ref newOffset)); + return true; + } + } + return false; + } + } + + class DelegateDeclarationCompleter : ConstructCompleter + { + public override bool TryFix(ConstructFixer fixer, SyntaxTree syntaxTree, IDocument document, TextLocation location, ref int newOffset) + { + var typeDeclaration = syntaxTree.GetNodeAt(location); + if (typeDeclaration != null) { + if (typeDeclaration.RParToken.IsNull) { + var lastNode = GetLastNonErrorChild (typeDeclaration); + if (lastNode == null) + return false; + var insertionOffset = document.GetOffset(lastNode.EndLocation); + document.Insert(insertionOffset, ");\n"); + newOffset += ");\n".Length; + return true; + } + } + return false; + } + } + + class MethodDeclarationCompleter : ConstructCompleter + { + public override bool TryFix(ConstructFixer fixer, SyntaxTree syntaxTree, IDocument document, TextLocation location, ref int newOffset) + { + var methodDeclaration = syntaxTree.GetNodeAt(location); + if (methodDeclaration != null) { + if (!methodDeclaration.LParToken.IsNull && methodDeclaration.RParToken.IsNull) { + var lastNode = GetLastNonErrorChild (methodDeclaration); + if (lastNode == null) + return false; + + var insertionOffset = document.GetOffset(lastNode.EndLocation); + document.Insert(insertionOffset, ")\n\t{\t\t\n\t}"); + newOffset += ")\n\t{\t\t".Length; + return true; + } + } + return false; + } + } + + class IfStatementCompleter : ConstructCompleter + { + public override bool TryFix(ConstructFixer fixer, SyntaxTree syntaxTree, IDocument document, TextLocation location, ref int newOffset) + { + var ifStatement = syntaxTree.GetNodeAt(location); + if (ifStatement != null) { + if (!ifStatement.LParToken.IsNull && ifStatement.RParToken.IsNull) { + var lastNode = GetLastNonErrorChild (ifStatement); + if (lastNode == null) + return false; + + var insertionOffset = document.GetOffset(lastNode.EndLocation); + document.Insert(insertionOffset, fixer.GenerateBody (ifStatement, fixer.Options.StatementBraceStyle, true, ref newOffset)); + return true; + } + } + return false; + } + } + + class ForeachStatementCompleter : ConstructCompleter + { + public override bool TryFix(ConstructFixer fixer, SyntaxTree syntaxTree, IDocument document, TextLocation location, ref int newOffset) + { + var ifStatement = syntaxTree.GetNodeAt(location); + if (ifStatement != null) { + if (!ifStatement.LParToken.IsNull && ifStatement.RParToken.IsNull) { + var lastNode = GetLastNonErrorChild (ifStatement); + if (lastNode == null) + return false; + + var insertionOffset = document.GetOffset(lastNode.EndLocation); + document.Insert(insertionOffset, fixer.GenerateBody (ifStatement, fixer.Options.StatementBraceStyle, true, ref newOffset)); + return true; + } + } + return false; + } + } + + class WhileStatementCompleter : ConstructCompleter + { + public override bool TryFix(ConstructFixer fixer, SyntaxTree syntaxTree, IDocument document, TextLocation location, ref int newOffset) + { + var ifStatement = syntaxTree.GetNodeAt(location); + if (ifStatement != null) { + if (!ifStatement.LParToken.IsNull && ifStatement.RParToken.IsNull) { + var lastNode = GetLastNonErrorChild (ifStatement); + if (lastNode == null) + return false; + + var insertionOffset = document.GetOffset(lastNode.EndLocation); + document.Insert(insertionOffset, fixer.GenerateBody (ifStatement, fixer.Options.StatementBraceStyle, true, ref newOffset)); + return true; + } + } + return false; + } + } + + class DoWhileStatementCompleter : ConstructCompleter + { + public override bool TryFix(ConstructFixer fixer, SyntaxTree syntaxTree, IDocument document, TextLocation location, ref int newOffset) + { + var stmt = syntaxTree.GetNodeAt(location); + if (stmt != null) { + if (!stmt.LParToken.IsNull && stmt.RParToken.IsNull) { + var lastNode = GetLastNonErrorChild (stmt); + if (lastNode == null) + return false; + + var insertionOffset = document.GetOffset(lastNode.EndLocation); + document.Insert(insertionOffset, ");"); + newOffset = insertionOffset + 2; + return true; + } + } + return false; + } + } + + class FixedStatementCompleter : ConstructCompleter + { + public override bool TryFix(ConstructFixer fixer, SyntaxTree syntaxTree, IDocument document, TextLocation location, ref int newOffset) + { + var stmt = syntaxTree.GetNodeAt(location); + if (stmt != null) { + if (!stmt.LParToken.IsNull && stmt.RParToken.IsNull) { + var lastNode = GetLastNonErrorChild (stmt); + if (lastNode == null) + return false; + + var insertionOffset = document.GetOffset(lastNode.EndLocation); + document.Insert(insertionOffset, fixer.GenerateBody (stmt, fixer.Options.StatementBraceStyle, true, ref newOffset)); + return true; + } + } + return false; + } + } + + class SwitchStatementCompleter : ConstructCompleter + { + public override bool TryFix(ConstructFixer fixer, SyntaxTree syntaxTree, IDocument document, TextLocation location, ref int newOffset) + { + var switchStatement = syntaxTree.GetNodeAt(location); + if (switchStatement != null) { + if (!switchStatement.LParToken.IsNull && switchStatement.RParToken.IsNull) { + var lastNode = GetLastNonErrorChild (switchStatement); + if (lastNode == null) + return false; + + var insertionOffset = document.GetOffset(lastNode.EndLocation); + document.Insert(insertionOffset, fixer.GenerateBody (switchStatement, fixer.Options.StatementBraceStyle, true, ref newOffset)); + return true; + } + } + return false; + } + } + + class InvocationCompleter : ConstructCompleter + { + public override bool TryFix(ConstructFixer fixer, SyntaxTree syntaxTree, IDocument document, TextLocation location, ref int newOffset) + { + var invocationExpression = syntaxTree.GetNodeAt(location); + + if (invocationExpression != null) { + if (!invocationExpression.LParToken.IsNull && invocationExpression.RParToken.IsNull) { + var lastNode = GetLastNonErrorChild (invocationExpression); + if (lastNode == null) + return false; + + var insertionOffset = document.GetOffset(lastNode.EndLocation); + + newOffset = insertionOffset; + + + var text = ")"; + newOffset++; + var expressionStatement = invocationExpression.Parent as ExpressionStatement; + if (expressionStatement != null) { + if (expressionStatement.SemicolonToken.IsNull) + text = ");"; + newOffset ++; + } + document.Insert(insertionOffset, text); + + + return true; + } + + } + return false; + } + } + + class BreakStatementCompleter : ConstructCompleter + { + public override bool TryFix(ConstructFixer fixer, SyntaxTree syntaxTree, IDocument document, TextLocation location, ref int newOffset) + { + var stmt = syntaxTree.GetNodeAt(location); + + if (stmt != null && stmt.SemicolonToken.IsNull) { + // TODO !!!! + return true; + } + return false; + } + } + + class CheckedStatementCompleter : ConstructCompleter + { + public override bool TryFix(ConstructFixer fixer, SyntaxTree syntaxTree, IDocument document, TextLocation location, ref int newOffset) + { + var stmt = syntaxTree.GetNodeAt(location); + + if (stmt != null && stmt.Parent is ExpressionStatement) { + var insertionOffset = document.GetOffset(stmt.EndLocation); + document.Insert(insertionOffset, fixer.GenerateBody (stmt, fixer.Options.StatementBraceStyle, false, ref newOffset)); + return true; + } + return false; + } + } + + class UncheckedStatementCompleter : ConstructCompleter + { + public override bool TryFix(ConstructFixer fixer, SyntaxTree syntaxTree, IDocument document, TextLocation location, ref int newOffset) + { + var stmt = syntaxTree.GetNodeAt(location); + + if (stmt != null && stmt.Parent is ExpressionStatement) { + var insertionOffset = document.GetOffset(stmt.EndLocation); + document.Insert(insertionOffset, fixer.GenerateBody (stmt, fixer.Options.StatementBraceStyle, false, ref newOffset)); + return true; + } + return false; + } + } + + class ExpressionStatementCompleter : ConstructCompleter + { + public override bool TryFix(ConstructFixer fixer, SyntaxTree syntaxTree, IDocument document, TextLocation location, ref int newOffset) + { + var expressionStatement = syntaxTree.GetNodeAt(location); + + if (expressionStatement != null) { + int offset = document.GetOffset(expressionStatement.Expression.EndLocation); + if (expressionStatement.SemicolonToken.IsNull) { + document.Insert(offset, ";"); + newOffset = offset + 1; + } + return true; + } + return false; + } + } + + class LockStatementCompleter : ConstructCompleter + { + public override bool TryFix(ConstructFixer fixer, SyntaxTree syntaxTree, IDocument document, TextLocation location, ref int newOffset) + { + var stmt = syntaxTree.GetNodeAt(location); + if (stmt != null) { + if (!stmt.LParToken.IsNull && stmt.RParToken.IsNull) { + var lastNode = GetLastNonErrorChild (stmt); + if (lastNode == null) + return false; + + var insertionOffset = document.GetOffset(lastNode.EndLocation); + document.Insert(insertionOffset, fixer.GenerateBody (stmt, fixer.Options.StatementBraceStyle, true, ref newOffset)); + return true; + } + } + return false; + } + } + + class ReturnStatementCompleter : ConstructCompleter + { + public override bool TryFix(ConstructFixer fixer, SyntaxTree syntaxTree, IDocument document, TextLocation location, ref int newOffset) + { + var stmt = syntaxTree.GetNodeAt(location); + + if (stmt != null && stmt.SemicolonToken.IsNull) { + var insertionOffset = document.GetOffset(stmt.EndLocation); + document.Insert(insertionOffset, ";"); + newOffset = insertionOffset + 1; + return true; + } + return false; + } + } + + class YieldReturnStatementCompleter : ConstructCompleter + { + public override bool TryFix(ConstructFixer fixer, SyntaxTree syntaxTree, IDocument document, TextLocation location, ref int newOffset) + { + var stmt = syntaxTree.GetNodeAt(location); + + if (stmt != null && stmt.SemicolonToken.IsNull) { + var insertionOffset = document.GetOffset(stmt.EndLocation); + document.Insert(insertionOffset, ";"); + newOffset = insertionOffset + 1; + return true; + } + return false; + } + } + + class ThrowStatementCompleter : ConstructCompleter + { + public override bool TryFix(ConstructFixer fixer, SyntaxTree syntaxTree, IDocument document, TextLocation location, ref int newOffset) + { + var stmt = syntaxTree.GetNodeAt(location); + + if (stmt != null && stmt.SemicolonToken.IsNull) { + var insertionOffset = document.GetOffset(stmt.EndLocation); + document.Insert(insertionOffset, ";"); + newOffset = insertionOffset + 1; + return true; + } + return false; + } + } + + + public class ConstructFixer + { + static readonly ConstructCompleter[] completer = { + new TypeDeclarationCompleter(), + new DelegateDeclarationCompleter (), + new MethodDeclarationCompleter (), + new IfStatementCompleter (), + new ForeachStatementCompleter (), + new WhileStatementCompleter (), + new LockStatementCompleter (), + new FixedStatementCompleter (), + new DoWhileStatementCompleter (), + new SwitchStatementCompleter (), + new BreakStatementCompleter (), + new ThrowStatementCompleter (), + new ReturnStatementCompleter (), + new YieldReturnStatementCompleter (), + new CheckedStatementCompleter (), + new UncheckedStatementCompleter (), + + new InvocationCompleter (), + new ExpressionStatementCompleter () + }; + + readonly CSharpFormattingOptions options; + readonly TextEditorOptions textEditorOptions; + + public CSharpFormattingOptions Options { + get { + return options; + } + } + + public ConstructFixer(CSharpFormattingOptions options, TextEditorOptions textEditorOptions) + { + this.options = options; + this.textEditorOptions = textEditorOptions; + } + + + string GetIndent(AstNode node) + { + if (node == null || node is SyntaxTree) + return ""; + if (node is BlockStatement || node is TypeDeclaration || node is NamespaceDeclaration) + return "\t" + GetIndent(node.Parent); + return GetIndent(node.Parent); + } + + internal string GenerateBody(AstNode node, BraceStyle braceStyle, bool addClosingBracket, ref int newOffset) + { + StringBuilder result = new StringBuilder(); + if (addClosingBracket) + result.Append(")"); + var nodeIndent = GetIndent(node.Parent); + switch (braceStyle) { + case BraceStyle.DoNotChange: + case BraceStyle.BannerStyle: + case BraceStyle.EndOfLine: + result.Append(" "); + result.Append("{"); + result.Append(textEditorOptions.EolMarker); + result.Append(nodeIndent + "\t"); + break; + case BraceStyle.EndOfLineWithoutSpace: + result.Append("{"); + result.Append(textEditorOptions.EolMarker); + result.Append(nodeIndent + "\t"); + break; + case BraceStyle.NextLine: + result.Append(textEditorOptions.EolMarker); + result.Append(nodeIndent); + result.Append("{"); + result.Append(textEditorOptions.EolMarker); + result.Append(nodeIndent + "\t"); + break; + case BraceStyle.NextLineShifted: + result.Append(textEditorOptions.EolMarker); + result.Append(nodeIndent + "\t"); + result.Append("{"); + result.Append(textEditorOptions.EolMarker); + result.Append(nodeIndent + "\t"); + break; + case BraceStyle.NextLineShifted2: + result.Append(textEditorOptions.EolMarker); + result.Append(nodeIndent + "\t"); + result.Append("{"); + result.Append(textEditorOptions.EolMarker); + result.Append(nodeIndent + "\t" + "\t"); + break; + } + + newOffset += result.Length; + result.Append(textEditorOptions.EolMarker); + result.Append(nodeIndent); + result.Append("}"); + + return result.ToString(); + } + + public bool TryFix (IDocument document, int offset, out int newOffset) + { + newOffset = offset; + + var syntaxTree = SyntaxTree.Parse(document, "a.cs"); + var location = document.GetLocation(offset - 1); + foreach (var c in completer) { + if (c.TryFix(this, syntaxTree, document, location, ref newOffset)) { + return true; + } + } + return false; + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingChanges.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingChanges.cs new file mode 100644 index 000000000..f60b58bbd --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingChanges.cs @@ -0,0 +1,169 @@ +// +// CSharpFormatter.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2013 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 ICSharpCode.NRefactory.Editor; +using System.Threading; +using System.Linq; +using ICSharpCode.NRefactory.CSharp.Refactoring; +using ICSharpCode.NRefactory.TypeSystem; +using System.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// The formatting changes are used to format a specific region inside a document and apply a minimal formatting + /// changeset to a given document. This is useful for a text editor environment. + /// + public class FormattingChanges + { + readonly IDocument document; + readonly internal List changes = new List (); + + internal FormattingChanges (IDocument document) + { + if (document == null) + throw new ArgumentNullException("document"); + this.document = document; + } + + public int Count { + get { + return changes.Count; + } + } + + /// + /// Applies the changes to the input document. + /// + public void ApplyChanges() + { + ApplyChanges(0, document.TextLength, document.Replace, (o, l, v) => document.GetText(o, l) == v); + } + + public void ApplyChanges(int startOffset, int length) + { + ApplyChanges(startOffset, length, document.Replace, (o, l, v) => document.GetText(o, l) == v); + } + + /// + /// Applies the changes to the given Script instance. + /// + public void ApplyChanges(Script script) + { + ApplyChanges(0, document.TextLength, script.Replace); + } + + public void ApplyChanges(int startOffset, int length, Script script) + { + ApplyChanges(startOffset, length, script.Replace); + } + + public void ApplyChanges(int startOffset, int length, Action documentReplace, Func filter = null) + { + int endOffset = startOffset + length; + // Console.WriteLine ("apply:"+ startOffset + "->" + endOffset); + // Console.WriteLine (document.Text.Substring (0, startOffset) + new string ('x',length) + document.Text.Substring (startOffset+ length)); + + TextReplaceAction previousChange = null; + int delta = 0; + var depChanges = new List (); + foreach (var change in changes.OrderBy(c => c.Offset)) { + if (previousChange != null) { + if (change.Equals(previousChange)) { + // ignore duplicate changes + continue; + } + if (change.Offset < previousChange.Offset + previousChange.RemovalLength) { + throw new InvalidOperationException ("Detected overlapping changes " + change + "/" + previousChange); + } + } + previousChange = change; + bool skipChange = change.Offset + change.RemovalLength < startOffset || change.Offset > endOffset; + skipChange |= filter != null && filter(change.Offset + delta, change.RemovalLength, change.NewText); + skipChange &= !depChanges.Contains(change); + if (!skipChange) { + documentReplace(change.Offset + delta, change.RemovalLength, change.NewText); + delta += change.NewText.Length - change.RemovalLength; + if (change.DependsOn != null) { + depChanges.Add(change.DependsOn); + } + } + } + changes.Clear(); + } + + internal TextReplaceAction AddChange(int offset, int removedChars, string insertedText) + { + if (offset < 0) + throw new ArgumentOutOfRangeException("offset", "Should be >= 0"); + if (offset >= document.TextLength) + throw new ArgumentOutOfRangeException("offset", "Should be < document.TextLength"); + if (removedChars < 0) + throw new ArgumentOutOfRangeException("removedChars", "Should be >= 0"); + if (removedChars > offset + document.TextLength) + throw new ArgumentOutOfRangeException("removedChars", "Tried to remove beyond end of text"); + if (removedChars == 0 && string.IsNullOrEmpty (insertedText)) + return null; + var action = new TextReplaceAction (offset, removedChars, insertedText); + changes.Add(action); + return action; + } + + internal sealed class TextReplaceAction + { + internal readonly int Offset; + internal readonly int RemovalLength; + internal readonly string NewText; + internal TextReplaceAction DependsOn; + + public TextReplaceAction (int offset, int removalLength, string newText) + { + this.Offset = offset; + this.RemovalLength = removalLength; + this.NewText = newText ?? string.Empty; + } + + public override bool Equals(object obj) + { + TextReplaceAction other = obj as TextReplaceAction; + if (other == null) { + return false; + } + return this.Offset == other.Offset && this.RemovalLength == other.RemovalLength && this.NewText == other.NewText; + } + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() + { + return string.Format("[TextReplaceAction: Offset={0}, RemovalLength={1}, NewText={2}]", Offset, RemovalLength, NewText); + } + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingOptionsFactory.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingOptionsFactory.cs new file mode 100644 index 000000000..ff32c1f8c --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingOptionsFactory.cs @@ -0,0 +1,446 @@ +// +// FormattingOptionsFactory.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; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// The formatting options factory creates pre defined formatting option styles. + /// + public static class FormattingOptionsFactory + { + /// + /// Creates empty CSharpFormatting options. + /// + public static CSharpFormattingOptions CreateEmpty() + { + return new CSharpFormattingOptions(); + } + + /// + /// Creates mono indent style CSharpFormatting options. + /// + public static CSharpFormattingOptions CreateMono() + { + return new CSharpFormattingOptions { + IndentNamespaceBody = true, + IndentClassBody = true, + IndentInterfaceBody = true, + IndentStructBody = true, + IndentEnumBody = true, + IndentMethodBody = true, + IndentPropertyBody = true, + IndentEventBody = true, + IndentBlocks = true, + IndentSwitchBody = false, + IndentCaseBody = true, + IndentBreakStatements = true, + IndentPreprocessorDirectives = true, + IndentBlocksInsideExpressions = false, + NamespaceBraceStyle = BraceStyle.NextLine, + ClassBraceStyle = BraceStyle.NextLine, + InterfaceBraceStyle = BraceStyle.NextLine, + StructBraceStyle = BraceStyle.NextLine, + EnumBraceStyle = BraceStyle.NextLine, + MethodBraceStyle = BraceStyle.NextLine, + ConstructorBraceStyle = BraceStyle.NextLine, + DestructorBraceStyle = BraceStyle.NextLine, + AnonymousMethodBraceStyle = BraceStyle.EndOfLine, + + PropertyBraceStyle = BraceStyle.EndOfLine, + PropertyGetBraceStyle = BraceStyle.EndOfLine, + PropertySetBraceStyle = BraceStyle.EndOfLine, + SimpleGetBlockFormatting = PropertyFormatting.AllowOneLine, + SimpleSetBlockFormatting = PropertyFormatting.AllowOneLine, + + EventBraceStyle = BraceStyle.EndOfLine, + EventAddBraceStyle = BraceStyle.EndOfLine, + EventRemoveBraceStyle = BraceStyle.EndOfLine, + AllowEventAddBlockInline = true, + AllowEventRemoveBlockInline = true, + StatementBraceStyle = BraceStyle.EndOfLine, + + ElseNewLinePlacement = NewLinePlacement.SameLine, + ElseIfNewLinePlacement = NewLinePlacement.SameLine, + CatchNewLinePlacement = NewLinePlacement.SameLine, + FinallyNewLinePlacement = NewLinePlacement.SameLine, + WhileNewLinePlacement = NewLinePlacement.SameLine, + ArrayInitializerWrapping = Wrapping.WrapIfTooLong, + ArrayInitializerBraceStyle = BraceStyle.EndOfLine, + AllowOneLinedArrayInitialziers = true, + + SpaceBeforeMethodCallParentheses = true, + SpaceBeforeMethodDeclarationParentheses = true, + SpaceBeforeConstructorDeclarationParentheses = true, + SpaceBeforeDelegateDeclarationParentheses = true, + SpaceAfterMethodCallParameterComma = true, + SpaceAfterConstructorDeclarationParameterComma = true, + + SpaceBeforeNewParentheses = true, + SpacesWithinNewParentheses = false, + SpacesBetweenEmptyNewParentheses = false, + SpaceBeforeNewParameterComma = false, + SpaceAfterNewParameterComma = true, + + SpaceBeforeIfParentheses = true, + SpaceBeforeWhileParentheses = true, + SpaceBeforeForParentheses = true, + SpaceBeforeForeachParentheses = true, + SpaceBeforeCatchParentheses = true, + SpaceBeforeSwitchParentheses = true, + SpaceBeforeLockParentheses = true, + SpaceBeforeUsingParentheses = true, + SpaceAroundAssignment = true, + SpaceAroundLogicalOperator = true, + SpaceAroundEqualityOperator = true, + SpaceAroundRelationalOperator = true, + SpaceAroundBitwiseOperator = true, + SpaceAroundAdditiveOperator = true, + SpaceAroundMultiplicativeOperator = true, + SpaceAroundShiftOperator = true, + SpaceAroundNullCoalescingOperator = true, + SpacesWithinParentheses = false, + SpaceWithinMethodCallParentheses = false, + SpaceWithinMethodDeclarationParentheses = false, + SpacesWithinIfParentheses = false, + SpacesWithinWhileParentheses = false, + SpacesWithinForParentheses = false, + SpacesWithinForeachParentheses = false, + SpacesWithinCatchParentheses = false, + SpacesWithinSwitchParentheses = false, + SpacesWithinLockParentheses = false, + SpacesWithinUsingParentheses = false, + SpacesWithinCastParentheses = false, + SpacesWithinSizeOfParentheses = false, + SpacesWithinTypeOfParentheses = false, + SpacesWithinCheckedExpressionParantheses = false, + SpaceBeforeConditionalOperatorCondition = true, + SpaceAfterConditionalOperatorCondition = true, + SpaceBeforeConditionalOperatorSeparator = true, + SpaceAfterConditionalOperatorSeparator = true, + + SpacesWithinBrackets = false, + SpacesBeforeBrackets = true, + SpaceBeforeBracketComma = false, + SpaceAfterBracketComma = true, + + SpaceBeforeForSemicolon = false, + SpaceAfterForSemicolon = true, + SpaceAfterTypecast = false, + + AlignEmbeddedStatements = true, + SimplePropertyFormatting = PropertyFormatting.AllowOneLine, + AutoPropertyFormatting = PropertyFormatting.AllowOneLine, + EmptyLineFormatting = EmptyLineFormatting.DoNotIndent, + SpaceBeforeMethodDeclarationParameterComma = false, + SpaceAfterMethodDeclarationParameterComma = true, + SpaceAfterDelegateDeclarationParameterComma = true, + SpaceBeforeFieldDeclarationComma = false, + SpaceAfterFieldDeclarationComma = true, + SpaceBeforeLocalVariableDeclarationComma = false, + SpaceAfterLocalVariableDeclarationComma = true, + + SpaceBeforeIndexerDeclarationBracket = true, + SpaceWithinIndexerDeclarationBracket = false, + SpaceBeforeIndexerDeclarationParameterComma = false, + SpaceInNamedArgumentAfterDoubleColon = true, + RemoveEndOfLineWhiteSpace = true, + + SpaceAfterIndexerDeclarationParameterComma = true, + + MinimumBlankLinesBeforeUsings = 0, + MinimumBlankLinesAfterUsings = 1, + UsingPlacement = UsingPlacement.TopOfFile, + + MinimumBlankLinesBeforeFirstDeclaration = 0, + MinimumBlankLinesBetweenTypes = 1, + MinimumBlankLinesBetweenFields = 0, + MinimumBlankLinesBetweenEventFields = 0, + MinimumBlankLinesBetweenMembers = 1, + MinimumBlankLinesAroundRegion = 1, + MinimumBlankLinesInsideRegion = 1, + AlignToFirstIndexerArgument = false, + AlignToFirstIndexerDeclarationParameter = true, + AlignToFirstMethodCallArgument = false, + AlignToFirstMethodDeclarationParameter = true, + KeepCommentsAtFirstColumn = true, + ChainedMethodCallWrapping = Wrapping.DoNotChange, + MethodCallArgumentWrapping = Wrapping.DoNotChange, + NewLineAferMethodCallOpenParentheses = NewLinePlacement.DoNotCare, + MethodCallClosingParenthesesOnNewLine = NewLinePlacement.DoNotCare, + + IndexerArgumentWrapping = Wrapping.DoNotChange, + NewLineAferIndexerOpenBracket = NewLinePlacement.DoNotCare, + IndexerClosingBracketOnNewLine = NewLinePlacement.DoNotCare, + + NewLineBeforeNewQueryClause = NewLinePlacement.NewLine + }; + } + + /// + /// Creates sharp develop indent style CSharpFormatting options. + /// + public static CSharpFormattingOptions CreateSharpDevelop() + { + var baseOptions = CreateKRStyle(); + return baseOptions; + } + + /// + /// The K&R style, so named because it was used in Kernighan and Ritchie's book The C Programming Language, + /// is commonly used in C. It is less common for C++, C#, and others. + /// + public static CSharpFormattingOptions CreateKRStyle() + { + return new CSharpFormattingOptions() { + IndentNamespaceBody = true, + IndentClassBody = true, + IndentInterfaceBody = true, + IndentStructBody = true, + IndentEnumBody = true, + IndentMethodBody = true, + IndentPropertyBody = true, + IndentEventBody = true, + IndentBlocks = true, + IndentSwitchBody = true, + IndentCaseBody = true, + IndentBreakStatements = true, + IndentPreprocessorDirectives = true, + NamespaceBraceStyle = BraceStyle.NextLine, + ClassBraceStyle = BraceStyle.NextLine, + InterfaceBraceStyle = BraceStyle.NextLine, + StructBraceStyle = BraceStyle.NextLine, + EnumBraceStyle = BraceStyle.NextLine, + MethodBraceStyle = BraceStyle.NextLine, + ConstructorBraceStyle = BraceStyle.NextLine, + DestructorBraceStyle = BraceStyle.NextLine, + AnonymousMethodBraceStyle = BraceStyle.EndOfLine, + PropertyBraceStyle = BraceStyle.EndOfLine, + PropertyGetBraceStyle = BraceStyle.EndOfLine, + PropertySetBraceStyle = BraceStyle.EndOfLine, + SimpleGetBlockFormatting = PropertyFormatting.AllowOneLine, + SimpleSetBlockFormatting = PropertyFormatting.AllowOneLine, + + EventBraceStyle = BraceStyle.EndOfLine, + EventAddBraceStyle = BraceStyle.EndOfLine, + EventRemoveBraceStyle = BraceStyle.EndOfLine, + AllowEventAddBlockInline = true, + AllowEventRemoveBlockInline = true, + StatementBraceStyle = BraceStyle.EndOfLine, + + ElseNewLinePlacement = NewLinePlacement.SameLine, + ElseIfNewLinePlacement = NewLinePlacement.SameLine, + CatchNewLinePlacement = NewLinePlacement.SameLine, + FinallyNewLinePlacement = NewLinePlacement.SameLine, + WhileNewLinePlacement = NewLinePlacement.SameLine, + ArrayInitializerWrapping = Wrapping.WrapIfTooLong, + ArrayInitializerBraceStyle = BraceStyle.EndOfLine, + + SpaceBeforeMethodCallParentheses = false, + SpaceBeforeMethodDeclarationParentheses = false, + SpaceBeforeConstructorDeclarationParentheses = false, + SpaceBeforeDelegateDeclarationParentheses = false, + SpaceBeforeIndexerDeclarationBracket = false, + SpaceAfterMethodCallParameterComma = true, + SpaceAfterConstructorDeclarationParameterComma = true, + NewLineBeforeConstructorInitializerColon = NewLinePlacement.NewLine, + NewLineAfterConstructorInitializerColon = NewLinePlacement.SameLine, + + SpaceBeforeNewParentheses = false, + SpacesWithinNewParentheses = false, + SpacesBetweenEmptyNewParentheses = false, + SpaceBeforeNewParameterComma = false, + SpaceAfterNewParameterComma = true, + + SpaceBeforeIfParentheses = true, + SpaceBeforeWhileParentheses = true, + SpaceBeforeForParentheses = true, + SpaceBeforeForeachParentheses = true, + SpaceBeforeCatchParentheses = true, + SpaceBeforeSwitchParentheses = true, + SpaceBeforeLockParentheses = true, + SpaceBeforeUsingParentheses = true, + + SpaceAroundAssignment = true, + SpaceAroundLogicalOperator = true, + SpaceAroundEqualityOperator = true, + SpaceAroundRelationalOperator = true, + SpaceAroundBitwiseOperator = true, + SpaceAroundAdditiveOperator = true, + SpaceAroundMultiplicativeOperator = true, + SpaceAroundShiftOperator = true, + SpaceAroundNullCoalescingOperator = true, + SpacesWithinParentheses = false, + SpaceWithinMethodCallParentheses = false, + SpaceWithinMethodDeclarationParentheses = false, + SpacesWithinIfParentheses = false, + SpacesWithinWhileParentheses = false, + SpacesWithinForParentheses = false, + SpacesWithinForeachParentheses = false, + SpacesWithinCatchParentheses = false, + SpacesWithinSwitchParentheses = false, + SpacesWithinLockParentheses = false, + SpacesWithinUsingParentheses = false, + SpacesWithinCastParentheses = false, + SpacesWithinSizeOfParentheses = false, + SpacesWithinTypeOfParentheses = false, + SpacesWithinCheckedExpressionParantheses = false, + SpaceBeforeConditionalOperatorCondition = true, + SpaceAfterConditionalOperatorCondition = true, + SpaceBeforeConditionalOperatorSeparator = true, + SpaceAfterConditionalOperatorSeparator = true, + SpaceBeforeArrayDeclarationBrackets = false, + + SpacesWithinBrackets = false, + SpacesBeforeBrackets = false, + SpaceBeforeBracketComma = false, + SpaceAfterBracketComma = true, + + SpaceBeforeForSemicolon = false, + SpaceAfterForSemicolon = true, + SpaceAfterTypecast = false, + + AlignEmbeddedStatements = true, + SimplePropertyFormatting = PropertyFormatting.AllowOneLine, + AutoPropertyFormatting = PropertyFormatting.AllowOneLine, + EmptyLineFormatting = EmptyLineFormatting.DoNotIndent, + SpaceBeforeMethodDeclarationParameterComma = false, + SpaceAfterMethodDeclarationParameterComma = true, + SpaceAfterDelegateDeclarationParameterComma = true, + SpaceBeforeFieldDeclarationComma = false, + SpaceAfterFieldDeclarationComma = true, + SpaceBeforeLocalVariableDeclarationComma = false, + SpaceAfterLocalVariableDeclarationComma = true, + + SpaceWithinIndexerDeclarationBracket = false, + SpaceBeforeIndexerDeclarationParameterComma = false, + SpaceInNamedArgumentAfterDoubleColon = true, + + SpaceAfterIndexerDeclarationParameterComma = true, + RemoveEndOfLineWhiteSpace = true, + + MinimumBlankLinesBeforeUsings = 0, + MinimumBlankLinesAfterUsings = 1, + + MinimumBlankLinesBeforeFirstDeclaration = 0, + MinimumBlankLinesBetweenTypes = 1, + MinimumBlankLinesBetweenFields = 0, + MinimumBlankLinesBetweenEventFields = 0, + MinimumBlankLinesBetweenMembers = 1, + MinimumBlankLinesAroundRegion = 1, + MinimumBlankLinesInsideRegion = 1, + + KeepCommentsAtFirstColumn = true, + ChainedMethodCallWrapping = Wrapping.DoNotChange, + MethodCallArgumentWrapping = Wrapping.DoNotChange, + NewLineAferMethodCallOpenParentheses = NewLinePlacement.DoNotCare, + MethodCallClosingParenthesesOnNewLine = NewLinePlacement.DoNotCare, + + IndexerArgumentWrapping = Wrapping.DoNotChange, + NewLineAferIndexerOpenBracket = NewLinePlacement.DoNotCare, + IndexerClosingBracketOnNewLine = NewLinePlacement.DoNotCare, + + NewLineBeforeNewQueryClause = NewLinePlacement.NewLine + }; + } + + /// + /// Creates allman indent style CSharpFormatting options used in Visual Studio. + /// + public static CSharpFormattingOptions CreateAllman() + { + var baseOptions = CreateKRStyle(); + baseOptions.AnonymousMethodBraceStyle = BraceStyle.NextLine; + baseOptions.PropertyBraceStyle = BraceStyle.NextLine; + baseOptions.PropertyGetBraceStyle = BraceStyle.NextLine; + baseOptions.PropertySetBraceStyle = BraceStyle.NextLine; + + baseOptions.EventBraceStyle = BraceStyle.NextLine; + baseOptions.EventAddBraceStyle = BraceStyle.NextLine; + baseOptions.EventRemoveBraceStyle = BraceStyle.NextLine; + baseOptions.StatementBraceStyle = BraceStyle.NextLine; + baseOptions.ArrayInitializerBraceStyle = BraceStyle.NextLine; + + baseOptions.CatchNewLinePlacement = NewLinePlacement.NewLine; + baseOptions.ElseNewLinePlacement = NewLinePlacement.NewLine; + baseOptions.ElseIfNewLinePlacement = NewLinePlacement.SameLine; + + baseOptions.FinallyNewLinePlacement = NewLinePlacement.NewLine; + baseOptions.WhileNewLinePlacement = NewLinePlacement.DoNotCare; + baseOptions.ArrayInitializerWrapping = Wrapping.DoNotChange; + baseOptions.IndentBlocksInsideExpressions = true; + + return baseOptions; + } + + /// + /// The Whitesmiths style, also called Wishart style to a lesser extent, is less common today than the previous three. It was originally used in the documentation for the first commercial C compiler, the Whitesmiths Compiler. + /// + public static CSharpFormattingOptions CreateWhitesmiths() + { + var baseOptions = CreateKRStyle(); + + baseOptions.NamespaceBraceStyle = BraceStyle.NextLineShifted; + baseOptions.ClassBraceStyle = BraceStyle.NextLineShifted; + baseOptions.InterfaceBraceStyle = BraceStyle.NextLineShifted; + baseOptions.StructBraceStyle = BraceStyle.NextLineShifted; + baseOptions.EnumBraceStyle = BraceStyle.NextLineShifted; + baseOptions.MethodBraceStyle = BraceStyle.NextLineShifted; + baseOptions.ConstructorBraceStyle = BraceStyle.NextLineShifted; + baseOptions.DestructorBraceStyle = BraceStyle.NextLineShifted; + baseOptions.AnonymousMethodBraceStyle = BraceStyle.NextLineShifted; + baseOptions.PropertyBraceStyle = BraceStyle.NextLineShifted; + baseOptions.PropertyGetBraceStyle = BraceStyle.NextLineShifted; + baseOptions.PropertySetBraceStyle = BraceStyle.NextLineShifted; + + baseOptions.EventBraceStyle = BraceStyle.NextLineShifted; + baseOptions.EventAddBraceStyle = BraceStyle.NextLineShifted; + baseOptions.EventRemoveBraceStyle = BraceStyle.NextLineShifted; + baseOptions.StatementBraceStyle = BraceStyle.NextLineShifted; + baseOptions.IndentBlocksInsideExpressions = true; + return baseOptions; + } + + /// + /// Like the Allman and Whitesmiths styles, GNU style puts braces on a line by themselves, indented by 2 spaces, + /// except when opening a function definition, where they are not indented. + /// In either case, the contained code is indented by 2 spaces from the braces. + /// Popularised by Richard Stallman, the layout may be influenced by his background of writing Lisp code. + /// In Lisp the equivalent to a block (a progn) + /// is a first class data entity and giving it its own indent level helps to emphasize that, + /// whereas in C a block is just syntax. + /// Although not directly related to indentation, GNU coding style also includes a space before the bracketed + /// list of arguments to a function. + /// + public static CSharpFormattingOptions CreateGNU() + { + var baseOptions = CreateAllman(); + baseOptions.StatementBraceStyle = BraceStyle.NextLineShifted2; + return baseOptions; + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingVisitor.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingVisitor.cs new file mode 100644 index 000000000..485996c91 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingVisitor.cs @@ -0,0 +1,662 @@ +// +// FormattingVisitor.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// Copyright (c) 2013 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.Text; +using System.Linq; +using ICSharpCode.NRefactory.Editor; +using ICSharpCode.NRefactory.TypeSystem; +using System.Threading; +using System.Collections.Generic; +using ICSharpCode.NRefactory.Utils; + +namespace ICSharpCode.NRefactory.CSharp +{ + [Obsolete("This class was replaced by CSharpFormatter.")] + public class AstFormattingVisitor {} + + partial class FormattingVisitor + { + readonly CSharpFormatter formatter; + readonly FormattingChanges changes; + readonly IDocument document; + readonly CancellationToken token; + + Indent curIndent; + + public bool HadErrors { + get; + set; + } + + + CSharpFormattingOptions policy { + get { + return formatter.Policy; + } + } + + TextEditorOptions options { + get { + return formatter.TextEditorOptions; + } + } + + FormattingChanges.TextReplaceAction AddChange(int offset, int removedChars, string insertedText) + { + return changes.AddChange(offset, removedChars, insertedText); + } + + public FormattingVisitor(CSharpFormatter formatter, IDocument document, FormattingChanges changes, CancellationToken token) + { + if (formatter == null) + throw new ArgumentNullException("formatter"); + if (document == null) + throw new ArgumentNullException("document"); + if (changes == null) + throw new ArgumentNullException("changes"); + + this.formatter = formatter; + this.changes = changes; + this.document = document; + this.token = token; + + curIndent = new Indent(formatter.TextEditorOptions); + } + + void VisitChildrenToFormat (AstNode parent, Action callback) + { + AstNode next; + for (var child = parent.FirstChild; child != null; child = next) { + token.ThrowIfCancellationRequested(); + // Store next to allow the loop to continue + // if the visitor removes/replaces child. + next = child.GetNextSibling(NoWhitespacePredicate); + + if (formatter.FormattingRegions.Count > 0) { + if (formatter.FormattingRegions.Any(r => r.IsInside(child.StartLocation) || r.IsInside(child.EndLocation))) { + callback(child); + } else { + var childRegion = child.Region; + if (formatter.FormattingRegions.Any(r => childRegion.IsInside(r.Begin) || childRegion.IsInside(r.End))) + callback(child); + } + if (child.StartLocation > formatter.lastFormattingLocation) + break; + } else { + callback(child); + } + } + } + + protected override void VisitChildren (AstNode node) + { + VisitChildrenToFormat (node, n => n.AcceptVisitor (this)); + } + + #region NewLines + + void AdjustNewLineBlock(AstNode startNode, int targetMinimumNewLineCount) + { + var indentString = policy.EmptyLineFormatting == EmptyLineFormatting.Indent ? curIndent.IndentString : ""; + + TextLocation newLineInsertPosition = startNode.EndLocation; + var node = startNode.NextSibling; + int currentNewLineCount = 0; + // Check the existing newlines + for (; currentNewLineCount < targetMinimumNewLineCount; node = node.NextSibling) { + if (node is WhitespaceNode) + continue; + if (!(node is NewLineNode)) + break; + newLineInsertPosition = node.EndLocation; + currentNewLineCount++; + if (policy.EmptyLineFormatting == EmptyLineFormatting.DoNotChange) { + if (node.NextSibling == null) + // end of file/block etc, nothing more to do but break before assigning null to node + break; + continue; + } + var isBlankLine = IsSpacing(document.GetLineByNumber(node.StartLocation.Line)); + if (!isBlankLine) { + // remove EOL whitespace if appropriate + if (policy.RemoveEndOfLineWhiteSpace) { + var offset = document.GetOffset(node.StartLocation); + var start = SearchWhitespaceStart(offset); + if (start != offset) + AddChange(start, offset - start, null); + } + } else { + var actualIndent = GetIndentation(node.StartLocation.Line); + if (actualIndent != indentString) { + var start = document.GetOffset(new TextLocation(node.StartLocation.Line, 0)); + AddChange(start, actualIndent.Length, indentString); + } + } + if (node.NextSibling == null) + // end of file/block etc, nothing more to do but break before assigning null to node + break; + } + if (currentNewLineCount < targetMinimumNewLineCount) { + // We need to add more newlines + var builder = new StringBuilder(); + for (; currentNewLineCount < targetMinimumNewLineCount; currentNewLineCount++) { + if (currentNewLineCount > 0) + // Don't indent the first line in the block since that is not an empty line. + builder.Append(indentString); + builder.Append(options.EolMarker); + } + var offset = document.GetOffset(newLineInsertPosition); + if (offset >= 0) + AddChange(offset, 0, builder.ToString()); + } else if (currentNewLineCount == targetMinimumNewLineCount && node is NewLineNode){ +// // Check to see if there are any newlines to remove +// var endNode = node.GetNextSibling(n => !(n is NewLineNode || n is WhitespaceNode)); +// if (endNode != null) { +// var startOffset = document.GetOffset(newLineInsertPosition); +// var endOffset = document.GetOffset(new TextLocation(endNode.StartLocation.Line, 0)); +// EnsureText(startOffset, endOffset, null); +// } + } + } + + public void EnsureMinimumNewLinesAfter(AstNode node, int blankLines) + { + if (node is PreProcessorDirective) { + var directive = (PreProcessorDirective)node; + if (directive.Type == PreProcessorDirectiveType.Pragma) + return; + } + if (blankLines < 0) + return; + if (formatter.FormattingMode != FormattingMode.Intrusive) + blankLines = Math.Min(1, blankLines); + AdjustNewLineBlock(node, blankLines); + } + + public void EnsureMinimumBlankLinesBefore(AstNode node, int blankLines) + { + if (formatter.FormattingMode != FormattingMode.Intrusive) + return; + var loc = node.StartLocation; + int line = loc.Line; + do { + line--; + } while (line > 0 && IsSpacing(document.GetLineByNumber(line))); + if (line > 0 && !IsSpacing(document.GetLineByNumber(line))) + line++; + + if (loc.Line - line >= blankLines) + return; + + var sb = new StringBuilder (); + for (int i = 0; i < blankLines; i++) + sb.Append(options.EolMarker); + int end = document.GetOffset(loc.Line, 1); + if (loc.Line == line) { + AddChange(end, 0, sb.ToString()); + return; + } + if (line + 1 > document.LineCount) + return; + int start = document.GetOffset(line + 1, 1); + if (end - start <= 0 && sb.Length == 0) + return; + AddChange(start, end - start, sb.ToString()); + } + + #endregion + + bool IsSimpleAccessor(Accessor accessor) + { + if (accessor.IsNull || accessor.Body.IsNull || accessor.Body.FirstChild == null) { + return true; + } + var firstStatement = accessor.Body.Statements.FirstOrDefault(); + if (firstStatement == null) + return true; + + if (!(firstStatement is ReturnStatement || firstStatement is ExpressionStatement|| firstStatement is EmptyStatement || firstStatement is ThrowStatement)) + return false; + + if (firstStatement.GetNextSibling(s => s.Role == BlockStatement.StatementRole) != null) + return false; + + return !(accessor.Body.Statements.FirstOrDefault() is BlockStatement); + } + + static bool IsSpacing(char ch) + { + return ch == ' ' || ch == '\t'; + } + + bool IsSpacing(ISegment segment) + { + int endOffset = segment.EndOffset; + for (int i = segment.Offset; i < endOffset; i++) { + if (!IsSpacing(document.GetCharAt(i))) { + return false; + } + } + return true; + } + + int SearchLastNonWsChar(int startOffset, int endOffset) + { + startOffset = Math.Max(0, startOffset); + endOffset = Math.Max(startOffset, endOffset); + if (startOffset >= endOffset) { + return startOffset; + } + int result = -1; + bool inComment = false; + + for (int i = startOffset; i < endOffset && i < document.TextLength; i++) { + char ch = document.GetCharAt(i); + if (IsSpacing(ch)) { + continue; + } + if (ch == '/' && i + 1 < document.TextLength && document.GetCharAt(i + 1) == '/') { + return result; + } + if (ch == '/' && i + 1 < document.TextLength && document.GetCharAt(i + 1) == '*') { + inComment = true; + i++; + continue; + } + if (inComment && ch == '*' && i + 1 < document.TextLength && document.GetCharAt(i + 1) == '/') { + inComment = false; + i++; + continue; + } + if (!inComment) { + result = i; + } + } + return result; + } + + void ForceSpace(int startOffset, int endOffset, bool forceSpace) + { + int lastNonWs = SearchLastNonWsChar(startOffset, endOffset); + if (lastNonWs < 0) + return; + + var spaceCount = Math.Max(0, endOffset - lastNonWs - 1); + if (forceSpace) { + if (spaceCount != 1) { + // Here we could technically remove spaceCount - 1 chars instead + // and skip replacing that with new space, but we want to trigger the + // overlap detection if this space is changed again for some reason + AddChange(lastNonWs + 1, spaceCount, " "); + } + } else if (spaceCount > 0 && !forceSpace) { + AddChange(lastNonWs + 1, spaceCount, ""); + } + } + + void ForceSpacesAfter(AstNode n, bool forceSpaces) + { + if (n == null) { + return; + } + TextLocation location = n.EndLocation; + int offset = document.GetOffset(location); + if (location.Column > document.GetLineByNumber(location.Line).Length) { + return; + } + int i = offset; + while (i < document.TextLength && IsSpacing (document.GetCharAt (i))) { + i++; + } + ForceSpace(offset - 1, i, forceSpaces); + } + + int ForceSpacesBefore(AstNode n, bool forceSpaces) + { + if (n == null || n.IsNull) { + return 0; + } + TextLocation location = n.StartLocation; + // respect manual line breaks. + if (location.Column <= 1 || GetIndentation(location.Line).Length == location.Column - 1) { + return 0; + } + + int offset = document.GetOffset(location); + int i = offset - 1; + while (i >= 0 && IsSpacing (document.GetCharAt (i))) { + i--; + } + ForceSpace(i, offset, forceSpaces); + return i; + } + + int ForceSpacesBeforeRemoveNewLines(AstNode n, bool forceSpace = true) + { + if (n == null || n.IsNull) { + return 0; + } + int offset = document.GetOffset(n.StartLocation); + int i = offset - 1; + while (i >= 0) { + char ch = document.GetCharAt(i); + if (!IsSpacing(ch) && ch != '\r' && ch != '\n') + break; + i--; + } + var length = Math.Max(0, (offset - 1) - i); + AddChange(i + 1, length, forceSpace ? " " : ""); + return i; + } + + internal static bool NoWhitespacePredicate(AstNode arg) + { + return !(arg is NewLineNode || arg is WhitespaceNode); + } + + static bool IsMember(AstNode nextSibling) + { + return nextSibling != null && nextSibling.NodeType == NodeType.Member; + } + + static bool ShouldBreakLine(NewLinePlacement placement, CSharpTokenNode token) + { + if (placement == NewLinePlacement.NewLine) + return true; + if (placement == NewLinePlacement.SameLine) + return false; + if (token.IsNull) + return false; + var prevMeaningfulNode = token.GetPrevNode (n =>n.Role !=Roles.NewLine && n.Role != Roles.Whitespace && n.Role !=Roles.Comment); + return prevMeaningfulNode.EndLocation.Line != token.StartLocation.Line; + } + + void ForceSpaceBefore(AstNode node, bool forceSpace) + { + var offset = document.GetOffset(node.StartLocation); + int end = offset; + // ForceSpace inserts a space one char after start in the case of a missing space + // Therefore, make sure that start < offset by starting at offset - 1 + int start = SearchWhitespaceStart(offset - 1); + ForceSpace(start, end, forceSpace); + } + + public void FixSemicolon(CSharpTokenNode semicolon) + { + if (semicolon.IsNull) + return; + int endOffset = document.GetOffset(semicolon.StartLocation); + int offset = endOffset; + while (offset - 1 > 0 && char.IsWhiteSpace (document.GetCharAt (offset - 1))) { + offset--; + } + if (policy.SpaceBeforeSemicolon) { + AddChange(offset, endOffset - offset, " "); + } else { + if (offset < endOffset) + AddChange(offset, endOffset - offset, null); + } + } + + void PlaceOnNewLine(NewLinePlacement newLine, AstNode keywordNode) + { + if (keywordNode == null || keywordNode.StartLocation.IsEmpty) + return; + + var prev = keywordNode.GetPrevNode (NoWhitespacePredicate); + if (prev is Comment || prev is PreProcessorDirective) + return; + + if (newLine == NewLinePlacement.DoNotCare) + newLine = prev.EndLocation.Line == keywordNode.StartLocation.Line ? NewLinePlacement.SameLine : NewLinePlacement.NewLine; + + int offset = document.GetOffset(keywordNode.StartLocation); + + int whitespaceStart = SearchWhitespaceStart(offset); + string indentString = newLine == NewLinePlacement.NewLine ? options.EolMarker + curIndent.IndentString : " "; + AddChange(whitespaceStart, offset - whitespaceStart, indentString); + } + + string nextStatementIndent; + + void FixStatementIndentation(TextLocation location) + { + if (location.Line < 1 || location.Column < 1) { + Console.WriteLine("invalid location!"); + return; + } + int offset = document.GetOffset(location); + if (offset <= 0) { + Console.WriteLine("possible wrong offset"); + Console.WriteLine(Environment.StackTrace); + return; + } + bool isEmpty = IsLineIsEmptyUpToEol(offset); + int lineStart = SearchWhitespaceLineStart(offset); + string indentString = nextStatementIndent ?? (isEmpty ? "" : options.EolMarker) + curIndent.IndentString; + nextStatementIndent = null; + EnsureText(lineStart, offset, indentString); + } + + void FixIndentation (AstNode node) + { + FixIndentation(node.StartLocation, 0); + } + + void FixIndentation(TextLocation location, int relOffset) + { + if (location.Line < 1 || location.Line > document.LineCount) { + Console.WriteLine("Invalid location " + location); + Console.WriteLine(Environment.StackTrace); + return; + } + + string lineIndent = GetIndentation(location.Line); + string indentString = curIndent.IndentString; + if (indentString != lineIndent && location.Column - 1 + relOffset == lineIndent.Length) { + AddChange(document.GetOffset(location.Line, 1), lineIndent.Length, indentString); + } + } + + void FixIndentationForceNewLine(AstNode node) + { + var directive = node as PreProcessorDirective; + if (node.GetPrevNode () is NewLineNode) { + if (directive != null && !policy.IndentPreprocessorDirectives) { + var startNode = node.GetPrevNode (); + var startOffset = document.GetOffset(startNode.EndLocation); + int endOffset = document.GetOffset(node.StartLocation); + AddChange(startOffset, endOffset - startOffset, ""); + return; + } else { + FixIndentation(node); + } + } else { + // if no new line preceeds an #endif directive it's excluded + if (directive != null) { + if (directive.Type == PreProcessorDirectiveType.Endif) + return; + } + var startNode = node.GetPrevSibling(n => !(n is WhitespaceNode)) ?? node; + var startOffset = document.GetOffset(startNode.EndLocation); + int endOffset = document.GetOffset(node.StartLocation); + if (startOffset >= endOffset) + return; + if (directive != null && !policy.IndentPreprocessorDirectives) { + AddChange(startOffset, endOffset - startOffset, ""); + return; + } + + AddChange(startOffset, endOffset - startOffset, curIndent.IndentString); + } + } + + string GetIndentation(int lineNumber) + { + var line = document.GetLineByNumber(lineNumber); + var b = new StringBuilder (); + int endOffset = line.EndOffset; + for (int i = line.Offset; i < endOffset; i++) { + char c = document.GetCharAt(i); + if (!IsSpacing(c)) { + break; + } + b.Append(c); + } + return b.ToString(); + } + + void EnsureText(int start, int end, string replacementText) + { + var length = end - start; + if (length == 0 && string.IsNullOrEmpty(replacementText)) + return; + if (replacementText == null || replacementText.Length != length) { + AddChange(start, length, replacementText); + return; + } + for (int i = 0; i < length; i++) { + if (document.GetCharAt(start + i) != replacementText[i]) { + AddChange(start, length, replacementText); + break; + } + } + } + + void FixOpenBrace(BraceStyle braceStyle, AstNode lbrace) + { + if (lbrace.IsNull) + return; + switch (braceStyle) { + case BraceStyle.DoNotChange: + return; + + case BraceStyle.BannerStyle: + case BraceStyle.EndOfLine: + var prev = lbrace.GetPrevNode (NoWhitespacePredicate); + if (prev is PreProcessorDirective) + return; + int prevOffset = document.GetOffset(prev.EndLocation); + + if (prev is Comment || prev is PreProcessorDirective) { + int next = document.GetOffset(lbrace.GetNextNode ().StartLocation); + EnsureText(prevOffset, next, ""); + while (prev is Comment || prev is PreProcessorDirective) + prev = prev.GetPrevNode(); + prevOffset = document.GetOffset(prev.EndLocation); + AddChange(prevOffset, 0, " {"); + } else { + int braceOffset2 = document.GetOffset(lbrace.StartLocation); + EnsureText(prevOffset, braceOffset2, " "); + } + break; + case BraceStyle.EndOfLineWithoutSpace: + prev = lbrace.GetPrevNode (NoWhitespacePredicate); + if (prev is PreProcessorDirective) + return; + prevOffset = document.GetOffset(prev.EndLocation); + int braceOffset = document.GetOffset(lbrace.StartLocation); + EnsureText(prevOffset, braceOffset, ""); + break; + + case BraceStyle.NextLine: + prev = lbrace.GetPrevNode (NoWhitespacePredicate); + if (prev is PreProcessorDirective) + return; + prevOffset = document.GetOffset(prev.EndLocation); + braceOffset = document.GetOffset(lbrace.StartLocation); + EnsureText(prevOffset, braceOffset, options.EolMarker + curIndent.IndentString); + break; + case BraceStyle.NextLineShifted: + prev = lbrace.GetPrevNode (NoWhitespacePredicate); + if (prev is PreProcessorDirective) + return; + prevOffset = document.GetOffset(prev.EndLocation); + braceOffset = document.GetOffset(lbrace.StartLocation); + curIndent.Push(IndentType.Block); + EnsureText(prevOffset, braceOffset, options.EolMarker + curIndent.IndentString); + curIndent.Pop(); + break; + case BraceStyle.NextLineShifted2: + prev = lbrace.GetPrevNode (NoWhitespacePredicate); + if (prev is PreProcessorDirective) + return; + prevOffset = document.GetOffset(prev.EndLocation); + braceOffset = document.GetOffset(lbrace.StartLocation); + curIndent.Push(IndentType.Block); + EnsureText(prevOffset, braceOffset, options.EolMarker + curIndent.IndentString); + curIndent.Pop(); + break; + } + } + + void CorrectClosingBrace (AstNode rbrace) + { + if (rbrace.IsNull) + return; + int braceOffset = document.GetOffset(rbrace.StartLocation); + var prevNode = rbrace.GetPrevNode(); + int prevNodeOffset = prevNode != null ? document.GetOffset(prevNode.EndLocation) : 0; + if (prevNode is NewLineNode) { + EnsureText(prevNodeOffset, braceOffset, curIndent.IndentString); + } else { + EnsureText(prevNodeOffset, braceOffset, options.EolMarker + curIndent.IndentString); + } + } + + void FixClosingBrace(BraceStyle braceStyle, AstNode rbrace) + { + if (rbrace.IsNull) + return; + switch (braceStyle) { + case BraceStyle.DoNotChange: + return; + + case BraceStyle.NextLineShifted: + case BraceStyle.BannerStyle: + curIndent.Push(IndentType.Block); + CorrectClosingBrace (rbrace); + curIndent.Pop (); + break; + case BraceStyle.EndOfLineWithoutSpace: + case BraceStyle.EndOfLine: + case BraceStyle.NextLine: + CorrectClosingBrace (rbrace); + break; + + case BraceStyle.NextLineShifted2: + curIndent.Push(IndentType.Block); + CorrectClosingBrace (rbrace); + curIndent.Pop (); + break; + } + + } + + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingVisitor_Expressions.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingVisitor_Expressions.cs new file mode 100644 index 000000000..582e26fea --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingVisitor_Expressions.cs @@ -0,0 +1,735 @@ +// +// AstFormattingVisitor_Expressions.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2013 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.Linq; +using System.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp +{ + partial class FormattingVisitor : DepthFirstAstVisitor + { + public override void VisitComposedType(ComposedType composedType) + { + var spec = composedType.ArraySpecifiers.FirstOrDefault(); + if (spec != null) + ForceSpacesBefore(spec.LBracketToken, policy.SpaceBeforeArrayDeclarationBrackets); + + if (composedType.HasNullableSpecifier) + ForceSpacesBefore(composedType.NullableSpecifierToken, false); + + if (composedType.PointerRank > 0) + foreach (var token in composedType.PointerTokens) + ForceSpacesBefore(token, false); + + base.VisitComposedType(composedType); + } + + public override void VisitAnonymousMethodExpression(AnonymousMethodExpression lambdaExpression) + { + FormatArguments(lambdaExpression); + + if (!lambdaExpression.Body.IsNull) { + var old = curIndent; + this.curIndent = curIndent.GetIndentWithoutSpace (); + FixOpenBrace(policy.AnonymousMethodBraceStyle, lambdaExpression.Body.LBraceToken); + VisitBlockWithoutFixingBraces(lambdaExpression.Body, policy.IndentBlocks); + FixClosingBrace(policy.AnonymousMethodBraceStyle, lambdaExpression.Body.RBraceToken); + curIndent = old; + } + + } + + public override void VisitAssignmentExpression(AssignmentExpression assignmentExpression) + { + ForceSpacesAround(assignmentExpression.OperatorToken, policy.SpaceAroundAssignment); + base.VisitAssignmentExpression(assignmentExpression); + } + + public override void VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression) + { + bool forceSpaces = false; + switch (binaryOperatorExpression.Operator) { + case BinaryOperatorType.Equality: + case BinaryOperatorType.InEquality: + forceSpaces = policy.SpaceAroundEqualityOperator; + break; + case BinaryOperatorType.GreaterThan: + case BinaryOperatorType.GreaterThanOrEqual: + case BinaryOperatorType.LessThan: + case BinaryOperatorType.LessThanOrEqual: + forceSpaces = policy.SpaceAroundRelationalOperator; + break; + case BinaryOperatorType.ConditionalAnd: + case BinaryOperatorType.ConditionalOr: + forceSpaces = policy.SpaceAroundLogicalOperator; + break; + case BinaryOperatorType.BitwiseAnd: + case BinaryOperatorType.BitwiseOr: + case BinaryOperatorType.ExclusiveOr: + forceSpaces = policy.SpaceAroundBitwiseOperator; + break; + case BinaryOperatorType.Add: + case BinaryOperatorType.Subtract: + forceSpaces = policy.SpaceAroundAdditiveOperator; + break; + case BinaryOperatorType.Multiply: + case BinaryOperatorType.Divide: + case BinaryOperatorType.Modulus: + forceSpaces = policy.SpaceAroundMultiplicativeOperator; + break; + case BinaryOperatorType.ShiftLeft: + case BinaryOperatorType.ShiftRight: + forceSpaces = policy.SpaceAroundShiftOperator; + break; + case BinaryOperatorType.NullCoalescing: + forceSpaces = policy.SpaceAroundNullCoalescingOperator; + break; + } + var opToken = binaryOperatorExpression.OperatorToken; + if (opToken.PrevSibling != null && opToken.PrevSibling.Role != Roles.NewLine) { + ForceSpacesBefore(opToken, forceSpaces); + } else { + ForceSpacesAfter(binaryOperatorExpression.Left, false); + FixIndentation(opToken); + } + ForceSpacesAfter(opToken, opToken.NextSibling != null && opToken.NextSibling.Role != Roles.NewLine && forceSpaces); + + binaryOperatorExpression.Left.AcceptVisitor(this); + // Handle line breaks in binary opeartor expression. + if (binaryOperatorExpression.Left.EndLocation.Line != binaryOperatorExpression.Right.StartLocation.Line) { + if (opToken.StartLocation.Line == binaryOperatorExpression.Right.StartLocation.Line) { + FixStatementIndentation(opToken.StartLocation); + } else { + FixStatementIndentation(binaryOperatorExpression.Right.StartLocation); + } + } + binaryOperatorExpression.Right.AcceptVisitor(this); + } + + public override void VisitConditionalExpression(ConditionalExpression conditionalExpression) + { + ForceSpacesBefore(conditionalExpression.QuestionMarkToken, policy.SpaceBeforeConditionalOperatorCondition); + ForceSpacesAfter(conditionalExpression.QuestionMarkToken, policy.SpaceAfterConditionalOperatorCondition); + ForceSpacesBefore(conditionalExpression.ColonToken, policy.SpaceBeforeConditionalOperatorSeparator); + ForceSpacesAfter(conditionalExpression.ColonToken, policy.SpaceAfterConditionalOperatorSeparator); + base.VisitConditionalExpression(conditionalExpression); + } + + public override void VisitCastExpression(CastExpression castExpression) + { + if (castExpression.RParToken != null) { + ForceSpacesAfter(castExpression.LParToken, policy.SpacesWithinCastParentheses); + ForceSpacesBefore(castExpression.RParToken, policy.SpacesWithinCastParentheses); + + ForceSpacesAfter(castExpression.RParToken, policy.SpaceAfterTypecast); + } + base.VisitCastExpression(castExpression); + } + + void ForceSpacesAround(AstNode node, bool forceSpaces) + { + if (node.IsNull) + return; + ForceSpacesBefore(node, forceSpaces); + ForceSpacesAfter(node, forceSpaces); + } + + void FormatCommas(AstNode parent, bool before, bool after) + { + if (parent.IsNull) { + return; + } + foreach (CSharpTokenNode comma in parent.Children.Where (node => node.Role == Roles.Comma)) { + ForceSpacesAfter(comma, after); + ForceSpacesBefore(comma, before); + } + } + + bool DoWrap(Wrapping wrapping, AstNode wrapNode, int argumentCount) + { + return wrapping == Wrapping.WrapAlways || + options.WrapLineLength > 0 && argumentCount > 1 && wrapping == Wrapping.WrapIfTooLong && wrapNode.StartLocation.Column >= options.WrapLineLength; + } + + void FormatArguments(AstNode node) + { + Wrapping methodCallArgumentWrapping; + NewLinePlacement newLineAferMethodCallOpenParentheses; + bool doAlignToFirstArgument; + NewLinePlacement methodClosingParenthesesOnNewLine; + bool spaceWithinMethodCallParentheses; + bool spaceWithinEmptyParentheses; + bool spaceAfterMethodCallParameterComma; + bool spaceBeforeMethodCallParameterComma; + + CSharpTokenNode rParToken, lParToken; + List arguments; + + var constructorDeclaration = node as ConstructorDeclaration; + if (constructorDeclaration != null) { + methodCallArgumentWrapping = policy.MethodDeclarationParameterWrapping; + newLineAferMethodCallOpenParentheses = policy.NewLineAferMethodDeclarationOpenParentheses; + methodClosingParenthesesOnNewLine = policy.MethodDeclarationClosingParenthesesOnNewLine; + doAlignToFirstArgument = policy.AlignToFirstMethodDeclarationParameter; + spaceWithinMethodCallParentheses = policy.SpaceWithinConstructorDeclarationParentheses; + spaceAfterMethodCallParameterComma = policy.SpaceAfterConstructorDeclarationParameterComma; + spaceBeforeMethodCallParameterComma = policy.SpaceBeforeConstructorDeclarationParameterComma; + spaceWithinEmptyParentheses = policy.SpaceBetweenEmptyMethodDeclarationParentheses; + lParToken = constructorDeclaration.LParToken; + rParToken = constructorDeclaration.RParToken; + arguments = constructorDeclaration.Parameters.Cast().ToList(); + } else if (node is IndexerDeclaration) { + var indexer = (IndexerDeclaration)node; + methodCallArgumentWrapping = policy.IndexerDeclarationParameterWrapping; + newLineAferMethodCallOpenParentheses = policy.NewLineAferIndexerDeclarationOpenBracket; + methodClosingParenthesesOnNewLine = policy.IndexerDeclarationClosingBracketOnNewLine; + doAlignToFirstArgument = policy.AlignToFirstIndexerDeclarationParameter; + spaceWithinMethodCallParentheses = policy.SpaceWithinIndexerDeclarationBracket; + spaceAfterMethodCallParameterComma = policy.SpaceAfterIndexerDeclarationParameterComma; + spaceBeforeMethodCallParameterComma = policy.SpaceBeforeIndexerDeclarationParameterComma; + spaceWithinEmptyParentheses = policy.SpaceBetweenEmptyMethodDeclarationParentheses; + lParToken = indexer.LBracketToken; + rParToken = indexer.RBracketToken; + arguments = indexer.Parameters.Cast().ToList(); + } else if (node is OperatorDeclaration) { + var op = (OperatorDeclaration)node; + methodCallArgumentWrapping = policy.MethodDeclarationParameterWrapping; + newLineAferMethodCallOpenParentheses = policy.NewLineAferMethodDeclarationOpenParentheses; + methodClosingParenthesesOnNewLine = policy.MethodDeclarationClosingParenthesesOnNewLine; + doAlignToFirstArgument = policy.AlignToFirstMethodDeclarationParameter; + spaceWithinMethodCallParentheses = policy.SpaceWithinMethodDeclarationParentheses; + spaceAfterMethodCallParameterComma = policy.SpaceAfterMethodDeclarationParameterComma; + spaceBeforeMethodCallParameterComma = policy.SpaceBeforeMethodDeclarationParameterComma; + spaceWithinEmptyParentheses = policy.SpaceBetweenEmptyMethodDeclarationParentheses; + lParToken = op.LParToken; + rParToken = op.RParToken; + arguments = op.Parameters.Cast().ToList(); + } else if (node is MethodDeclaration) { + var methodDeclaration = node as MethodDeclaration; + methodCallArgumentWrapping = policy.MethodDeclarationParameterWrapping; + newLineAferMethodCallOpenParentheses = policy.NewLineAferMethodDeclarationOpenParentheses; + methodClosingParenthesesOnNewLine = policy.MethodDeclarationClosingParenthesesOnNewLine; + doAlignToFirstArgument = policy.AlignToFirstMethodDeclarationParameter; + spaceWithinMethodCallParentheses = policy.SpaceWithinMethodDeclarationParentheses; + spaceAfterMethodCallParameterComma = policy.SpaceAfterMethodDeclarationParameterComma; + spaceBeforeMethodCallParameterComma = policy.SpaceBeforeMethodDeclarationParameterComma; + spaceWithinEmptyParentheses = policy.SpaceBetweenEmptyMethodDeclarationParentheses; + lParToken = methodDeclaration.LParToken; + rParToken = methodDeclaration.RParToken; + arguments = methodDeclaration.Parameters.Cast().ToList(); + } else if (node is IndexerExpression) { + var indexer = (IndexerExpression)node; + methodCallArgumentWrapping = policy.IndexerArgumentWrapping; + newLineAferMethodCallOpenParentheses = policy.NewLineAferIndexerOpenBracket; + doAlignToFirstArgument = policy.AlignToFirstIndexerArgument; + methodClosingParenthesesOnNewLine = policy.IndexerClosingBracketOnNewLine; + spaceWithinMethodCallParentheses = policy.SpacesWithinBrackets; + spaceAfterMethodCallParameterComma = policy.SpaceAfterBracketComma; + spaceWithinEmptyParentheses = spaceWithinMethodCallParentheses; + spaceBeforeMethodCallParameterComma = policy.SpaceBeforeBracketComma; + rParToken = indexer.RBracketToken; + lParToken = indexer.LBracketToken; + arguments = indexer.Arguments.Cast().ToList(); + } else if (node is ObjectCreateExpression) { + var oce = node as ObjectCreateExpression; + methodCallArgumentWrapping = policy.MethodCallArgumentWrapping; + newLineAferMethodCallOpenParentheses = policy.NewLineAferMethodCallOpenParentheses; + doAlignToFirstArgument = policy.AlignToFirstMethodCallArgument; + methodClosingParenthesesOnNewLine = policy.MethodCallClosingParenthesesOnNewLine; + spaceWithinMethodCallParentheses = policy.SpacesWithinNewParentheses; + spaceAfterMethodCallParameterComma = policy.SpaceAfterNewParameterComma; + spaceBeforeMethodCallParameterComma = policy.SpaceBeforeNewParameterComma; + spaceWithinEmptyParentheses = policy.SpacesBetweenEmptyNewParentheses; + + rParToken = oce.RParToken; + lParToken = oce.LParToken; + arguments = oce.Arguments.Cast().ToList(); + } else if (node is Attribute) { + var oce = node as Attribute; + methodCallArgumentWrapping = policy.MethodCallArgumentWrapping; + newLineAferMethodCallOpenParentheses = policy.NewLineAferMethodCallOpenParentheses; + doAlignToFirstArgument = policy.AlignToFirstMethodCallArgument; + methodClosingParenthesesOnNewLine = policy.MethodCallClosingParenthesesOnNewLine; + spaceWithinMethodCallParentheses = policy.SpacesWithinNewParentheses; + spaceAfterMethodCallParameterComma = policy.SpaceAfterNewParameterComma; + spaceBeforeMethodCallParameterComma = policy.SpaceBeforeNewParameterComma; + spaceWithinEmptyParentheses = policy.SpacesBetweenEmptyNewParentheses; + + rParToken = oce.RParToken; + lParToken = oce.LParToken; + arguments = oce.Arguments.Cast().ToList(); + } else if (node is LambdaExpression) { + var methodDeclaration = node as LambdaExpression; + methodCallArgumentWrapping = policy.MethodDeclarationParameterWrapping; + newLineAferMethodCallOpenParentheses = policy.NewLineAferMethodDeclarationOpenParentheses; + methodClosingParenthesesOnNewLine = policy.MethodDeclarationClosingParenthesesOnNewLine; + doAlignToFirstArgument = policy.AlignToFirstMethodDeclarationParameter; + spaceWithinMethodCallParentheses = policy.SpaceWithinMethodDeclarationParentheses; + spaceAfterMethodCallParameterComma = policy.SpaceAfterMethodDeclarationParameterComma; + spaceBeforeMethodCallParameterComma = policy.SpaceBeforeMethodDeclarationParameterComma; + spaceWithinEmptyParentheses = policy.SpaceBetweenEmptyMethodDeclarationParentheses; + lParToken = methodDeclaration.LParToken; + rParToken = methodDeclaration.RParToken; + arguments = methodDeclaration.Parameters.Cast().ToList(); + } else if (node is AnonymousMethodExpression) { + var methodDeclaration = node as AnonymousMethodExpression; + methodCallArgumentWrapping = policy.MethodDeclarationParameterWrapping; + newLineAferMethodCallOpenParentheses = policy.NewLineAferMethodDeclarationOpenParentheses; + methodClosingParenthesesOnNewLine = policy.MethodDeclarationClosingParenthesesOnNewLine; + doAlignToFirstArgument = policy.AlignToFirstMethodDeclarationParameter; + spaceWithinMethodCallParentheses = policy.SpaceWithinMethodDeclarationParentheses; + spaceAfterMethodCallParameterComma = policy.SpaceAfterMethodDeclarationParameterComma; + spaceBeforeMethodCallParameterComma = policy.SpaceBeforeMethodDeclarationParameterComma; + spaceWithinEmptyParentheses = policy.SpaceBetweenEmptyMethodDeclarationParentheses; + lParToken = methodDeclaration.LParToken; + rParToken = methodDeclaration.RParToken; + arguments = methodDeclaration.Parameters.Cast().ToList(); + } else if (node is ConstructorInitializer) { + var constructorInitializer = node as ConstructorInitializer; + methodCallArgumentWrapping = policy.MethodDeclarationParameterWrapping; + newLineAferMethodCallOpenParentheses = policy.NewLineAferMethodDeclarationOpenParentheses; + methodClosingParenthesesOnNewLine = policy.MethodDeclarationClosingParenthesesOnNewLine; + doAlignToFirstArgument = policy.AlignToFirstMethodDeclarationParameter; + spaceWithinMethodCallParentheses = policy.SpaceWithinMethodDeclarationParentheses; + spaceAfterMethodCallParameterComma = policy.SpaceAfterMethodDeclarationParameterComma; + spaceBeforeMethodCallParameterComma = policy.SpaceBeforeMethodDeclarationParameterComma; + spaceWithinEmptyParentheses = policy.SpaceBetweenEmptyMethodDeclarationParentheses; + lParToken = constructorInitializer.LParToken; + rParToken = constructorInitializer.RParToken; + arguments = constructorInitializer.Arguments.Cast().ToList(); + } else { + InvocationExpression invocationExpression = node as InvocationExpression; + methodCallArgumentWrapping = policy.MethodCallArgumentWrapping; + newLineAferMethodCallOpenParentheses = policy.NewLineAferMethodCallOpenParentheses; + methodClosingParenthesesOnNewLine = policy.MethodCallClosingParenthesesOnNewLine; + doAlignToFirstArgument = policy.AlignToFirstMethodCallArgument; + spaceWithinMethodCallParentheses = policy.SpaceWithinMethodCallParentheses; + spaceAfterMethodCallParameterComma = policy.SpaceAfterMethodCallParameterComma; + spaceBeforeMethodCallParameterComma = policy.SpaceBeforeMethodCallParameterComma; + spaceWithinEmptyParentheses = policy.SpaceBetweenEmptyMethodCallParentheses; + + rParToken = invocationExpression.RParToken; + lParToken = invocationExpression.LParToken; + arguments = invocationExpression.Arguments.Cast().ToList(); + } + + if (formatter.FormattingMode == ICSharpCode.NRefactory.CSharp.FormattingMode.OnTheFly) + methodCallArgumentWrapping = Wrapping.DoNotChange; + int argumentStart = 1; + var firstarg = arguments.FirstOrDefault(); + if (firstarg != null && firstarg.GetPrevNode().Role == Roles.NewLine) { + doAlignToFirstArgument = false; + argumentStart = 0; + } + bool wrapMethodCall = DoWrap(methodCallArgumentWrapping, rParToken, arguments.Count); + if (wrapMethodCall && arguments.Any()) { + if (ShouldBreakLine(newLineAferMethodCallOpenParentheses, lParToken)) { + curIndent.Push(IndentType.Continuation); + foreach (var arg in arguments) { + FixStatementIndentation(arg.StartLocation); + arg.AcceptVisitor(this); + } + curIndent.Pop(); + } else { + if (!doAlignToFirstArgument) { + curIndent.Push(IndentType.Continuation); + foreach (var arg in arguments.Take (argumentStart)) { + FixStatementIndentation(arg.StartLocation); + arg.AcceptVisitor(this); + } + foreach (var arg in arguments.Skip (argumentStart)) { + FixStatementIndentation(arg.StartLocation); + arg.AcceptVisitor(this); + } + curIndent.Pop(); + } else { + int extraSpaces = Math.Max(0, arguments.First().StartLocation.Column - 1 - curIndent.IndentString.Length); + curIndent.ExtraSpaces += extraSpaces; + foreach (var arg in arguments.Take (argumentStart)) { + arg.AcceptVisitor(this); + } + foreach (var arg in arguments.Skip(argumentStart)) { + FixStatementIndentation(arg.StartLocation); + arg.AcceptVisitor(this); + } + curIndent.ExtraSpaces -= extraSpaces; + } + } + + if (!rParToken.IsNull) { + if (ShouldBreakLine(methodClosingParenthesesOnNewLine, rParToken)) { + FixStatementIndentation(rParToken.StartLocation); + } else if (methodClosingParenthesesOnNewLine == NewLinePlacement.SameLine) { + ForceSpacesBeforeRemoveNewLines(rParToken, spaceWithinMethodCallParentheses); + } + } + } else { + + foreach (var arg in arguments.Take (argumentStart)) { + if (policy.IndentBlocksInsideExpressions) + curIndent.Push(IndentType.Continuation); + arg.AcceptVisitor(this); + if (policy.IndentBlocksInsideExpressions) + curIndent.Pop(); + } + foreach (var arg in arguments.Skip(argumentStart)) { + if (arg.GetPrevSibling(NoWhitespacePredicate) != null) { + if (methodCallArgumentWrapping == Wrapping.DoNotWrap) { + ForceSpacesBeforeRemoveNewLines(arg, spaceAfterMethodCallParameterComma && arg.GetPrevSibling(NoWhitespacePredicate).Role == Roles.Comma); + if (policy.IndentBlocksInsideExpressions) + curIndent.Push(IndentType.Continuation); + arg.AcceptVisitor(this); + if (policy.IndentBlocksInsideExpressions) + curIndent.Pop(); + } else { + if (!doAlignToFirstArgument && arg.PrevSibling.Role == Roles.NewLine) { + curIndent.Push(IndentType.Continuation); + FixStatementIndentation(arg.StartLocation); + arg.AcceptVisitor(this); + curIndent.Pop(); + } else { + if (arg.PrevSibling.StartLocation.Line == arg.StartLocation.Line) { + ForceSpacesBefore(arg, spaceAfterMethodCallParameterComma && arg.GetPrevSibling(NoWhitespacePredicate).Role == Roles.Comma); + if (policy.IndentBlocksInsideExpressions) + curIndent.Push(IndentType.Continuation); + arg.AcceptVisitor(this); + if (policy.IndentBlocksInsideExpressions) + curIndent.Pop(); + } else { + int extraSpaces = Math.Max(0, arguments.First().StartLocation.Column - 1 - curIndent.IndentString.Length); + curIndent.ExtraSpaces += extraSpaces; + FixStatementIndentation(arg.StartLocation); + arg.AcceptVisitor(this); + curIndent.ExtraSpaces -= extraSpaces; + } + } + } + } else { + arg.AcceptVisitor(this); + } + } + if (!rParToken.IsNull) { + if (methodCallArgumentWrapping == Wrapping.DoNotWrap) { + ForceSpacesBeforeRemoveNewLines(rParToken, arguments.Any() ? spaceWithinMethodCallParentheses : spaceWithinEmptyParentheses); + } else { + bool sameLine = rParToken.GetPrevNode(n => n.Role == Roles.Argument || n.Role == Roles.Parameter || n.Role == Roles.LPar || n.Role == Roles.Comma).EndLocation.Line == rParToken.StartLocation.Line; + if (sameLine) { + ForceSpacesBeforeRemoveNewLines(rParToken, arguments.Any() ? spaceWithinMethodCallParentheses : spaceWithinEmptyParentheses); + } else { + FixStatementIndentation(rParToken.StartLocation); + } + } + } + } + if (!rParToken.IsNull) { + foreach (CSharpTokenNode comma in rParToken.Parent.Children.Where(n => n.Role == Roles.Comma)) { + ForceSpacesBefore(comma, spaceBeforeMethodCallParameterComma); + } + } + } + + public override void VisitInvocationExpression(InvocationExpression invocationExpression) + { + if (!invocationExpression.Target.IsNull) + invocationExpression.Target.AcceptVisitor(this); + + ForceSpacesBefore(invocationExpression.LParToken, policy.SpaceBeforeMethodCallParentheses); + if (invocationExpression.Arguments.Any()) { + ForceSpacesAfter(invocationExpression.LParToken, policy.SpaceWithinMethodCallParentheses); + } else { + ForceSpacesAfter(invocationExpression.LParToken, policy.SpaceBetweenEmptyMethodCallParentheses); + ForceSpacesBefore(invocationExpression.RParToken, policy.SpaceBetweenEmptyMethodCallParentheses); + } + bool popIndent = false; + if (invocationExpression.Target is MemberReferenceExpression) { + var mt = (MemberReferenceExpression)invocationExpression.Target; + if (mt.Target is InvocationExpression) { + if (DoWrap(policy.ChainedMethodCallWrapping, mt.DotToken, 2)) { + curIndent.Push(IndentType.Block); + popIndent = true; + FixStatementIndentation(mt.DotToken.StartLocation); + } else { + if (policy.ChainedMethodCallWrapping == Wrapping.DoNotWrap) + ForceSpacesBeforeRemoveNewLines(mt.DotToken, false); + } + } + } + FormatArguments(invocationExpression); + if (popIndent) + curIndent.Pop(); + } + + public override void VisitIndexerExpression(IndexerExpression indexerExpression) + { + ForceSpacesBeforeRemoveNewLines(indexerExpression.LBracketToken, policy.SpacesBeforeBrackets); + ForceSpacesAfter(indexerExpression.LBracketToken, policy.SpacesWithinBrackets); + + if (!indexerExpression.Target.IsNull) + indexerExpression.Target.AcceptVisitor(this); + + FormatArguments(indexerExpression); + + + + } + + public override void VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression) + { + var lp = parenthesizedExpression.LParToken; + var expr = parenthesizedExpression.Expression; + int extraSpaces = 0; + if (lp.StartLocation.Line == expr.StartLocation.Line) { + ForceSpacesAfter(lp, policy.SpacesWithinParentheses); + } else { + extraSpaces += options.IndentSize; + curIndent.ExtraSpaces += extraSpaces; + FixIndentation(expr); + } + + base.VisitParenthesizedExpression(parenthesizedExpression); + + var rp = parenthesizedExpression.RParToken; + + curIndent.ExtraSpaces -= extraSpaces; + if (rp.StartLocation.Line == expr.EndLocation.Line) { + ForceSpacesBefore(rp, policy.SpacesWithinParentheses); + } else { + FixIndentation(rp); + } + } + + public override void VisitSizeOfExpression(SizeOfExpression sizeOfExpression) + { + ForceSpacesBeforeRemoveNewLines(sizeOfExpression.LParToken, policy.SpaceBeforeSizeOfParentheses); + ForceSpacesAfter(sizeOfExpression.LParToken, policy.SpacesWithinSizeOfParentheses); + ForceSpacesBeforeRemoveNewLines(sizeOfExpression.RParToken, policy.SpacesWithinSizeOfParentheses); + base.VisitSizeOfExpression(sizeOfExpression); + } + + public override void VisitTypeOfExpression(TypeOfExpression typeOfExpression) + { + ForceSpacesBeforeRemoveNewLines(typeOfExpression.LParToken, policy.SpaceBeforeTypeOfParentheses); + ForceSpacesAfter(typeOfExpression.LParToken, policy.SpacesWithinTypeOfParentheses); + ForceSpacesBeforeRemoveNewLines(typeOfExpression.RParToken, policy.SpacesWithinTypeOfParentheses); + base.VisitTypeOfExpression(typeOfExpression); + } + + public override void VisitCheckedExpression(CheckedExpression checkedExpression) + { + ForceSpacesAfter(checkedExpression.LParToken, policy.SpacesWithinCheckedExpressionParantheses); + ForceSpacesBeforeRemoveNewLines(checkedExpression.RParToken, policy.SpacesWithinCheckedExpressionParantheses); + base.VisitCheckedExpression(checkedExpression); + } + + public override void VisitUncheckedExpression(UncheckedExpression uncheckedExpression) + { + ForceSpacesAfter(uncheckedExpression.LParToken, policy.SpacesWithinCheckedExpressionParantheses); + ForceSpacesBeforeRemoveNewLines(uncheckedExpression.RParToken, policy.SpacesWithinCheckedExpressionParantheses); + base.VisitUncheckedExpression(uncheckedExpression); + } + + public override void VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression) + { + ForceSpacesBeforeRemoveNewLines(objectCreateExpression.LParToken, policy.SpaceBeforeNewParentheses); + + if (objectCreateExpression.Arguments.Any()) { + if (!objectCreateExpression.LParToken.IsNull) + ForceSpacesAfter(objectCreateExpression.LParToken, policy.SpacesWithinNewParentheses); + } else { + if (!objectCreateExpression.LParToken.IsNull) + ForceSpacesAfter(objectCreateExpression.LParToken, policy.SpacesBetweenEmptyNewParentheses); + } + + if (!objectCreateExpression.Type.IsNull) + objectCreateExpression.Type.AcceptVisitor(this); + objectCreateExpression.Initializer.AcceptVisitor(this); + FormatArguments(objectCreateExpression); + } + + public override void VisitArrayCreateExpression(ArrayCreateExpression arrayObjectCreateExpression) + { + FormatCommas(arrayObjectCreateExpression, policy.SpaceBeforeMethodCallParameterComma, policy.SpaceAfterMethodCallParameterComma); + base.VisitArrayCreateExpression(arrayObjectCreateExpression); + } + + public override void VisitArrayInitializerExpression(ArrayInitializerExpression arrayInitializerExpression) + { + var old = curIndent; + curIndent = curIndent.Clone(); + curIndent.ExtraSpaces = 0; + + if (DoWrap(policy.ArrayInitializerWrapping, arrayInitializerExpression.RBraceToken, arrayInitializerExpression.Elements.Count)) { + FixOpenBrace(policy.ArrayInitializerBraceStyle, arrayInitializerExpression.LBraceToken); + curIndent.Push(IndentType.Block); + foreach (var init in arrayInitializerExpression.Elements) { + FixStatementIndentation(init.StartLocation); + init.AcceptVisitor(this); + } + curIndent.Pop(); + FixClosingBrace(policy.ArrayInitializerBraceStyle, arrayInitializerExpression.RBraceToken); + } else if (policy.ArrayInitializerWrapping == Wrapping.DoNotWrap) { + ForceSpacesBeforeRemoveNewLines(arrayInitializerExpression.LBraceToken); + foreach (var init in arrayInitializerExpression.Elements) { + ForceSpacesBeforeRemoveNewLines(init); + init.AcceptVisitor(this); + } + ForceSpacesBeforeRemoveNewLines(arrayInitializerExpression.RBraceToken); + } else { + var lBrace = arrayInitializerExpression.LBraceToken; + var rBrace = arrayInitializerExpression.RBraceToken; + + foreach (var child in arrayInitializerExpression.Children) { + if (child.Role == Roles.LBrace) { + if (lBrace.StartLocation.Line == rBrace.StartLocation.Line && policy.AllowOneLinedArrayInitialziers) { + ForceSpacesAfter(child, true); + } else { + FixOpenBrace(policy.ArrayInitializerBraceStyle, child); + } + curIndent.Push(IndentType.Block); + continue; + } + if (child.Role == Roles.RBrace) { + curIndent.Pop(); + if (lBrace.StartLocation.Line == rBrace.StartLocation.Line && policy.AllowOneLinedArrayInitialziers) { + ForceSpaceBefore(child, true); + + } else { + FixClosingBrace(policy.ArrayInitializerBraceStyle, child); + } + continue; + } + if (child.Role == Roles.Expression) { + if (child.PrevSibling != null) { + if (child.PrevSibling.Role == Roles.NewLine) { + FixIndentation(child); + } + if (child.PrevSibling.Role == Roles.Comma) { + ForceSpaceBefore(child, true); + } + } + child.AcceptVisitor(this); + if (child.NextSibling != null && child.NextSibling.Role == Roles.Comma) + ForceSpacesAfter(child, false); + continue; + } + + child.AcceptVisitor(this); + } + } + curIndent = old; + } + + public override void VisitParameterDeclaration(ParameterDeclaration parameterDeclaration) + { + var assignToken = parameterDeclaration.AssignToken; + if (!assignToken.IsNull) + ForceSpacesAround(assignToken, policy.SpaceAroundAssignment); + base.VisitParameterDeclaration(parameterDeclaration); + } + + public override void VisitLambdaExpression(LambdaExpression lambdaExpression) + { + FormatArguments(lambdaExpression); + ForceSpacesBeforeRemoveNewLines(lambdaExpression.ArrowToken, true); + + if (!lambdaExpression.Body.IsNull) { + var body = lambdaExpression.Body as BlockStatement; + if (body != null) { + var old = curIndent; + this.curIndent = curIndent.GetIndentWithoutSpace (); + FixOpenBrace(policy.AnonymousMethodBraceStyle, body.LBraceToken); + VisitBlockWithoutFixingBraces(body, policy.IndentMethodBody); + FixClosingBrace(policy.AnonymousMethodBraceStyle, body.RBraceToken); + curIndent = old; + } else { + ForceSpacesAfter(lambdaExpression.ArrowToken, true); + lambdaExpression.Body.AcceptVisitor(this); + } + } + } + + public override void VisitNamedExpression(NamedExpression namedExpression) + { + ForceSpacesAround(namedExpression.AssignToken, policy.SpaceAroundAssignment); + base.VisitNamedExpression(namedExpression); + } + + public override void VisitNamedArgumentExpression(NamedArgumentExpression namedArgumentExpression) + { + ForceSpacesAfter(namedArgumentExpression.ColonToken, policy.SpaceInNamedArgumentAfterDoubleColon); + + base.VisitNamedArgumentExpression(namedArgumentExpression); + } + + public override void VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression) + { + var dot = memberReferenceExpression.DotToken; + if (dot.PrevSibling.EndLocation.Line == dot.StartLocation.Line) + ForceSpacesBefore(dot, false); + ForceSpacesAfter(dot, false); + base.VisitMemberReferenceExpression(memberReferenceExpression); + } + + public override void VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression) + { + ForceSpacesAround(pointerReferenceExpression.ArrowToken, policy.SpaceAroundUnsafeArrowOperator); + base.VisitPointerReferenceExpression(pointerReferenceExpression); + } + + public override void VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression) + { + base.VisitUnaryOperatorExpression(unaryOperatorExpression); + switch (unaryOperatorExpression.Operator) { + case UnaryOperatorType.Any: + break; + case UnaryOperatorType.Not: + case UnaryOperatorType.BitNot: + case UnaryOperatorType.Minus: + case UnaryOperatorType.Plus: + case UnaryOperatorType.Increment: + case UnaryOperatorType.Decrement: + ForceSpacesBeforeRemoveNewLines(unaryOperatorExpression.Expression, false); + break; + case UnaryOperatorType.PostIncrement: + case UnaryOperatorType.PostDecrement: + ForceSpacesBeforeRemoveNewLines(unaryOperatorExpression.OperatorToken, false); + break; + case UnaryOperatorType.Dereference: + ForceSpacesAfter(unaryOperatorExpression.OperatorToken, policy.SpaceAfterUnsafeAsteriskOfOperator); + break; + case UnaryOperatorType.AddressOf: + ForceSpacesAfter(unaryOperatorExpression.OperatorToken, policy.SpaceAfterUnsafeAddressOfOperator); + break; + case UnaryOperatorType.Await: + ForceSpacesBeforeRemoveNewLines(unaryOperatorExpression.Expression, true); + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingVisitor_Global.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingVisitor_Global.cs new file mode 100644 index 000000000..b91cbdd9a --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingVisitor_Global.cs @@ -0,0 +1,351 @@ +// +// AstFormattingVisitor_Global.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2013 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.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + partial class FormattingVisitor : DepthFirstAstVisitor + { + int GetGlobalNewLinesFor(AstNode child) + { + if (child.NextSibling == null) + // last node in the document => no extra newlines + return 0; + if (child.NextSibling.Role == Roles.RBrace) + // Last node in a block => no extra newlines, it's handled later by FixClosingBrace() + return 0; + + int newLines = 1; + var nextSibling = child.GetNextSibling(NoWhitespacePredicate); + if (nextSibling is PreProcessorDirective) { + var directive = (PreProcessorDirective)nextSibling; + if (directive.Type == PreProcessorDirectiveType.Endif) + return -1; + if (directive.Type == PreProcessorDirectiveType.Undef) + return -1; + } + if ((child is UsingDeclaration || child is UsingAliasDeclaration) && !(nextSibling is UsingDeclaration || nextSibling is UsingAliasDeclaration)) { + newLines += policy.MinimumBlankLinesAfterUsings; + } else if ((child is TypeDeclaration) && (nextSibling is TypeDeclaration)) { + newLines += policy.MinimumBlankLinesBetweenTypes; + } + + return newLines; + } + + public override void VisitSyntaxTree(SyntaxTree unit) + { + bool first = true; + VisitChildrenToFormat(unit, child => { + if (first && (child is UsingDeclaration || child is UsingAliasDeclaration)) { + EnsureMinimumBlankLinesBefore(child, policy.MinimumBlankLinesBeforeUsings); + first = false; + } + if (NoWhitespacePredicate(child)) + FixIndentation(child); + child.AcceptVisitor(this); + if (NoWhitespacePredicate(child) && !first) + EnsureMinimumNewLinesAfter(child, GetGlobalNewLinesFor(child)); + }); + } + + public override void VisitAttributeSection(AttributeSection attributeSection) + { + VisitChildrenToFormat(attributeSection, child => { + child.AcceptVisitor(this); + if (child.NextSibling != null && child.NextSibling.Role == Roles.RBracket) { + ForceSpacesAfter(child, false); + } + }); + } + + public override void VisitAttribute(Attribute attribute) + { + if (attribute.HasArgumentList) { + ForceSpacesBefore(attribute.LParToken, policy.SpaceBeforeMethodCallParentheses); + if (attribute.Arguments.Any()) { + ForceSpacesAfter(attribute.LParToken, policy.SpaceWithinMethodCallParentheses); + } else { + ForceSpacesAfter(attribute.LParToken, policy.SpaceBetweenEmptyMethodCallParentheses); + ForceSpacesBefore(attribute.RParToken, policy.SpaceBetweenEmptyMethodCallParentheses); + } + FormatArguments(attribute); + } + } + + public override void VisitUsingDeclaration(UsingDeclaration usingDeclaration) + { + ForceSpacesAfter(usingDeclaration.UsingToken, true); + FixSemicolon(usingDeclaration.SemicolonToken); + } + + public override void VisitUsingAliasDeclaration(UsingAliasDeclaration usingDeclaration) + { + ForceSpacesAfter(usingDeclaration.UsingToken, true); + ForceSpacesAround(usingDeclaration.AssignToken, policy.SpaceAroundAssignment); + FixSemicolon(usingDeclaration.SemicolonToken); + } + + public override void VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration) + { + FixOpenBrace(policy.NamespaceBraceStyle, namespaceDeclaration.LBraceToken); + if (policy.IndentNamespaceBody) + curIndent.Push(IndentType.Block); + + bool first = true; + bool startFormat = false; + VisitChildrenToFormat(namespaceDeclaration, child => { + if (first) { + startFormat = child.StartLocation > namespaceDeclaration.LBraceToken.StartLocation; + } + if (child.Role == Roles.LBrace) { + var next = child.GetNextSibling(NoWhitespacePredicate); + var blankLines = 1; + if (next is UsingDeclaration || next is UsingAliasDeclaration) { + blankLines += policy.MinimumBlankLinesBeforeUsings; + } else { + blankLines += policy.MinimumBlankLinesBeforeFirstDeclaration; + } + EnsureMinimumNewLinesAfter(child, blankLines); + startFormat = true; + return; + } + if (child.Role == Roles.RBrace) { + startFormat = false; + return; + } + if (!startFormat || !NoWhitespacePredicate (child)) + return; + if (first && (child is UsingDeclaration || child is UsingAliasDeclaration)) { + // TODO: policy.BlankLinesBeforeUsings + first = false; + } + if (NoWhitespacePredicate(child)) + FixIndentationForceNewLine(child); + child.AcceptVisitor(this); + if (NoWhitespacePredicate(child)) + EnsureMinimumNewLinesAfter(child, GetGlobalNewLinesFor(child)); + }); + + if (policy.IndentNamespaceBody) + curIndent.Pop(); + + FixClosingBrace(policy.NamespaceBraceStyle, namespaceDeclaration.RBraceToken); + } + + void FixAttributesAndDocComment(EntityDeclaration entity) + { + var node = entity.FirstChild; + while (node != null && node.Role == Roles.Comment) { + node = node.GetNextSibling(NoWhitespacePredicate); + FixIndentation(node); + } + if (entity.Attributes.Count > 0) { + AstNode n = null; + entity.Attributes.First().AcceptVisitor(this); + foreach (var attr in entity.Attributes.Skip (1)) { + FixIndentation(attr); + attr.AcceptVisitor(this); + n = attr; + } + if (n != null) { + FixIndentation(n.GetNextNode(NoWhitespacePredicate)); + } else { + FixIndentation(entity.Attributes.First().GetNextNode(NoWhitespacePredicate)); + } + } + } + + public override void VisitTypeDeclaration(TypeDeclaration typeDeclaration) + { + FixAttributesAndDocComment(typeDeclaration); + BraceStyle braceStyle; + bool indentBody = false; + switch (typeDeclaration.ClassType) { + case ClassType.Class: + braceStyle = policy.ClassBraceStyle; + indentBody = policy.IndentClassBody; + break; + case ClassType.Struct: + braceStyle = policy.StructBraceStyle; + indentBody = policy.IndentStructBody; + break; + case ClassType.Interface: + braceStyle = policy.InterfaceBraceStyle; + indentBody = policy.IndentInterfaceBody; + break; + case ClassType.Enum: + braceStyle = policy.EnumBraceStyle; + indentBody = policy.IndentEnumBody; + break; + default: + throw new InvalidOperationException("unsupported class type : " + typeDeclaration.ClassType); + } + + foreach (var constraint in typeDeclaration.Constraints) + constraint.AcceptVisitor (this); + + FixOpenBrace(braceStyle, typeDeclaration.LBraceToken); + + if (indentBody) + curIndent.Push(IndentType.Block); + bool startFormat = true; + bool first = true; + VisitChildrenToFormat(typeDeclaration, child => { + if (first) { + startFormat = child.StartLocation > typeDeclaration.LBraceToken.StartLocation; + first = false; + } + if (child.Role == Roles.LBrace) { + startFormat = true; + if (braceStyle != BraceStyle.DoNotChange) + EnsureMinimumNewLinesAfter(child, GetTypeLevelNewLinesFor(child)); + return; + } + if (child.Role == Roles.RBrace) { + startFormat = false; + return; + } + if (!startFormat || !NoWhitespacePredicate (child)) + return; + if (child.Role == Roles.Comma) { + ForceSpacesBeforeRemoveNewLines (child, false); + EnsureMinimumNewLinesAfter(child, 1); + return; + } + if (NoWhitespacePredicate(child)) + FixIndentationForceNewLine(child); + child.AcceptVisitor(this); + if (NoWhitespacePredicate(child) && child.GetNextSibling (NoWhitespacePredicate).Role != Roles.Comma) + EnsureMinimumNewLinesAfter(child, GetTypeLevelNewLinesFor(child)); + }); + + if (indentBody) + curIndent.Pop(); + + FixClosingBrace(braceStyle, typeDeclaration.RBraceToken); + } + + int GetTypeLevelNewLinesFor(AstNode child) + { + var blankLines = 1; + var nextSibling = child.GetNextSibling(NoWhitespacePredicate); + if (child is PreProcessorDirective) { + var directive = (PreProcessorDirective)child; + if (directive.Type == PreProcessorDirectiveType.Region) { + blankLines += policy.MinimumBlankLinesInsideRegion; + } + if (directive.Type == PreProcessorDirectiveType.Endregion) { + if (child.GetNextSibling(NoWhitespacePredicate) is CSharpTokenNode) + return 1; + blankLines += policy.MinimumBlankLinesAroundRegion; + } + return blankLines; + } + + if (nextSibling is PreProcessorDirective) { + var directive = (PreProcessorDirective)nextSibling; + if (directive.Type == PreProcessorDirectiveType.Region) { + if (child is CSharpTokenNode) + return 1; + blankLines += policy.MinimumBlankLinesAroundRegion; + } + if (directive.Type == PreProcessorDirectiveType.Endregion) + blankLines += policy.MinimumBlankLinesInsideRegion; + if (directive.Type == PreProcessorDirectiveType.Endif) + return -1; + if (directive.Type == PreProcessorDirectiveType.Undef) + return -1; + return blankLines; + } + if (child.Role == Roles.LBrace) + return 1; + if (child is Comment) + return 1; + if (child is EventDeclaration) { + if (nextSibling is EventDeclaration) { + blankLines += policy.MinimumBlankLinesBetweenEventFields; + return blankLines; + } + } + + if (child is FieldDeclaration || child is FixedFieldDeclaration) { + if (nextSibling is FieldDeclaration || nextSibling is FixedFieldDeclaration) { + blankLines += policy.MinimumBlankLinesBetweenFields; + return blankLines; + } + } + + if (child is TypeDeclaration) { + if (nextSibling is TypeDeclaration) { + blankLines += policy.MinimumBlankLinesBetweenTypes; + return blankLines; + } + } + + if (nextSibling.Role == Roles.TypeMemberRole) + blankLines += policy.MinimumBlankLinesBetweenMembers; + return blankLines; + } + + public override void VisitDelegateDeclaration(DelegateDeclaration delegateDeclaration) + { + ForceSpacesBefore(delegateDeclaration.LParToken, policy.SpaceBeforeDelegateDeclarationParentheses); + if (delegateDeclaration.Parameters.Any()) { + ForceSpacesAfter(delegateDeclaration.LParToken, policy.SpaceWithinDelegateDeclarationParentheses); + ForceSpacesBefore(delegateDeclaration.RParToken, policy.SpaceWithinDelegateDeclarationParentheses); + } else { + ForceSpacesAfter(delegateDeclaration.LParToken, policy.SpaceBetweenEmptyDelegateDeclarationParentheses); + ForceSpacesBefore(delegateDeclaration.RParToken, policy.SpaceBetweenEmptyDelegateDeclarationParentheses); + } + FormatCommas(delegateDeclaration, policy.SpaceBeforeDelegateDeclarationParameterComma, policy.SpaceAfterDelegateDeclarationParameterComma); + + base.VisitDelegateDeclaration(delegateDeclaration); + } + + public override void VisitComment(Comment comment) + { + if (comment.StartsLine && !HadErrors && (!policy.KeepCommentsAtFirstColumn || comment.StartLocation.Column > 1)) + FixIndentation(comment); + } + + public override void VisitConstraint(Constraint constraint) + { + VisitChildrenToFormat (constraint, node => { + if (node is AstType) { + node.AcceptVisitor (this); + } else if (node.Role == Roles.LPar) { + ForceSpacesBefore (node, false); + ForceSpacesAfter (node, false); + } else if (node.Role ==Roles.Comma) { + ForceSpacesBefore (node, false); + ForceSpacesAfter (node, true); + } + }); + } + } +} \ No newline at end of file diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingVisitor_Query.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingVisitor_Query.cs new file mode 100644 index 000000000..bfdc47640 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingVisitor_Query.cs @@ -0,0 +1,124 @@ +// +// AstFormattingVisitor_Query.cs +// +// Author: +// Luís Reis +// +// Copyright (c) 2013 Luís Reis +// +// 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.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + partial class FormattingVisitor + { + int GetUpdatedStartLocation(QueryExpression queryExpression) + { + //TODO + return queryExpression.StartLocation.Column; + } + + public override void VisitQueryExpression(QueryExpression queryExpression) + { + var oldIndent = curIndent.Clone(); + + var column = GetUpdatedStartLocation(queryExpression); + + int extraSpaces = column - 1 - (curIndent.CurIndent / options.TabSize); + if (extraSpaces < 0) { + //This check should probably be removed in the future, when GetUpdatedStartLocation is implemented + extraSpaces = 0; + } + + curIndent.ExtraSpaces = extraSpaces; + VisitChildren(queryExpression); + + curIndent = oldIndent; + } + + public override void VisitQueryFromClause(QueryFromClause queryFromClause) + { + FixClauseIndentation(queryFromClause, queryFromClause.FromKeyword); + } + + public override void VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause) + { + VisitChildren(queryContinuationClause); + } + + public override void VisitQueryGroupClause(QueryGroupClause queryGroupClause) + { + FixClauseIndentation(queryGroupClause, queryGroupClause.GroupKeyword); + } + + public override void VisitQueryJoinClause(QueryJoinClause queryJoinClause) + { + FixClauseIndentation(queryJoinClause, queryJoinClause.JoinKeyword); + } + + public override void VisitQueryLetClause(QueryLetClause queryLetClause) + { + FixClauseIndentation(queryLetClause, queryLetClause.LetKeyword); + } + + public override void VisitQuerySelectClause(QuerySelectClause querySelectClause) + { + FixClauseIndentation(querySelectClause, querySelectClause.SelectKeyword); + } + + public override void VisitQueryOrderClause(QueryOrderClause queryOrderClause) + { + FixClauseIndentation(queryOrderClause, queryOrderClause.OrderbyToken); + } + + public override void VisitQueryWhereClause(QueryWhereClause queryWhereClause) + { + FixClauseIndentation(queryWhereClause, queryWhereClause.WhereKeyword); + } + + void FixClauseIndentation(QueryClause clause, AstNode keyword) { + var parentExpression = clause.GetParent(); + bool isFirstClause = parentExpression.Clauses.First() == clause; + if (!isFirstClause) { + PlaceOnNewLine(policy.NewLineBeforeNewQueryClause, keyword); + } + + int extraSpaces = options.IndentSize; + curIndent.ExtraSpaces += extraSpaces; + foreach (var child in clause.Children) { + var expression = child as Expression; + if (expression != null) { + FixIndentation(child); + child.AcceptVisitor(this); + } + + var tokenNode = child as CSharpTokenNode; + if (tokenNode != null) { + if (tokenNode.GetNextSibling(NoWhitespacePredicate).StartLocation.Line != tokenNode.EndLocation.Line) { + ForceSpacesAfter(tokenNode, false); + } + } + } + curIndent.ExtraSpaces -= extraSpaces; + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingVisitor_Statements.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingVisitor_Statements.cs new file mode 100644 index 000000000..3c5c92095 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingVisitor_Statements.cs @@ -0,0 +1,517 @@ +// +// AstFormattingVisitor_Statements.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2013 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.Linq; +using ICSharpCode.NRefactory.Editor; + +namespace ICSharpCode.NRefactory.CSharp +{ + partial class FormattingVisitor : DepthFirstAstVisitor + { + public override void VisitExpressionStatement(ExpressionStatement expressionStatement) + { + base.VisitExpressionStatement(expressionStatement); + FixSemicolon(expressionStatement.SemicolonToken); + } + + void VisitBlockWithoutFixingBraces(BlockStatement blockStatement, bool indent) + { + if (indent) { + curIndent.Push(IndentType.Block); + } + + VisitChildrenToFormat (blockStatement, child => { + if (child.Role == Roles.LBrace || child.Role == Roles.RBrace) { + return; + } + + if (child is Statement) { + FixStatementIndentation(child.StartLocation); + child.AcceptVisitor(this); + } else if (child is Comment) { + child.AcceptVisitor(this); + } else if (child is NewLineNode) { + // ignore + } else { + // pre processor directives at line start, if they are there. + if (child.StartLocation.Column > 1) + FixStatementIndentation(child.StartLocation); + } + }); + + if (indent) { + curIndent.Pop (); + } + } + + public override void VisitBlockStatement(BlockStatement blockStatement) + { + FixIndentation(blockStatement); + VisitBlockWithoutFixingBraces(blockStatement, policy.IndentBlocks); + FixIndentation(blockStatement.RBraceToken); + } + + public override void VisitBreakStatement(BreakStatement breakStatement) + { + FixSemicolon(breakStatement.SemicolonToken); + } + + public override void VisitCheckedStatement(CheckedStatement checkedStatement) + { + FixEmbeddedStatment(policy.StatementBraceStyle, checkedStatement.Body); + } + + public override void VisitContinueStatement(ContinueStatement continueStatement) + { + FixSemicolon(continueStatement.SemicolonToken); + } + + public override void VisitEmptyStatement(EmptyStatement emptyStatement) + { + // Empty + } + + public override void VisitFixedStatement(FixedStatement fixedStatement) + { + FixEmbeddedStatment(policy.StatementBraceStyle, fixedStatement.EmbeddedStatement); + } + + public override void VisitForeachStatement(ForeachStatement foreachStatement) + { + ForceSpacesBeforeRemoveNewLines(foreachStatement.LParToken, policy.SpaceBeforeForeachParentheses); + + ForceSpacesAfter(foreachStatement.LParToken, policy.SpacesWithinForeachParentheses); + ForceSpacesBeforeRemoveNewLines(foreachStatement.RParToken, policy.SpacesWithinForeachParentheses); + + FixEmbeddedStatment(policy.StatementBraceStyle, foreachStatement.EmbeddedStatement); + } + + void FixEmbeddedStatment(BraceStyle braceStyle, AstNode node) + { + FixEmbeddedStatment(braceStyle, null, false, node); + } + + void FixEmbeddedStatment(BraceStyle braceStyle, CSharpTokenNode token, bool allowInLine, AstNode node, bool statementAlreadyIndented = false) + { + if (node == null) + return; + bool isBlock = node is BlockStatement; + FormattingChanges.TextReplaceAction beginBraceAction = null; + FormattingChanges.TextReplaceAction endBraceAction = null; + BlockStatement closeBlockToBeFixed = null; + if (isBlock) { + BlockStatement block = node as BlockStatement; + if (allowInLine && block.StartLocation.Line == block.EndLocation.Line && block.Statements.Count() <= 1) { + if (block.Statements.Count() == 1) + nextStatementIndent = " "; + } else { + if (!statementAlreadyIndented) + FixOpenBrace(braceStyle, block.LBraceToken); + closeBlockToBeFixed = block; + } + + if (braceStyle == BraceStyle.NextLineShifted2) + curIndent.Push(IndentType.Block); + } else { + if (allowInLine && token.StartLocation.Line == node.EndLocation.Line) { + nextStatementIndent = " "; + } + } + bool pushed = false; + + if (policy.IndentBlocks && !( + policy.AlignEmbeddedStatements && node is IfElseStatement && node.Parent is IfElseStatement || + policy.AlignEmbeddedStatements && node is UsingStatement && node.Parent is UsingStatement || + policy.AlignEmbeddedStatements && node is LockStatement && node.Parent is LockStatement)) { + curIndent.Push(IndentType.Block); + pushed = true; + } + if (isBlock) { + VisitBlockWithoutFixingBraces((BlockStatement)node, false); + } else { + if (!statementAlreadyIndented) { + PlaceOnNewLine(policy.EmbeddedStatementPlacement, node); + nextStatementIndent = null; + } + node.AcceptVisitor(this); + } + nextStatementIndent = null; + if (pushed) + curIndent.Pop(); + if (beginBraceAction != null && endBraceAction != null) { + beginBraceAction.DependsOn = endBraceAction; + endBraceAction.DependsOn = beginBraceAction; + } + + if (isBlock && braceStyle == BraceStyle.NextLineShifted2) + curIndent.Pop(); + if (closeBlockToBeFixed != null) { + FixClosingBrace(braceStyle, closeBlockToBeFixed.RBraceToken); + } + + } + + public bool IsLineIsEmptyUpToEol(TextLocation startLocation) + { + return IsLineIsEmptyUpToEol(document.GetOffset(startLocation) - 1); + } + + bool IsLineIsEmptyUpToEol(int startOffset) + { + for (int offset = startOffset - 1; offset >= 0; offset--) { + char ch = document.GetCharAt(offset); + if (ch != ' ' && ch != '\t') { + return NewLine.IsNewLine (ch); + } + } + return true; + } + + int SearchWhitespaceStart(int startOffset) + { + if (startOffset < 0) { + throw new ArgumentOutOfRangeException ("startoffset", "value : " + startOffset); + } + for (int offset = startOffset - 1; offset >= 0; offset--) { + char ch = document.GetCharAt(offset); + if (!Char.IsWhiteSpace(ch)) { + return offset + 1; + } + } + return 0; + } + + int SearchWhitespaceEnd(int startOffset) + { + if (startOffset > document.TextLength) { + throw new ArgumentOutOfRangeException ("startoffset", "value : " + startOffset); + } + for (int offset = startOffset + 1; offset < document.TextLength; offset++) { + char ch = document.GetCharAt(offset); + if (!Char.IsWhiteSpace(ch)) { + return offset + 1; + } + } + return document.TextLength - 1; + } + + int SearchWhitespaceLineStart(int startOffset) + { + if (startOffset < 0) { + throw new ArgumentOutOfRangeException ("startoffset", "value : " + startOffset); + } + for (int offset = startOffset - 1; offset >= 0; offset--) { + char ch = document.GetCharAt(offset); + if (ch != ' ' && ch != '\t') { + return offset + 1; + } + } + return 0; + } + + public override void VisitForStatement(ForStatement forStatement) + { + foreach (AstNode node in forStatement.Children) { + if (node.Role == Roles.Semicolon) { + if (node.GetNextSibling(NoWhitespacePredicate) is CSharpTokenNode || node.GetNextSibling(NoWhitespacePredicate) is EmptyStatement) { + continue; + } + ForceSpacesBefore(node, policy.SpaceBeforeForSemicolon); + ForceSpacesAfter(node, policy.SpaceAfterForSemicolon); + } else if (node.Role == Roles.LPar) { + ForceSpacesBeforeRemoveNewLines(node, policy.SpaceBeforeForParentheses); + ForceSpacesAfter(node, policy.SpacesWithinForParentheses); + } else if (node.Role == Roles.RPar) { + ForceSpacesBeforeRemoveNewLines(node, policy.SpacesWithinForParentheses); + } else if (node.Role == Roles.EmbeddedStatement) { + FixEmbeddedStatment(policy.StatementBraceStyle, node); + } else { + node.AcceptVisitor(this); + } + } + } + + public override void VisitGotoStatement(GotoStatement gotoStatement) + { + VisitChildren(gotoStatement); + FixSemicolon(gotoStatement.SemicolonToken); + } + + public override void VisitIfElseStatement(IfElseStatement ifElseStatement) + { + ForceSpacesBeforeRemoveNewLines(ifElseStatement.LParToken, policy.SpaceBeforeIfParentheses); + Align(ifElseStatement.LParToken, ifElseStatement.Condition, policy.SpacesWithinIfParentheses); + ForceSpacesBeforeRemoveNewLines(ifElseStatement.RParToken, policy.SpacesWithinIfParentheses); + + + if (!ifElseStatement.TrueStatement.IsNull) { + FixEmbeddedStatment(policy.StatementBraceStyle, ifElseStatement.IfToken, policy.AllowIfBlockInline, ifElseStatement.TrueStatement); + } + + if (!ifElseStatement.FalseStatement.IsNull) { + var placeElseOnNewLine = policy.ElseNewLinePlacement; + if (!(ifElseStatement.TrueStatement is BlockStatement)) { + placeElseOnNewLine = NewLinePlacement.NewLine; + } + PlaceOnNewLine(placeElseOnNewLine, ifElseStatement.ElseToken); + if (ifElseStatement.FalseStatement is IfElseStatement) { + PlaceOnNewLine(policy.ElseIfNewLinePlacement, ((IfElseStatement)ifElseStatement.FalseStatement).IfToken); + } + FixEmbeddedStatment(policy.StatementBraceStyle, ifElseStatement.ElseToken, policy.AllowIfBlockInline, ifElseStatement.FalseStatement, ifElseStatement.FalseStatement is IfElseStatement); + } + } + + public override void VisitLabelStatement(LabelStatement labelStatement) + { + // TODO + VisitChildren(labelStatement); + } + + public override void VisitLockStatement(LockStatement lockStatement) + { + ForceSpacesBeforeRemoveNewLines(lockStatement.LParToken, policy.SpaceBeforeLockParentheses); + + ForceSpacesAfter(lockStatement.LParToken, policy.SpacesWithinLockParentheses); + ForceSpacesBeforeRemoveNewLines(lockStatement.RParToken, policy.SpacesWithinLockParentheses); + + FixEmbeddedStatment(policy.StatementBraceStyle, lockStatement.EmbeddedStatement); + } + + public override void VisitReturnStatement(ReturnStatement returnStatement) + { + VisitChildren(returnStatement); + FixSemicolon(returnStatement.SemicolonToken); + } + + public override void VisitSwitchStatement(SwitchStatement switchStatement) + { + ForceSpacesBeforeRemoveNewLines(switchStatement.LParToken, policy.SpaceBeforeSwitchParentheses); + + ForceSpacesAfter(switchStatement.LParToken, policy.SpacesWithinSwitchParentheses); + ForceSpacesBeforeRemoveNewLines(switchStatement.RParToken, policy.SpacesWithinSwitchParentheses); + + FixOpenBrace(policy.StatementBraceStyle, switchStatement.LBraceToken); + VisitChildren(switchStatement); + FixClosingBrace(policy.StatementBraceStyle, switchStatement.RBraceToken); + } + + public override void VisitSwitchSection(SwitchSection switchSection) + { + if (policy.IndentSwitchBody) { + curIndent.Push(IndentType.Block); + } + + foreach (CaseLabel label in switchSection.CaseLabels) { + FixStatementIndentation(label.StartLocation); + label.AcceptVisitor(this); + } + if (policy.IndentCaseBody) { + curIndent.Push(IndentType.Block); + } + + foreach (var stmt in switchSection.Statements) { + if (stmt is BreakStatement && !policy.IndentBreakStatements && policy.IndentCaseBody) { + curIndent.Pop(); + FixStatementIndentation(stmt.StartLocation); + stmt.AcceptVisitor(this); + curIndent.Push(IndentType.Block); + continue; + } + FixStatementIndentation(stmt.StartLocation); + stmt.AcceptVisitor(this); + } + if (policy.IndentCaseBody) { + curIndent.Pop (); + } + + if (policy.IndentSwitchBody) { + curIndent.Pop (); + } + } + + public override void VisitCaseLabel(CaseLabel caseLabel) + { + ForceSpacesBefore(caseLabel.ColonToken, false); + } + + public override void VisitThrowStatement(ThrowStatement throwStatement) + { + VisitChildren(throwStatement); + FixSemicolon(throwStatement.SemicolonToken); + } + + public override void VisitTryCatchStatement(TryCatchStatement tryCatchStatement) + { + if (!tryCatchStatement.TryBlock.IsNull) { + FixEmbeddedStatment(policy.StatementBraceStyle, tryCatchStatement.TryBlock); + } + + foreach (CatchClause clause in tryCatchStatement.CatchClauses) { + PlaceOnNewLine(policy.CatchNewLinePlacement, clause.CatchToken); + if (!clause.LParToken.IsNull) { + ForceSpacesBeforeRemoveNewLines(clause.LParToken, policy.SpaceBeforeCatchParentheses); + + ForceSpacesAfter(clause.LParToken, policy.SpacesWithinCatchParentheses); + ForceSpacesBeforeRemoveNewLines(clause.RParToken, policy.SpacesWithinCatchParentheses); + } + FixEmbeddedStatment(policy.StatementBraceStyle, clause.Body); + } + + if (!tryCatchStatement.FinallyBlock.IsNull) { + PlaceOnNewLine(policy.FinallyNewLinePlacement, tryCatchStatement.FinallyToken); + + FixEmbeddedStatment(policy.StatementBraceStyle, tryCatchStatement.FinallyBlock); + } + + } + + public override void VisitCatchClause(CatchClause catchClause) + { + // Handled in TryCatchStatement + } + + public override void VisitUncheckedStatement(UncheckedStatement uncheckedStatement) + { + FixEmbeddedStatment(policy.StatementBraceStyle, uncheckedStatement.Body); + } + + public override void VisitUnsafeStatement(UnsafeStatement unsafeStatement) + { + FixEmbeddedStatment(policy.StatementBraceStyle, unsafeStatement.Body); + } + + public override void VisitUsingStatement(UsingStatement usingStatement) + { + ForceSpacesBeforeRemoveNewLines(usingStatement.LParToken, policy.SpaceBeforeUsingParentheses); + + Align(usingStatement.LParToken, usingStatement.ResourceAcquisition, policy.SpacesWithinUsingParentheses); + + ForceSpacesBeforeRemoveNewLines(usingStatement.RParToken, policy.SpacesWithinUsingParentheses); + + FixEmbeddedStatment(policy.StatementBraceStyle, usingStatement.EmbeddedStatement); + } + + public override void VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement) + { + var returnType = variableDeclarationStatement.Type; + returnType.AcceptVisitor(this); + if ((variableDeclarationStatement.Modifiers & Modifiers.Const) == Modifiers.Const) { + ForceSpacesAround(returnType, true); + } else { + ForceSpacesAfter(returnType, true); + } + var lastLoc = variableDeclarationStatement.StartLocation; + foreach (var initializer in variableDeclarationStatement.Variables) { + if (lastLoc.Line != initializer.StartLocation.Line) { + FixStatementIndentation(initializer.StartLocation); + lastLoc = initializer.StartLocation; + } + initializer.AcceptVisitor(this); + } + + FormatCommas(variableDeclarationStatement, policy.SpaceBeforeLocalVariableDeclarationComma, policy.SpaceAfterLocalVariableDeclarationComma); + FixSemicolon(variableDeclarationStatement.SemicolonToken); + } + + public override void VisitDoWhileStatement(DoWhileStatement doWhileStatement) + { + FixEmbeddedStatment(policy.StatementBraceStyle, doWhileStatement.EmbeddedStatement); + PlaceOnNewLine(doWhileStatement.EmbeddedStatement is BlockStatement ? policy.WhileNewLinePlacement : NewLinePlacement.NewLine, doWhileStatement.WhileToken); + + Align(doWhileStatement.LParToken, doWhileStatement.Condition, policy.SpacesWithinWhileParentheses); + ForceSpacesBeforeRemoveNewLines(doWhileStatement.RParToken, policy.SpacesWithinWhileParentheses); + } + + void Align(AstNode lPar, AstNode alignNode, bool space) + { + int extraSpaces = 0; + var useExtraSpaces = lPar.StartLocation.Line == alignNode.StartLocation.Line; + if (useExtraSpaces) { + extraSpaces = Math.Max(0, lPar.StartLocation.Column + (space ? 1 : 0) - curIndent.IndentString.Length); + curIndent.ExtraSpaces += extraSpaces; + ForceSpacesAfter(lPar, space); + } else { + curIndent.Push(IndentType.Continuation); + FixIndentation(alignNode); + } + alignNode.AcceptVisitor(this); + + if (useExtraSpaces) { + curIndent.ExtraSpaces -= extraSpaces; + } else { + curIndent.Pop(); + } + + } + + public override void VisitWhileStatement(WhileStatement whileStatement) + { + ForceSpacesBeforeRemoveNewLines(whileStatement.LParToken, policy.SpaceBeforeWhileParentheses); + Align(whileStatement.LParToken, whileStatement.Condition, policy.SpacesWithinWhileParentheses); + ForceSpacesBeforeRemoveNewLines(whileStatement.RParToken, policy.SpacesWithinWhileParentheses); + + FixEmbeddedStatment(policy.StatementBraceStyle, whileStatement.EmbeddedStatement); + } + + public override void VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement) + { + FixSemicolon(yieldBreakStatement.SemicolonToken); + } + + public override void VisitYieldReturnStatement(YieldReturnStatement yieldStatement) + { + yieldStatement.Expression.AcceptVisitor(this); + FixSemicolon(yieldStatement.SemicolonToken); + } + + public override void VisitVariableInitializer(VariableInitializer variableInitializer) + { + if (!variableInitializer.AssignToken.IsNull) { + ForceSpacesAround(variableInitializer.AssignToken, policy.SpaceAroundAssignment); + } + if (!variableInitializer.Initializer.IsNull) { + int extraSpaces = 0; + var useExtraSpaces = variableInitializer.AssignToken.StartLocation.Line == variableInitializer.Initializer.StartLocation.Line; + if (useExtraSpaces) { + extraSpaces = Math.Max(0, variableInitializer.AssignToken.StartLocation.Column + 1 - curIndent.IndentString.Length); + curIndent.ExtraSpaces += extraSpaces; + } else { + curIndent.Push(IndentType.Continuation); + FixIndentation(variableInitializer.Initializer); + } + variableInitializer.Initializer.AcceptVisitor(this); + + if (useExtraSpaces) { + curIndent.ExtraSpaces -= extraSpaces; + } else { + curIndent.Pop(); + } + } + + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingVisitor_TypeMembers.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingVisitor_TypeMembers.cs new file mode 100644 index 000000000..913a61e2e --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/FormattingVisitor_TypeMembers.cs @@ -0,0 +1,477 @@ +// +// AstFormattingVisitor_TypeMembers.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2013 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.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + partial class FormattingVisitor : DepthFirstAstVisitor + { + public override void VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration) + { + FixAttributesAndDocComment(propertyDeclaration); + bool oneLine = false; + bool fixClosingBrace = false; + PropertyFormatting propertyFormatting; + + if ((propertyDeclaration.Getter.IsNull || propertyDeclaration.Getter.Body.IsNull) && + (propertyDeclaration.Setter.IsNull || propertyDeclaration.Setter.Body.IsNull)) { + propertyFormatting = policy.AutoPropertyFormatting; + } else { + propertyFormatting = policy.SimplePropertyFormatting; + } + + switch (propertyFormatting) { + case PropertyFormatting.AllowOneLine: + bool isSimple = IsSimpleAccessor(propertyDeclaration.Getter) && IsSimpleAccessor(propertyDeclaration.Setter); + int accessorLine = propertyDeclaration.RBraceToken.StartLocation.Line; + if (!propertyDeclaration.Getter.IsNull && propertyDeclaration.Setter.IsNull) { + accessorLine = propertyDeclaration.Getter.StartLocation.Line; + } else if (propertyDeclaration.Getter.IsNull && !propertyDeclaration.Setter.IsNull) { + accessorLine = propertyDeclaration.Setter.StartLocation.Line; + } else { + var acc = propertyDeclaration.Getter.StartLocation < propertyDeclaration.Setter.StartLocation ? + propertyDeclaration.Getter : propertyDeclaration.Setter; + accessorLine = acc.StartLocation.Line; + } + if (isSimple && + Math.Min(propertyDeclaration.Getter.StartLocation.Line, propertyDeclaration.Setter.StartLocation.Line) == propertyDeclaration.LBraceToken.StartLocation.Line && + propertyDeclaration.Getter.StartLocation.Line != propertyDeclaration.Setter.StartLocation.Line) + goto case PropertyFormatting.ForceOneLine; + if (!isSimple || propertyDeclaration.LBraceToken.StartLocation.Line != accessorLine) { + fixClosingBrace = true; + FixOpenBrace(policy.PropertyBraceStyle, propertyDeclaration.LBraceToken); + } else { + ForceSpacesBefore(propertyDeclaration.Getter, true); + ForceSpacesBefore(propertyDeclaration.Setter, true); + ForceSpacesBeforeRemoveNewLines(propertyDeclaration.RBraceToken, true); + oneLine = true; + } + break; + case PropertyFormatting.ForceNewLine: + fixClosingBrace = true; + FixOpenBrace(policy.PropertyBraceStyle, propertyDeclaration.LBraceToken); + break; + case PropertyFormatting.ForceOneLine: + isSimple = IsSimpleAccessor(propertyDeclaration.Getter) && IsSimpleAccessor(propertyDeclaration.Setter); + if (isSimple) { + var lBraceToken = propertyDeclaration.LBraceToken; + var rBraceToken = propertyDeclaration.RBraceToken; + ForceSpacesBeforeRemoveNewLines(lBraceToken, true); + if (!propertyDeclaration.Getter.IsNull) + ForceSpacesBeforeRemoveNewLines(propertyDeclaration.Getter, true); + if (!propertyDeclaration.Setter.IsNull) + ForceSpacesBeforeRemoveNewLines(propertyDeclaration.Setter, true); + + ForceSpacesBeforeRemoveNewLines(rBraceToken, true); + oneLine = true; + } else { + fixClosingBrace = true; + FixOpenBrace(policy.PropertyBraceStyle, propertyDeclaration.LBraceToken); + } + break; + } + if (policy.IndentPropertyBody) + curIndent.Push(IndentType.Block); + + FormatAccessor(propertyDeclaration.Getter, policy.PropertyGetBraceStyle, policy.SimpleGetBlockFormatting, oneLine); + FormatAccessor(propertyDeclaration.Setter, policy.PropertySetBraceStyle, policy.SimpleSetBlockFormatting, oneLine); + + if (policy.IndentPropertyBody) { + curIndent.Pop(); + } + if (fixClosingBrace) + FixClosingBrace(policy.PropertyBraceStyle, propertyDeclaration.RBraceToken); + + } + + void FormatAccessor(Accessor accessor, BraceStyle braceStyle, PropertyFormatting blockFormatting, bool oneLine) + { + if (accessor.IsNull) + return; + if (!oneLine) { + if (!IsLineIsEmptyUpToEol(accessor.StartLocation)) { + int offset = this.document.GetOffset(accessor.StartLocation); + int start = SearchWhitespaceStart(offset); + string indentString = this.curIndent.IndentString; + AddChange(start, offset - start, this.options.EolMarker + indentString); + } else { + FixIndentation(accessor); + } + } else { + blockFormatting = PropertyFormatting.ForceOneLine; + if (!accessor.Body.IsNull) { + ForceSpacesBeforeRemoveNewLines(accessor.Body.LBraceToken, true); + ForceSpacesBeforeRemoveNewLines(accessor.Body.RBraceToken, true); + } + } + + + if (!accessor.IsNull) { + if (!accessor.Body.IsNull) { + if (IsSimpleAccessor (accessor)) { + switch (blockFormatting) { + case PropertyFormatting.AllowOneLine: + if (accessor.Body.LBraceToken.StartLocation.Line != accessor.Body.RBraceToken.StartLocation.Line) + goto case PropertyFormatting.ForceNewLine; + nextStatementIndent = " "; + VisitBlockWithoutFixingBraces(accessor.Body, policy.IndentBlocks); + nextStatementIndent = null; + if (!oneLine) + ForceSpacesBeforeRemoveNewLines(accessor.Body.RBraceToken, true); + break; + case PropertyFormatting.ForceOneLine: + FixOpenBrace(BraceStyle.EndOfLine, accessor.Body.LBraceToken); + + + var statement = accessor.Body.Statements.FirstOrDefault(); + if (statement != null) { + ForceSpacesBeforeRemoveNewLines(statement, true); + statement.AcceptVisitor(this); + } + if (!oneLine) + ForceSpacesBeforeRemoveNewLines(accessor.Body.RBraceToken, true); + break; + case PropertyFormatting.ForceNewLine: + FixOpenBrace(braceStyle, accessor.Body.LBraceToken); + VisitBlockWithoutFixingBraces(accessor.Body, policy.IndentBlocks); + if (!oneLine) + FixClosingBrace(braceStyle, accessor.Body.RBraceToken); + break; + } + } else { + FixOpenBrace(braceStyle, accessor.Body.LBraceToken); + VisitBlockWithoutFixingBraces(accessor.Body, policy.IndentBlocks); + FixClosingBrace(braceStyle, accessor.Body.RBraceToken); + } + } + } + } + + public override void VisitAccessor(Accessor accessor) + { + FixAttributesAndDocComment(accessor); + + base.VisitAccessor(accessor); + } + + public override void VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration) + { + FixAttributesAndDocComment(indexerDeclaration); + + ForceSpacesBefore(indexerDeclaration.LBracketToken, policy.SpaceBeforeIndexerDeclarationBracket); + ForceSpacesAfter(indexerDeclaration.LBracketToken, policy.SpaceWithinIndexerDeclarationBracket); + + FormatArguments(indexerDeclaration); + + bool oneLine = false; + bool fixClosingBrace = false; + switch (policy.SimplePropertyFormatting) { + case PropertyFormatting.AllowOneLine: + bool isSimple = IsSimpleAccessor(indexerDeclaration.Getter) && IsSimpleAccessor(indexerDeclaration.Setter); + int accessorLine = indexerDeclaration.RBraceToken.StartLocation.Line; + if (!indexerDeclaration.Getter.IsNull && indexerDeclaration.Setter.IsNull) { + accessorLine = indexerDeclaration.Getter.StartLocation.Line; + } else if (indexerDeclaration.Getter.IsNull && !indexerDeclaration.Setter.IsNull) { + accessorLine = indexerDeclaration.Setter.StartLocation.Line; + } else { + var acc = indexerDeclaration.Getter.StartLocation < indexerDeclaration.Setter.StartLocation ? + indexerDeclaration.Getter : indexerDeclaration.Setter; + accessorLine = acc.StartLocation.Line; + } + if (!isSimple || indexerDeclaration.LBraceToken.StartLocation.Line != accessorLine) { + fixClosingBrace = true; + FixOpenBrace(policy.PropertyBraceStyle, indexerDeclaration.LBraceToken); + } else { + ForceSpacesBefore(indexerDeclaration.Getter, true); + ForceSpacesBefore(indexerDeclaration.Setter, true); + ForceSpacesBeforeRemoveNewLines(indexerDeclaration.RBraceToken, true); + oneLine = true; + } + break; + case PropertyFormatting.ForceNewLine: + fixClosingBrace = true; + FixOpenBrace(policy.PropertyBraceStyle, indexerDeclaration.LBraceToken); + break; + case PropertyFormatting.ForceOneLine: + isSimple = IsSimpleAccessor(indexerDeclaration.Getter) && IsSimpleAccessor(indexerDeclaration.Setter); + if (isSimple) { + int offset = this.document.GetOffset(indexerDeclaration.LBraceToken.StartLocation); + + int start = SearchWhitespaceStart(offset); + int end = SearchWhitespaceEnd(offset); + AddChange(start, offset - start, " "); + AddChange(offset + 1, end - offset - 2, " "); + + offset = this.document.GetOffset(indexerDeclaration.RBraceToken.StartLocation); + start = SearchWhitespaceStart(offset); + AddChange(start, offset - start, " "); + oneLine = true; + + } else { + fixClosingBrace = true; + FixOpenBrace(policy.PropertyBraceStyle, indexerDeclaration.LBraceToken); + } + break; + } + + if (policy.IndentPropertyBody) + curIndent.Push(IndentType.Block); + + FormatAccessor(indexerDeclaration.Getter, policy.PropertyGetBraceStyle, policy.SimpleGetBlockFormatting, oneLine); + FormatAccessor(indexerDeclaration.Setter, policy.PropertySetBraceStyle, policy.SimpleSetBlockFormatting, oneLine); + if (policy.IndentPropertyBody) + curIndent.Pop(); + + if (fixClosingBrace) + FixClosingBrace(policy.PropertyBraceStyle, indexerDeclaration.RBraceToken); + } + + static bool IsSimpleEvent(AstNode node) + { + return node is EventDeclaration; + } + + public override void VisitCustomEventDeclaration(CustomEventDeclaration eventDeclaration) + { + FixAttributesAndDocComment(eventDeclaration); + + FixOpenBrace(policy.EventBraceStyle, eventDeclaration.LBraceToken); + if (policy.IndentEventBody) + curIndent.Push(IndentType.Block); + + if (!eventDeclaration.AddAccessor.IsNull) { + FixIndentation(eventDeclaration.AddAccessor); + if (!eventDeclaration.AddAccessor.Body.IsNull) { + if (!policy.AllowEventAddBlockInline || eventDeclaration.AddAccessor.Body.LBraceToken.StartLocation.Line != eventDeclaration.AddAccessor.Body.RBraceToken.StartLocation.Line) { + FixOpenBrace(policy.EventAddBraceStyle, eventDeclaration.AddAccessor.Body.LBraceToken); + VisitBlockWithoutFixingBraces(eventDeclaration.AddAccessor.Body, policy.IndentBlocks); + FixClosingBrace(policy.EventAddBraceStyle, eventDeclaration.AddAccessor.Body.RBraceToken); + } else { + nextStatementIndent = " "; + VisitBlockWithoutFixingBraces(eventDeclaration.AddAccessor.Body, policy.IndentBlocks); + nextStatementIndent = null; + } + } + } + + if (!eventDeclaration.RemoveAccessor.IsNull) { + FixIndentation(eventDeclaration.RemoveAccessor); + if (!eventDeclaration.RemoveAccessor.Body.IsNull) { + if (!policy.AllowEventRemoveBlockInline || eventDeclaration.RemoveAccessor.Body.LBraceToken.StartLocation.Line != eventDeclaration.RemoveAccessor.Body.RBraceToken.StartLocation.Line) { + FixOpenBrace(policy.EventRemoveBraceStyle, eventDeclaration.RemoveAccessor.Body.LBraceToken); + VisitBlockWithoutFixingBraces(eventDeclaration.RemoveAccessor.Body, policy.IndentBlocks); + FixClosingBrace(policy.EventRemoveBraceStyle, eventDeclaration.RemoveAccessor.Body.RBraceToken); + } else { + nextStatementIndent = " "; + VisitBlockWithoutFixingBraces(eventDeclaration.RemoveAccessor.Body, policy.IndentBlocks); + nextStatementIndent = null; + } + } + } + + if (policy.IndentEventBody) + curIndent.Pop(); + + FixClosingBrace(policy.EventBraceStyle, eventDeclaration.RBraceToken); + } + + public override void VisitEventDeclaration(EventDeclaration eventDeclaration) + { + FixAttributesAndDocComment(eventDeclaration); + + foreach (var m in eventDeclaration.ModifierTokens) { + ForceSpacesAfter(m, true); + } + + ForceSpacesBeforeRemoveNewLines(eventDeclaration.EventToken.GetNextSibling(NoWhitespacePredicate), true); + eventDeclaration.ReturnType.AcceptVisitor(this); + ForceSpacesAfter(eventDeclaration.ReturnType, true); + /* + var lastLoc = eventDeclaration.StartLocation; + curIndent.Push(IndentType.Block); + foreach (var initializer in eventDeclaration.Variables) { + if (lastLoc.Line != initializer.StartLocation.Line) { + FixStatementIndentation(initializer.StartLocation); + lastLoc = initializer.StartLocation; + } + initializer.AcceptVisitor(this); + } + curIndent.Pop (); + */ + FixSemicolon(eventDeclaration.SemicolonToken); + } + + public override void VisitFieldDeclaration(FieldDeclaration fieldDeclaration) + { + FixAttributesAndDocComment(fieldDeclaration); + + fieldDeclaration.ReturnType.AcceptVisitor(this); + ForceSpacesAfter(fieldDeclaration.ReturnType, true); + + FormatCommas(fieldDeclaration, policy.SpaceBeforeFieldDeclarationComma, policy.SpaceAfterFieldDeclarationComma); + + var lastLoc = fieldDeclaration.ReturnType.StartLocation; + foreach (var initializer in fieldDeclaration.Variables) { + if (lastLoc.Line != initializer.StartLocation.Line) { + curIndent.Push(IndentType.Block); + FixStatementIndentation(initializer.StartLocation); + curIndent.Pop(); + lastLoc = initializer.StartLocation; + } + initializer.AcceptVisitor(this); + } + FixSemicolon(fieldDeclaration.SemicolonToken); + } + + public override void VisitFixedFieldDeclaration(FixedFieldDeclaration fixedFieldDeclaration) + { + FixAttributesAndDocComment(fixedFieldDeclaration); + + FormatCommas(fixedFieldDeclaration, policy.SpaceBeforeFieldDeclarationComma, policy.SpaceAfterFieldDeclarationComma); + + var lastLoc = fixedFieldDeclaration.StartLocation; + curIndent.Push(IndentType.Block); + foreach (var initializer in fixedFieldDeclaration.Variables) { + if (lastLoc.Line != initializer.StartLocation.Line) { + FixStatementIndentation(initializer.StartLocation); + lastLoc = initializer.StartLocation; + } + initializer.AcceptVisitor(this); + } + curIndent.Pop(); + FixSemicolon(fixedFieldDeclaration.SemicolonToken); + } + + public override void VisitEnumMemberDeclaration(EnumMemberDeclaration enumMemberDeclaration) + { + FixAttributesAndDocComment(enumMemberDeclaration); + var initializer = enumMemberDeclaration.Initializer; + if (!initializer.IsNull) { + ForceSpacesAround(enumMemberDeclaration.AssignToken, policy.SpaceAroundAssignment); + initializer.AcceptVisitor(this); + } + } + + public override void VisitMethodDeclaration(MethodDeclaration methodDeclaration) + { + FixAttributesAndDocComment(methodDeclaration); + + ForceSpacesBefore(methodDeclaration.LParToken, policy.SpaceBeforeMethodDeclarationParentheses); + if (methodDeclaration.Parameters.Any()) { + ForceSpacesAfter(methodDeclaration.LParToken, policy.SpaceWithinMethodDeclarationParentheses); + FormatArguments(methodDeclaration); + } else { + ForceSpacesAfter(methodDeclaration.LParToken, policy.SpaceBetweenEmptyMethodDeclarationParentheses); + ForceSpacesBefore(methodDeclaration.RParToken, policy.SpaceBetweenEmptyMethodDeclarationParentheses); + } + + foreach (var constraint in methodDeclaration.Constraints) + constraint.AcceptVisitor(this); + + if (!methodDeclaration.Body.IsNull) { + FixOpenBrace(policy.MethodBraceStyle, methodDeclaration.Body.LBraceToken); + VisitBlockWithoutFixingBraces(methodDeclaration.Body, policy.IndentMethodBody); + FixClosingBrace(policy.MethodBraceStyle, methodDeclaration.Body.RBraceToken); + } + } + + public override void VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration) + { + FixAttributesAndDocComment(operatorDeclaration); + + ForceSpacesBefore(operatorDeclaration.LParToken, policy.SpaceBeforeMethodDeclarationParentheses); + if (operatorDeclaration.Parameters.Any()) { + ForceSpacesAfter(operatorDeclaration.LParToken, policy.SpaceWithinMethodDeclarationParentheses); + FormatArguments(operatorDeclaration); + } else { + ForceSpacesAfter(operatorDeclaration.LParToken, policy.SpaceBetweenEmptyMethodDeclarationParentheses); + ForceSpacesBefore(operatorDeclaration.RParToken, policy.SpaceBetweenEmptyMethodDeclarationParentheses); + } + + if (!operatorDeclaration.Body.IsNull) { + FixOpenBrace(policy.MethodBraceStyle, operatorDeclaration.Body.LBraceToken); + VisitBlockWithoutFixingBraces(operatorDeclaration.Body, policy.IndentMethodBody); + FixClosingBrace(policy.MethodBraceStyle, operatorDeclaration.Body.RBraceToken); + } + } + + public override void VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration) + { + FixAttributesAndDocComment(constructorDeclaration); + + ForceSpacesBefore(constructorDeclaration.LParToken, policy.SpaceBeforeConstructorDeclarationParentheses); + if (constructorDeclaration.Parameters.Any()) { + ForceSpacesAfter(constructorDeclaration.LParToken, policy.SpaceWithinConstructorDeclarationParentheses); + FormatArguments(constructorDeclaration); + } else { + ForceSpacesAfter(constructorDeclaration.LParToken, policy.SpaceBetweenEmptyConstructorDeclarationParentheses); + ForceSpacesBefore(constructorDeclaration.RParToken, policy.SpaceBetweenEmptyConstructorDeclarationParentheses); + } + + var initializer = constructorDeclaration.Initializer; + if (!initializer.IsNull) { + curIndent.Push(IndentType.Block); + PlaceOnNewLine(policy.NewLineBeforeConstructorInitializerColon, constructorDeclaration.ColonToken); + PlaceOnNewLine(policy.NewLineAfterConstructorInitializerColon, initializer); + initializer.AcceptVisitor(this); + curIndent.Pop(); + } + if (!constructorDeclaration.Body.IsNull) { + FixOpenBrace(policy.ConstructorBraceStyle, constructorDeclaration.Body.LBraceToken); + VisitBlockWithoutFixingBraces(constructorDeclaration.Body, policy.IndentMethodBody); + FixClosingBrace(policy.ConstructorBraceStyle, constructorDeclaration.Body.RBraceToken); + } + } + public override void VisitConstructorInitializer(ConstructorInitializer constructorInitializer) + { + ForceSpacesBefore(constructorInitializer.LParToken, policy.SpaceBeforeMethodCallParentheses); + if (constructorInitializer.Arguments.Any()) { + ForceSpacesAfter(constructorInitializer.LParToken, policy.SpaceWithinMethodCallParentheses); + } else { + ForceSpacesAfter(constructorInitializer.LParToken, policy.SpaceBetweenEmptyMethodCallParentheses); + ForceSpacesBefore(constructorInitializer.RParToken, policy.SpaceBetweenEmptyMethodCallParentheses); + } + + FormatArguments(constructorInitializer); + + } + public override void VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration) + { + FixAttributesAndDocComment(destructorDeclaration); + + CSharpTokenNode lParen = destructorDeclaration.LParToken; + ForceSpaceBefore(lParen, policy.SpaceBeforeConstructorDeclarationParentheses); + + if (!destructorDeclaration.Body.IsNull) { + FixOpenBrace(policy.DestructorBraceStyle, destructorDeclaration.Body.LBraceToken); + VisitBlockWithoutFixingBraces(destructorDeclaration.Body, policy.IndentMethodBody); + FixClosingBrace(policy.DestructorBraceStyle, destructorDeclaration.Body.RBraceToken); + } + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/GeneratedCodeSettings.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/GeneratedCodeSettings.cs new file mode 100644 index 000000000..dd2df993e --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/GeneratedCodeSettings.cs @@ -0,0 +1,216 @@ +// +// GeneratedCodeSettings.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; + +namespace ICSharpCode.NRefactory.CSharp +{ + public enum GeneratedCodeMember + { + Unknown, + + StaticFields, + InstanceFields, + StaticProperties, + InstanceProperties, + Indexer, + Constructors, + StaticMethods, + InstanceMethods, + StaticEvents, + InstanceEvents, + Operators, + NestedTypes + } + + public class GeneratedCodeSettings + { + List codeMemberOrder; + + public List CodeMemberOrder { + get { + return codeMemberOrder; + } + set { + codeMemberOrder = value; + } + } + + public bool GenerateCategoryComments { + get; + set; + } + + public bool SubOrderAlphabetical { + get; + set; + } + + public void Apply (AstNode rootNode) + { + if (rootNode == null) + throw new ArgumentNullException ("rootNode"); + rootNode.AcceptVisitor (new GenerateCodeVisitior (this)); + } + + public virtual string GetCategoryLabel(GeneratedCodeMember memberCategory) + { + switch (memberCategory) { + case GeneratedCodeMember.StaticFields: + return "Static Fields"; + case GeneratedCodeMember.InstanceFields: + return "Fields"; + case GeneratedCodeMember.StaticProperties: + return "Static Properties"; + case GeneratedCodeMember.InstanceProperties: + return "Properties"; + case GeneratedCodeMember.Indexer: + return "Indexer"; + case GeneratedCodeMember.Constructors: + return "Constructors"; + case GeneratedCodeMember.StaticMethods: + return "Static Methods"; + case GeneratedCodeMember.InstanceMethods: + return "Methods"; + case GeneratedCodeMember.StaticEvents: + return "Static Events"; + case GeneratedCodeMember.InstanceEvents: + return "Events"; + case GeneratedCodeMember.Operators: + return "Operators"; + case GeneratedCodeMember.NestedTypes: + return "Nested Types"; + } + return null; + } + + class GenerateCodeVisitior : DepthFirstAstVisitor + { + GeneratedCodeSettings settings; + + public GenerateCodeVisitior(GeneratedCodeSettings settings) + { + if (settings == null) + throw new ArgumentNullException("settings"); + this.settings = settings; + } + + GeneratedCodeMember GetCodeMemberCategory(EntityDeclaration x) + { + bool isStatic = x.HasModifier(Modifiers.Static) || x.HasModifier(Modifiers.Const); + if (x is FieldDeclaration) + return isStatic ? GeneratedCodeMember.StaticFields : GeneratedCodeMember.InstanceFields; + if (x is IndexerDeclaration) + return GeneratedCodeMember.Indexer; + if (x is PropertyDeclaration) + return isStatic ? GeneratedCodeMember.StaticProperties : GeneratedCodeMember.InstanceProperties; + if (x is ConstructorDeclaration || x is DestructorDeclaration) + return GeneratedCodeMember.Constructors; + if (x is MethodDeclaration) + return isStatic ? GeneratedCodeMember.StaticMethods : GeneratedCodeMember.InstanceMethods; + if (x is OperatorDeclaration) + return GeneratedCodeMember.Operators; + if (x is EventDeclaration || x is CustomEventDeclaration) + return isStatic ? GeneratedCodeMember.StaticEvents : GeneratedCodeMember.InstanceEvents; + + if (x is TypeDeclaration) + return GeneratedCodeMember.NestedTypes; + + return GeneratedCodeMember.Unknown; + } + + public override void VisitTypeDeclaration (TypeDeclaration typeDeclaration) + { + if (typeDeclaration.ClassType == ClassType.Enum) + return; + var entities = new List (typeDeclaration.Members); + entities.Sort ((x, y) => { + int i1 = settings.CodeMemberOrder.IndexOf (GetCodeMemberCategory (x)); + int i2 = settings.CodeMemberOrder.IndexOf (GetCodeMemberCategory (y)); + if (i1 != i2) + return i1.CompareTo (i2); + if (settings.SubOrderAlphabetical) + return (x.Name ?? "").CompareTo ((y.Name ?? "")); + return entities.IndexOf (x).CompareTo (entities.IndexOf (y)); + }); + typeDeclaration.Members.Clear (); + typeDeclaration.Members.AddRange (entities); + + if (settings.GenerateCategoryComments) { + var curCat = GeneratedCodeMember.Unknown; + foreach (var mem in entities) { + if (mem.NextSibling is EntityDeclaration) + mem.Parent.InsertChildAfter (mem, new NewLineNode (), Roles.NewLine); + + var cat = GetCodeMemberCategory (mem); + if (cat == curCat) + continue; + curCat = cat; + var label = settings.GetCategoryLabel (curCat); + if (string.IsNullOrEmpty (label)) + continue; + + var cmt = new Comment ("", CommentType.SingleLine); + var cmt2 = new Comment (" " + label, CommentType.SingleLine); + var cmt3 = new Comment ("", CommentType.SingleLine); + mem.Parent.InsertChildBefore (mem, cmt, Roles.Comment); + mem.Parent.InsertChildBefore (mem, cmt2, Roles.Comment); + mem.Parent.InsertChildBefore (mem, cmt3, Roles.Comment); + if (cmt.PrevSibling is EntityDeclaration) + mem.Parent.InsertChildBefore (cmt, new NewLineNode (), Roles.NewLine); + } + } + } + } + + static Lazy defaultSettings = new Lazy( + () => new GeneratedCodeSettings() { + CodeMemberOrder = new List() { + GeneratedCodeMember.StaticFields, + GeneratedCodeMember.InstanceFields, + GeneratedCodeMember.StaticProperties, + GeneratedCodeMember.InstanceProperties, + GeneratedCodeMember.Indexer, + GeneratedCodeMember.Constructors, + GeneratedCodeMember.StaticMethods, + GeneratedCodeMember.InstanceMethods, + GeneratedCodeMember.StaticEvents, + GeneratedCodeMember.InstanceEvents, + GeneratedCodeMember.Operators, + GeneratedCodeMember.NestedTypes + }, + GenerateCategoryComments = true, + SubOrderAlphabetical = true + }); + + public static GeneratedCodeSettings Default { + get { + return defaultSettings.Value; + } + } + } +} \ No newline at end of file diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/Indent.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/Indent.cs new file mode 100644 index 000000000..347e66f6e --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/Indent.cs @@ -0,0 +1,246 @@ +// +// Indent.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.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 System.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + public enum IndentType + { + Block, + DoubleBlock, + Continuation, + Alignment, + Label, + Empty + } + + public class Indent + { + readonly CloneableStack indentStack = new CloneableStack(); + readonly TextEditorOptions options; + int curIndent; + int extraSpaces; + string indentString; + + public int CurIndent { + get { + return curIndent; + } + } + + public Indent(TextEditorOptions options) + { + this.options = options; + Reset(); + } + + Indent(Indent engine) + { + this.indentStack = engine.indentStack.Clone(); + this.options = engine.options; + this.curIndent = engine.curIndent; + this.extraSpaces = engine.extraSpaces; + this.indentString = engine.indentString; + } + + public Indent Clone() + { + return new Indent(this); + } + + public void Reset() + { + curIndent = 0; + indentString = ""; + indentStack.Clear(); + } + + public void Push(IndentType type) + { + indentStack.Push(type); + curIndent += GetIndent(type); + Update(); + } + + public void Push(Indent indent) + { + foreach (var i in indent.indentStack) + Push(i); + } + + public void Pop() + { + curIndent -= GetIndent(indentStack.Pop()); + Update(); + } + + public bool PopIf(IndentType type) + { + if (Count > 0 && Peek() == type) + { + Pop(); + return true; + } + + return false; + } + + public void PopWhile(IndentType type) + { + while (Count > 0 && Peek() == type) + { + Pop(); + } + } + + public bool PopTry() + { + if (Count > 0) + { + Pop(); + return true; + } + + return false; + } + + public int Count { + get { + return indentStack.Count; + } + } + + public IndentType Peek() + { + return indentStack.Peek(); + } + + int GetIndent(IndentType indentType) + { + switch (indentType) { + case IndentType.Block: + return options.IndentSize; + case IndentType.DoubleBlock: + return options.IndentSize * 2; + case IndentType.Alignment: + case IndentType.Continuation: + return options.ContinuationIndent; + case IndentType.Label: + return options.LabelIndent; + case IndentType.Empty: + return 0; + default: + throw new ArgumentOutOfRangeException(); + } + } + + void Update() + { + if (options.TabsToSpaces) { + indentString = new string(' ', curIndent + ExtraSpaces); + return; + } + indentString = new string('\t', curIndent / options.TabSize) + new string(' ', curIndent % options.TabSize) + new string(' ', ExtraSpaces); + } + + public int ExtraSpaces { + get { + return extraSpaces; + } + set { + if (value < 0) + throw new ArgumentOutOfRangeException("ExtraSpaces >= 0 but was " + value); + extraSpaces = value; + Update(); + } + } + + + public string IndentString { + get { + return indentString; + } + } + + public override string ToString() + { + return string.Format("[Indent: curIndent={0}]", curIndent); + } + + public Indent GetIndentWithoutSpace () + { + var result = new Indent(options); + foreach (var i in indentStack) + result.Push(i); + return result; + } + + public static Indent ConvertFrom(string indentString, Indent correctIndent, TextEditorOptions options = null) + { + options = options ?? TextEditorOptions.Default; + var result = new Indent(options); + + var indent = string.Concat(indentString.Where(c => c == ' ' || c == '\t')); + var indentTypes = new Stack(correctIndent.indentStack); + + foreach (var _ in indent.TakeWhile(c => c == '\t')) + { + if (indentTypes.Count > 0) + result.Push(indentTypes.Pop()); + else + result.Push(IndentType.Continuation); + } + + result.ExtraSpaces = indent + .SkipWhile(c => c == '\t') + .TakeWhile(c => c == ' ') + .Count(); + + return result; + } + + public void RemoveAlignment() + { + ExtraSpaces = 0; + if (Count > 0 && Peek() == IndentType.Alignment) + Pop(); + } + + public void SetAlignment(int i, bool forceSpaces = false) + { + var alignChars = Math.Max(0, i); + if (forceSpaces) { + ExtraSpaces = alignChars; + return; + } + RemoveAlignment(); + Push(IndentType.Alignment); + } + + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/TextEditorOptions.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/TextEditorOptions.cs new file mode 100644 index 000000000..7c3eef2d2 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/TextEditorOptions.cs @@ -0,0 +1,116 @@ +// +// TextEditorOptions.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; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// The text editor options class holds basic information about the text editor settings that influences code generation and formatting beside + /// the CSharpFormattingOptions. + /// + public class TextEditorOptions + { + public static readonly TextEditorOptions Default = new TextEditorOptions (); + + /// + /// Gets or sets a value indicating if tabs need to be replaced by spaces. If that is true, all indenting will be done with spaces only, + /// otherwise the indenting will start with tabs. + /// + public bool TabsToSpaces { + get; + set; + } + + /// + /// Gets or sets the size of the tab chacter as spaces. + /// + public int TabSize { + get; + set; + } + + /// + /// Gets or sets the size of a single indent as spaces. + /// + public int IndentSize { + get; + set; + } + + /// + /// Gets or sets the continuation indent. A continuation indent is the indent that will be put after an embedded statement that is no block. + /// + public int ContinuationIndent { + get; + set; + } + + /// + /// Gets or sets the label indent. A label indent is the indent that will be put before an label. + /// (Note: it may be negative -IndentSize would cause that labels are unindented) + /// + public int LabelIndent { + get; + set; + } + + /// + /// Gets or sets the eol marker. + /// + public string EolMarker { + get; + set; + } + + /// + /// If true blank lines will be indented up to the indent level, otherwise blank lines will have the length 0. + /// + public bool IndentBlankLines { + get; + set; + } + + /// + /// Gets or sets the length of the desired line length. The formatting engine will wrap at wrap points set to Wrapping.WrapIfTooLong if the line length is too long. + /// 0 means do not wrap. + /// + public int WrapLineLength { + get; + set; + } + + public TextEditorOptions() + { + TabsToSpaces = false; + TabSize = 4; + IndentSize = 4; + ContinuationIndent = 4; + WrapLineLength = 0; + EolMarker = Environment.NewLine; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj b/NRefactory/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj new file mode 100644 index 000000000..4c53d5ff5 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj @@ -0,0 +1,420 @@ + + + + {53DCA265-3C3C-42F9-B647-F72BA678122B} + Debug + AnyCPU + Library + ICSharpCode.NRefactory.CSharp + ICSharpCode.NRefactory.CSharp + Properties + False + False + 4 + False + 8.0.30703 + 2.0 + true + ..\ICSharpCode.NRefactory.snk + False + File + ..\bin\$(Configuration)\ICSharpCode.NRefactory.CSharp.xml + 1591,1587,1570 + ..\bin\$(Configuration)\ + False + obj\$(Configuration)\ + + + AnyCPU + False + Auto + 4194304 + 4096 + + + False + False + DEBUG;TRACE;FULL_AST;NET_4_0 + + + True + False + TRACE;FULL_AST;NET_4_0 + obj\ + + + PdbOnly + True + ..\bin\Release\ + + + full + True + True + ..\bin\Debug\ + + + False + False + DEBUG;TRACE;FULL_AST;NET_4_0;NET_4_5 + v4.5 + + + full + True + True + v4.5 + ..\bin\net_4_5_Debug\ + + + True + False + TRACE;FULL_AST;NET_4_0;NET_4_5 + v4.5 + + + none + True + v4.5 + ..\bin\net_4_5_Release\ + + + + + + + + + + Properties\GlobalAssemblyInfo.cs + + + + + + + + + + + + + + + + + + + + + + ArrayInitializerExpression.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {3B2A5653-EC97-4001-BB9B-D90F1AF2C371} + ICSharpCode.NRefactory + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/CSharpIndentEngine.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/CSharpIndentEngine.cs new file mode 100644 index 000000000..478bc45c6 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/CSharpIndentEngine.cs @@ -0,0 +1,557 @@ +// +// CSharpIndentEngine.cs +// +// Author: +// Matej Miklečić +// +// Copyright (c) 2013 Matej Miklečić (matej.miklecic@gmail.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 ICSharpCode.NRefactory.Editor; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Indentation engine based on a state machine. + /// Supports only pushing new chars to the end. + /// + /// + /// Represents the context for transitions between . + /// Delegates the responsibility for pushing a new char to the current + /// state and changes between states depending on the pushed chars. + /// + public class CSharpIndentEngine : IStateMachineIndentEngine + { + #region Properties + + /// + /// Formatting options. + /// + internal readonly CSharpFormattingOptions formattingOptions; + + /// + /// Text editor options. + /// + internal readonly TextEditorOptions textEditorOptions; + + /// + /// A readonly reference to the document that's parsed + /// by the engine. + /// + internal readonly IDocument document; + + /// + /// Represents the new line character. + /// + internal readonly char newLineChar; + + /// + /// The current indentation state. + /// + internal IndentState currentState; + + /// + /// Stores conditional symbols of #define directives. + /// + internal HashSet conditionalSymbols; + + /// + /// Stores custom conditional symbols. + /// + internal HashSet customConditionalSymbols; + + /// + /// Stores the results of evaluations of the preprocessor if/elif directives + /// in the current block (between #if and #endif). + /// + internal CloneableStack ifDirectiveEvalResults = new CloneableStack (); + + /// + /// Stores the indentation levels of the if directives in the current block. + /// + internal CloneableStack ifDirectiveIndents = new CloneableStack(); + + /// + /// Stores the last sequence of characters that can form a + /// valid keyword or variable name. + /// + internal StringBuilder wordToken; + + /// + /// Stores the previous sequence of chars that formed a + /// valid keyword or variable name. + /// + internal string previousKeyword; + + #endregion + + #region IDocumentIndentEngine + + /// + public IDocument Document + { + get { return document; } + } + + /// + public string ThisLineIndent + { + get + { + // OPTION: IndentBlankLines + // remove the indentation of this line if isLineStart is true +// if (!textEditorOptions.IndentBlankLines && isLineStart) +// { +// return string.Empty; +// } + + return currentState.ThisLineIndent.IndentString; + } + } + + /// + public string NextLineIndent + { + get + { + return currentState.NextLineIndent.IndentString; + } + } + + /// + public string CurrentIndent + { + get + { + return currentIndent.ToString(); + } + } + + /// + /// + /// This is set depending on the current and + /// can change its value until the char is + /// pushed. If this is true, that doesn't necessarily mean that the + /// current line has an incorrect indent (this can be determined + /// only at the end of the current line). + /// + public bool NeedsReindent + { + get + { + // return true if it's the first column of the line and it has an indent + if (Location.Column == 1) + { + return ThisLineIndent.Length > 0; + } + + // ignore incorrect indentations when there's only ws on this line + if (isLineStart) + { + return false; + } + + return ThisLineIndent != CurrentIndent.ToString(); + } + } + + /// + public int Offset + { + get + { + return offset; + } + } + + /// + public TextLocation Location + { + get + { + return new TextLocation(line, column); + } + } + + /// + public bool EnableCustomIndentLevels + { + get; + set; + } + + #endregion + + #region Fields + + /// + /// Represents the number of pushed chars. + /// + internal int offset = 0; + + /// + /// The current line number. + /// + internal int line = 1; + + /// + /// The current column number. + /// + /// + /// One char can take up multiple columns (e.g. \t). + /// + internal int column = 1; + + /// + /// True if is true for all + /// chars at the current line. + /// + internal bool isLineStart = true; + + /// + /// True if was true before the current + /// . + /// + internal bool isLineStartBeforeWordToken = true; + + /// + /// Current char that's being pushed. + /// + internal char currentChar = '\0'; + + /// + /// Last non-whitespace char that has been pushed. + /// + internal char previousChar = '\0'; + + /// + /// Previous new line char + /// + internal char previousNewline = '\0'; + + /// + /// Current indent level on this line. + /// + internal StringBuilder currentIndent = new StringBuilder(); + + /// + /// True if this line began in . + /// + internal bool lineBeganInsideVerbatimString = false; + + /// + /// True if this line began in . + /// + internal bool lineBeganInsideMultiLineComment = false; + + #endregion + + #region Constructors + + /// + /// Creates a new CSharpIndentEngine instance. + /// + /// + /// An instance of which is being parsed. + /// + /// + /// C# formatting options. + /// + /// + /// Text editor options for indentation. + /// + public CSharpIndentEngine(IDocument document, TextEditorOptions textEditorOptions, CSharpFormattingOptions formattingOptions) + { + this.formattingOptions = formattingOptions; + this.textEditorOptions = textEditorOptions; + this.document = document; + + this.currentState = new GlobalBodyState(this); + + this.conditionalSymbols = new HashSet(); + this.customConditionalSymbols = new HashSet(); + this.wordToken = new StringBuilder(); + this.previousKeyword = string.Empty; + this.newLineChar = textEditorOptions.EolMarker[0]; + } + + /// + /// Creates a new CSharpIndentEngine instance from the given prototype. + /// + /// + /// An CSharpIndentEngine instance. + /// + public CSharpIndentEngine(CSharpIndentEngine prototype) + { + this.formattingOptions = prototype.formattingOptions; + this.textEditorOptions = prototype.textEditorOptions; + this.document = prototype.document; + + this.newLineChar = prototype.newLineChar; + this.currentState = prototype.currentState.Clone(this); + this.conditionalSymbols = new HashSet(prototype.conditionalSymbols); + this.customConditionalSymbols = new HashSet(prototype.customConditionalSymbols); + + this.wordToken = new StringBuilder(prototype.wordToken.ToString()); + this.previousKeyword = string.Copy(prototype.previousKeyword); + + this.offset = prototype.offset; + this.line = prototype.line; + this.column = prototype.column; + this.isLineStart = prototype.isLineStart; + this.isLineStartBeforeWordToken = prototype.isLineStartBeforeWordToken; + this.currentChar = prototype.currentChar; + this.previousChar = prototype.previousChar; + this.previousNewline = prototype.previousNewline; + this.currentIndent = new StringBuilder(prototype.CurrentIndent.ToString()); + this.lineBeganInsideMultiLineComment = prototype.lineBeganInsideMultiLineComment; + this.lineBeganInsideVerbatimString = prototype.lineBeganInsideVerbatimString; + this.ifDirectiveEvalResults = prototype.ifDirectiveEvalResults.Clone(); + this.ifDirectiveIndents = prototype.ifDirectiveIndents.Clone(); + + this.EnableCustomIndentLevels = prototype.EnableCustomIndentLevels; + } + + #endregion + + #region IClonable + + object ICloneable.Clone() + { + return Clone(); + } + + /// + IDocumentIndentEngine IDocumentIndentEngine.Clone() + { + return Clone(); + } + + public IStateMachineIndentEngine Clone() + { + return new CSharpIndentEngine(this); + } + + #endregion + + #region Methods + + /// + public void Push(char ch) + { + // append this char to the wordbuf if it can form a valid keyword, otherwise check + // if the last sequence of chars form a valid keyword and reset the wordbuf. + if ((wordToken.Length == 0 ? char.IsLetter(ch) : char.IsLetterOrDigit(ch)) || ch == '_') + { + wordToken.Append(ch); + } + else if (wordToken.Length > 0) + { + currentState.CheckKeyword(wordToken.ToString()); + previousKeyword = wordToken.ToString(); + wordToken.Length = 0; + isLineStartBeforeWordToken = false; + } + + var isNewLine = NewLine.IsNewLine(ch); + if (!isNewLine) { + currentState.Push(currentChar = ch); + offset++; + previousNewline = '\0'; + // ignore whitespace and newline chars + var isWhitespace = currentChar == ' ' || currentChar == '\t'; + if (!isWhitespace) + { + previousChar = currentChar; + isLineStart = false; + } + + if (isLineStart) + { + currentIndent.Append(ch); + } + + if (ch == '\t') + { + var nextTabStop = (column - 1 + textEditorOptions.IndentSize) / textEditorOptions.IndentSize; + column = 1 + nextTabStop * textEditorOptions.IndentSize; + } + else + { + column++; + } + } else { + if (ch == NewLine.LF && previousNewline == NewLine.CR) { + offset++; + return; + } + currentState.Push(currentChar = newLineChar); + offset++; + + previousNewline = ch; + // there can be more than one chars that determine the EOL, + // the engine uses only one of them defined with newLineChar + if (currentChar != newLineChar) + { + return; + } + currentIndent.Length = 0; + isLineStart = true; + isLineStartBeforeWordToken = true; + column = 1; + line++; + + lineBeganInsideMultiLineComment = IsInsideMultiLineComment; + lineBeganInsideVerbatimString = IsInsideVerbatimString; + } + } + + /// + public void Reset() + { + currentState = new GlobalBodyState(this); + conditionalSymbols.Clear(); + ifDirectiveEvalResults.Clear(); + ifDirectiveIndents.Clear(); + + offset = 0; + line = 1; + column = 1; + isLineStart = true; + currentChar = '\0'; + previousChar = '\0'; + currentIndent.Length = 0; + lineBeganInsideMultiLineComment = false; + lineBeganInsideVerbatimString = false; + } + + /// + public void Update(int offset) + { + if (Offset > offset) + { + Reset(); + } + + while (Offset < offset) + { + Push(Document.GetCharAt(Offset)); + } + } + + /// + /// Defines the conditional symbol. + /// + /// The symbol to define. + public void DefineSymbol(string defineSymbol) + { + if (!customConditionalSymbols.Contains(defineSymbol)) + customConditionalSymbols.Add(defineSymbol); + } + + /// + /// Removes the symbol. + /// + /// The symbol to undefine. + public void RemoveSymbol(string undefineSymbol) + { + if (customConditionalSymbols.Contains(undefineSymbol)) + customConditionalSymbols.Remove(undefineSymbol); + } + #endregion + + #region IStateMachineIndentEngine + + public bool IsInsidePreprocessorDirective + { + get { return currentState is PreProcessorState; } + } + + public bool IsInsidePreprocessorComment + { + get { return currentState is PreProcessorCommentState; } + } + + public bool IsInsideStringLiteral + { + get { return currentState is StringLiteralState; } + } + + public bool IsInsideVerbatimString + { + get { return currentState is VerbatimStringState; } + } + + public bool IsInsideCharacter + { + get { return currentState is CharacterState; } + } + + public bool IsInsideString + { + get { return IsInsideStringLiteral || IsInsideVerbatimString || IsInsideCharacter; } + } + + public bool IsInsideLineComment + { + get { return currentState is LineCommentState; } + } + + public bool IsInsideMultiLineComment + { + get { return currentState is MultiLineCommentState; } + } + + public bool IsInsideDocLineComment + { + get { return currentState is DocCommentState; } + } + + public bool IsInsideComment + { + get { return IsInsideLineComment || IsInsideMultiLineComment || IsInsideDocLineComment; } + } + + public bool IsInsideOrdinaryComment + { + get { return IsInsideLineComment || IsInsideMultiLineComment; } + } + + public bool IsInsideOrdinaryCommentOrString + { + get { return IsInsideOrdinaryComment || IsInsideString; } + } + + public bool LineBeganInsideVerbatimString + { + get { return lineBeganInsideVerbatimString; } + } + + public bool LineBeganInsideMultiLineComment + { + get { return lineBeganInsideMultiLineComment; } + } + + #endregion + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/CacheIndentEngine.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/CacheIndentEngine.cs new file mode 100644 index 000000000..b19f38868 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/CacheIndentEngine.cs @@ -0,0 +1,627 @@ +// +// CacheIndentEngine.cs +// +// Author: +// Matej Miklečić +// +// Copyright (c) 2013 Matej Miklečić (matej.miklecic@gmail.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 ICSharpCode.NRefactory.Editor; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Represents a decorator of an IStateMachineIndentEngine instance that provides + /// logic for reseting and updating the engine on text changed events. + /// + /// + /// The decorator is based on periodical caching of the engine's state and + /// delegating all logic behind indentation to the currently active engine. + /// + public class CacheIndentEngine : IStateMachineIndentEngine + { + + #region Properties + + IStateMachineIndentEngine currentEngine; + Stack cachedEngines = new Stack(); + + #endregion + + #region Constructors + + /// + /// Creates a new CacheIndentEngine instance. + /// + /// + /// An instance of to which the + /// logic for indentation will be delegated. + /// + /// + /// The number of chars between caching. + /// + public CacheIndentEngine(IStateMachineIndentEngine decoratedEngine, int cacheRate = 2000) + { + this.currentEngine = decoratedEngine; + } + + /// + /// Creates a new CacheIndentEngine instance from the given prototype. + /// + /// + /// A CacheIndentEngine instance. + /// + public CacheIndentEngine(CacheIndentEngine prototype) + { + this.currentEngine = prototype.currentEngine.Clone(); + } + + #endregion + + #region IDocumentIndentEngine + + /// + public IDocument Document { + get { return currentEngine.Document; } + } + + /// + public string ThisLineIndent { + get { return currentEngine.ThisLineIndent; } + } + + /// + public string NextLineIndent { + get { return currentEngine.NextLineIndent; } + } + + /// + public string CurrentIndent { + get { return currentEngine.CurrentIndent; } + } + + /// + public bool NeedsReindent { + get { return currentEngine.NeedsReindent; } + } + + /// + public int Offset { + get { return currentEngine.Offset; } + } + + /// + public TextLocation Location { + get { return currentEngine.Location; } + } + + /// + public bool EnableCustomIndentLevels + { + get { return currentEngine.EnableCustomIndentLevels; } + set { currentEngine.EnableCustomIndentLevels = value; } + } + + /// + public void Push(char ch) + { + currentEngine.Push(ch); + } + + /// + public void Reset() + { + currentEngine.Reset(); + cachedEngines.Clear(); + } + + /// + /// Resets the engine to offset. Clears all cached engines after the given offset. + /// + public void ResetEngineToPosition(int offset) + { + // We are already there + if (currentEngine.Offset <= offset) + return; + + bool gotCachedEngine = false; + while (cachedEngines.Count > 0) { + var topEngine = cachedEngines.Peek(); + if (topEngine.Offset <= offset) { + currentEngine = topEngine.Clone(); + gotCachedEngine = true; + break; + } else { + cachedEngines.Pop(); + } + } + if (!gotCachedEngine) + currentEngine.Reset(); + } + + /// + /// + /// If the is negative, the engine will + /// update to: document.TextLength + (offset % document.TextLength+1) + /// Otherwise it will update to: offset % document.TextLength+1 + /// + public void Update(int position) + { + const int BUFFER_SIZE = 2000; + + if (currentEngine.Offset == position) { + //positions match, nothing to be done + return; + } else if (currentEngine.Offset > position) { + //moving backwards, so reset from previous saved location + ResetEngineToPosition(position); + } + + // get the engine caught up + int nextSave = (cachedEngines.Count == 0) ? BUFFER_SIZE : cachedEngines.Peek().Offset + BUFFER_SIZE; + if (currentEngine.Offset + 1 == position) { + char ch = currentEngine.Document.GetCharAt(currentEngine.Offset); + currentEngine.Push(ch); + if (currentEngine.Offset == nextSave) + cachedEngines.Push(currentEngine.Clone()); + } else { + //bulk copy characters in case buffer is unmanaged + //(faster if we reduce managed/unmanaged transitions) + while (currentEngine.Offset < position) { + int endCut = currentEngine.Offset + BUFFER_SIZE; + if (endCut > position) + endCut = position; + string buffer = currentEngine.Document.GetText(currentEngine.Offset, endCut - currentEngine.Offset); + foreach (char ch in buffer) { + currentEngine.Push(ch); + //ConsoleWrite ("pushing character '{0}'", ch); + if (currentEngine.Offset == nextSave) { + cachedEngines.Push(currentEngine.Clone()); + nextSave += BUFFER_SIZE; + } + } + } + } + } + + public IStateMachineIndentEngine GetEngine(int offset) + { + ResetEngineToPosition(offset); + return currentEngine; + } + + #endregion + + #region IClonable + + /// + public IStateMachineIndentEngine Clone() + { + return new CacheIndentEngine(this); + } + + /// + IDocumentIndentEngine IDocumentIndentEngine.Clone() + { + return Clone(); + } + + object ICloneable.Clone() + { + return Clone(); + } + + #endregion + + #region IStateMachineIndentEngine + + public bool IsInsidePreprocessorDirective { + get { return currentEngine.IsInsidePreprocessorDirective; } + } + + public bool IsInsidePreprocessorComment { + get { return currentEngine.IsInsidePreprocessorComment; } + } + + public bool IsInsideStringLiteral { + get { return currentEngine.IsInsideStringLiteral; } + } + + public bool IsInsideVerbatimString { + get { return currentEngine.IsInsideVerbatimString; } + } + + public bool IsInsideCharacter { + get { return currentEngine.IsInsideCharacter; } + } + + public bool IsInsideString { + get { return currentEngine.IsInsideString; } + } + + public bool IsInsideLineComment { + get { return currentEngine.IsInsideLineComment; } + } + + public bool IsInsideMultiLineComment { + get { return currentEngine.IsInsideMultiLineComment; } + } + + public bool IsInsideDocLineComment { + get { return currentEngine.IsInsideDocLineComment; } + } + + public bool IsInsideComment { + get { return currentEngine.IsInsideComment; } + } + + public bool IsInsideOrdinaryComment { + get { return currentEngine.IsInsideOrdinaryComment; } + } + + public bool IsInsideOrdinaryCommentOrString { + get { return currentEngine.IsInsideOrdinaryCommentOrString; } + } + + public bool LineBeganInsideVerbatimString { + get { return currentEngine.LineBeganInsideVerbatimString; } + } + + public bool LineBeganInsideMultiLineComment { + get { return currentEngine.LineBeganInsideMultiLineComment; } + } + + #endregion + + } + /* +/ // + /// Represents a decorator of an IStateMachineIndentEngine instance that provides + /// logic for reseting and updating the engine on text changed events. + /// + /// + /// The decorator is based on periodical caching of the engine's state and + /// delegating all logic behind indentation to the currently active engine. + /// + public class CacheIndentEngine : IStateMachineIndentEngine + { + #region Properties + + /// + /// Represents the cache interval in number of chars pushed to the engine. + /// + /// + /// When this many new chars are pushed to the engine, the currently active + /// engine gets cloned and added to the end of . + /// + readonly int cacheRate; + + /// + /// Determines how much memory to reserve on initialization for the + /// cached engines. + /// + const int cacheCapacity = 25; + + /// + /// Currently active engine. + /// + /// + /// Should be equal to the last engine in . + /// + IStateMachineIndentEngine currentEngine; + + /// + /// List of cached engines sorted ascending by + /// . + /// + IStateMachineIndentEngine[] cachedEngines; + + /// + /// The index of the last cached engine in cachedEngines. + /// + /// + /// Should be equal to: currentEngine.Offset / CacheRate + /// + int lastCachedEngine; + + #endregion + + #region Constructors + + /// + /// Creates a new CacheIndentEngine instance. + /// + /// + /// An instance of to which the + /// logic for indentation will be delegated. + /// + /// + /// The number of chars between caching. + /// + public CacheIndentEngine(IStateMachineIndentEngine decoratedEngine, int cacheRate = 2000) + { + this.cachedEngines = new IStateMachineIndentEngine[cacheCapacity]; + + this.cachedEngines[0] = decoratedEngine.Clone(); + this.currentEngine = this.cachedEngines[0].Clone(); + this.cacheRate = cacheRate; + } + + /// + /// Creates a new CacheIndentEngine instance from the given prototype. + /// + /// + /// A CacheIndentEngine instance. + /// + public CacheIndentEngine(CacheIndentEngine prototype) + { + this.cachedEngines = new IStateMachineIndentEngine[prototype.cachedEngines.Length]; + Array.Copy(prototype.cachedEngines, this.cachedEngines, prototype.cachedEngines.Length); + + this.lastCachedEngine = prototype.lastCachedEngine; + this.currentEngine = prototype.currentEngine.Clone(); + this.cacheRate = prototype.cacheRate; + } + + #endregion + + #region Methods + + /// + /// Performs caching of the . + /// + void cache() + { + if (currentEngine.Offset % cacheRate != 0) + { + throw new Exception("The current engine's offset is not divisable with the cacheRate."); + } + + // determine the new current engine from cachedEngines + lastCachedEngine = currentEngine.Offset / cacheRate; + + if (cachedEngines.Length < lastCachedEngine + 1) + { + Array.Resize(ref cachedEngines, lastCachedEngine * 2); + } + + cachedEngines[lastCachedEngine] = currentEngine.Clone(); + } + + #endregion + + #region IDocumentIndentEngine + + /// + public IDocument Document + { + get { return currentEngine.Document; } + } + + /// + public string ThisLineIndent + { + get { return currentEngine.ThisLineIndent; } + } + + /// + public string NextLineIndent + { + get { return currentEngine.NextLineIndent; } + } + + /// + public string CurrentIndent + { + get { return currentEngine.CurrentIndent; } + } + + /// + public bool NeedsReindent + { + get { return currentEngine.NeedsReindent; } + } + + /// + public int Offset + { + get { return currentEngine.Offset; } + } + + /// + public TextLocation Location + { + get { return currentEngine.Location; } + } + + /// + public void Push(char ch) + { + currentEngine.Push(ch); + + if (currentEngine.Offset % cacheRate == 0) + { + cache(); + } + } + + /// + public void Reset() + { + currentEngine = cachedEngines[lastCachedEngine = 0]; + } + + /// + /// + /// If the is negative, the engine will + /// update to: document.TextLength + (offset % document.TextLength+1) + /// Otherwise it will update to: offset % document.TextLength+1 + /// + public void Update(int offset) + { + // map the given offset to the [0, document.TextLength] interval + // using modulo arithmetics + offset %= Document.TextLength + 1; + if (offset < 0) + { + offset += Document.TextLength + 1; + } + + // check if the engine has to be updated to some previous offset + if (currentEngine.Offset > offset) + { + // replace the currentEngine with the first one whose offset + // is less then the given + lastCachedEngine = offset / cacheRate; + currentEngine = cachedEngines[lastCachedEngine].Clone(); + } + + // update the engine to the given offset + while (Offset < offset) + { + Push(Document.GetCharAt(Offset)); + } + } + + public IStateMachineIndentEngine GetEngine(int offset) + { + // map the given offset to the [0, document.TextLength] interval + // using modulo arithmetics + offset %= Document.TextLength + 1; + if (offset < 0) + { + offset += Document.TextLength + 1; + } + + // check if the engine has to be updated to some previous offset + if (currentEngine.Offset > offset) + { + // replace the currentEngine with the first one whose offset + // is less then the given + lastCachedEngine = offset / cacheRate; + return cachedEngines[lastCachedEngine].Clone(); + } + + return currentEngine; + } + + #endregion + + #region IClonable + + /// + public IStateMachineIndentEngine Clone() + { + return new CacheIndentEngine(this); + } + + /// + IDocumentIndentEngine IDocumentIndentEngine.Clone() + { + return Clone(); + } + + object ICloneable.Clone() + { + return Clone(); + } + + #endregion + + #region IStateMachineIndentEngine + + public bool IsInsidePreprocessorDirective + { + get { return currentEngine.IsInsidePreprocessorDirective; } + } + + public bool IsInsidePreprocessorComment + { + get { return currentEngine.IsInsidePreprocessorComment; } + } + + public bool IsInsideStringLiteral + { + get { return currentEngine.IsInsideStringLiteral; } + } + + public bool IsInsideVerbatimString + { + get { return currentEngine.IsInsideVerbatimString; } + } + + public bool IsInsideCharacter + { + get { return currentEngine.IsInsideCharacter; } + } + + public bool IsInsideString + { + get { return currentEngine.IsInsideString; } + } + + public bool IsInsideLineComment + { + get { return currentEngine.IsInsideLineComment; } + } + + public bool IsInsideMultiLineComment + { + get { return currentEngine.IsInsideMultiLineComment; } + } + + public bool IsInsideDocLineComment + { + get { return currentEngine.IsInsideDocLineComment; } + } + + public bool IsInsideComment + { + get { return currentEngine.IsInsideComment; } + } + + public bool IsInsideOrdinaryComment + { + get { return currentEngine.IsInsideOrdinaryComment; } + } + + public bool IsInsideOrdinaryCommentOrString + { + get { return currentEngine.IsInsideOrdinaryCommentOrString; } + } + + public bool LineBeganInsideVerbatimString + { + get { return currentEngine.LineBeganInsideVerbatimString; } + } + + public bool LineBeganInsideMultiLineComment + { + get { return currentEngine.LineBeganInsideMultiLineComment; } + } + + #endregion + } + + */ +} \ No newline at end of file diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/IDocumentIndentEngine.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/IDocumentIndentEngine.cs new file mode 100644 index 000000000..821aa0bc8 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/IDocumentIndentEngine.cs @@ -0,0 +1,108 @@ +// +// IDocumentIndentEngine.cs +// +// Author: +// Matej Miklečić +// +// Copyright (c) 2013 Matej Miklečić (matej.miklecic@gmail.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 ICSharpCode.NRefactory.Editor; +using System; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// The base interface for all indent engines. + /// + public interface IDocumentIndentEngine : ICloneable + { + /// + /// A reference to the document that's parsed by the engine. + /// + IDocument Document { get; } + + /// + /// The indentation string of the current line. + /// + string ThisLineIndent { get; } + + /// + /// The indentation string of the next line. + /// + string NextLineIndent { get; } + + /// + /// The indent string on the beginning of the current line. + /// + string CurrentIndent { get; } + + /// + /// True if the current line needs to be reindented. + /// + bool NeedsReindent { get; } + + /// + /// The current offset of the engine. + /// + int Offset { get; } + + /// + /// The current location of the engine. + /// + TextLocation Location { get; } + + /// + /// If this is true, the engine should try to adjust its indent + /// levels to manual user's corrections, even if they are wrong. + /// + bool EnableCustomIndentLevels { get; set; } + + /// + /// Pushes a new char into the engine which calculates the new + /// indentation levels. + /// + /// + /// A new character. + /// + void Push(char ch); + + /// + /// Resets the engine. + /// + void Reset(); + + /// + /// Updates the engine to the given offset. + /// + /// + /// Valid offset in . + /// + void Update(int offset); + + /// + /// Clones the engine and preserves the current state. + /// + /// + /// An indentical clone which can operate without interference + /// with this engine. + /// + new IDocumentIndentEngine Clone(); + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/IStateMachineIndentEngine.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/IStateMachineIndentEngine.cs new file mode 100644 index 000000000..44c3949cf --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/IStateMachineIndentEngine.cs @@ -0,0 +1,60 @@ +// +// IStateMachineIndentEngine.cs +// +// Author: +// Matej Miklečić +// +// Copyright (c) 2013 Matej Miklečić (matej.miklecic@gmail.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. +namespace ICSharpCode.NRefactory.CSharp +{ + public interface IStateMachineIndentEngine : IDocumentIndentEngine + { + bool IsInsidePreprocessorDirective { get; } + + bool IsInsidePreprocessorComment { get; } + + bool IsInsideStringLiteral { get; } + + bool IsInsideVerbatimString { get; } + + bool IsInsideCharacter { get; } + + bool IsInsideString { get; } + + bool IsInsideLineComment { get; } + + bool IsInsideMultiLineComment { get; } + + bool IsInsideDocLineComment { get; } + + bool IsInsideComment { get; } + + bool IsInsideOrdinaryComment { get; } + + bool IsInsideOrdinaryCommentOrString { get; } + + bool LineBeganInsideVerbatimString { get; } + + bool LineBeganInsideMultiLineComment { get; } + + new IStateMachineIndentEngine Clone(); + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/IndentState.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/IndentState.cs new file mode 100644 index 000000000..8867268e6 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/IndentState.cs @@ -0,0 +1,2017 @@ +// +// IndentState.cs +// +// Author: +// Matej Miklečić +// +// Copyright (c) 2013 Matej Miklečić (matej.miklecic@gmail.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 System.Globalization; +using System.Linq; +using System.Text; + +namespace ICSharpCode.NRefactory.CSharp +{ + #region IndentState + + /// + /// The base class for all indentation states. + /// Each state defines the logic for indentation based on chars that + /// are pushed to it. + /// + public abstract class IndentState : ICloneable + { + #region Properties + + /// + /// The indentation engine using this state. + /// + public CSharpIndentEngine Engine; + + /// + /// The parent state. + /// This state can use the indentation levels of its parent. + /// When this state exits, the engine returns to the parent. + /// + public IndentState Parent; + + /// + /// The indentation of the current line. + /// This is set when the state is created and will be changed to + /// when the + /// is pushed. + /// + public Indent ThisLineIndent; + + /// + /// The indentation of the next line. + /// This is set when the state is created and can change depending + /// on the pushed chars. + /// + public Indent NextLineIndent; + + #endregion + + #region Constructors + + protected IndentState() + { + } + + /// + /// Creates a new indentation state that is a copy of the given + /// prototype. + /// + /// + /// The prototype state. + /// + /// + /// The engine of the new state. + /// + protected IndentState(IndentState prototype, CSharpIndentEngine engine) + { + Engine = engine; + Parent = prototype.Parent != null ? prototype.Parent.Clone(engine) : null; + + ThisLineIndent = prototype.ThisLineIndent.Clone(); + NextLineIndent = prototype.NextLineIndent.Clone(); + } + + #endregion + + #region IClonable + + object ICloneable.Clone() + { + return Clone(Engine); + } + + public abstract IndentState Clone(CSharpIndentEngine engine); + + #endregion + + #region Methods + + internal void Initialize (CSharpIndentEngine engine, IndentState parent = null) + { + Parent = parent; + Engine = engine; + + InitializeState(); + } + + /// + /// Initializes the state: + /// - sets the default indentation levels. + /// + /// + /// Each state can override this method if it needs a different + /// logic for setting up the default indentations. + /// + public virtual void InitializeState() + { + ThisLineIndent = new Indent(Engine.textEditorOptions); + NextLineIndent = ThisLineIndent.Clone(); + } + + /// + /// Actions performed when this state exits. + /// + public virtual void OnExit() + { + if (Parent != null) + { + // if a state exits on the newline character, it has to push + // it back to its parent (and so on recursively if the parent + // state also exits). Otherwise, the parent state wouldn't + // know that the engine isn't on the same line anymore. + if (Engine.currentChar == Engine.newLineChar) + { + Parent.Push(Engine.newLineChar); + } + + // when a state exits the engine stays on the same line and this + // state has to override the Parent.ThisLineIndent. + Parent.ThisLineIndent = ThisLineIndent.Clone(); + } + } + + /// + /// Changes the current state of the using the current + /// state as the parent for the new one. + /// + /// + /// The type of the new state. Must be assignable from . + /// + public void ChangeState() + where T : IndentState, new () + { + var t = new T(); + t.Initialize(Engine, Engine.currentState); + Engine.currentState = t; + } + + /// + /// Exits this state by setting the current state of the + /// to this state's parent. + /// + public void ExitState() + { + OnExit(); + Engine.currentState = Engine.currentState.Parent ?? new GlobalBodyState(Engine); + } + + /// + /// Common logic behind the push method. + /// Each state can override this method and implement its own logic. + /// + /// + /// The current character that's being pushed. + /// + public virtual void Push(char ch) + { + // replace ThisLineIndent with NextLineIndent if the newLineChar is pushed + if (ch == Engine.newLineChar) + { + var delta = Engine.textEditorOptions.ContinuationIndent; + while (NextLineIndent.CurIndent - ThisLineIndent.CurIndent > delta && + NextLineIndent.PopIf(IndentType.Continuation)) ; + ThisLineIndent = NextLineIndent.Clone(); + } + } + + /// + /// When derived, checks if the given sequence of chars form + /// a valid keyword or variable name, depending on the state. + /// + /// + /// A possible keyword. + /// + public virtual void CheckKeyword(string keyword) + { } + + /// + /// When derived, checks if the given sequence of chars form + /// a valid keyword or variable name, depending on the state. + /// + /// + /// A possible keyword. + /// + /// + /// This method should be called from . + /// It is left to derived classes to call this method because of + /// performance issues. + /// + public virtual void CheckKeywordOnPush(string keyword) + { } + + #endregion + } + + #endregion + + #region Null state + + /// + /// Null state. + /// + /// + /// Doesn't define any transitions to new states. + /// + public class NullState : IndentState + { + public NullState() + { } + + public NullState(NullState prototype, CSharpIndentEngine engine) + : base(prototype, engine) + { } + + public override void Push(char ch) + { } + + public override IndentState Clone(CSharpIndentEngine engine) + { + return new NullState(this, engine); + } + } + + #endregion + + #region Brackets body states + + #region Brackets body base + + /// + /// The base for all brackets body states. + /// + /// + /// Represents a block of code between a pair of brackets. + /// + public abstract class BracketsBodyBaseState : IndentState + { + + /// + /// When derived in a concrete bracket body state, represents + /// the closed bracket character pair. + /// + public abstract char ClosedBracket { get; } + + protected BracketsBodyBaseState() + { } + + protected BracketsBodyBaseState(BracketsBodyBaseState prototype, CSharpIndentEngine engine) + : base(prototype, engine) + { } + + public override void Push(char ch) + { + base.Push(ch); + switch (ch) { + case '#': + if (Engine.isLineStart) + ChangeState(); + break; + case '/': + if (Engine.previousChar == '/') + ChangeState(); + break; + case '*': + if (Engine.previousChar == '/') + ChangeState(); + break; + case '"': + if (Engine.previousChar == '@') + { + ChangeState(); + } + else + { + ChangeState(); + } + break; + case '\'': + ChangeState(); + break; + case '{': + ChangeState(); + break; + case '(': + ChangeState(); + break; + case '[': + ChangeState(); + break; + default: + if (ch == ClosedBracket) + ExitState(); + break; + } + } + } + + #endregion + + #region Braces body state + + /// + /// Braces body state. + /// + /// + /// Represents a block of code between { and }. + /// + public class BracesBodyState : BracketsBodyBaseState + { + /// + /// Type of the current block body. + /// + public Body CurrentBody; + + /// + /// Type of the next block body. + /// Same as if none of the + /// keywords have been read. + /// + public Body NextBody; + + /// + /// Type of the current statement. + /// + public Statement CurrentStatement + { + get + { + return currentStatement; + } + set + { + // clear NestedIfStatementLevels if this statement breaks the sequence + if (currentStatement == Statement.None && value != Statement.Else) + { + NestedIfStatementLevels.Clear(); + } + + currentStatement = value; + } + } + Statement currentStatement; + + /// + /// Contains indent levels of nested if statements. + /// + internal CloneableStack NestedIfStatementLevels = new CloneableStack(); + + /// + /// Contains the indent level of the last statement or body keyword. + /// + public Indent LastBlockIndent; + + /// + /// True if the engine is on the right side of the equal operator '='. + /// + public bool IsRightHandExpression; + + /// + /// True if the '=' char has been pushed and it's not + /// a part of a relational operator (>=, <=, !=, ==). + /// + public bool IsEqualCharPushed; + + /// + /// The indentation of the previous line. + /// + public int PreviousLineIndent; + + /// + /// True if the dot member (e.g. method invocation) indentation has + /// been handled in the current statement. + /// + public bool IsMemberReferenceDotHandled; + + public override char ClosedBracket + { + get { return '}'; } + } + + public BracesBodyState() + { + } + + public BracesBodyState(BracesBodyState prototype, CSharpIndentEngine engine) + : base(prototype, engine) + { + CurrentBody = prototype.CurrentBody; + NextBody = prototype.NextBody; + CurrentStatement = prototype.CurrentStatement; + NestedIfStatementLevels = prototype.NestedIfStatementLevels.Clone(); + IsRightHandExpression = prototype.IsRightHandExpression; + IsEqualCharPushed = prototype.IsEqualCharPushed; + IsMemberReferenceDotHandled = prototype.IsMemberReferenceDotHandled; + LastBlockIndent = prototype.LastBlockIndent; + PreviousLineIndent = prototype.PreviousLineIndent; + } + + public override void Push(char ch) + { + // handle IsRightHandExpression property + if (IsEqualCharPushed) + { + if (IsRightHandExpression) + { + if (ch == Engine.newLineChar) + { + NextLineIndent.RemoveAlignment(); + NextLineIndent.Push(IndentType.Continuation); + } + } + // ignore "==" and "=>" operators + else if (ch != '=' && ch != '>') + { + IsRightHandExpression = true; + + if (ch == Engine.newLineChar) + { + NextLineIndent.Push(IndentType.Continuation); + } + else + { + NextLineIndent.SetAlignment(Engine.column - NextLineIndent.CurIndent); + } + } + + IsEqualCharPushed = ch == ' ' || ch == '\t'; + } + + if (ch == ';' || (ch == ',' && IsRightHandExpression)) + { + OnStatementExit(); + } + else if (ch == '=' && !(Engine.previousChar == '=' || Engine.previousChar == '<' || Engine.previousChar == '>' || Engine.previousChar == '!')) + { + IsEqualCharPushed = true; + } + else if (ch == '.' && !IsMemberReferenceDotHandled) + { + // OPTION: CSharpFormattingOptions.AlignToMemberReferenceDot + if (Engine.formattingOptions.AlignToMemberReferenceDot && !Engine.isLineStart) + { + IsMemberReferenceDotHandled = true; + NextLineIndent.RemoveAlignment(); + NextLineIndent.SetAlignment(Engine.column - NextLineIndent.CurIndent - 1, true); + } + else if (Engine.isLineStart) + { + IsMemberReferenceDotHandled = true; + + ThisLineIndent.RemoveAlignment(); + while (ThisLineIndent.CurIndent > PreviousLineIndent && + ThisLineIndent.PopIf(IndentType.Continuation)) ; + ThisLineIndent.Push(IndentType.Continuation); + NextLineIndent = ThisLineIndent.Clone(); + } + } + else if (ch == ':' && Engine.isLineStart && !IsRightHandExpression) + { + // try to capture ': base(...)', ': this(...)' and inherit statements when they are on a new line + ThisLineIndent.Push(IndentType.Continuation); + } + else if (ch == Engine.newLineChar) + { + PreviousLineIndent = ThisLineIndent.CurIndent; + } + + if (Engine.wordToken.ToString() == "else") + { + CheckKeywordOnPush("else"); + } + + base.Push(ch); + } + + public override void InitializeState() + { + ThisLineIndent = Parent.ThisLineIndent.Clone(); + NextLineIndent = Parent.NextLineIndent.Clone(); + + // OPTION: IDocumentIndentEngine.EnableCustomIndentLevels + var parent = Parent as BracesBodyState; + if (parent == null || parent.LastBlockIndent == null || !Engine.EnableCustomIndentLevels) + { + NextLineIndent.RemoveAlignment(); + NextLineIndent.PopIf(IndentType.Continuation); + } + else + { + NextLineIndent = parent.LastBlockIndent.Clone(); + } + + if (Engine.isLineStart) + { + ThisLineIndent = NextLineIndent.Clone(); + } + + CurrentBody = extractBody(Parent); + NextBody = Body.None; + CurrentStatement = Statement.None; + + AddIndentation(CurrentBody); + } + + public override IndentState Clone(CSharpIndentEngine engine) + { + return new BracesBodyState(this, engine); + } + + public override void OnExit() + { + if (Parent is BracesBodyState && !((BracesBodyState)Parent).IsRightHandExpression) + { + ((BracesBodyState)Parent).OnStatementExit(); + } + + if (Engine.isLineStart) + { + ThisLineIndent.RemoveAlignment(); + ThisLineIndent.PopTry(); + BraceStyle style; + if (TryGetBraceStyle(this.CurrentBody, out style)) { + if (style == BraceStyle.NextLineShifted || + style == BraceStyle.NextLineShifted2|| + style == BraceStyle.BannerStyle) { + ThisLineIndent.Push(IndentType.Block); + } + } + } + + base.OnExit(); + } + + /// + /// Actions performed when the current statement exits. + /// + public virtual void OnStatementExit() + { + IsRightHandExpression = false; + IsMemberReferenceDotHandled = false; + + NextLineIndent.RemoveAlignment(); + NextLineIndent.PopWhile(IndentType.Continuation); + + CurrentStatement = Statement.None; + NextBody = Body.None; + LastBlockIndent = null; + } + + #region Helpers + + /// + /// Types of braces bodies. + /// + public enum Body + { + None, + Namespace, + Class, + Struct, + Interface, + Enum, + Switch, + Case, + Try, + Catch, + Finally + } + + /// + /// Types of statements. + /// + public enum Statement + { + None, + If, + Else, + Do, + While, + For, + Foreach, + Lock, + Using, + Return + } + + static readonly Dictionary bodies = new Dictionary + { + { "namespace", Body.Namespace }, + { "class", Body.Class }, + { "struct", Body.Struct }, + { "interface", Body.Interface }, + { "enum", Body.Enum }, + { "switch", Body.Switch }, + { "try", Body.Try }, + { "catch", Body.Catch }, + { "finally", Body.Finally }, + }; + + static readonly Dictionary statements = new Dictionary + { + { "if", Statement.If }, + // { "else", Statement.Else }, // should be handled in CheckKeywordAtPush + { "do", Statement.Do }, + { "while", Statement.While }, + { "for", Statement.For }, + { "foreach", Statement.Foreach }, + { "lock", Statement.Lock }, + { "using", Statement.Using }, + { "return", Statement.Return }, + }; + + static readonly HashSet blocks = new HashSet + { + "namespace", + "class", + "struct", + "interface", + "enum", + "switch", + "try", + "catch", + "finally", + "if", + "else", + "do", + "while", + "for", + "foreach", + "lock", + "using", + }; + + readonly string[] caseDefaultKeywords = { + "case", + "default" + }; + + readonly string[] classStructKeywords = { + "class", + "struct" + }; + + /// + /// Checks if the given string is a keyword and sets the + /// and the + /// variables appropriately. + /// + /// + /// A possible keyword. + /// + /// + /// This method is called from + /// + public override void CheckKeywordOnPush(string keyword) + { + if (keyword == "else") + { + CurrentStatement = Statement.Else; + + // OPTION: CSharpFormattingOptions.AlignElseInIfStatements + if (!Engine.formattingOptions.AlignElseInIfStatements && NestedIfStatementLevels.Count > 0) + { + ThisLineIndent = NestedIfStatementLevels.Pop().Clone(); + NextLineIndent = ThisLineIndent.Clone(); + } + + NextLineIndent.Push(IndentType.Continuation); + } + + if (blocks.Contains(keyword) && Engine.NeedsReindent) + { + LastBlockIndent = Indent.ConvertFrom(Engine.CurrentIndent, ThisLineIndent, Engine.textEditorOptions); + } + } + + /// + /// Checks if the given string is a keyword and sets the + /// and the + /// variables appropriately. + /// + /// + /// A possible keyword. + /// + public override void CheckKeyword(string keyword) + { + if (bodies.ContainsKey(keyword)) + { + var isKeywordTemplateConstraint = + classStructKeywords.Contains(keyword) && + (NextBody == Body.Class || NextBody == Body.Struct || NextBody == Body.Interface); + + if (!isKeywordTemplateConstraint) + { + NextBody = bodies[keyword]; + } + } + else if (caseDefaultKeywords.Contains(keyword) && CurrentBody == Body.Switch && Engine.isLineStartBeforeWordToken) + { + ChangeState(); + } + else if (keyword == "where" && Engine.isLineStartBeforeWordToken) + { + // try to capture where (generic type constraint) + ThisLineIndent.Push(IndentType.Continuation); + } + else if (statements.ContainsKey(keyword)) + { + Statement previousStatement = CurrentStatement; + CurrentStatement = statements[keyword]; + + // return if this is a using declaration or alias + if (CurrentStatement == Statement.Using && + (this is GlobalBodyState || CurrentBody == Body.Namespace)) + { + return; + } + + // OPTION: CSharpFormattingOptions.AlignEmbeddedIfStatements + if (Engine.formattingOptions.AlignEmbeddedStatements && + previousStatement == Statement.If && + CurrentStatement == Statement.If) + { + ThisLineIndent.PopIf(IndentType.Continuation); + NextLineIndent.PopIf(IndentType.Continuation); + } + + // OPTION: CSharpFormattingOptions.AlignEmbeddedStatements + if (Engine.formattingOptions.AlignEmbeddedStatements && + previousStatement == Statement.Lock && + CurrentStatement == Statement.Lock) + { + ThisLineIndent.PopIf(IndentType.Continuation); + NextLineIndent.PopIf(IndentType.Continuation); + } + + // OPTION: CSharpFormattingOptions.AlignEmbeddedUsingStatements + if (Engine.formattingOptions.AlignEmbeddedStatements && + previousStatement == Statement.Using && + CurrentStatement == Statement.Using) + { + ThisLineIndent.PopIf(IndentType.Continuation); + NextLineIndent.PopIf(IndentType.Continuation); + } + + // only add continuation for 'else' in 'else if' statement. + if (!(CurrentStatement == Statement.If && previousStatement == Statement.Else && !Engine.isLineStartBeforeWordToken)) + { + NextLineIndent.Push(IndentType.Continuation); + } + + if (CurrentStatement == Statement.If) + { + NestedIfStatementLevels.Push(ThisLineIndent); + } + } + + if (blocks.Contains(keyword) && Engine.NeedsReindent) + { + LastBlockIndent = Indent.ConvertFrom(Engine.CurrentIndent, ThisLineIndent, Engine.textEditorOptions); + } + } + + /// + /// Pushes a new level of indentation depending on the given + /// . + /// + void AddIndentation(BraceStyle braceStyle) + { + switch (braceStyle) + { + case BraceStyle.NextLineShifted: + ThisLineIndent.Push(IndentType.Block); + NextLineIndent.Push(IndentType.Block); + break; + case BraceStyle.DoNotChange: + case BraceStyle.EndOfLine: + case BraceStyle.EndOfLineWithoutSpace: + case BraceStyle.NextLine: + case BraceStyle.BannerStyle: + NextLineIndent.Push(IndentType.Block); + break; + case BraceStyle.NextLineShifted2: + ThisLineIndent.Push(IndentType.Block); + NextLineIndent.Push(IndentType.DoubleBlock); + break; + } + } + + bool TryGetBraceStyle (Body body, out BraceStyle style) + { + style = BraceStyle.DoNotChange; + switch (body) + { + case Body.None: + if (!Engine.formattingOptions.IndentBlocks) + return false; + style = Engine.formattingOptions.StatementBraceStyle; + return true; + case Body.Namespace: + if (!Engine.formattingOptions.IndentNamespaceBody) + return false; + style = Engine.formattingOptions.NamespaceBraceStyle; + return true; + case Body.Class: + if (!Engine.formattingOptions.IndentClassBody) + return false; + style = Engine.formattingOptions.ClassBraceStyle; + return true; + case Body.Struct: + if (!Engine.formattingOptions.IndentStructBody) + return false; + style = Engine.formattingOptions.StructBraceStyle; + return true; + case Body.Interface: + if (!Engine.formattingOptions.IndentInterfaceBody) + return false; + style = Engine.formattingOptions.InterfaceBraceStyle; + return true; + case Body.Enum: + if (!Engine.formattingOptions.IndentEnumBody) + return false; + style = Engine.formattingOptions.EnumBraceStyle; + return true; + case Body.Switch: + if (!Engine.formattingOptions.IndentSwitchBody) + return false; + style = Engine.formattingOptions.StatementBraceStyle; + return true; + case Body.Try: + case Body.Catch: + case Body.Finally: + style = Engine.formattingOptions.StatementBraceStyle; + return true; + } + return false; + } + + /// + /// Pushes a new level of indentation depending on the given + /// . + /// + void AddIndentation(Body body) + { + var isExpression = Parent is ParenthesesBodyState || Parent is SquareBracketsBodyState || + (Parent is BracesBodyState && ((BracesBodyState)Parent).IsRightHandExpression); + if (isExpression && Engine.formattingOptions.IndentBlocksInsideExpressions && Engine.isLineStart) + { + AddIndentation(BraceStyle.NextLineShifted); + } + + BraceStyle style; + if (TryGetBraceStyle(body, out style)) + { + AddIndentation(style); + } else { + NextLineIndent.Push(IndentType.Empty); + } + } + + /// + /// Extracts the from the given state. + /// + /// + /// The correct type for this state. + /// + static Body extractBody(IndentState state) + { + if (state != null && state is BracesBodyState) + { + return ((BracesBodyState)state).NextBody; + } + + return Body.None; + } + + #endregion + } + + #endregion + + #region Global body state + + /// + /// Global body state. + /// + /// + /// Represents the global space of the program. + /// + public class GlobalBodyState : BracesBodyState + { + public override char ClosedBracket + { + get { return '\0'; } + } + + public GlobalBodyState() + { } + + + public GlobalBodyState(CSharpIndentEngine engine) + { + Initialize (engine, null); + } + + public GlobalBodyState(GlobalBodyState prototype, CSharpIndentEngine engine) + : base(prototype, engine) + { } + + public override IndentState Clone(CSharpIndentEngine engine) + { + return new GlobalBodyState(this, engine); + } + + public override void InitializeState() + { + ThisLineIndent = new Indent(Engine.textEditorOptions); + NextLineIndent = ThisLineIndent.Clone(); + } + } + + #endregion + + #region Switch-case body state + + /// + /// Switch-case statement state. + /// + /// + /// Represents the block of code in one switch case (including default). + /// + public class SwitchCaseState : BracesBodyState + { + public SwitchCaseState() + { } + + public SwitchCaseState(SwitchCaseState prototype, CSharpIndentEngine engine) + : base(prototype, engine) + { } + + public override void Push(char ch) + { + // on ClosedBracket both this state (a case or a default statement) + // and also the whole switch block (handled in the base class) must exit. + if (ch == ClosedBracket) + { + ExitState(); + if (Parent is BracesBodyState) + Parent.OnExit(); + } + + base.Push(ch); + } + + public override void InitializeState() + { + ThisLineIndent = Parent.ThisLineIndent.Clone(); + NextLineIndent = ThisLineIndent.Clone(); + + // remove all continuations and extra spaces + ThisLineIndent.RemoveAlignment(); + ThisLineIndent.PopWhile(IndentType.Continuation); + + NextLineIndent.RemoveAlignment(); + NextLineIndent.PopWhile(IndentType.Continuation); + + if (Engine.formattingOptions.IndentCaseBody) + { + NextLineIndent.Push(IndentType.Block); + } + else + { + NextLineIndent.Push(IndentType.Empty); + } + } + + static readonly string[] caseDefaultKeywords = { + "case", + "default" + }; + + static readonly string[] breakContinueReturnGotoKeywords = { + "break", + "continue", + "return", + "goto" + }; + + public override void CheckKeyword(string keyword) + { + if (caseDefaultKeywords.Contains(keyword) && Engine.isLineStartBeforeWordToken) + { + ExitState(); + ChangeState(); + } + else if (breakContinueReturnGotoKeywords.Contains(keyword) && Engine.isLineStartBeforeWordToken) + { + // OPTION: Engine.formattingOptions.IndentBreakStatements + if (!Engine.formattingOptions.IndentBreakStatements) + { + ThisLineIndent = Parent.ThisLineIndent.Clone(); + } + } + + base.CheckKeyword(keyword); + } + + + public override void OnExit() + { + //Parent.OnExit(); + } + + public override IndentState Clone(CSharpIndentEngine engine) + { + return new SwitchCaseState(this, engine); + } + } + + #endregion + + #region Parentheses body state + + /// + /// Parentheses body state. + /// + /// + /// Represents a block of code between ( and ). + /// + public class ParenthesesBodyState : BracketsBodyBaseState + { + /// + /// True if any char has been pushed. + /// + public bool IsSomethingPushed; + + public override char ClosedBracket + { + get { return ')'; } + } + + public ParenthesesBodyState() + { } + + public ParenthesesBodyState(ParenthesesBodyState prototype, CSharpIndentEngine engine) + : base(prototype, engine) + { + IsSomethingPushed = prototype.IsSomethingPushed; + } + + public override void Push(char ch) + { + if (ch == Engine.newLineChar) + { + if (Engine.formattingOptions.AnonymousMethodBraceStyle == BraceStyle.EndOfLine || + Engine.formattingOptions.AnonymousMethodBraceStyle == BraceStyle.EndOfLineWithoutSpace) { + if (NextLineIndent.PopIf(IndentType.Continuation)) { + NextLineIndent.Push(IndentType.Block); + } + } + } + else if (!IsSomethingPushed) + { + // OPTION: CSharpFormattingOptions.AlignToFirstMethodCallArgument + if (Engine.formattingOptions.AlignToFirstMethodCallArgument) + { + NextLineIndent.PopTry(); + // align the next line at the beginning of the open bracket + NextLineIndent.ExtraSpaces = Math.Max(0, Engine.column - NextLineIndent.CurIndent - 1); + } + } + + base.Push(ch); + IsSomethingPushed = true; + } + + public override void InitializeState() + { + ThisLineIndent = Parent.ThisLineIndent.Clone(); + NextLineIndent = ThisLineIndent.Clone(); + NextLineIndent.Push(IndentType.Continuation); + } + + public override IndentState Clone(CSharpIndentEngine engine) + { + return new ParenthesesBodyState(this, engine); + } + + public override void OnExit() + { + if (Engine.isLineStart) + { + if (ThisLineIndent.ExtraSpaces > 0) + { + ThisLineIndent.ExtraSpaces--; + } + else + { + ThisLineIndent.PopTry(); + } + } + + base.OnExit(); + } + } + + #endregion + + #region Square brackets body state + + /// + /// Square brackets body state. + /// + /// + /// Represents a block of code between [ and ]. + /// + public class SquareBracketsBodyState : BracketsBodyBaseState + { + /// + /// True if any char has been pushed. + /// + public bool IsSomethingPushed; + + public override char ClosedBracket + { + get { return ']'; } + } + + public SquareBracketsBodyState() + { } + + public SquareBracketsBodyState(SquareBracketsBodyState prototype, CSharpIndentEngine engine) + : base(prototype, engine) + { + IsSomethingPushed = prototype.IsSomethingPushed; + } + + public override void Push(char ch) + { + if (ch == Engine.newLineChar) + { + if (NextLineIndent.PopIf(IndentType.Continuation)) + { + NextLineIndent.Push(IndentType.Block); + } + } + else if (!IsSomethingPushed) + { + // OPTION: CSharpFormattingOptions.AlignToFirstIndexerArgument + if (Engine.formattingOptions.AlignToFirstIndexerArgument) + { + NextLineIndent.PopTry(); + // align the next line at the beginning of the open bracket + NextLineIndent.ExtraSpaces = Math.Max(0, Engine.column - NextLineIndent.CurIndent - 1); + } + } + + base.Push(ch); + IsSomethingPushed = true; + } + + public override void InitializeState() + { + ThisLineIndent = Parent.ThisLineIndent.Clone(); + NextLineIndent = ThisLineIndent.Clone(); + NextLineIndent.Push(IndentType.Continuation); + } + + public override IndentState Clone(CSharpIndentEngine engine) + { + return new SquareBracketsBodyState(this, engine); + } + + public override void OnExit() + { + if (Engine.isLineStart) + { + if (ThisLineIndent.ExtraSpaces > 0) + { + ThisLineIndent.ExtraSpaces--; + } + else + { + ThisLineIndent.PopTry(); + } + } + + base.OnExit(); + } + } + + #endregion + + #endregion + + #region PreProcessor state + + /// + /// PreProcessor directive state. + /// + /// + /// Activated when the '#' char is pushed and the + /// is true. + /// + public class PreProcessorState : IndentState + { + /// + /// The type of the preprocessor directive. + /// + public PreProcessorDirective DirectiveType; + + /// + /// If is set (not equal to 'None'), this + /// stores the expression of the directive. + /// + public StringBuilder DirectiveStatement; + + public PreProcessorState() + { + DirectiveType = PreProcessorDirective.None; + DirectiveStatement = new StringBuilder(); + } + + public PreProcessorState(PreProcessorState prototype, CSharpIndentEngine engine) + : base(prototype, engine) + { + DirectiveType = prototype.DirectiveType; + DirectiveStatement = new StringBuilder(prototype.DirectiveStatement.ToString()); + } + + public override void Push(char ch) + { + // HACK: if this change would be left for the CheckKeyword method, we will lose + // it if the next pushed char is newLineChar since ThisLineIndent will be + // immediately replaced with NextLineIndent. As this most likely will + // happen, we check for "endregion" on every push. + if (Engine.wordToken.ToString() == "endregion") + { + CheckKeywordOnPush("endregion"); + } + + base.Push(ch); + + if (DirectiveType != PreProcessorDirective.None) + { + DirectiveStatement.Append(ch); + } + + if (ch == Engine.newLineChar) + { + ExitState(); + switch (DirectiveType) + { + case PreProcessorDirective.If: + Engine.ifDirectiveEvalResults.Push(eval(DirectiveStatement.ToString())); + if (Engine.ifDirectiveEvalResults.Peek()) + { + // the if/elif directive is true -> continue with the previous state + } + else + { + // the if/elif directive is false -> change to a state that will + // ignore any chars until #endif or #elif + ChangeState(); + } + break; + case PreProcessorDirective.Elif: + if (Engine.ifDirectiveEvalResults.Count > 0) + { + if (!Engine.ifDirectiveEvalResults.Peek()) + { + ExitState(); + Engine.ifDirectiveEvalResults.Pop(); + goto case PreProcessorDirective.If; + } + } + // previous if was true -> comment + ChangeState(); + break; + case PreProcessorDirective.Else: + if (Engine.ifDirectiveEvalResults.Count > 0 && Engine.ifDirectiveEvalResults.Peek()) + { + // some if/elif directive was true -> change to a state that will + // ignore any chars until #endif + ChangeState(); + } + else + { + // none if/elif directives were true -> exit comment state. + if (Engine.currentState is PreProcessorCommentState) + ExitState(); + } + break; + case PreProcessorDirective.Define: + var defineSymbol = DirectiveStatement.ToString().Trim(); + if (!Engine.conditionalSymbols.Contains(defineSymbol)) + { + Engine.conditionalSymbols.Add(defineSymbol); + } + break; + case PreProcessorDirective.Undef: + var undefineSymbol = DirectiveStatement.ToString().Trim(); + if (Engine.conditionalSymbols.Contains(undefineSymbol)) + { + Engine.conditionalSymbols.Remove(undefineSymbol); + } + break; + case PreProcessorDirective.Endif: + // marks the end of this block + if (Engine.currentState is PreProcessorCommentState) + ExitState(); + + Engine.ifDirectiveEvalResults.Pop(); + Engine.ifDirectiveIndents.Pop(); + break; + case PreProcessorDirective.Region: + case PreProcessorDirective.Pragma: + case PreProcessorDirective.Warning: + case PreProcessorDirective.Error: + case PreProcessorDirective.Line: + // continue with the previous state + break; + } + } + } + + public override void InitializeState() + { + // OPTION: IndentPreprocessorStatements + if (Engine.formattingOptions.IndentPreprocessorDirectives) + { + if (Engine.ifDirectiveIndents.Count > 0) + { + ThisLineIndent = Engine.ifDirectiveIndents.Peek().Clone(); + } + else + { + ThisLineIndent = Parent.ThisLineIndent.Clone(); + } + } + else + { + ThisLineIndent = new Indent(Engine.textEditorOptions); + } + + NextLineIndent = Parent.NextLineIndent.Clone(); + } + + static readonly Dictionary preProcessorDirectives = new Dictionary + { + { "if", PreProcessorDirective.If }, + { "elif", PreProcessorDirective.Elif }, + { "else", PreProcessorDirective.Else }, + { "endif", PreProcessorDirective.Endif }, + { "region", PreProcessorDirective.Region }, + { "endregion", PreProcessorDirective.Endregion }, + { "pragma", PreProcessorDirective.Pragma }, + { "warning", PreProcessorDirective.Warning }, + { "error", PreProcessorDirective.Error }, + { "line", PreProcessorDirective.Line }, + { "define", PreProcessorDirective.Define }, + { "undef", PreProcessorDirective.Undef } + }; + + public override void CheckKeywordOnPush(string keyword) + { + if (keyword == "endregion") + { + DirectiveType = PreProcessorDirective.Endregion; + ThisLineIndent = Parent.NextLineIndent.Clone(); + } + } + + public override void CheckKeyword(string keyword) + { + // check if the directive type has already been set + if (DirectiveType != PreProcessorDirective.None) + { + return; + } + + if (preProcessorDirectives.ContainsKey(keyword)) + { + DirectiveType = preProcessorDirectives[keyword]; + + // adjust the indentation for the region directive + if (DirectiveType == PreProcessorDirective.Region) + { + ThisLineIndent = Parent.NextLineIndent.Clone(); + } + else if (DirectiveType == PreProcessorDirective.If) + { + Engine.ifDirectiveIndents.Push(ThisLineIndent.Clone()); + } + } + } + + public override IndentState Clone(CSharpIndentEngine engine) + { + return new PreProcessorState(this, engine); + } + + /// + /// Types of preprocessor directives. + /// + public enum PreProcessorDirective + { + None, + If, + Elif, + Else, + Endif, + Region, + Endregion, + Pragma, + Warning, + Error, + Line, + Define, + Undef + } + + #region Pre processor evaluation (from cs-tokenizer.cs) + + static bool is_identifier_start_character(int c) + { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || Char.IsLetter((char)c); + } + + static bool is_identifier_part_character(char c) + { + if (c >= 'a' && c <= 'z') + return true; + + if (c >= 'A' && c <= 'Z') + return true; + + if (c == '_' || (c >= '0' && c <= '9')) + return true; + + if (c < 0x80) + return false; + + return Char.IsLetter(c) || Char.GetUnicodeCategory(c) == UnicodeCategory.ConnectorPunctuation; + } + + bool eval_val(string s) + { + if (s == "true") + return true; + if (s == "false") + return false; + + return Engine.conditionalSymbols != null && Engine.conditionalSymbols.Contains(s) || + Engine.customConditionalSymbols != null && Engine.customConditionalSymbols.Contains(s); + } + + bool pp_primary(ref string s) + { + s = s.Trim(); + int len = s.Length; + + if (len > 0) + { + char c = s[0]; + + if (c == '(') + { + s = s.Substring(1); + bool val = pp_expr(ref s, false); + if (s.Length > 0 && s[0] == ')') + { + s = s.Substring(1); + return val; + } + return false; + } + + if (is_identifier_start_character(c)) + { + int j = 1; + + while (j < len) + { + c = s[j]; + + if (is_identifier_part_character(c)) + { + j++; + continue; + } + bool v = eval_val(s.Substring(0, j)); + s = s.Substring(j); + return v; + } + bool vv = eval_val(s); + s = ""; + return vv; + } + } + return false; + } + + bool pp_unary(ref string s) + { + s = s.Trim(); + int len = s.Length; + + if (len > 0) + { + if (s[0] == '!') + { + if (len > 1 && s[1] == '=') + { + return false; + } + s = s.Substring(1); + return !pp_primary(ref s); + } + else + return pp_primary(ref s); + } + else + { + return false; + } + } + + bool pp_eq(ref string s) + { + bool va = pp_unary(ref s); + + s = s.Trim(); + int len = s.Length; + if (len > 0) + { + if (s[0] == '=') + { + if (len > 2 && s[1] == '=') + { + s = s.Substring(2); + return va == pp_unary(ref s); + } + else + { + return false; + } + } + else if (s[0] == '!' && len > 1 && s[1] == '=') + { + s = s.Substring(2); + + return va != pp_unary(ref s); + + } + } + + return va; + + } + + bool pp_and(ref string s) + { + bool va = pp_eq(ref s); + + s = s.Trim(); + int len = s.Length; + if (len > 0) + { + if (s[0] == '&') + { + if (len > 2 && s[1] == '&') + { + s = s.Substring(2); + return (va & pp_and(ref s)); + } + else + { + return false; + } + } + } + return va; + } + + // + // Evaluates an expression for `#if' or `#elif' + // + bool pp_expr(ref string s, bool isTerm) + { + bool va = pp_and(ref s); + s = s.Trim(); + int len = s.Length; + if (len > 0) + { + char c = s[0]; + + if (c == '|') + { + if (len > 2 && s[1] == '|') + { + s = s.Substring(2); + return va | pp_expr(ref s, isTerm); + } + else + { + + return false; + } + } + if (isTerm) + { + return false; + } + } + + return va; + } + + bool eval(string s) + { + bool v = pp_expr(ref s, true); + s = s.Trim(); + if (s.Length != 0) + { + return false; + } + + return v; + } + + #endregion + } + + #endregion + + #region PreProcessorComment state + + /// + /// PreProcessor comment state. + /// + /// + /// Activates when the #if or #elif directive is false and ignores + /// all pushed chars until the next '#'. + /// + public class PreProcessorCommentState : IndentState + { + public PreProcessorCommentState() + { } + + public PreProcessorCommentState(PreProcessorCommentState prototype, CSharpIndentEngine engine) + : base(prototype, engine) + { } + + public override void Push(char ch) + { + base.Push(ch); + + if (ch == '#' && Engine.isLineStart) + { + ChangeState(); + } + } + + public override void InitializeState() + { + if (Engine.formattingOptions.IndentPreprocessorDirectives && + Engine.ifDirectiveIndents.Count > 0) + { + ThisLineIndent = Engine.ifDirectiveIndents.Peek().Clone(); + NextLineIndent = ThisLineIndent.Clone(); + } + else + { + ThisLineIndent = Parent.NextLineIndent.Clone(); + NextLineIndent = ThisLineIndent.Clone(); + } + } + + public override IndentState Clone(CSharpIndentEngine engine) + { + return new PreProcessorCommentState(this, engine); + } + } + + #endregion + + #region LineComment state + + /// + /// Single-line comment state. + /// + public class LineCommentState : IndentState + { + /// + /// It's possible that this should be the DocComment state: + /// check if the first next pushed char is equal to '/'. + /// + public bool CheckForDocComment = true; + + public LineCommentState() + { + /* if (engine.formattingOptions.KeepCommentsAtFirstColumn && engine.column == 2) + ThisLineIndent.Reset();*/ + } + + public LineCommentState(LineCommentState prototype, CSharpIndentEngine engine) + : base(prototype, engine) + { + CheckForDocComment = prototype.CheckForDocComment; + } + + public override void Push(char ch) + { + base.Push(ch); + + if (ch == Engine.newLineChar) + { + // to handle cases like //\n/* + // Otherwise line 2 would be treated as line comment. + Engine.previousChar = '\0'; + ExitState(); + } + else if (ch == '/' && CheckForDocComment) + { + // wrong state, should be DocComment. + ExitState(); + ChangeState(); + } + + CheckForDocComment = false; + } + + public override void InitializeState() + { + ThisLineIndent = Parent.ThisLineIndent.Clone(); + NextLineIndent = Parent.NextLineIndent.Clone(); + } + + public override IndentState Clone(CSharpIndentEngine engine) + { + return new LineCommentState(this, engine); + } + } + + #endregion + + #region DocComment state + + /// + /// XML documentation comment state. + /// + public class DocCommentState : IndentState + { + public DocCommentState() + { } + + public DocCommentState(DocCommentState prototype, CSharpIndentEngine engine) + : base(prototype, engine) + { } + + public override void Push(char ch) + { + base.Push(ch); + + if (ch == Engine.newLineChar) + { + ExitState(); + } + } + + public override void InitializeState() + { + ThisLineIndent = Parent.ThisLineIndent.Clone(); + NextLineIndent = Parent.NextLineIndent.Clone(); + } + + public override IndentState Clone(CSharpIndentEngine engine) + { + return new DocCommentState(this, engine); + } + } + + #endregion + + #region MultiLineComment state + + /// + /// Multi-line comment state. + /// + public class MultiLineCommentState : IndentState + { + /// + /// True if any char has been pushed to this state. + /// + /// + /// Needed to resolve an issue when the first pushed char is '/'. + /// The state would falsely exit on this sequence of chars '/*/', + /// since it only checks if the last two chars are '/' and '*'. + /// + public bool IsAnyCharPushed; + + public MultiLineCommentState() + { } + + public MultiLineCommentState(MultiLineCommentState prototype, CSharpIndentEngine engine) + : base(prototype, engine) + { + IsAnyCharPushed = prototype.IsAnyCharPushed; + } + + public override void Push(char ch) + { + base.Push(ch); + + if (ch == '/' && Engine.previousChar == '*' && IsAnyCharPushed) + { + ExitState(); + } + + IsAnyCharPushed = true; + } + + public override void InitializeState() + { + ThisLineIndent = Parent.ThisLineIndent.Clone(); + NextLineIndent = ThisLineIndent.Clone(); + } + + public override IndentState Clone(CSharpIndentEngine engine) + { + return new MultiLineCommentState(this, engine); + } + } + + #endregion + + #region StringLiteral state + + /// + /// StringLiteral state. + /// + public class StringLiteralState : IndentState + { + /// + /// True if the next char is escaped with '\'. + /// + public bool IsEscaped; + + public StringLiteralState() + { } + + public StringLiteralState(StringLiteralState prototype, CSharpIndentEngine engine) + : base(prototype, engine) + { + IsEscaped = prototype.IsEscaped; + } + + public override void Push(char ch) + { + base.Push(ch); + + if (ch == Engine.newLineChar || (!IsEscaped && ch == '"')) { + ExitState(); + } else { + IsEscaped = ch == '\\' && !IsEscaped; + } + } + + public override void InitializeState() + { + ThisLineIndent = Parent.ThisLineIndent.Clone(); + NextLineIndent = Parent.NextLineIndent.Clone(); + } + + public override IndentState Clone(CSharpIndentEngine engine) + { + return new StringLiteralState(this, engine); + } + } + + #endregion + + #region Verbatim string state + + /// + /// Verbatim string state. + /// + public class VerbatimStringState : IndentState + { + /// + /// True if there is an odd number of '"' in a row. + /// + public bool IsEscaped; + + public VerbatimStringState() + { } + + public VerbatimStringState(VerbatimStringState prototype, CSharpIndentEngine engine) + : base(prototype, engine) + { + IsEscaped = prototype.IsEscaped; + } + + public override void Push(char ch) + { + base.Push(ch); + + if (IsEscaped && ch != '"') + { + ExitState(); + // the char has been pushed to the wrong state, push it back + Engine.currentState.Push(ch); + } + + IsEscaped = ch == '"' && !IsEscaped; + } + + public override void InitializeState() + { + ThisLineIndent = Parent.ThisLineIndent.Clone(); + NextLineIndent = new Indent(Engine.textEditorOptions); + } + + public override IndentState Clone(CSharpIndentEngine engine) + { + return new VerbatimStringState(this, engine); + } + } + + #endregion + + #region Character state + + /// + /// Character state. + /// + public class CharacterState : IndentState + { + /// + /// True if the next char is escaped with '\'. + /// + public bool IsEscaped; + + public CharacterState() + { } + + public CharacterState(CharacterState prototype, CSharpIndentEngine engine) + : base(prototype, engine) + { + IsEscaped = prototype.IsEscaped; + } + + public override void Push(char ch) + { + base.Push(ch); + + if (ch == Engine.newLineChar) + { + ExitState(); + } + else if (!IsEscaped && ch == '\'') + { + ExitState(); + } + + IsEscaped = ch == '\\' && !IsEscaped; + } + + public override void InitializeState() + { + ThisLineIndent = Parent.ThisLineIndent.Clone(); + NextLineIndent = Parent.NextLineIndent.Clone(); + } + + public override IndentState Clone(CSharpIndentEngine engine) + { + return new CharacterState(this, engine); + } + } + + #endregion +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/NullIStateMachineIndentEngine.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/NullIStateMachineIndentEngine.cs new file mode 100644 index 000000000..b62932e5b --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/NullIStateMachineIndentEngine.cs @@ -0,0 +1,215 @@ +// +// NullIStateMachineIndentEngine.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2013 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// An empty IStateMachineIndentEngine implementation that does nothing. + /// + public sealed class NullIStateMachineIndentEngine : IStateMachineIndentEngine + { + readonly ICSharpCode.NRefactory.Editor.IDocument document; + int offset; + + public NullIStateMachineIndentEngine(ICSharpCode.NRefactory.Editor.IDocument document) + { + if (document == null) + throw new ArgumentNullException("document"); + this.document = document; + } + + #region IStateMachineIndentEngine implementation + public IStateMachineIndentEngine Clone() + { + return new NullIStateMachineIndentEngine(document) { offset = this.offset }; + } + + bool IStateMachineIndentEngine.IsInsidePreprocessorDirective { + get { + return false; + } + } + + bool IStateMachineIndentEngine.IsInsidePreprocessorComment { + get { + return false; + } + } + + bool IStateMachineIndentEngine.IsInsideStringLiteral { + get { + return false; + } + } + + bool IStateMachineIndentEngine.IsInsideVerbatimString { + get { + return false; + } + } + + bool IStateMachineIndentEngine.IsInsideCharacter { + get { + return false; + } + } + + bool IStateMachineIndentEngine.IsInsideString { + get { + return false; + } + } + + bool IStateMachineIndentEngine.IsInsideLineComment { + get { + return false; + } + } + + bool IStateMachineIndentEngine.IsInsideMultiLineComment { + get { + return false; + } + } + + bool IStateMachineIndentEngine.IsInsideDocLineComment { + get { + return false; + } + } + + bool IStateMachineIndentEngine.IsInsideComment { + get { + return false; + } + } + + bool IStateMachineIndentEngine.IsInsideOrdinaryComment { + get { + return false; + } + } + + bool IStateMachineIndentEngine.IsInsideOrdinaryCommentOrString { + get { + return false; + } + } + + bool IStateMachineIndentEngine.LineBeganInsideVerbatimString { + get { + return false; + } + } + + bool IStateMachineIndentEngine.LineBeganInsideMultiLineComment { + get { + return false; + } + } + #endregion + + #region IDocumentIndentEngine implementation + void IDocumentIndentEngine.Push(char ch) + { + offset++; + } + + void IDocumentIndentEngine.Reset() + { + this.offset = 0; + } + + void IDocumentIndentEngine.Update(int offset) + { + this.offset = offset; + } + + IDocumentIndentEngine IDocumentIndentEngine.Clone() + { + return Clone(); + } + + ICSharpCode.NRefactory.Editor.IDocument IDocumentIndentEngine.Document { + get { + return document; + } + } + + string IDocumentIndentEngine.ThisLineIndent { + get { + return ""; + } + } + + string IDocumentIndentEngine.NextLineIndent { + get { + return ""; + } + } + + string IDocumentIndentEngine.CurrentIndent { + get { + return ""; + } + } + + bool IDocumentIndentEngine.NeedsReindent { + get { + return false; + } + } + + int IDocumentIndentEngine.Offset { + get { + return offset; + } + } + TextLocation IDocumentIndentEngine.Location { + get { + return TextLocation.Empty; + } + } + + /// + public bool EnableCustomIndentLevels + { + get { return false; } + set { } + } + + #endregion + + #region ICloneable implementation + object ICloneable.Clone() + { + return Clone(); + } + #endregion + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/TextPasteIndentEngine.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/TextPasteIndentEngine.cs new file mode 100644 index 000000000..9170b029f --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/IndentEngine/TextPasteIndentEngine.cs @@ -0,0 +1,632 @@ +// +// TextPasteIndentEngine.cs +// +// Author: +// Matej Miklečić +// +// Copyright (c) 2013 Matej Miklečić (matej.miklecic@gmail.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 ICSharpCode.NRefactory.Editor; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Represents a decorator of an IStateMachineIndentEngine instance + /// that provides logic for text paste events. + /// + public class TextPasteIndentEngine : IDocumentIndentEngine, ITextPasteHandler + { + + #region Properties + + /// + /// An instance of IStateMachineIndentEngine which handles + /// the indentation logic. + /// + IStateMachineIndentEngine engine; + /// + /// Text editor options. + /// + internal readonly TextEditorOptions textEditorOptions; + internal readonly CSharpFormattingOptions formattingOptions; + #endregion + + #region Constructors + + /// + /// Creates a new TextPasteIndentEngine instance. + /// + /// + /// An instance of to which the + /// logic for indentation will be delegated. + /// + /// + /// Text editor options for indentation. + /// + /// + /// C# formatting options. + /// + public TextPasteIndentEngine(IStateMachineIndentEngine decoratedEngine, TextEditorOptions textEditorOptions, CSharpFormattingOptions formattingOptions) + { + this.engine = decoratedEngine; + this.textEditorOptions = textEditorOptions; + this.formattingOptions = formattingOptions; + + this.engine.EnableCustomIndentLevels = false; + } + + #endregion + + #region ITextPasteHandler + + /// + string ITextPasteHandler.FormatPlainText(int offset, string text, byte[] copyData) + { + if (copyData != null && copyData.Length == 1) { + var strategy = TextPasteUtils.Strategies [(PasteStrategy)copyData [0]]; + text = strategy.Decode(text); + } + engine.Update(offset); + if (engine.IsInsideStringLiteral) { + int idx = text.IndexOf('"'); + if (idx > 0) { + var o = offset; + while (o < engine.Document.TextLength) { + char ch = engine.Document.GetCharAt(o); + engine.Push(ch); + if (NewLine.IsNewLine(ch)) + break; + o++; + if (!engine.IsInsideStringLiteral) + return TextPasteUtils.StringLiteralStrategy.Encode(text); + } + return TextPasteUtils.StringLiteralStrategy.Encode(text.Substring(0, idx)) + text.Substring(idx); + } + return TextPasteUtils.StringLiteralStrategy.Encode(text); + + } else if (engine.IsInsideVerbatimString) { + + int idx = text.IndexOf('"'); + if (idx > 0) { + var o = offset; + while (o < engine.Document.TextLength) { + char ch = engine.Document.GetCharAt(o); + engine.Push(ch); + o++; + if (!engine.IsInsideVerbatimString) + return TextPasteUtils.VerbatimStringStrategy.Encode(text); + } + return TextPasteUtils.VerbatimStringStrategy.Encode(text.Substring(0, idx)) + text.Substring(idx); + } + + return TextPasteUtils.VerbatimStringStrategy.Encode(text); + } + var line = engine.Document.GetLineByOffset(offset); + var pasteAtLineStart = line.Offset == offset; + var indentedText = new StringBuilder(); + var curLine = new StringBuilder(); + var clonedEngine = engine.Clone(); + bool isNewLine = false, gotNewLine = false; + for (int i = 0; i < text.Length; i++) { + var ch = text [i]; + if (clonedEngine.IsInsideVerbatimString || clonedEngine.IsInsideMultiLineComment) { + clonedEngine.Push(ch); + curLine.Append(ch); + continue; + } + + var delimiterLength = NewLine.GetDelimiterLength(ch, i + 1 < text.Length ? text[i + 1] : ' '); + if (delimiterLength > 0) { + isNewLine = true; + if (gotNewLine || pasteAtLineStart) { + if (curLine.Length > 0 || formattingOptions.EmptyLineFormatting == EmptyLineFormatting.Indent) + indentedText.Append(clonedEngine.ThisLineIndent); + } + indentedText.Append(curLine); + indentedText.Append(textEditorOptions.EolMarker); + curLine.Length = 0; + gotNewLine = true; + i += delimiterLength - 1; + // textEditorOptions.EolMarker[0] is the newLineChar used by the indentation engine. + clonedEngine.Push(textEditorOptions.EolMarker[0]); + } else { + if (isNewLine) { + if (ch == '\t' || ch == ' ') { + clonedEngine.Push(ch); + continue; + } + isNewLine = false; + } + curLine.Append(ch); + clonedEngine.Push(ch); + } + if (clonedEngine.IsInsideVerbatimString || clonedEngine.IsInsideMultiLineComment && + !(clonedEngine.LineBeganInsideVerbatimString || clonedEngine.LineBeganInsideMultiLineComment)) { + if (gotNewLine) { + if (curLine.Length > 0 || formattingOptions.EmptyLineFormatting == EmptyLineFormatting.Indent) + indentedText.Append(clonedEngine.ThisLineIndent); + } + pasteAtLineStart = false; + indentedText.Append(curLine); + curLine.Length = 0; + gotNewLine = false; + continue; + } + } + if (gotNewLine && (!pasteAtLineStart || curLine.Length > 0)) { + indentedText.Append(clonedEngine.ThisLineIndent); + } + if (curLine.Length > 0) { + indentedText.Append(curLine); + } + return indentedText.ToString(); + } + + /// + byte[] ITextPasteHandler.GetCopyData(ISegment segment) + { + engine.Update(segment.Offset); + + if (engine.IsInsideStringLiteral) { + return new[] { (byte)PasteStrategy.StringLiteral }; + } else if (engine.IsInsideVerbatimString) { + return new[] { (byte)PasteStrategy.VerbatimString }; + } + + return null; + } + + #endregion + + #region IDocumentIndentEngine + + /// + public IDocument Document { + get { return engine.Document; } + } + + /// + public string ThisLineIndent { + get { return engine.ThisLineIndent; } + } + + /// + public string NextLineIndent { + get { return engine.NextLineIndent; } + } + + /// + public string CurrentIndent { + get { return engine.CurrentIndent; } + } + + /// + public bool NeedsReindent { + get { return engine.NeedsReindent; } + } + + /// + public int Offset { + get { return engine.Offset; } + } + + /// + public TextLocation Location { + get { return engine.Location; } + } + + /// + public bool EnableCustomIndentLevels { + get { return engine.EnableCustomIndentLevels; } + set { engine.EnableCustomIndentLevels = value; } + } + + /// + public void Push(char ch) + { + engine.Push(ch); + } + + /// + public void Reset() + { + engine.Reset(); + } + + /// + public void Update(int offset) + { + engine.Update(offset); + } + + #endregion + + #region IClonable + + public IDocumentIndentEngine Clone() + { + return new TextPasteIndentEngine(engine, textEditorOptions, formattingOptions); + } + + object ICloneable.Clone() + { + return Clone(); + } + + #endregion + + } + + /// + /// Types of text-paste strategies. + /// + public enum PasteStrategy : byte + { + PlainText = 0, + StringLiteral = 1, + VerbatimString = 2 + } + + /// + /// Defines some helper methods for dealing with text-paste events. + /// + public static class TextPasteUtils + { + /// + /// Collection of text-paste strategies. + /// + public static TextPasteStrategies Strategies = new TextPasteStrategies(); + + /// + /// The interface for a text-paste strategy. + /// + public interface IPasteStrategy + { + /// + /// Formats the given text according with this strategy rules. + /// + /// + /// The text to format. + /// + /// + /// Formatted text. + /// + string Encode(string text); + + /// + /// Converts text formatted according with this strategy rules + /// to its original form. + /// + /// + /// Formatted text to convert. + /// + /// + /// Original form of the given formatted text. + /// + string Decode(string text); + + /// + /// Type of this strategy. + /// + PasteStrategy Type { get; } + } + + /// + /// Wrapper that discovers all defined text-paste strategies and defines a way + /// to easily access them through their type. + /// + public sealed class TextPasteStrategies + { + /// + /// Collection of discovered text-paste strategies. + /// + IDictionary strategies; + + /// + /// Uses reflection to find all types derived from + /// and adds an instance of each strategy to . + /// + public TextPasteStrategies() + { + strategies = Assembly.GetExecutingAssembly() + .GetTypes() + .Where(t => typeof(IPasteStrategy).IsAssignableFrom(t) && t.IsClass) + .Select(t => (IPasteStrategy)t.GetProperty("Instance").GetValue(null, null)) + .ToDictionary(s => s.Type); + } + + /// + /// Checks if there is a strategy of the given type and returns it. + /// + /// + /// Type of the strategy instance. + /// + /// + /// A strategy instance of the requested type, + /// or if it wasn't found. + /// + public IPasteStrategy this [PasteStrategy strategy] { + get { + if (strategies.ContainsKey(strategy)) { + return strategies [strategy]; + } + + return DefaultStrategy; + } + } + } + + /// + /// Doesn't do any formatting. Serves as the default strategy. + /// + public class PlainTextPasteStrategy : IPasteStrategy + { + + #region Singleton + + public static IPasteStrategy Instance { + get { + return instance ?? (instance = new PlainTextPasteStrategy()); + } + } + + static PlainTextPasteStrategy instance; + + protected PlainTextPasteStrategy() + { + } + + #endregion + + /// + public string Encode(string text) + { + return text; + } + + /// + public string Decode(string text) + { + return text; + } + + /// + public PasteStrategy Type { + get { return PasteStrategy.PlainText; } + } + } + + /// + /// Escapes chars in the given text so that they don't + /// break a valid string literal. + /// + public class StringLiteralPasteStrategy : IPasteStrategy + { + + #region Singleton + + public static IPasteStrategy Instance { + get { + return instance ?? (instance = new StringLiteralPasteStrategy()); + } + } + + static StringLiteralPasteStrategy instance; + + protected StringLiteralPasteStrategy() + { + } + + #endregion + + /// + public string Encode(string text) + { + return CSharpOutputVisitor.ConvertString(text); + } + + /// + public string Decode(string text) + { + var result = new StringBuilder(); + bool isEscaped = false; + + for (int i = 0; i < text.Length; i++) { + var ch = text[i]; + if (isEscaped) { + switch (ch) { + case 'a': + result.Append('\a'); + break; + case 'b': + result.Append('\b'); + break; + case 'n': + result.Append('\n'); + break; + case 't': + result.Append('\t'); + break; + case 'v': + result.Append('\v'); + break; + case 'r': + result.Append('\r'); + break; + case '\\': + result.Append('\\'); + break; + case 'f': + result.Append('\f'); + break; + case '0': + result.Append(0); + break; + case '"': + result.Append('"'); + break; + case '\'': + result.Append('\''); + break; + case 'x': + char r; + if (TryGetHex(text, -1, ref i, out r)) { + result.Append(r); + break; + } + goto default; + case 'u': + if (TryGetHex(text, 4, ref i, out r)) { + result.Append(r); + break; + } + goto default; + case 'U': + if (TryGetHex(text, 8, ref i, out r)) { + result.Append(r); + break; + } + goto default; + default: + result.Append('\\'); + result.Append(ch); + break; + } + isEscaped = false; + continue; + } + if (ch != '\\') { + result.Append(ch); + } + else { + isEscaped = true; + } + } + + return result.ToString(); + } + + static bool TryGetHex(string text, int count, ref int idx, out char r) + { + int i; + int total = 0; + int top = count != -1 ? count : 4; + + for (i = 0; i < top; i++) { + int c = text[idx + 1 + i]; + + if (c >= '0' && c <= '9') + c = (int) c - (int) '0'; + else if (c >= 'A' && c <= 'F') + c = (int) c - (int) 'A' + 10; + else if (c >= 'a' && c <= 'f') + c = (int) c - (int) 'a' + 10; + else { + r = '\0'; + return false; + } + total = (total * 16) + c; + } + + if (top == 8) { + if (total > 0x0010FFFF) { + r = '\0'; + return false; + } + + if (total >= 0x00010000) + total = ((total - 0x00010000) / 0x0400 + 0xD800); + } + r = (char)total; + idx += top; + return true; + } + + /// + public PasteStrategy Type { + get { return PasteStrategy.StringLiteral; } + } + } + + /// + /// Escapes chars in the given text so that they don't + /// break a valid verbatim string. + /// + public class VerbatimStringPasteStrategy : IPasteStrategy + { + + #region Singleton + + public static IPasteStrategy Instance { + get { + return instance ?? (instance = new VerbatimStringPasteStrategy()); + } + } + + static VerbatimStringPasteStrategy instance; + + protected VerbatimStringPasteStrategy() + { + } + + #endregion + + static readonly Dictionary> encodeReplace = new Dictionary> { + { '\"', "\"\"" }, + }; + + /// + public string Encode(string text) + { + return string.Concat(text.SelectMany(c => encodeReplace.ContainsKey(c) ? encodeReplace [c] : new[] { c })); + } + + /// + public string Decode(string text) + { + bool isEscaped = false; + return string.Concat(text.Where(c => !(isEscaped = !isEscaped && c == '"'))); + } + + /// + public PasteStrategy Type { + get { return PasteStrategy.VerbatimString; } + } + } + + /// + /// The default text-paste strategy. + /// + public static IPasteStrategy DefaultStrategy = PlainTextPasteStrategy.Instance; + /// + /// String literal text-paste strategy. + /// + public static IPasteStrategy StringLiteralStrategy = StringLiteralPasteStrategy.Instance; + /// + /// Verbatim string text-paste strategy. + /// + public static IPasteStrategy VerbatimStringStrategy = VerbatimStringPasteStrategy.Instance; + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/IntroduceQueryExpressions.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/IntroduceQueryExpressions.cs new file mode 100644 index 000000000..d80ddb82c --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/IntroduceQueryExpressions.cs @@ -0,0 +1,386 @@ +// +// IntroduceQueryExpressions.cs +// +// Modified by Luís Reis (Copyright (C) 2013) +// +// Copyright header of the original version follows: +// +// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team +// +// 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.Diagnostics; +using System.Linq; +using ICSharpCode.NRefactory.CSharp; +using ICSharpCode.NRefactory.PatternMatching; + +namespace ICSharpCode.NRefactory.CSharp +{ + static class NRefactoryExtensions + { + public static T Detach(this T node) where T : AstNode + { + node.Remove(); + return node; + } + + public static T CopyAnnotationsFrom(this T node, AstNode other) where T : AstNode + { + foreach (object annotation in other.Annotations) { + node.AddAnnotation(annotation); + } + return node; + } + + public static Expression WithName(this Expression node, string patternGroupName) + { + return new NamedNode(patternGroupName, node); + } + } + + /// + /// Decompiles query expressions. + /// Based on C# 4.0 spec, §7.16.2 Query expression translation + /// + public class IntroduceQueryExpressions + { + static readonly InvocationExpression castPattern = new InvocationExpression { + Target = new MemberReferenceExpression { + Target = new AnyNode("inExpr"), + MemberName = "Cast", + TypeArguments = { new AnyNode("targetType") } + }}; + + public Expression ConvertFluentToQuery(Expression node) + { + node = node.Clone(); + + var artificialParent = new ExpressionStatement(); + artificialParent.Expression = node; + + DecompileQueries(node); + // After all queries were decompiled, detect degenerate queries (queries not property terminated with 'select' or 'group') + // and fix them, either by adding a degenerate select, or by combining them with another query. + foreach (QueryExpression query in artificialParent.Descendants.OfType()) { + QueryFromClause fromClause = (QueryFromClause)query.Clauses.First(); + if (IsDegenerateQuery(query)) { + string identifierName = fromClause.Identifier; + + // introduce select for degenerate query + query.Clauses.Add(new QuerySelectClause { Expression = new IdentifierExpression(identifierName) }); + } + + if (fromClause.Type.IsNull) { + // See if the data source of this query is a degenerate query, + // and combine the queries if possible. + QueryExpression innerQuery = fromClause.Expression as QueryExpression; + while (IsDegenerateQuery(innerQuery)) { + QueryFromClause innerFromClause = (QueryFromClause)innerQuery.Clauses.First(); + if (fromClause.Identifier != innerFromClause.Identifier && !innerFromClause.Identifier.StartsWith("<>")) + break; + // Replace the fromClause with all clauses from the inner query + fromClause.Remove(); + foreach (var identifierChild in innerQuery.Descendants.OfType().Where(identifier => identifier.Name == innerFromClause.Identifier)) { + //When the identifier is "<>X", then replace it with the outer one + identifierChild.ReplaceWith(fromClause.IdentifierToken.Clone()); + } + QueryClause insertionPos = null; + foreach (var clause in innerQuery.Clauses) { + query.Clauses.InsertAfter(insertionPos, insertionPos = clause.Detach()); + } + fromClause = innerFromClause; + innerQuery = fromClause.Expression as QueryExpression; + } + } + } + + return artificialParent.Expression.Clone(); + } + + bool IsDegenerateQuery(QueryExpression query) + { + if (query == null) + return false; + var lastClause = query.Clauses.LastOrDefault(); + return !(lastClause is QuerySelectClause || lastClause is QueryGroupClause); + } + + void DecompileQueries(AstNode node) + { + QueryExpression query = DecompileQuery(node as InvocationExpression); + if (query != null) + node.ReplaceWith(query); + } + + Expression ExtractQuery(MemberReferenceExpression mre) + { + var inExpression = mre.Target.Clone(); + var inContent = DecompileQuery(inExpression as InvocationExpression) ?? inExpression; + return inContent; + } + + QueryExpression DecompileQuery(InvocationExpression invocation) + { + if (invocation == null) + return null; + MemberReferenceExpression mre = invocation.Target as MemberReferenceExpression; + if (mre == null) + return null; + + switch (mre.MemberName) { + case "Select": + { + if (invocation.Arguments.Count != 1) + return null; + string parameterName; + Expression body; + if (MatchSimpleLambda(invocation.Arguments.Single(), out parameterName, out body)) { + QueryExpression query = new QueryExpression(); + query.Clauses.Add(new QueryFromClause { Identifier = parameterName, Expression = ExtractQuery(mre) }); + query.Clauses.Add(new QuerySelectClause { Expression = body.Detach() }); + return query; + } + return null; + } + case "Cast": + { + if (invocation.Arguments.Count == 0 && mre.TypeArguments.Count == 1) { + var typeArgument = mre.TypeArguments.First(); + + QueryExpression query = new QueryExpression(); + string varName = GenerateVariableName(); + query.Clauses.Add(new QueryFromClause { + Identifier = varName, + Expression = ExtractQuery(mre), + Type = typeArgument.Detach() + }); + return query; + + } + return null; + } + case "GroupBy": + { + if (invocation.Arguments.Count == 2) { + string parameterName1, parameterName2; + Expression keySelector, elementSelector; + if (MatchSimpleLambda(invocation.Arguments.ElementAt(0), out parameterName1, out keySelector) + && MatchSimpleLambda(invocation.Arguments.ElementAt(1), out parameterName2, out elementSelector) + && parameterName1 == parameterName2) { + QueryExpression query = new QueryExpression(); + query.Clauses.Add(new QueryFromClause { Identifier = parameterName1, Expression = ExtractQuery(mre) }); + query.Clauses.Add(new QueryGroupClause { Projection = elementSelector.Detach(), Key = keySelector.Detach() }); + return query; + } + } else if (invocation.Arguments.Count == 1) { + string parameterName; + Expression keySelector; + if (MatchSimpleLambda(invocation.Arguments.Single(), out parameterName, out keySelector)) { + QueryExpression query = new QueryExpression(); + query.Clauses.Add(new QueryFromClause { Identifier = parameterName, Expression = ExtractQuery(mre) }); + query.Clauses.Add(new QueryGroupClause { + Projection = new IdentifierExpression(parameterName), + Key = keySelector.Detach() + }); + return query; + } + } + return null; + } + case "SelectMany": + { + if (invocation.Arguments.Count != 2) + return null; + string parameterName; + Expression collectionSelector; + if (!MatchSimpleLambda(invocation.Arguments.ElementAt(0), out parameterName, out collectionSelector)) + return null; + LambdaExpression lambda = invocation.Arguments.ElementAt(1) as LambdaExpression; + if (lambda != null && lambda.Parameters.Count == 2 && lambda.Body is Expression) { + ParameterDeclaration p1 = lambda.Parameters.ElementAt(0); + ParameterDeclaration p2 = lambda.Parameters.ElementAt(1); + QueryExpression query = new QueryExpression(); + query.Clauses.Add(new QueryFromClause { Identifier = p1.Name, Expression = ExtractQuery(mre) }); + query.Clauses.Add(new QueryFromClause { Identifier = p2.Name, Expression = collectionSelector.Detach() }); + query.Clauses.Add(new QuerySelectClause { Expression = ((Expression)lambda.Body).Detach() }); + return query; + } + return null; + } + case "Where": + { + if (invocation.Arguments.Count != 1) + return null; + string parameterName; + Expression body; + if (MatchSimpleLambda(invocation.Arguments.Single(), out parameterName, out body)) { + QueryExpression query = new QueryExpression(); + query.Clauses.Add(new QueryFromClause { Identifier = parameterName, Expression = ExtractQuery(mre) }); + query.Clauses.Add(new QueryWhereClause { Condition = body.Detach() }); + return query; + } + return null; + } + case "OrderBy": + case "OrderByDescending": + case "ThenBy": + case "ThenByDescending": + { + if (invocation.Arguments.Count != 1) + return null; + string parameterName; + Expression orderExpression; + if (MatchSimpleLambda(invocation.Arguments.Single(), out parameterName, out orderExpression)) { + if (ValidateThenByChain(invocation, parameterName)) { + QueryOrderClause orderClause = new QueryOrderClause(); + InvocationExpression tmp = invocation; + while (mre.MemberName == "ThenBy" || mre.MemberName == "ThenByDescending") { + // insert new ordering at beginning + orderClause.Orderings.InsertAfter( + null, new QueryOrdering { + Expression = orderExpression.Detach(), + Direction = (mre.MemberName == "ThenBy" ? QueryOrderingDirection.None : QueryOrderingDirection.Descending) + }); + + tmp = (InvocationExpression)mre.Target; + mre = (MemberReferenceExpression)tmp.Target; + MatchSimpleLambda(tmp.Arguments.Single(), out parameterName, out orderExpression); + } + // insert new ordering at beginning + orderClause.Orderings.InsertAfter( + null, new QueryOrdering { + Expression = orderExpression.Detach(), + Direction = (mre.MemberName == "OrderBy" ? QueryOrderingDirection.None : QueryOrderingDirection.Descending) + }); + + QueryExpression query = new QueryExpression(); + query.Clauses.Add(new QueryFromClause { Identifier = parameterName, Expression = ExtractQuery(mre) }); + query.Clauses.Add(orderClause); + return query; + } + } + return null; + } + case "Join": + case "GroupJoin": + { + if (invocation.Arguments.Count != 4) + return null; + Expression source1 = mre.Target; + Expression source2 = invocation.Arguments.ElementAt(0); + string elementName1, elementName2; + Expression key1, key2; + if (!MatchSimpleLambda(invocation.Arguments.ElementAt(1), out elementName1, out key1)) + return null; + if (!MatchSimpleLambda(invocation.Arguments.ElementAt(2), out elementName2, out key2)) + return null; + LambdaExpression lambda = invocation.Arguments.ElementAt(3) as LambdaExpression; + if (lambda != null && lambda.Parameters.Count == 2 && lambda.Body is Expression) { + ParameterDeclaration p1 = lambda.Parameters.ElementAt(0); + ParameterDeclaration p2 = lambda.Parameters.ElementAt(1); + QueryExpression query = new QueryExpression(); + query.Clauses.Add(new QueryFromClause { Identifier = elementName1, Expression = source1.Detach() }); + QueryJoinClause joinClause = new QueryJoinClause(); + + joinClause.JoinIdentifier = elementName2; // join elementName2 + joinClause.InExpression = source2.Detach(); // in source2 + + Match castMatch = castPattern.Match(source2); + if (castMatch.Success) { + Expression target = castMatch.Get("inExpr").Single().Detach(); + joinClause.Type = castMatch.Get("targetType").Single().Detach(); + joinClause.InExpression = target; + } + + joinClause.OnExpression = key1.Detach(); // on key1 + joinClause.EqualsExpression = key2.Detach(); // equals key2 + if (mre.MemberName == "GroupJoin") { + joinClause.IntoIdentifier = p2.Name; // into p2.Name + } + query.Clauses.Add(joinClause); + Expression resultExpr = ((Expression)lambda.Body).Detach(); + if (p1.Name != elementName1) { + foreach (var identifier in resultExpr.Descendants.OfType().Where(id => id.Name == p1.Name)) + { + identifier.Name = elementName1; + } + } + if (p2.Name != elementName2 && mre.MemberName != "GroupJoin") { + foreach (var identifier in resultExpr.Descendants.OfType().Where(id => id.Name == p2.Name)) + { + identifier.Name = elementName2; + } + } + query.Clauses.Add(new QuerySelectClause { Expression = resultExpr }); + return query; + } + return null; + } + default: + return null; + } + } + + int id = 1; + string GenerateVariableName() + { + return "<>" + id++; + } + + /// + /// Ensure that all ThenBy's are correct, and that the list of ThenBy's is terminated by an 'OrderBy' invocation. + /// + bool ValidateThenByChain(InvocationExpression invocation, string expectedParameterName) + { + if (invocation == null || invocation.Arguments.Count != 1) + return false; + MemberReferenceExpression mre = invocation.Target as MemberReferenceExpression; + if (mre == null) + return false; + string parameterName; + Expression body; + if (!MatchSimpleLambda(invocation.Arguments.Single(), out parameterName, out body)) + return false; + if (parameterName != expectedParameterName) + return false; + + if (mre.MemberName == "OrderBy" || mre.MemberName == "OrderByDescending") + return true; + else if (mre.MemberName == "ThenBy" || mre.MemberName == "ThenByDescending") + return ValidateThenByChain(mre.Target as InvocationExpression, expectedParameterName); + else + return false; + } + + /// Matches simple lambdas of the form "a => b" + bool MatchSimpleLambda(Expression expr, out string parameterName, out Expression body) + { + LambdaExpression lambda = expr as LambdaExpression; + if (lambda != null && lambda.Parameters.Count == 1 && lambda.Body is Expression) { + ParameterDeclaration p = lambda.Parameters.Single(); + if (p.ParameterModifier == ParameterModifier.None) { + parameterName = p.Name; + body = (Expression)lambda.Body; + return true; + } + } + parameterName = null; + body = null; + return false; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/NameLookupMode.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/NameLookupMode.cs new file mode 100644 index 000000000..3196dc583 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/NameLookupMode.cs @@ -0,0 +1,47 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + public enum NameLookupMode + { + /// + /// Normal name lookup in expressions + /// + Expression, + /// + /// Name lookup in expression, where the expression is the target of an invocation. + /// Such a lookup will only return methods and delegate-typed fields. + /// + InvocationTarget, + /// + /// Normal name lookup in type references. + /// + Type, + /// + /// Name lookup in the type reference inside a using declaration. + /// + TypeInUsingDeclaration, + /// + /// Name lookup for base type references. + /// + BaseTypeReference + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/CSharpAmbience.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/CSharpAmbience.cs new file mode 100644 index 000000000..f6c153b1a --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/CSharpAmbience.cs @@ -0,0 +1,312 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// 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.IO; +using ICSharpCode.NRefactory.CSharp.Refactoring; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// C# ambience. Used to convert type system symbols to text (usually for displaying the symbol to the user; e.g. in editor tooltips) + /// + public class CSharpAmbience : IAmbience + { + public ConversionFlags ConversionFlags { get; set; } + + #region ConvertSymbol + [Obsolete("Use ConvertSymbol() instead")] + public string ConvertEntity(IEntity entity) + { + return ConvertSymbol(entity); + } + + public string ConvertSymbol(ISymbol symbol) + { + if (symbol == null) + throw new ArgumentNullException("symbol"); + + StringWriter writer = new StringWriter(); + ConvertSymbol(symbol, new TextWriterTokenWriter(writer), FormattingOptionsFactory.CreateMono ()); + return writer.ToString(); + } + + [Obsolete("Use ConvertSymbol() instead")] + public void ConvertEntity(IEntity entity, TokenWriter writer, CSharpFormattingOptions formattingPolicy) + { + ConvertSymbol(entity, writer, formattingPolicy); + } + + public void ConvertSymbol(ISymbol symbol, TokenWriter writer, CSharpFormattingOptions formattingPolicy) + { + if (symbol == null) + throw new ArgumentNullException("symbol"); + if (writer == null) + throw new ArgumentNullException("writer"); + if (formattingPolicy == null) + throw new ArgumentNullException("formattingPolicy"); + + TypeSystemAstBuilder astBuilder = CreateAstBuilder(); + AstNode node = astBuilder.ConvertSymbol(symbol); + EntityDeclaration entityDecl = node as EntityDeclaration; + if (entityDecl != null) + PrintModifiers(entityDecl.Modifiers, writer); + + if ((ConversionFlags & ConversionFlags.ShowDefinitionKeyword) == ConversionFlags.ShowDefinitionKeyword) { + if (node is TypeDeclaration) { + switch (((TypeDeclaration)node).ClassType) { + case ClassType.Class: + writer.WriteKeyword(Roles.ClassKeyword, "class"); + break; + case ClassType.Struct: + writer.WriteKeyword(Roles.StructKeyword, "struct"); + break; + case ClassType.Interface: + writer.WriteKeyword(Roles.InterfaceKeyword, "interface"); + break; + case ClassType.Enum: + writer.WriteKeyword(Roles.EnumKeyword, "enum"); + break; + default: + throw new Exception("Invalid value for ClassType"); + } + writer.Space(); + } else if (node is DelegateDeclaration) { + writer.WriteKeyword(Roles.DelegateKeyword, "delegate"); + writer.Space(); + } else if (node is EventDeclaration) { + writer.WriteKeyword(EventDeclaration.EventKeywordRole, "event"); + writer.Space(); + } else if (node is NamespaceDeclaration) { + writer.WriteKeyword(Roles.NamespaceKeyword, "namespace"); + writer.Space(); + } + } + + if ((ConversionFlags & ConversionFlags.ShowReturnType) == ConversionFlags.ShowReturnType) { + var rt = node.GetChildByRole(Roles.Type); + if (!rt.IsNull) { + rt.AcceptVisitor(new CSharpOutputVisitor(writer, formattingPolicy)); + writer.Space(); + } + } + + if (symbol is ITypeDefinition) + WriteTypeDeclarationName((ITypeDefinition)symbol, writer, formattingPolicy); + else if (symbol is IMember) + WriteMemberDeclarationName((IMember)symbol, writer, formattingPolicy); + else + writer.WriteIdentifier(Identifier.Create(symbol.Name)); + + if ((ConversionFlags & ConversionFlags.ShowParameterList) == ConversionFlags.ShowParameterList && HasParameters(symbol)) { + writer.WriteToken(symbol.SymbolKind == SymbolKind.Indexer ? Roles.LBracket : Roles.LPar, symbol.SymbolKind == SymbolKind.Indexer ? "[" : "("); + bool first = true; + foreach (var param in node.GetChildrenByRole(Roles.Parameter)) { + if (first) { + first = false; + } else { + writer.WriteToken(Roles.Comma, ","); + writer.Space(); + } + param.AcceptVisitor(new CSharpOutputVisitor(writer, formattingPolicy)); + } + writer.WriteToken(symbol.SymbolKind == SymbolKind.Indexer ? Roles.RBracket : Roles.RPar, symbol.SymbolKind == SymbolKind.Indexer ? "]" : ")"); + } + + if ((ConversionFlags & ConversionFlags.ShowBody) == ConversionFlags.ShowBody && !(node is TypeDeclaration)) { + IProperty property = symbol as IProperty; + if (property != null) { + writer.Space(); + writer.WriteToken(Roles.LBrace, "{"); + writer.Space(); + if (property.CanGet) { + writer.WriteKeyword(PropertyDeclaration.GetKeywordRole, "get"); + writer.WriteToken(Roles.Semicolon, ";"); + writer.Space(); + } + if (property.CanSet) { + writer.WriteKeyword(PropertyDeclaration.SetKeywordRole, "set"); + writer.WriteToken(Roles.Semicolon, ";"); + writer.Space(); + } + writer.WriteToken(Roles.RBrace, "}"); + } else { + writer.WriteToken(Roles.Semicolon, ";"); + } + } + } + + static bool HasParameters(ISymbol e) + { + switch (e.SymbolKind) { + case SymbolKind.TypeDefinition: + return ((ITypeDefinition)e).Kind == TypeKind.Delegate; + case SymbolKind.Indexer: + case SymbolKind.Method: + case SymbolKind.Operator: + case SymbolKind.Constructor: + case SymbolKind.Destructor: + return true; + default: + return false; + } + } + + TypeSystemAstBuilder CreateAstBuilder() + { + TypeSystemAstBuilder astBuilder = new TypeSystemAstBuilder(); + astBuilder.AddAnnotations = true; + astBuilder.ShowModifiers = (ConversionFlags & ConversionFlags.ShowModifiers) == ConversionFlags.ShowModifiers; + astBuilder.ShowAccessibility = (ConversionFlags & ConversionFlags.ShowAccessibility) == ConversionFlags.ShowAccessibility; + astBuilder.AlwaysUseShortTypeNames = (ConversionFlags & ConversionFlags.UseFullyQualifiedTypeNames) != ConversionFlags.UseFullyQualifiedTypeNames; + astBuilder.ShowParameterNames = (ConversionFlags & ConversionFlags.ShowParameterNames) == ConversionFlags.ShowParameterNames; + return astBuilder; + } + + void WriteTypeDeclarationName(ITypeDefinition typeDef, TokenWriter writer, CSharpFormattingOptions formattingPolicy) + { + TypeSystemAstBuilder astBuilder = CreateAstBuilder(); + EntityDeclaration node = astBuilder.ConvertEntity(typeDef); + if (typeDef.DeclaringTypeDefinition != null) { + WriteTypeDeclarationName(typeDef.DeclaringTypeDefinition, writer, formattingPolicy); + writer.WriteToken(Roles.Dot, "."); + } else if ((ConversionFlags & ConversionFlags.UseFullyQualifiedEntityNames) == ConversionFlags.UseFullyQualifiedEntityNames) { + if (!string.IsNullOrEmpty(typeDef.Namespace)) { + WriteQualifiedName(typeDef.Namespace, writer, formattingPolicy); + writer.WriteToken(Roles.Dot, "."); + } + } + writer.WriteIdentifier(node.NameToken); + if ((ConversionFlags & ConversionFlags.ShowTypeParameterList) == ConversionFlags.ShowTypeParameterList) { + var outputVisitor = new CSharpOutputVisitor(writer, formattingPolicy); + outputVisitor.WriteTypeParameters(node.GetChildrenByRole(Roles.TypeParameter)); + } + } + + void WriteMemberDeclarationName(IMember member, TokenWriter writer, CSharpFormattingOptions formattingPolicy) + { + TypeSystemAstBuilder astBuilder = CreateAstBuilder(); + EntityDeclaration node = astBuilder.ConvertEntity(member); + if ((ConversionFlags & ConversionFlags.ShowDeclaringType) == ConversionFlags.ShowDeclaringType) { + ConvertType(member.DeclaringType, writer, formattingPolicy); + writer.WriteToken(Roles.Dot, "."); + } + switch (member.SymbolKind) { + case SymbolKind.Indexer: + writer.WriteKeyword(Roles.Identifier, "this"); + break; + case SymbolKind.Constructor: + WriteQualifiedName(member.DeclaringType.Name, writer, formattingPolicy); + break; + case SymbolKind.Destructor: + writer.WriteToken(DestructorDeclaration.TildeRole, "~"); + WriteQualifiedName(member.DeclaringType.Name, writer, formattingPolicy); + break; + case SymbolKind.Operator: + switch (member.Name) { + case "op_Implicit": + writer.WriteKeyword(OperatorDeclaration.ImplicitRole, "implicit"); + writer.Space(); + writer.WriteKeyword(OperatorDeclaration.OperatorKeywordRole, "operator"); + writer.Space(); + ConvertType(member.ReturnType, writer, formattingPolicy); + break; + case "op_Explicit": + writer.WriteKeyword(OperatorDeclaration.ExplicitRole, "explicit"); + writer.Space(); + writer.WriteKeyword(OperatorDeclaration.OperatorKeywordRole, "operator"); + writer.Space(); + ConvertType(member.ReturnType, writer, formattingPolicy); + break; + default: + writer.WriteKeyword(OperatorDeclaration.OperatorKeywordRole, "operator"); + writer.Space(); + var operatorType = OperatorDeclaration.GetOperatorType(member.Name); + if (operatorType.HasValue) + writer.WriteToken(OperatorDeclaration.GetRole(operatorType.Value), OperatorDeclaration.GetToken(operatorType.Value)); + else + writer.WriteIdentifier(node.NameToken); + break; + } + break; + default: + writer.WriteIdentifier(Identifier.Create(member.Name)); + break; + } + if ((ConversionFlags & ConversionFlags.ShowTypeParameterList) == ConversionFlags.ShowTypeParameterList && member.SymbolKind == SymbolKind.Method) { + var outputVisitor = new CSharpOutputVisitor(writer, formattingPolicy); + outputVisitor.WriteTypeParameters(node.GetChildrenByRole(Roles.TypeParameter)); + } + } + + void PrintModifiers(Modifiers modifiers, TokenWriter writer) + { + foreach (var m in CSharpModifierToken.AllModifiers) { + if ((modifiers & m) == m) { + writer.WriteKeyword(EntityDeclaration.ModifierRole, CSharpModifierToken.GetModifierName(m)); + writer.Space(); + } + } + } + + void WriteQualifiedName(string name, TokenWriter writer, CSharpFormattingOptions formattingPolicy) + { + var node = AstType.Create(name); + var outputVisitor = new CSharpOutputVisitor(writer, formattingPolicy); + node.AcceptVisitor(outputVisitor); + } + #endregion + + public string ConvertVariable(IVariable v) + { + TypeSystemAstBuilder astBuilder = CreateAstBuilder(); + AstNode astNode = astBuilder.ConvertVariable(v); + return astNode.ToString().TrimEnd(';', '\r', '\n', (char)8232); + } + + public string ConvertType(IType type) + { + if (type == null) + throw new ArgumentNullException("type"); + + TypeSystemAstBuilder astBuilder = CreateAstBuilder(); + astBuilder.AlwaysUseShortTypeNames = (ConversionFlags & ConversionFlags.UseFullyQualifiedEntityNames) != ConversionFlags.UseFullyQualifiedEntityNames; + AstType astType = astBuilder.ConvertType(type); + return astType.ToString(); + } + + public void ConvertType(IType type, TokenWriter writer, CSharpFormattingOptions formattingPolicy) + { + TypeSystemAstBuilder astBuilder = CreateAstBuilder(); + astBuilder.AlwaysUseShortTypeNames = (ConversionFlags & ConversionFlags.UseFullyQualifiedEntityNames) != ConversionFlags.UseFullyQualifiedEntityNames; + AstType astType = astBuilder.ConvertType(type); + astType.AcceptVisitor(new CSharpOutputVisitor(writer, formattingPolicy)); + } + + public string ConvertConstantValue(object constantValue) + { + return TextWriterTokenWriter.PrintPrimitiveValue(constantValue); + } + + public string WrapComment(string comment) + { + return "// " + comment; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/CSharpOutputVisitor.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/CSharpOutputVisitor.cs new file mode 100644 index 000000000..4fc5ed9f7 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/CSharpOutputVisitor.cs @@ -0,0 +1,2401 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ICSharpCode.NRefactory.PatternMatching; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.CSharp; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Outputs the AST. + /// + public class CSharpOutputVisitor : IAstVisitor + { + readonly TokenWriter writer; + readonly CSharpFormattingOptions policy; + readonly Stack containerStack = new Stack (); + + public CSharpOutputVisitor (TextWriter textWriter, CSharpFormattingOptions formattingPolicy) + { + if (textWriter == null) { + throw new ArgumentNullException ("textWriter"); + } + if (formattingPolicy == null) { + throw new ArgumentNullException ("formattingPolicy"); + } + this.writer = TokenWriter.Create(textWriter); + this.policy = formattingPolicy; + } + + public CSharpOutputVisitor (TokenWriter writer, CSharpFormattingOptions formattingPolicy) + { + if (writer == null) { + throw new ArgumentNullException ("writer"); + } + if (formattingPolicy == null) { + throw new ArgumentNullException ("formattingPolicy"); + } + this.writer = new InsertSpecialsDecorator(new InsertRequiredSpacesDecorator(writer)); + this.policy = formattingPolicy; + } + + #region StartNode/EndNode + void StartNode(AstNode node) + { + // Ensure that nodes are visited in the proper nested order. + // Jumps to different subtrees are allowed only for the child of a placeholder node. + Debug.Assert(containerStack.Count == 0 || node.Parent == containerStack.Peek() || containerStack.Peek().NodeType == NodeType.Pattern); + containerStack.Push(node); + writer.StartNode(node); + } + + void EndNode(AstNode node) + { + Debug.Assert(node == containerStack.Peek()); + containerStack.Pop(); + writer.EndNode(node); + } + #endregion + + #region Comma + /// + /// Writes a comma. + /// + /// The next node after the comma. + /// When set prevents printing a space after comma. + void Comma(AstNode nextNode, bool noSpaceAfterComma = false) + { + Space(policy.SpaceBeforeBracketComma); + // TODO: Comma policy has changed. + writer.WriteToken(Roles.Comma, ","); + Space(!noSpaceAfterComma && policy.SpaceAfterBracketComma); + // TODO: Comma policy has changed. + } + + /// + /// Writes an optional comma, e.g. at the end of an enum declaration or in an array initializer + /// + void OptionalComma(AstNode pos) + { + // Look if there's a comma after the current node, and insert it if it exists. + while (pos != null && pos.NodeType == NodeType.Whitespace) { + pos = pos.NextSibling; + } + if (pos != null && pos.Role == Roles.Comma) { + Comma(null, noSpaceAfterComma: true); + } + } + + /// + /// Writes an optional semicolon, e.g. at the end of a type or namespace declaration. + /// + void OptionalSemicolon(AstNode pos) + { + // Look if there's a semicolon after the current node, and insert it if it exists. + while (pos != null && pos.NodeType == NodeType.Whitespace) { + pos = pos.PrevSibling; + } + if (pos != null && pos.Role == Roles.Semicolon) { + Semicolon(); + } + } + + void WriteCommaSeparatedList(IEnumerable list) + { + bool isFirst = true; + foreach (AstNode node in list) { + if (isFirst) { + isFirst = false; + } else { + Comma(node); + } + node.AcceptVisitor(this); + } + } + + void WriteCommaSeparatedListInParenthesis(IEnumerable list, bool spaceWithin) + { + LPar(); + if (list.Any()) { + Space(spaceWithin); + WriteCommaSeparatedList(list); + Space(spaceWithin); + } + RPar(); + } + + #if DOTNET35 + void WriteCommaSeparatedList(IEnumerable list) + { + WriteCommaSeparatedList(list.SafeCast()); + } + + void WriteCommaSeparatedList(IEnumerable list) + { + WriteCommaSeparatedList(list.SafeCast()); + } + + void WriteCommaSeparatedListInParenthesis(IEnumerable list, bool spaceWithin) + { + WriteCommaSeparatedListInParenthesis(list.SafeCast(), spaceWithin); + } + + void WriteCommaSeparatedListInParenthesis(IEnumerable list, bool spaceWithin) + { + WriteCommaSeparatedListInParenthesis(list.SafeCast(), spaceWithin); + } + + #endif + + void WriteCommaSeparatedListInBrackets(IEnumerable list, bool spaceWithin) + { + WriteToken(Roles.LBracket); + if (list.Any()) { + Space(spaceWithin); + WriteCommaSeparatedList(list); + Space(spaceWithin); + } + WriteToken(Roles.RBracket); + } + + void WriteCommaSeparatedListInBrackets(IEnumerable list) + { + WriteToken(Roles.LBracket); + if (list.Any()) { + Space(policy.SpacesWithinBrackets); + WriteCommaSeparatedList(list); + Space(policy.SpacesWithinBrackets); + } + WriteToken(Roles.RBracket); + } + #endregion + + #region Write tokens + bool isAtStartOfLine = true; + + /// + /// Writes a keyword, and all specials up to + /// + void WriteKeyword(TokenRole tokenRole) + { + WriteKeyword(tokenRole.Token, tokenRole); + } + + void WriteKeyword(string token, Role tokenRole = null) + { + writer.WriteKeyword(tokenRole, token); + isAtStartOfLine = false; + } + + void WriteIdentifier(Identifier identifier) + { + writer.WriteIdentifier(identifier); + isAtStartOfLine = false; + } + + void WriteIdentifier(string identifier) + { + AstType.Create(identifier).AcceptVisitor(this); + isAtStartOfLine = false; + } + + void WriteToken(TokenRole tokenRole) + { + WriteToken(tokenRole.Token, tokenRole); + } + + void WriteToken(string token, Role tokenRole) + { + writer.WriteToken(tokenRole, token); + isAtStartOfLine = false; + } + + void LPar() + { + WriteToken(Roles.LPar); + } + + void RPar() + { + WriteToken(Roles.RPar); + } + + /// + /// Marks the end of a statement + /// + void Semicolon() + { + Role role = containerStack.Peek().Role; + // get the role of the current node + if (!(role == ForStatement.InitializerRole || role == ForStatement.IteratorRole || role == UsingStatement.ResourceAcquisitionRole)) { + WriteToken(Roles.Semicolon); + NewLine(); + } + } + + /// + /// Writes a space depending on policy. + /// + void Space(bool addSpace = true) + { + if (addSpace) { + writer.Space(); + } + } + + void NewLine() + { + writer.NewLine(); + isAtStartOfLine = true; + } + + void OpenBrace(BraceStyle style) + { + switch (style) { + case BraceStyle.DoNotChange: + case BraceStyle.EndOfLine: + case BraceStyle.BannerStyle: + if (!isAtStartOfLine) + writer.Space(); + writer.WriteToken(Roles.LBrace, "{"); + break; + case BraceStyle.EndOfLineWithoutSpace: + writer.WriteToken(Roles.LBrace, "{"); + break; + case BraceStyle.NextLine: + if (!isAtStartOfLine) + NewLine(); + writer.WriteToken(Roles.LBrace, "{"); + break; + case BraceStyle.NextLineShifted: + NewLine(); + writer.Indent(); + writer.WriteToken(Roles.LBrace, "{"); + NewLine(); + return; + case BraceStyle.NextLineShifted2: + NewLine(); + writer.Indent(); + writer.WriteToken(Roles.LBrace, "{"); + break; + default: + throw new ArgumentOutOfRangeException (); + } + writer.Indent(); + NewLine(); + } + + void CloseBrace(BraceStyle style) + { + switch (style) { + case BraceStyle.DoNotChange: + case BraceStyle.EndOfLine: + case BraceStyle.EndOfLineWithoutSpace: + case BraceStyle.NextLine: + writer.Unindent(); + writer.WriteToken(Roles.RBrace, "}"); + isAtStartOfLine = false; + break; + case BraceStyle.BannerStyle: + case BraceStyle.NextLineShifted: + writer.WriteToken(Roles.RBrace, "}"); + isAtStartOfLine = false; + writer.Unindent(); + break; + case BraceStyle.NextLineShifted2: + writer.Unindent(); + writer.WriteToken(Roles.RBrace, "}"); + isAtStartOfLine = false; + writer.Unindent(); + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + + #endregion + + #region IsKeyword Test + static readonly HashSet unconditionalKeywords = new HashSet { + "abstract", "as", "base", "bool", "break", "byte", "case", "catch", + "char", "checked", "class", "const", "continue", "decimal", "default", "delegate", + "do", "double", "else", "enum", "event", "explicit", "extern", "false", + "finally", "fixed", "float", "for", "foreach", "goto", "if", "implicit", + "in", "int", "interface", "internal", "is", "lock", "long", "namespace", + "new", "null", "object", "operator", "out", "override", "params", "private", + "protected", "public", "readonly", "ref", "return", "sbyte", "sealed", "short", + "sizeof", "stackalloc", "static", "string", "struct", "switch", "this", "throw", + "true", "try", "typeof", "uint", "ulong", "unchecked", "unsafe", "ushort", + "using", "virtual", "void", "volatile", "while" + }; + static readonly HashSet queryKeywords = new HashSet { + "from", "where", "join", "on", "equals", "into", "let", "orderby", + "ascending", "descending", "select", "group", "by" + }; + + /// + /// Determines whether the specified identifier is a keyword in the given context. + /// + public static bool IsKeyword(string identifier, AstNode context) + { + if (unconditionalKeywords.Contains(identifier)) { + return true; + } + foreach (AstNode ancestor in context.Ancestors) { + if (ancestor is QueryExpression && queryKeywords.Contains(identifier)) { + return true; + } + if (identifier == "await") { + // with lambdas/anonymous methods, + if (ancestor is LambdaExpression) { + return ((LambdaExpression)ancestor).IsAsync; + } + if (ancestor is AnonymousMethodExpression) { + return ((AnonymousMethodExpression)ancestor).IsAsync; + } + if (ancestor is EntityDeclaration) { + return (((EntityDeclaration)ancestor).Modifiers & Modifiers.Async) == Modifiers.Async; + } + } + } + return false; + } + #endregion + + #region Write constructs + void WriteTypeArguments(IEnumerable typeArguments) + { + if (typeArguments.Any()) { + WriteToken(Roles.LChevron); + WriteCommaSeparatedList(typeArguments); + WriteToken(Roles.RChevron); + } + } + + public void WriteTypeParameters(IEnumerable typeParameters) + { + if (typeParameters.Any()) { + WriteToken(Roles.LChevron); + WriteCommaSeparatedList(typeParameters); + WriteToken(Roles.RChevron); + } + } + + void WriteModifiers(IEnumerable modifierTokens) + { + foreach (CSharpModifierToken modifier in modifierTokens) { + modifier.AcceptVisitor(this); + } + } + + void WriteQualifiedIdentifier(IEnumerable identifiers) + { + bool first = true; + foreach (Identifier ident in identifiers) { + if (first) { + first = false; + } else { + writer.WriteToken(Roles.Dot, "."); + } + writer.WriteIdentifier(ident); + } + } + + void WriteEmbeddedStatement(Statement embeddedStatement) + { + if (embeddedStatement.IsNull) { + NewLine(); + return; + } + BlockStatement block = embeddedStatement as BlockStatement; + if (block != null) { + VisitBlockStatement(block); + } else { + NewLine(); + writer.Indent(); + embeddedStatement.AcceptVisitor(this); + writer.Unindent(); + } + } + + void WriteMethodBody(BlockStatement body) + { + if (body.IsNull) { + Semicolon(); + } else { + VisitBlockStatement(body); + } + } + + void WriteAttributes(IEnumerable attributes) + { + foreach (AttributeSection attr in attributes) { + attr.AcceptVisitor(this); + } + } + + void WritePrivateImplementationType(AstType privateImplementationType) + { + if (!privateImplementationType.IsNull) { + privateImplementationType.AcceptVisitor(this); + WriteToken(Roles.Dot); + } + } + + #endregion + + #region Expressions + public void VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression) + { + StartNode(anonymousMethodExpression); + if (anonymousMethodExpression.IsAsync) { + WriteKeyword(AnonymousMethodExpression.AsyncModifierRole); + Space(); + } + WriteKeyword(AnonymousMethodExpression.DelegateKeywordRole); + if (anonymousMethodExpression.HasParameterList) { + Space(policy.SpaceBeforeMethodDeclarationParentheses); + WriteCommaSeparatedListInParenthesis(anonymousMethodExpression.Parameters, policy.SpaceWithinMethodDeclarationParentheses); + } + anonymousMethodExpression.Body.AcceptVisitor(this); + EndNode(anonymousMethodExpression); + } + + public void VisitUndocumentedExpression(UndocumentedExpression undocumentedExpression) + { + StartNode(undocumentedExpression); + switch (undocumentedExpression.UndocumentedExpressionType) { + case UndocumentedExpressionType.ArgList: + case UndocumentedExpressionType.ArgListAccess: + WriteKeyword(UndocumentedExpression.ArglistKeywordRole); + break; + case UndocumentedExpressionType.MakeRef: + WriteKeyword(UndocumentedExpression.MakerefKeywordRole); + break; + case UndocumentedExpressionType.RefType: + WriteKeyword(UndocumentedExpression.ReftypeKeywordRole); + break; + case UndocumentedExpressionType.RefValue: + WriteKeyword(UndocumentedExpression.RefvalueKeywordRole); + break; + } + if (undocumentedExpression.Arguments.Count > 0) { + Space(policy.SpaceBeforeMethodCallParentheses); + WriteCommaSeparatedListInParenthesis(undocumentedExpression.Arguments, policy.SpaceWithinMethodCallParentheses); + } + EndNode(undocumentedExpression); + } + + public void VisitArrayCreateExpression(ArrayCreateExpression arrayCreateExpression) + { + StartNode(arrayCreateExpression); + WriteKeyword(ArrayCreateExpression.NewKeywordRole); + arrayCreateExpression.Type.AcceptVisitor(this); + if (arrayCreateExpression.Arguments.Count > 0) { + WriteCommaSeparatedListInBrackets(arrayCreateExpression.Arguments); + } + foreach (var specifier in arrayCreateExpression.AdditionalArraySpecifiers) { + specifier.AcceptVisitor(this); + } + arrayCreateExpression.Initializer.AcceptVisitor(this); + EndNode(arrayCreateExpression); + } + + public void VisitArrayInitializerExpression(ArrayInitializerExpression arrayInitializerExpression) + { + StartNode(arrayInitializerExpression); + // "new List { { 1 } }" and "new List { 1 }" are the same semantically. + // We also use the same AST for both: we always use two nested ArrayInitializerExpressions + // for collection initializers, even if the user did not write nested brackets. + // The output visitor will output nested braces only if they are necessary, + // or if the braces tokens exist in the AST. + bool bracesAreOptional = arrayInitializerExpression.Elements.Count == 1 + && IsObjectOrCollectionInitializer(arrayInitializerExpression.Parent) + && !CanBeConfusedWithObjectInitializer(arrayInitializerExpression.Elements.Single()); + if (bracesAreOptional && arrayInitializerExpression.LBraceToken.IsNull) { + arrayInitializerExpression.Elements.Single().AcceptVisitor(this); + } else { + PrintInitializerElements(arrayInitializerExpression.Elements); + } + EndNode(arrayInitializerExpression); + } + + bool CanBeConfusedWithObjectInitializer(Expression expr) + { + // "int a; new List { a = 1 };" is an object initalizers and invalid, but + // "int a; new List { { a = 1 } };" is a valid collection initializer. + AssignmentExpression ae = expr as AssignmentExpression; + return ae != null && ae.Operator == AssignmentOperatorType.Assign; + } + + bool IsObjectOrCollectionInitializer(AstNode node) + { + if (!(node is ArrayInitializerExpression)) { + return false; + } + if (node.Parent is ObjectCreateExpression) { + return node.Role == ObjectCreateExpression.InitializerRole; + } + if (node.Parent is NamedExpression) { + return node.Role == Roles.Expression; + } + return false; + } + + void PrintInitializerElements(AstNodeCollection elements) + { + BraceStyle style; + if (policy.ArrayInitializerWrapping == Wrapping.WrapAlways) { + style = BraceStyle.NextLine; + } else { + style = BraceStyle.EndOfLine; + } + OpenBrace(style); + bool isFirst = true; + AstNode last = null; + foreach (AstNode node in elements) { + if (isFirst) { + isFirst = false; + } else { + Comma(node, noSpaceAfterComma: true); + NewLine(); + } + last = node; + node.AcceptVisitor(this); + } + if (last != null) + OptionalComma(last.NextSibling); + NewLine(); + CloseBrace(style); + } + + public void VisitAsExpression(AsExpression asExpression) + { + StartNode(asExpression); + asExpression.Expression.AcceptVisitor(this); + Space(); + WriteKeyword(AsExpression.AsKeywordRole); + Space(); + asExpression.Type.AcceptVisitor(this); + EndNode(asExpression); + } + + public void VisitAssignmentExpression(AssignmentExpression assignmentExpression) + { + StartNode(assignmentExpression); + assignmentExpression.Left.AcceptVisitor(this); + Space(policy.SpaceAroundAssignment); + WriteToken(AssignmentExpression.GetOperatorRole(assignmentExpression.Operator)); + Space(policy.SpaceAroundAssignment); + assignmentExpression.Right.AcceptVisitor(this); + EndNode(assignmentExpression); + } + + public void VisitBaseReferenceExpression(BaseReferenceExpression baseReferenceExpression) + { + StartNode(baseReferenceExpression); + WriteKeyword("base", baseReferenceExpression.Role); + EndNode(baseReferenceExpression); + } + + public void VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression) + { + StartNode(binaryOperatorExpression); + binaryOperatorExpression.Left.AcceptVisitor(this); + bool spacePolicy; + switch (binaryOperatorExpression.Operator) { + case BinaryOperatorType.BitwiseAnd: + case BinaryOperatorType.BitwiseOr: + case BinaryOperatorType.ExclusiveOr: + spacePolicy = policy.SpaceAroundBitwiseOperator; + break; + case BinaryOperatorType.ConditionalAnd: + case BinaryOperatorType.ConditionalOr: + spacePolicy = policy.SpaceAroundLogicalOperator; + break; + case BinaryOperatorType.GreaterThan: + case BinaryOperatorType.GreaterThanOrEqual: + case BinaryOperatorType.LessThanOrEqual: + case BinaryOperatorType.LessThan: + spacePolicy = policy.SpaceAroundRelationalOperator; + break; + case BinaryOperatorType.Equality: + case BinaryOperatorType.InEquality: + spacePolicy = policy.SpaceAroundEqualityOperator; + break; + case BinaryOperatorType.Add: + case BinaryOperatorType.Subtract: + spacePolicy = policy.SpaceAroundAdditiveOperator; + break; + case BinaryOperatorType.Multiply: + case BinaryOperatorType.Divide: + case BinaryOperatorType.Modulus: + spacePolicy = policy.SpaceAroundMultiplicativeOperator; + break; + case BinaryOperatorType.ShiftLeft: + case BinaryOperatorType.ShiftRight: + spacePolicy = policy.SpaceAroundShiftOperator; + break; + case BinaryOperatorType.NullCoalescing: + spacePolicy = true; + break; + default: + throw new NotSupportedException ("Invalid value for BinaryOperatorType"); + } + Space(spacePolicy); + WriteToken(BinaryOperatorExpression.GetOperatorRole(binaryOperatorExpression.Operator)); + Space(spacePolicy); + binaryOperatorExpression.Right.AcceptVisitor(this); + EndNode(binaryOperatorExpression); + } + + public void VisitCastExpression(CastExpression castExpression) + { + StartNode(castExpression); + LPar(); + Space(policy.SpacesWithinCastParentheses); + castExpression.Type.AcceptVisitor(this); + Space(policy.SpacesWithinCastParentheses); + RPar(); + Space(policy.SpaceAfterTypecast); + castExpression.Expression.AcceptVisitor(this); + EndNode(castExpression); + } + + public void VisitCheckedExpression(CheckedExpression checkedExpression) + { + StartNode(checkedExpression); + WriteKeyword(CheckedExpression.CheckedKeywordRole); + LPar(); + Space(policy.SpacesWithinCheckedExpressionParantheses); + checkedExpression.Expression.AcceptVisitor(this); + Space(policy.SpacesWithinCheckedExpressionParantheses); + RPar(); + EndNode(checkedExpression); + } + + public void VisitConditionalExpression(ConditionalExpression conditionalExpression) + { + StartNode(conditionalExpression); + conditionalExpression.Condition.AcceptVisitor(this); + + Space(policy.SpaceBeforeConditionalOperatorCondition); + WriteToken(ConditionalExpression.QuestionMarkRole); + Space(policy.SpaceAfterConditionalOperatorCondition); + + conditionalExpression.TrueExpression.AcceptVisitor(this); + + Space(policy.SpaceBeforeConditionalOperatorSeparator); + WriteToken(ConditionalExpression.ColonRole); + Space(policy.SpaceAfterConditionalOperatorSeparator); + + conditionalExpression.FalseExpression.AcceptVisitor(this); + + EndNode(conditionalExpression); + } + + public void VisitDefaultValueExpression(DefaultValueExpression defaultValueExpression) + { + StartNode(defaultValueExpression); + + WriteKeyword(DefaultValueExpression.DefaultKeywordRole); + LPar(); + Space(policy.SpacesWithinTypeOfParentheses); + defaultValueExpression.Type.AcceptVisitor(this); + Space(policy.SpacesWithinTypeOfParentheses); + RPar(); + + EndNode(defaultValueExpression); + } + + public void VisitDirectionExpression(DirectionExpression directionExpression) + { + StartNode(directionExpression); + + switch (directionExpression.FieldDirection) { + case FieldDirection.Out: + WriteKeyword(DirectionExpression.OutKeywordRole); + break; + case FieldDirection.Ref: + WriteKeyword(DirectionExpression.RefKeywordRole); + break; + default: + throw new NotSupportedException ("Invalid value for FieldDirection"); + } + Space(); + directionExpression.Expression.AcceptVisitor(this); + + EndNode(directionExpression); + } + + public void VisitIdentifierExpression(IdentifierExpression identifierExpression) + { + StartNode(identifierExpression); + WriteIdentifier(identifierExpression.IdentifierToken); + WriteTypeArguments(identifierExpression.TypeArguments); + EndNode(identifierExpression); + } + + public void VisitIndexerExpression(IndexerExpression indexerExpression) + { + StartNode(indexerExpression); + indexerExpression.Target.AcceptVisitor(this); + Space(policy.SpaceBeforeMethodCallParentheses); + WriteCommaSeparatedListInBrackets(indexerExpression.Arguments); + EndNode(indexerExpression); + } + + public void VisitInvocationExpression(InvocationExpression invocationExpression) + { + StartNode(invocationExpression); + invocationExpression.Target.AcceptVisitor(this); + Space(policy.SpaceBeforeMethodCallParentheses); + WriteCommaSeparatedListInParenthesis(invocationExpression.Arguments, policy.SpaceWithinMethodCallParentheses); + EndNode(invocationExpression); + } + + public void VisitIsExpression(IsExpression isExpression) + { + StartNode(isExpression); + isExpression.Expression.AcceptVisitor(this); + Space(); + WriteKeyword(IsExpression.IsKeywordRole); + isExpression.Type.AcceptVisitor(this); + EndNode(isExpression); + } + + public void VisitLambdaExpression(LambdaExpression lambdaExpression) + { + StartNode(lambdaExpression); + if (lambdaExpression.IsAsync) { + WriteKeyword(LambdaExpression.AsyncModifierRole); + Space(); + } + if (LambdaNeedsParenthesis(lambdaExpression)) { + WriteCommaSeparatedListInParenthesis(lambdaExpression.Parameters, policy.SpaceWithinMethodDeclarationParentheses); + } else { + lambdaExpression.Parameters.Single().AcceptVisitor(this); + } + Space(); + WriteToken(LambdaExpression.ArrowRole); + Space(); + lambdaExpression.Body.AcceptVisitor(this); + EndNode(lambdaExpression); + } + + bool LambdaNeedsParenthesis(LambdaExpression lambdaExpression) + { + if (lambdaExpression.Parameters.Count != 1) { + return true; + } + var p = lambdaExpression.Parameters.Single(); + return !(p.Type.IsNull && p.ParameterModifier == ParameterModifier.None); + } + + public void VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression) + { + StartNode(memberReferenceExpression); + memberReferenceExpression.Target.AcceptVisitor(this); + WriteToken(Roles.Dot); + WriteIdentifier(memberReferenceExpression.MemberNameToken); + WriteTypeArguments(memberReferenceExpression.TypeArguments); + EndNode(memberReferenceExpression); + } + + public void VisitNamedArgumentExpression(NamedArgumentExpression namedArgumentExpression) + { + StartNode(namedArgumentExpression); + WriteIdentifier(namedArgumentExpression.NameToken); + WriteToken(Roles.Colon); + Space(); + namedArgumentExpression.Expression.AcceptVisitor(this); + EndNode(namedArgumentExpression); + } + + public void VisitNamedExpression(NamedExpression namedExpression) + { + StartNode(namedExpression); + WriteIdentifier(namedExpression.NameToken); + Space(); + WriteToken(Roles.Assign); + Space(); + namedExpression.Expression.AcceptVisitor(this); + EndNode(namedExpression); + } + + public void VisitNullReferenceExpression(NullReferenceExpression nullReferenceExpression) + { + StartNode(nullReferenceExpression); + writer.WritePrimitiveValue(null); + EndNode(nullReferenceExpression); + } + + public void VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression) + { + StartNode(objectCreateExpression); + WriteKeyword(ObjectCreateExpression.NewKeywordRole); + objectCreateExpression.Type.AcceptVisitor(this); + bool useParenthesis = objectCreateExpression.Arguments.Any() || objectCreateExpression.Initializer.IsNull; + // also use parenthesis if there is an '(' token + if (!objectCreateExpression.LParToken.IsNull) { + useParenthesis = true; + } + if (useParenthesis) { + Space(policy.SpaceBeforeMethodCallParentheses); + WriteCommaSeparatedListInParenthesis(objectCreateExpression.Arguments, policy.SpaceWithinMethodCallParentheses); + } + objectCreateExpression.Initializer.AcceptVisitor(this); + EndNode(objectCreateExpression); + } + + public void VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression) + { + StartNode(anonymousTypeCreateExpression); + WriteKeyword(AnonymousTypeCreateExpression.NewKeywordRole); + PrintInitializerElements(anonymousTypeCreateExpression.Initializers); + EndNode(anonymousTypeCreateExpression); + } + + public void VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression) + { + StartNode(parenthesizedExpression); + LPar(); + Space(policy.SpacesWithinParentheses); + parenthesizedExpression.Expression.AcceptVisitor(this); + Space(policy.SpacesWithinParentheses); + RPar(); + EndNode(parenthesizedExpression); + } + + public void VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression) + { + StartNode(pointerReferenceExpression); + pointerReferenceExpression.Target.AcceptVisitor(this); + WriteToken(PointerReferenceExpression.ArrowRole); + WriteIdentifier(pointerReferenceExpression.MemberNameToken); + WriteTypeArguments(pointerReferenceExpression.TypeArguments); + EndNode(pointerReferenceExpression); + } + + #region VisitPrimitiveExpression + public void VisitPrimitiveExpression(PrimitiveExpression primitiveExpression) + { + StartNode(primitiveExpression); + writer.WritePrimitiveValue(primitiveExpression.Value, primitiveExpression.UnsafeLiteralValue); + EndNode(primitiveExpression); + } + #endregion + + public void VisitSizeOfExpression(SizeOfExpression sizeOfExpression) + { + StartNode(sizeOfExpression); + + WriteKeyword(SizeOfExpression.SizeofKeywordRole); + LPar(); + Space(policy.SpacesWithinSizeOfParentheses); + sizeOfExpression.Type.AcceptVisitor(this); + Space(policy.SpacesWithinSizeOfParentheses); + RPar(); + + EndNode(sizeOfExpression); + } + + public void VisitStackAllocExpression(StackAllocExpression stackAllocExpression) + { + StartNode(stackAllocExpression); + WriteKeyword(StackAllocExpression.StackallocKeywordRole); + stackAllocExpression.Type.AcceptVisitor(this); + WriteCommaSeparatedListInBrackets(new[] { stackAllocExpression.CountExpression }); + EndNode(stackAllocExpression); + } + + public void VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression) + { + StartNode(thisReferenceExpression); + WriteKeyword("this", thisReferenceExpression.Role); + EndNode(thisReferenceExpression); + } + + public void VisitTypeOfExpression(TypeOfExpression typeOfExpression) + { + StartNode(typeOfExpression); + + WriteKeyword(TypeOfExpression.TypeofKeywordRole); + LPar(); + Space(policy.SpacesWithinTypeOfParentheses); + typeOfExpression.Type.AcceptVisitor(this); + Space(policy.SpacesWithinTypeOfParentheses); + RPar(); + + EndNode(typeOfExpression); + } + + public void VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression) + { + StartNode(typeReferenceExpression); + typeReferenceExpression.Type.AcceptVisitor(this); + EndNode(typeReferenceExpression); + } + + public void VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression) + { + StartNode(unaryOperatorExpression); + UnaryOperatorType opType = unaryOperatorExpression.Operator; + var opSymbol = UnaryOperatorExpression.GetOperatorRole(opType); + if (opType == UnaryOperatorType.Await) { + WriteKeyword(opSymbol); + } else if (!(opType == UnaryOperatorType.PostIncrement || opType == UnaryOperatorType.PostDecrement)) { + WriteToken(opSymbol); + } + unaryOperatorExpression.Expression.AcceptVisitor(this); + if (opType == UnaryOperatorType.PostIncrement || opType == UnaryOperatorType.PostDecrement) { + WriteToken(opSymbol); + } + EndNode(unaryOperatorExpression); + } + + public void VisitUncheckedExpression(UncheckedExpression uncheckedExpression) + { + StartNode(uncheckedExpression); + WriteKeyword(UncheckedExpression.UncheckedKeywordRole); + LPar(); + Space(policy.SpacesWithinCheckedExpressionParantheses); + uncheckedExpression.Expression.AcceptVisitor(this); + Space(policy.SpacesWithinCheckedExpressionParantheses); + RPar(); + EndNode(uncheckedExpression); + } + + #endregion + + #region Query Expressions + public void VisitQueryExpression(QueryExpression queryExpression) + { + StartNode(queryExpression); + bool indent = queryExpression.Parent is QueryClause && !(queryExpression.Parent is QueryContinuationClause); + if (indent) { + writer.Indent(); + NewLine(); + } + bool first = true; + foreach (var clause in queryExpression.Clauses) { + if (first) { + first = false; + } else { + if (!(clause is QueryContinuationClause)) { + NewLine(); + } + } + clause.AcceptVisitor(this); + } + if (indent) { + writer.Unindent(); + } + EndNode(queryExpression); + } + + public void VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause) + { + StartNode(queryContinuationClause); + queryContinuationClause.PrecedingQuery.AcceptVisitor(this); + Space(); + WriteKeyword(QueryContinuationClause.IntoKeywordRole); + Space(); + WriteIdentifier(queryContinuationClause.IdentifierToken); + EndNode(queryContinuationClause); + } + + public void VisitQueryFromClause(QueryFromClause queryFromClause) + { + StartNode(queryFromClause); + WriteKeyword(QueryFromClause.FromKeywordRole); + queryFromClause.Type.AcceptVisitor(this); + Space(); + WriteIdentifier(queryFromClause.IdentifierToken); + Space(); + WriteKeyword(QueryFromClause.InKeywordRole); + Space(); + queryFromClause.Expression.AcceptVisitor(this); + EndNode(queryFromClause); + } + + public void VisitQueryLetClause(QueryLetClause queryLetClause) + { + StartNode(queryLetClause); + WriteKeyword(QueryLetClause.LetKeywordRole); + Space(); + WriteIdentifier(queryLetClause.IdentifierToken); + Space(policy.SpaceAroundAssignment); + WriteToken(Roles.Assign); + Space(policy.SpaceAroundAssignment); + queryLetClause.Expression.AcceptVisitor(this); + EndNode(queryLetClause); + } + + public void VisitQueryWhereClause(QueryWhereClause queryWhereClause) + { + StartNode(queryWhereClause); + WriteKeyword(QueryWhereClause.WhereKeywordRole); + Space(); + queryWhereClause.Condition.AcceptVisitor(this); + EndNode(queryWhereClause); + } + + public void VisitQueryJoinClause(QueryJoinClause queryJoinClause) + { + StartNode(queryJoinClause); + WriteKeyword(QueryJoinClause.JoinKeywordRole); + queryJoinClause.Type.AcceptVisitor(this); + Space(); + WriteIdentifier(queryJoinClause.JoinIdentifierToken); + Space(); + WriteKeyword(QueryJoinClause.InKeywordRole); + Space(); + queryJoinClause.InExpression.AcceptVisitor(this); + Space(); + WriteKeyword(QueryJoinClause.OnKeywordRole); + Space(); + queryJoinClause.OnExpression.AcceptVisitor(this); + Space(); + WriteKeyword(QueryJoinClause.EqualsKeywordRole); + Space(); + queryJoinClause.EqualsExpression.AcceptVisitor(this); + if (queryJoinClause.IsGroupJoin) { + Space(); + WriteKeyword(QueryJoinClause.IntoKeywordRole); + WriteIdentifier(queryJoinClause.IntoIdentifierToken); + } + EndNode(queryJoinClause); + } + + public void VisitQueryOrderClause(QueryOrderClause queryOrderClause) + { + StartNode(queryOrderClause); + WriteKeyword(QueryOrderClause.OrderbyKeywordRole); + Space(); + WriteCommaSeparatedList(queryOrderClause.Orderings); + EndNode(queryOrderClause); + } + + public void VisitQueryOrdering(QueryOrdering queryOrdering) + { + StartNode(queryOrdering); + queryOrdering.Expression.AcceptVisitor(this); + switch (queryOrdering.Direction) { + case QueryOrderingDirection.Ascending: + Space(); + WriteKeyword(QueryOrdering.AscendingKeywordRole); + break; + case QueryOrderingDirection.Descending: + Space(); + WriteKeyword(QueryOrdering.DescendingKeywordRole); + break; + } + EndNode(queryOrdering); + } + + public void VisitQuerySelectClause(QuerySelectClause querySelectClause) + { + StartNode(querySelectClause); + WriteKeyword(QuerySelectClause.SelectKeywordRole); + Space(); + querySelectClause.Expression.AcceptVisitor(this); + EndNode(querySelectClause); + } + + public void VisitQueryGroupClause(QueryGroupClause queryGroupClause) + { + StartNode(queryGroupClause); + WriteKeyword(QueryGroupClause.GroupKeywordRole); + Space(); + queryGroupClause.Projection.AcceptVisitor(this); + Space(); + WriteKeyword(QueryGroupClause.ByKeywordRole); + Space(); + queryGroupClause.Key.AcceptVisitor(this); + EndNode(queryGroupClause); + } + + #endregion + + #region GeneralScope + public void VisitAttribute(Attribute attribute) + { + StartNode(attribute); + attribute.Type.AcceptVisitor(this); + if (attribute.Arguments.Count != 0 || !attribute.GetChildByRole(Roles.LPar).IsNull) { + Space(policy.SpaceBeforeMethodCallParentheses); + WriteCommaSeparatedListInParenthesis(attribute.Arguments, policy.SpaceWithinMethodCallParentheses); + } + EndNode(attribute); + } + + public void VisitAttributeSection(AttributeSection attributeSection) + { + StartNode(attributeSection); + WriteToken(Roles.LBracket); + if (!string.IsNullOrEmpty(attributeSection.AttributeTarget)) { + WriteIdentifier(attributeSection.AttributeTargetToken); + WriteToken(Roles.Colon); + Space(); + } + WriteCommaSeparatedList(attributeSection.Attributes); + WriteToken(Roles.RBracket); + if (attributeSection.Parent is ParameterDeclaration || attributeSection.Parent is TypeParameterDeclaration) { + Space(); + } else { + NewLine(); + } + EndNode(attributeSection); + } + + public void VisitDelegateDeclaration(DelegateDeclaration delegateDeclaration) + { + StartNode(delegateDeclaration); + WriteAttributes(delegateDeclaration.Attributes); + WriteModifiers(delegateDeclaration.ModifierTokens); + WriteKeyword(Roles.DelegateKeyword); + delegateDeclaration.ReturnType.AcceptVisitor(this); + Space(); + WriteIdentifier(delegateDeclaration.NameToken); + WriteTypeParameters(delegateDeclaration.TypeParameters); + Space(policy.SpaceBeforeDelegateDeclarationParentheses); + WriteCommaSeparatedListInParenthesis(delegateDeclaration.Parameters, policy.SpaceWithinMethodDeclarationParentheses); + foreach (Constraint constraint in delegateDeclaration.Constraints) { + constraint.AcceptVisitor(this); + } + Semicolon(); + EndNode(delegateDeclaration); + } + + public void VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration) + { + StartNode(namespaceDeclaration); + WriteKeyword(Roles.NamespaceKeyword); + namespaceDeclaration.NamespaceName.AcceptVisitor (this); + OpenBrace(policy.NamespaceBraceStyle); + foreach (var member in namespaceDeclaration.Members) { + member.AcceptVisitor(this); + MaybeNewLinesAfterUsings(member); + } + CloseBrace(policy.NamespaceBraceStyle); + OptionalSemicolon(namespaceDeclaration.LastChild); + NewLine(); + EndNode(namespaceDeclaration); + } + + public void VisitTypeDeclaration(TypeDeclaration typeDeclaration) + { + StartNode(typeDeclaration); + WriteAttributes(typeDeclaration.Attributes); + WriteModifiers(typeDeclaration.ModifierTokens); + BraceStyle braceStyle; + switch (typeDeclaration.ClassType) { + case ClassType.Enum: + WriteKeyword(Roles.EnumKeyword); + braceStyle = policy.EnumBraceStyle; + break; + case ClassType.Interface: + WriteKeyword(Roles.InterfaceKeyword); + braceStyle = policy.InterfaceBraceStyle; + break; + case ClassType.Struct: + WriteKeyword(Roles.StructKeyword); + braceStyle = policy.StructBraceStyle; + break; + default: + WriteKeyword(Roles.ClassKeyword); + braceStyle = policy.ClassBraceStyle; + break; + } + WriteIdentifier(typeDeclaration.NameToken); + WriteTypeParameters(typeDeclaration.TypeParameters); + if (typeDeclaration.BaseTypes.Any()) { + Space(); + WriteToken(Roles.Colon); + Space(); + WriteCommaSeparatedList(typeDeclaration.BaseTypes); + } + foreach (Constraint constraint in typeDeclaration.Constraints) { + constraint.AcceptVisitor(this); + } + OpenBrace(braceStyle); + if (typeDeclaration.ClassType == ClassType.Enum) { + bool first = true; + AstNode last = null; + foreach (var member in typeDeclaration.Members) { + if (first) { + first = false; + } else { + Comma(member, noSpaceAfterComma: true); + NewLine(); + } + last = member; + member.AcceptVisitor(this); + } + if (last != null) + OptionalComma(last.NextSibling); + NewLine(); + } else { + bool first = true; + foreach (var member in typeDeclaration.Members) { + if (!first) { + for (int i = 0; i < policy.MinimumBlankLinesBetweenMembers; i++) + NewLine(); + } + first = false; + member.AcceptVisitor(this); + } + } + CloseBrace(braceStyle); + OptionalSemicolon(typeDeclaration.LastChild); + NewLine(); + EndNode(typeDeclaration); + } + + public void VisitUsingAliasDeclaration(UsingAliasDeclaration usingAliasDeclaration) + { + StartNode(usingAliasDeclaration); + WriteKeyword(UsingAliasDeclaration.UsingKeywordRole); + WriteIdentifier(usingAliasDeclaration.GetChildByRole(UsingAliasDeclaration.AliasRole)); + Space(policy.SpaceAroundEqualityOperator); + WriteToken(Roles.Assign); + Space(policy.SpaceAroundEqualityOperator); + usingAliasDeclaration.Import.AcceptVisitor(this); + Semicolon(); + EndNode(usingAliasDeclaration); + } + + public void VisitUsingDeclaration(UsingDeclaration usingDeclaration) + { + StartNode(usingDeclaration); + WriteKeyword(UsingDeclaration.UsingKeywordRole); + usingDeclaration.Import.AcceptVisitor(this); + Semicolon(); + EndNode(usingDeclaration); + } + + public void VisitExternAliasDeclaration(ExternAliasDeclaration externAliasDeclaration) + { + StartNode(externAliasDeclaration); + WriteKeyword(Roles.ExternKeyword); + Space(); + WriteKeyword(Roles.AliasKeyword); + Space(); + WriteIdentifier(externAliasDeclaration.NameToken); + Semicolon(); + EndNode(externAliasDeclaration); + } + + #endregion + + #region Statements + public void VisitBlockStatement(BlockStatement blockStatement) + { + StartNode(blockStatement); + BraceStyle style; + if (blockStatement.Parent is AnonymousMethodExpression || blockStatement.Parent is LambdaExpression) { + style = policy.AnonymousMethodBraceStyle; + } else if (blockStatement.Parent is ConstructorDeclaration) { + style = policy.ConstructorBraceStyle; + } else if (blockStatement.Parent is DestructorDeclaration) { + style = policy.DestructorBraceStyle; + } else if (blockStatement.Parent is MethodDeclaration) { + style = policy.MethodBraceStyle; + } else if (blockStatement.Parent is Accessor) { + if (blockStatement.Parent.Role == PropertyDeclaration.GetterRole) { + style = policy.PropertyGetBraceStyle; + } else if (blockStatement.Parent.Role == PropertyDeclaration.SetterRole) { + style = policy.PropertySetBraceStyle; + } else if (blockStatement.Parent.Role == CustomEventDeclaration.AddAccessorRole) { + style = policy.EventAddBraceStyle; + } else if (blockStatement.Parent.Role == CustomEventDeclaration.RemoveAccessorRole) { + style = policy.EventRemoveBraceStyle; + } else { + style = policy.StatementBraceStyle; + } + } else { + style = policy.StatementBraceStyle; + } + OpenBrace(style); + foreach (var node in blockStatement.Statements) { + node.AcceptVisitor(this); + } + EndNode(blockStatement); + CloseBrace(style); + if (!(blockStatement.Parent is Expression)) + NewLine(); + } + + public void VisitBreakStatement(BreakStatement breakStatement) + { + StartNode(breakStatement); + WriteKeyword("break", BreakStatement.BreakKeywordRole); + Semicolon(); + EndNode(breakStatement); + } + + public void VisitCheckedStatement(CheckedStatement checkedStatement) + { + StartNode(checkedStatement); + WriteKeyword(CheckedStatement.CheckedKeywordRole); + checkedStatement.Body.AcceptVisitor(this); + EndNode(checkedStatement); + } + + public void VisitContinueStatement(ContinueStatement continueStatement) + { + StartNode(continueStatement); + WriteKeyword("continue", ContinueStatement.ContinueKeywordRole); + Semicolon(); + EndNode(continueStatement); + } + + public void VisitDoWhileStatement(DoWhileStatement doWhileStatement) + { + StartNode(doWhileStatement); + WriteKeyword(DoWhileStatement.DoKeywordRole); + WriteEmbeddedStatement(doWhileStatement.EmbeddedStatement); + WriteKeyword(DoWhileStatement.WhileKeywordRole); + Space(policy.SpaceBeforeWhileParentheses); + LPar(); + Space(policy.SpacesWithinWhileParentheses); + doWhileStatement.Condition.AcceptVisitor(this); + Space(policy.SpacesWithinWhileParentheses); + RPar(); + Semicolon(); + EndNode(doWhileStatement); + } + + public void VisitEmptyStatement(EmptyStatement emptyStatement) + { + StartNode(emptyStatement); + Semicolon(); + EndNode(emptyStatement); + } + + public void VisitExpressionStatement(ExpressionStatement expressionStatement) + { + StartNode(expressionStatement); + expressionStatement.Expression.AcceptVisitor(this); + Semicolon(); + EndNode(expressionStatement); + } + + public void VisitFixedStatement(FixedStatement fixedStatement) + { + StartNode(fixedStatement); + WriteKeyword(FixedStatement.FixedKeywordRole); + Space(policy.SpaceBeforeUsingParentheses); + LPar(); + Space(policy.SpacesWithinUsingParentheses); + fixedStatement.Type.AcceptVisitor(this); + Space(); + WriteCommaSeparatedList(fixedStatement.Variables); + Space(policy.SpacesWithinUsingParentheses); + RPar(); + WriteEmbeddedStatement(fixedStatement.EmbeddedStatement); + EndNode(fixedStatement); + } + + public void VisitForeachStatement(ForeachStatement foreachStatement) + { + StartNode(foreachStatement); + WriteKeyword(ForeachStatement.ForeachKeywordRole); + Space(policy.SpaceBeforeForeachParentheses); + LPar(); + Space(policy.SpacesWithinForeachParentheses); + foreachStatement.VariableType.AcceptVisitor(this); + Space(); + WriteIdentifier(foreachStatement.VariableNameToken); + WriteKeyword(ForeachStatement.InKeywordRole); + Space(); + foreachStatement.InExpression.AcceptVisitor(this); + Space(policy.SpacesWithinForeachParentheses); + RPar(); + WriteEmbeddedStatement(foreachStatement.EmbeddedStatement); + EndNode(foreachStatement); + } + + public void VisitForStatement(ForStatement forStatement) + { + StartNode(forStatement); + WriteKeyword(ForStatement.ForKeywordRole); + Space(policy.SpaceBeforeForParentheses); + LPar(); + Space(policy.SpacesWithinForParentheses); + + WriteCommaSeparatedList(forStatement.Initializers); + Space(policy.SpaceBeforeForSemicolon); + WriteToken(Roles.Semicolon); + Space(policy.SpaceAfterForSemicolon); + + forStatement.Condition.AcceptVisitor(this); + Space(policy.SpaceBeforeForSemicolon); + WriteToken(Roles.Semicolon); + if (forStatement.Iterators.Any()) { + Space(policy.SpaceAfterForSemicolon); + WriteCommaSeparatedList(forStatement.Iterators); + } + + Space(policy.SpacesWithinForParentheses); + RPar(); + WriteEmbeddedStatement(forStatement.EmbeddedStatement); + EndNode(forStatement); + } + + public void VisitGotoCaseStatement(GotoCaseStatement gotoCaseStatement) + { + StartNode(gotoCaseStatement); + WriteKeyword(GotoCaseStatement.GotoKeywordRole); + WriteKeyword(GotoCaseStatement.CaseKeywordRole); + Space(); + gotoCaseStatement.LabelExpression.AcceptVisitor(this); + Semicolon(); + EndNode(gotoCaseStatement); + } + + public void VisitGotoDefaultStatement(GotoDefaultStatement gotoDefaultStatement) + { + StartNode(gotoDefaultStatement); + WriteKeyword(GotoDefaultStatement.GotoKeywordRole); + WriteKeyword(GotoDefaultStatement.DefaultKeywordRole); + Semicolon(); + EndNode(gotoDefaultStatement); + } + + public void VisitGotoStatement(GotoStatement gotoStatement) + { + StartNode(gotoStatement); + WriteKeyword(GotoStatement.GotoKeywordRole); + WriteIdentifier(gotoStatement.GetChildByRole(Roles.Identifier)); + Semicolon(); + EndNode(gotoStatement); + } + + public void VisitIfElseStatement(IfElseStatement ifElseStatement) + { + StartNode(ifElseStatement); + WriteKeyword(IfElseStatement.IfKeywordRole); + Space(policy.SpaceBeforeIfParentheses); + LPar(); + Space(policy.SpacesWithinIfParentheses); + ifElseStatement.Condition.AcceptVisitor(this); + Space(policy.SpacesWithinIfParentheses); + RPar(); + WriteEmbeddedStatement(ifElseStatement.TrueStatement); + if (!ifElseStatement.FalseStatement.IsNull) { + WriteKeyword(IfElseStatement.ElseKeywordRole); + if (ifElseStatement.FalseStatement is IfElseStatement) { + // don't put newline between 'else' and 'if' + ifElseStatement.FalseStatement.AcceptVisitor(this); + } else { + WriteEmbeddedStatement(ifElseStatement.FalseStatement); + } + } + EndNode(ifElseStatement); + } + + public void VisitLabelStatement(LabelStatement labelStatement) + { + StartNode(labelStatement); + WriteIdentifier(labelStatement.GetChildByRole(Roles.Identifier)); + WriteToken(Roles.Colon); + bool foundLabelledStatement = false; + for (AstNode tmp = labelStatement.NextSibling; tmp != null; tmp = tmp.NextSibling) { + if (tmp.Role == labelStatement.Role) { + foundLabelledStatement = true; + } + } + if (!foundLabelledStatement) { + // introduce an EmptyStatement so that the output becomes syntactically valid + WriteToken(Roles.Semicolon); + } + NewLine(); + EndNode(labelStatement); + } + + public void VisitLockStatement(LockStatement lockStatement) + { + StartNode(lockStatement); + WriteKeyword(LockStatement.LockKeywordRole); + Space(policy.SpaceBeforeLockParentheses); + LPar(); + Space(policy.SpacesWithinLockParentheses); + lockStatement.Expression.AcceptVisitor(this); + Space(policy.SpacesWithinLockParentheses); + RPar(); + WriteEmbeddedStatement(lockStatement.EmbeddedStatement); + EndNode(lockStatement); + } + + public void VisitReturnStatement(ReturnStatement returnStatement) + { + StartNode(returnStatement); + WriteKeyword(ReturnStatement.ReturnKeywordRole); + if (!returnStatement.Expression.IsNull) { + Space(); + returnStatement.Expression.AcceptVisitor(this); + } + Semicolon(); + EndNode(returnStatement); + } + + public void VisitSwitchStatement(SwitchStatement switchStatement) + { + StartNode(switchStatement); + WriteKeyword(SwitchStatement.SwitchKeywordRole); + Space(policy.SpaceBeforeSwitchParentheses); + LPar(); + Space(policy.SpacesWithinSwitchParentheses); + switchStatement.Expression.AcceptVisitor(this); + Space(policy.SpacesWithinSwitchParentheses); + RPar(); + OpenBrace(policy.StatementBraceStyle); + if (!policy.IndentSwitchBody) { + writer.Unindent(); + } + + foreach (var section in switchStatement.SwitchSections) { + section.AcceptVisitor(this); + } + + if (!policy.IndentSwitchBody) { + writer.Indent(); + } + CloseBrace(policy.StatementBraceStyle); + NewLine(); + EndNode(switchStatement); + } + + public void VisitSwitchSection(SwitchSection switchSection) + { + StartNode(switchSection); + bool first = true; + foreach (var label in switchSection.CaseLabels) { + if (!first) { + NewLine(); + } + label.AcceptVisitor(this); + first = false; + } + bool isBlock = switchSection.Statements.Count == 1 && switchSection.Statements.Single() is BlockStatement; + if (policy.IndentCaseBody && !isBlock) { + writer.Indent(); + } + + if (!isBlock) + NewLine(); + + foreach (var statement in switchSection.Statements) { + statement.AcceptVisitor(this); + } + + if (policy.IndentCaseBody && !isBlock) { + writer.Unindent(); + } + + EndNode(switchSection); + } + + public void VisitCaseLabel(CaseLabel caseLabel) + { + StartNode(caseLabel); + if (caseLabel.Expression.IsNull) { + WriteKeyword(CaseLabel.DefaultKeywordRole); + } else { + WriteKeyword(CaseLabel.CaseKeywordRole); + Space(); + caseLabel.Expression.AcceptVisitor(this); + } + WriteToken(Roles.Colon); + EndNode(caseLabel); + } + + public void VisitThrowStatement(ThrowStatement throwStatement) + { + StartNode(throwStatement); + WriteKeyword(ThrowStatement.ThrowKeywordRole); + if (!throwStatement.Expression.IsNull) { + Space(); + throwStatement.Expression.AcceptVisitor(this); + } + Semicolon(); + EndNode(throwStatement); + } + + public void VisitTryCatchStatement(TryCatchStatement tryCatchStatement) + { + StartNode(tryCatchStatement); + WriteKeyword(TryCatchStatement.TryKeywordRole); + tryCatchStatement.TryBlock.AcceptVisitor(this); + foreach (var catchClause in tryCatchStatement.CatchClauses) { + catchClause.AcceptVisitor(this); + } + if (!tryCatchStatement.FinallyBlock.IsNull) { + WriteKeyword(TryCatchStatement.FinallyKeywordRole); + tryCatchStatement.FinallyBlock.AcceptVisitor(this); + } + EndNode(tryCatchStatement); + } + + public void VisitCatchClause(CatchClause catchClause) + { + StartNode(catchClause); + WriteKeyword(CatchClause.CatchKeywordRole); + if (!catchClause.Type.IsNull) { + Space(policy.SpaceBeforeCatchParentheses); + LPar(); + Space(policy.SpacesWithinCatchParentheses); + catchClause.Type.AcceptVisitor(this); + if (!string.IsNullOrEmpty(catchClause.VariableName)) { + Space(); + WriteIdentifier(catchClause.VariableNameToken); + } + Space(policy.SpacesWithinCatchParentheses); + RPar(); + } + catchClause.Body.AcceptVisitor(this); + EndNode(catchClause); + } + + public void VisitUncheckedStatement(UncheckedStatement uncheckedStatement) + { + StartNode(uncheckedStatement); + WriteKeyword(UncheckedStatement.UncheckedKeywordRole); + uncheckedStatement.Body.AcceptVisitor(this); + EndNode(uncheckedStatement); + } + + public void VisitUnsafeStatement(UnsafeStatement unsafeStatement) + { + StartNode(unsafeStatement); + WriteKeyword(UnsafeStatement.UnsafeKeywordRole); + unsafeStatement.Body.AcceptVisitor(this); + EndNode(unsafeStatement); + } + + public void VisitUsingStatement(UsingStatement usingStatement) + { + StartNode(usingStatement); + WriteKeyword(UsingStatement.UsingKeywordRole); + Space(policy.SpaceBeforeUsingParentheses); + LPar(); + Space(policy.SpacesWithinUsingParentheses); + + usingStatement.ResourceAcquisition.AcceptVisitor(this); + + Space(policy.SpacesWithinUsingParentheses); + RPar(); + + WriteEmbeddedStatement(usingStatement.EmbeddedStatement); + + EndNode(usingStatement); + } + + public void VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement) + { + StartNode(variableDeclarationStatement); + WriteModifiers(variableDeclarationStatement.GetChildrenByRole(VariableDeclarationStatement.ModifierRole)); + variableDeclarationStatement.Type.AcceptVisitor(this); + Space(); + WriteCommaSeparatedList(variableDeclarationStatement.Variables); + Semicolon(); + EndNode(variableDeclarationStatement); + } + + public void VisitWhileStatement(WhileStatement whileStatement) + { + StartNode(whileStatement); + WriteKeyword(WhileStatement.WhileKeywordRole); + Space(policy.SpaceBeforeWhileParentheses); + LPar(); + Space(policy.SpacesWithinWhileParentheses); + whileStatement.Condition.AcceptVisitor(this); + Space(policy.SpacesWithinWhileParentheses); + RPar(); + WriteEmbeddedStatement(whileStatement.EmbeddedStatement); + EndNode(whileStatement); + } + + public void VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement) + { + StartNode(yieldBreakStatement); + WriteKeyword(YieldBreakStatement.YieldKeywordRole); + WriteKeyword(YieldBreakStatement.BreakKeywordRole); + Semicolon(); + EndNode(yieldBreakStatement); + } + + public void VisitYieldReturnStatement(YieldReturnStatement yieldReturnStatement) + { + StartNode(yieldReturnStatement); + WriteKeyword(YieldReturnStatement.YieldKeywordRole); + WriteKeyword(YieldReturnStatement.ReturnKeywordRole); + Space(); + yieldReturnStatement.Expression.AcceptVisitor(this); + Semicolon(); + EndNode(yieldReturnStatement); + } + + #endregion + + #region TypeMembers + public void VisitAccessor(Accessor accessor) + { + StartNode(accessor); + WriteAttributes(accessor.Attributes); + WriteModifiers(accessor.ModifierTokens); + if (accessor.Role == PropertyDeclaration.GetterRole) { + WriteKeyword("get", PropertyDeclaration.GetKeywordRole); + } else if (accessor.Role == PropertyDeclaration.SetterRole) { + WriteKeyword("set", PropertyDeclaration.SetKeywordRole); + } else if (accessor.Role == CustomEventDeclaration.AddAccessorRole) { + WriteKeyword("add", CustomEventDeclaration.AddKeywordRole); + } else if (accessor.Role == CustomEventDeclaration.RemoveAccessorRole) { + WriteKeyword("remove", CustomEventDeclaration.RemoveKeywordRole); + } + WriteMethodBody(accessor.Body); + EndNode(accessor); + } + + public void VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration) + { + StartNode(constructorDeclaration); + WriteAttributes(constructorDeclaration.Attributes); + WriteModifiers(constructorDeclaration.ModifierTokens); + TypeDeclaration type = constructorDeclaration.Parent as TypeDeclaration; + if (type != null && type.Name != constructorDeclaration.Name) + WriteIdentifier((Identifier)type.NameToken.Clone()); + else + WriteIdentifier(constructorDeclaration.NameToken); + Space(policy.SpaceBeforeConstructorDeclarationParentheses); + WriteCommaSeparatedListInParenthesis(constructorDeclaration.Parameters, policy.SpaceWithinMethodDeclarationParentheses); + if (!constructorDeclaration.Initializer.IsNull) { + Space(); + constructorDeclaration.Initializer.AcceptVisitor(this); + } + WriteMethodBody(constructorDeclaration.Body); + EndNode(constructorDeclaration); + } + + public void VisitConstructorInitializer(ConstructorInitializer constructorInitializer) + { + StartNode(constructorInitializer); + WriteToken(Roles.Colon); + Space(); + if (constructorInitializer.ConstructorInitializerType == ConstructorInitializerType.This) { + WriteKeyword(ConstructorInitializer.ThisKeywordRole); + } else { + WriteKeyword(ConstructorInitializer.BaseKeywordRole); + } + Space(policy.SpaceBeforeMethodCallParentheses); + WriteCommaSeparatedListInParenthesis(constructorInitializer.Arguments, policy.SpaceWithinMethodCallParentheses); + EndNode(constructorInitializer); + } + + public void VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration) + { + StartNode(destructorDeclaration); + WriteAttributes(destructorDeclaration.Attributes); + WriteModifiers(destructorDeclaration.ModifierTokens); + WriteToken(DestructorDeclaration.TildeRole); + TypeDeclaration type = destructorDeclaration.Parent as TypeDeclaration; + if (type != null && type.Name != destructorDeclaration.Name) + WriteIdentifier((Identifier)type.NameToken.Clone()); + else + WriteIdentifier(destructorDeclaration.NameToken); + Space(policy.SpaceBeforeConstructorDeclarationParentheses); + LPar(); + RPar(); + WriteMethodBody(destructorDeclaration.Body); + EndNode(destructorDeclaration); + } + + public void VisitEnumMemberDeclaration(EnumMemberDeclaration enumMemberDeclaration) + { + StartNode(enumMemberDeclaration); + WriteAttributes(enumMemberDeclaration.Attributes); + WriteModifiers(enumMemberDeclaration.ModifierTokens); + WriteIdentifier(enumMemberDeclaration.NameToken); + if (!enumMemberDeclaration.Initializer.IsNull) { + Space(policy.SpaceAroundAssignment); + WriteToken(Roles.Assign); + Space(policy.SpaceAroundAssignment); + enumMemberDeclaration.Initializer.AcceptVisitor(this); + } + EndNode(enumMemberDeclaration); + } + + public void VisitEventDeclaration(EventDeclaration eventDeclaration) + { + StartNode(eventDeclaration); + WriteAttributes(eventDeclaration.Attributes); + WriteModifiers(eventDeclaration.ModifierTokens); + WriteKeyword(EventDeclaration.EventKeywordRole); + eventDeclaration.ReturnType.AcceptVisitor(this); + Space(); + WriteCommaSeparatedList(eventDeclaration.Variables); + Semicolon(); + EndNode(eventDeclaration); + } + + public void VisitCustomEventDeclaration(CustomEventDeclaration customEventDeclaration) + { + StartNode(customEventDeclaration); + WriteAttributes(customEventDeclaration.Attributes); + WriteModifiers(customEventDeclaration.ModifierTokens); + WriteKeyword(CustomEventDeclaration.EventKeywordRole); + customEventDeclaration.ReturnType.AcceptVisitor(this); + Space(); + WritePrivateImplementationType(customEventDeclaration.PrivateImplementationType); + WriteIdentifier(customEventDeclaration.NameToken); + OpenBrace(policy.EventBraceStyle); + // output add/remove in their original order + foreach (AstNode node in customEventDeclaration.Children) { + if (node.Role == CustomEventDeclaration.AddAccessorRole || node.Role == CustomEventDeclaration.RemoveAccessorRole) { + node.AcceptVisitor(this); + } + } + CloseBrace(policy.EventBraceStyle); + NewLine(); + EndNode(customEventDeclaration); + } + + public void VisitFieldDeclaration(FieldDeclaration fieldDeclaration) + { + StartNode(fieldDeclaration); + WriteAttributes(fieldDeclaration.Attributes); + WriteModifiers(fieldDeclaration.ModifierTokens); + fieldDeclaration.ReturnType.AcceptVisitor(this); + Space(); + WriteCommaSeparatedList(fieldDeclaration.Variables); + Semicolon(); + EndNode(fieldDeclaration); + } + + public void VisitFixedFieldDeclaration(FixedFieldDeclaration fixedFieldDeclaration) + { + StartNode(fixedFieldDeclaration); + WriteAttributes(fixedFieldDeclaration.Attributes); + WriteModifiers(fixedFieldDeclaration.ModifierTokens); + WriteKeyword(FixedFieldDeclaration.FixedKeywordRole); + Space(); + fixedFieldDeclaration.ReturnType.AcceptVisitor(this); + Space(); + WriteCommaSeparatedList(fixedFieldDeclaration.Variables); + Semicolon(); + EndNode(fixedFieldDeclaration); + } + + public void VisitFixedVariableInitializer(FixedVariableInitializer fixedVariableInitializer) + { + StartNode(fixedVariableInitializer); + WriteIdentifier(fixedVariableInitializer.NameToken); + if (!fixedVariableInitializer.CountExpression.IsNull) { + WriteToken(Roles.LBracket); + Space(policy.SpacesWithinBrackets); + fixedVariableInitializer.CountExpression.AcceptVisitor(this); + Space(policy.SpacesWithinBrackets); + WriteToken(Roles.RBracket); + } + EndNode(fixedVariableInitializer); + } + + public void VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration) + { + StartNode(indexerDeclaration); + WriteAttributes(indexerDeclaration.Attributes); + WriteModifiers(indexerDeclaration.ModifierTokens); + indexerDeclaration.ReturnType.AcceptVisitor(this); + Space(); + WritePrivateImplementationType(indexerDeclaration.PrivateImplementationType); + WriteKeyword(IndexerDeclaration.ThisKeywordRole); + Space(policy.SpaceBeforeMethodDeclarationParentheses); + WriteCommaSeparatedListInBrackets(indexerDeclaration.Parameters, policy.SpaceWithinMethodDeclarationParentheses); + OpenBrace(policy.PropertyBraceStyle); + // output get/set in their original order + foreach (AstNode node in indexerDeclaration.Children) { + if (node.Role == IndexerDeclaration.GetterRole || node.Role == IndexerDeclaration.SetterRole) { + node.AcceptVisitor(this); + } + } + CloseBrace(policy.PropertyBraceStyle); + NewLine(); + EndNode(indexerDeclaration); + } + + public void VisitMethodDeclaration(MethodDeclaration methodDeclaration) + { + StartNode(methodDeclaration); + WriteAttributes(methodDeclaration.Attributes); + WriteModifiers(methodDeclaration.ModifierTokens); + methodDeclaration.ReturnType.AcceptVisitor(this); + Space(); + WritePrivateImplementationType(methodDeclaration.PrivateImplementationType); + WriteIdentifier(methodDeclaration.NameToken); + WriteTypeParameters(methodDeclaration.TypeParameters); + Space(policy.SpaceBeforeMethodDeclarationParentheses); + WriteCommaSeparatedListInParenthesis(methodDeclaration.Parameters, policy.SpaceWithinMethodDeclarationParentheses); + foreach (Constraint constraint in methodDeclaration.Constraints) { + constraint.AcceptVisitor(this); + } + WriteMethodBody(methodDeclaration.Body); + EndNode(methodDeclaration); + } + + public void VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration) + { + StartNode(operatorDeclaration); + WriteAttributes(operatorDeclaration.Attributes); + WriteModifiers(operatorDeclaration.ModifierTokens); + if (operatorDeclaration.OperatorType == OperatorType.Explicit) { + WriteKeyword(OperatorDeclaration.ExplicitRole); + } else if (operatorDeclaration.OperatorType == OperatorType.Implicit) { + WriteKeyword(OperatorDeclaration.ImplicitRole); + } else { + operatorDeclaration.ReturnType.AcceptVisitor(this); + } + WriteKeyword(OperatorDeclaration.OperatorKeywordRole); + Space(); + if (operatorDeclaration.OperatorType == OperatorType.Explicit + || operatorDeclaration.OperatorType == OperatorType.Implicit) { + operatorDeclaration.ReturnType.AcceptVisitor(this); + } else { + WriteToken(OperatorDeclaration.GetToken(operatorDeclaration.OperatorType), OperatorDeclaration.GetRole(operatorDeclaration.OperatorType)); + } + Space(policy.SpaceBeforeMethodDeclarationParentheses); + WriteCommaSeparatedListInParenthesis(operatorDeclaration.Parameters, policy.SpaceWithinMethodDeclarationParentheses); + WriteMethodBody(operatorDeclaration.Body); + EndNode(operatorDeclaration); + } + + public void VisitParameterDeclaration(ParameterDeclaration parameterDeclaration) + { + StartNode(parameterDeclaration); + WriteAttributes(parameterDeclaration.Attributes); + switch (parameterDeclaration.ParameterModifier) { + case ParameterModifier.Ref: + WriteKeyword(ParameterDeclaration.RefModifierRole); + break; + case ParameterModifier.Out: + WriteKeyword(ParameterDeclaration.OutModifierRole); + break; + case ParameterModifier.Params: + WriteKeyword(ParameterDeclaration.ParamsModifierRole); + break; + case ParameterModifier.This: + WriteKeyword(ParameterDeclaration.ThisModifierRole); + break; + } + parameterDeclaration.Type.AcceptVisitor(this); + if (!parameterDeclaration.Type.IsNull && !string.IsNullOrEmpty(parameterDeclaration.Name)) { + Space(); + } + if (!string.IsNullOrEmpty(parameterDeclaration.Name)) { + WriteIdentifier(parameterDeclaration.NameToken); + } + if (!parameterDeclaration.DefaultExpression.IsNull) { + Space(policy.SpaceAroundAssignment); + WriteToken(Roles.Assign); + Space(policy.SpaceAroundAssignment); + parameterDeclaration.DefaultExpression.AcceptVisitor(this); + } + EndNode(parameterDeclaration); + } + + public void VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration) + { + StartNode(propertyDeclaration); + WriteAttributes(propertyDeclaration.Attributes); + WriteModifiers(propertyDeclaration.ModifierTokens); + propertyDeclaration.ReturnType.AcceptVisitor(this); + Space(); + WritePrivateImplementationType(propertyDeclaration.PrivateImplementationType); + WriteIdentifier(propertyDeclaration.NameToken); + OpenBrace(policy.PropertyBraceStyle); + // output get/set in their original order + foreach (AstNode node in propertyDeclaration.Children) { + if (node.Role == IndexerDeclaration.GetterRole || node.Role == IndexerDeclaration.SetterRole) { + node.AcceptVisitor(this); + } + } + CloseBrace(policy.PropertyBraceStyle); + NewLine(); + EndNode(propertyDeclaration); + } + + #endregion + + #region Other nodes + public void VisitVariableInitializer(VariableInitializer variableInitializer) + { + StartNode(variableInitializer); + WriteIdentifier(variableInitializer.NameToken); + if (!variableInitializer.Initializer.IsNull) { + Space(policy.SpaceAroundAssignment); + WriteToken(Roles.Assign); + Space(policy.SpaceAroundAssignment); + variableInitializer.Initializer.AcceptVisitor(this); + } + EndNode(variableInitializer); + } + + void MaybeNewLinesAfterUsings(AstNode node) + { + var nextSibling = node.NextSibling; + while (nextSibling is WhitespaceNode || nextSibling is NewLineNode) + nextSibling = nextSibling.NextSibling; + + if ((node is UsingDeclaration || node is UsingAliasDeclaration) && !(nextSibling is UsingDeclaration || nextSibling is UsingAliasDeclaration)) { + for (int i = 0; i < policy.MinimumBlankLinesAfterUsings; i++) + NewLine(); + } + } + + public void VisitSyntaxTree(SyntaxTree syntaxTree) + { + // don't do node tracking as we visit all children directly + foreach (AstNode node in syntaxTree.Children) { + node.AcceptVisitor(this); + MaybeNewLinesAfterUsings(node); + } + } + + public void VisitSimpleType(SimpleType simpleType) + { + StartNode(simpleType); + WriteIdentifier(simpleType.IdentifierToken); + WriteTypeArguments(simpleType.TypeArguments); + EndNode(simpleType); + } + + public void VisitMemberType(MemberType memberType) + { + StartNode(memberType); + memberType.Target.AcceptVisitor(this); + if (memberType.IsDoubleColon) { + WriteToken(Roles.DoubleColon); + } else { + WriteToken(Roles.Dot); + } + WriteIdentifier(memberType.MemberNameToken); + WriteTypeArguments(memberType.TypeArguments); + EndNode(memberType); + } + + public void VisitComposedType(ComposedType composedType) + { + StartNode(composedType); + composedType.BaseType.AcceptVisitor(this); + if (composedType.HasNullableSpecifier) { + WriteToken(ComposedType.NullableRole); + } + for (int i = 0; i < composedType.PointerRank; i++) { + WriteToken(ComposedType.PointerRole); + } + foreach (var node in composedType.ArraySpecifiers) { + node.AcceptVisitor(this); + } + EndNode(composedType); + } + + public void VisitArraySpecifier(ArraySpecifier arraySpecifier) + { + StartNode(arraySpecifier); + WriteToken(Roles.LBracket); + foreach (var comma in arraySpecifier.GetChildrenByRole(Roles.Comma)) { + writer.WriteToken(Roles.Comma, ","); + } + WriteToken(Roles.RBracket); + EndNode(arraySpecifier); + } + + public void VisitPrimitiveType(PrimitiveType primitiveType) + { + StartNode(primitiveType); + writer.WritePrimitiveType(primitiveType.Keyword); + EndNode(primitiveType); + } + + public void VisitComment(Comment comment) + { + writer.StartNode(comment); + writer.WriteComment(comment.CommentType, comment.Content); + writer.EndNode(comment); + } + + public void VisitNewLine(NewLineNode newLineNode) + { +// formatter.StartNode(newLineNode); +// formatter.NewLine(); +// formatter.EndNode(newLineNode); + } + + public void VisitWhitespace(WhitespaceNode whitespaceNode) + { + // unused + } + + public void VisitText(TextNode textNode) + { + // unused + } + + public void VisitPreProcessorDirective(PreProcessorDirective preProcessorDirective) + { + writer.StartNode(preProcessorDirective); + writer.WritePreProcessorDirective(preProcessorDirective.Type, preProcessorDirective.Argument); + writer.EndNode(preProcessorDirective); + } + + public void VisitTypeParameterDeclaration(TypeParameterDeclaration typeParameterDeclaration) + { + StartNode(typeParameterDeclaration); + WriteAttributes(typeParameterDeclaration.Attributes); + switch (typeParameterDeclaration.Variance) { + case VarianceModifier.Invariant: + break; + case VarianceModifier.Covariant: + WriteKeyword(TypeParameterDeclaration.OutVarianceKeywordRole); + break; + case VarianceModifier.Contravariant: + WriteKeyword(TypeParameterDeclaration.InVarianceKeywordRole); + break; + default: + throw new NotSupportedException ("Invalid value for VarianceModifier"); + } + WriteIdentifier(typeParameterDeclaration.NameToken); + EndNode(typeParameterDeclaration); + } + + public void VisitConstraint(Constraint constraint) + { + StartNode(constraint); + Space(); + WriteKeyword(Roles.WhereKeyword); + constraint.TypeParameter.AcceptVisitor(this); + Space(); + WriteToken(Roles.Colon); + Space(); + WriteCommaSeparatedList(constraint.BaseTypes); + EndNode(constraint); + } + + public void VisitCSharpTokenNode(CSharpTokenNode cSharpTokenNode) + { + CSharpModifierToken mod = cSharpTokenNode as CSharpModifierToken; + if (mod != null) { + // ITokenWriter assumes that each node processed between a + // StartNode(parentNode)-EndNode(parentNode)-pair is a child of parentNode. + WriteKeyword(CSharpModifierToken.GetModifierName(mod.Modifier), cSharpTokenNode.Role); + } else { + throw new NotSupportedException ("Should never visit individual tokens"); + } + } + + public void VisitIdentifier(Identifier identifier) + { + // Do not call StartNode and EndNode for Identifier, because they are handled by the ITokenWriter. + // ITokenWriter assumes that each node processed between a + // StartNode(parentNode)-EndNode(parentNode)-pair is a child of parentNode. + WriteIdentifier(identifier); + } + + void IAstVisitor.VisitNullNode(AstNode nullNode) + { + } + + void IAstVisitor.VisitErrorNode(AstNode errorNode) + { + StartNode(errorNode); + EndNode(errorNode); + } + #endregion + + #region Pattern Nodes + public void VisitPatternPlaceholder(AstNode placeholder, PatternMatching.Pattern pattern) + { + StartNode(placeholder); + VisitNodeInPattern(pattern); + EndNode(placeholder); + } + + void VisitAnyNode(AnyNode anyNode) + { + if (!string.IsNullOrEmpty(anyNode.GroupName)) { + WriteIdentifier(anyNode.GroupName); + WriteToken(Roles.Colon); + } + } + + void VisitBackreference(Backreference backreference) + { + WriteKeyword("backreference"); + LPar(); + WriteIdentifier(backreference.ReferencedGroupName); + RPar(); + } + + void VisitIdentifierExpressionBackreference(IdentifierExpressionBackreference identifierExpressionBackreference) + { + WriteKeyword("identifierBackreference"); + LPar(); + WriteIdentifier(identifierExpressionBackreference.ReferencedGroupName); + RPar(); + } + + void VisitChoice(Choice choice) + { + WriteKeyword("choice"); + Space(); + LPar(); + NewLine(); + writer.Indent(); + foreach (INode alternative in choice) { + VisitNodeInPattern(alternative); + if (alternative != choice.Last()) { + WriteToken(Roles.Comma); + } + NewLine(); + } + writer.Unindent(); + RPar(); + } + + void VisitNamedNode(NamedNode namedNode) + { + if (!string.IsNullOrEmpty(namedNode.GroupName)) { + WriteIdentifier(namedNode.GroupName); + WriteToken(Roles.Colon); + } + VisitNodeInPattern(namedNode.ChildNode); + } + + void VisitRepeat(Repeat repeat) + { + WriteKeyword("repeat"); + LPar(); + if (repeat.MinCount != 0 || repeat.MaxCount != int.MaxValue) { + WriteIdentifier(repeat.MinCount.ToString()); + WriteToken(Roles.Comma); + WriteIdentifier(repeat.MaxCount.ToString()); + WriteToken(Roles.Comma); + } + VisitNodeInPattern(repeat.ChildNode); + RPar(); + } + + void VisitOptionalNode(OptionalNode optionalNode) + { + WriteKeyword("optional"); + LPar(); + VisitNodeInPattern(optionalNode.ChildNode); + RPar(); + } + + void VisitNodeInPattern(INode childNode) + { + if (childNode is AstNode) { + ((AstNode)childNode).AcceptVisitor(this); + } else if (childNode is IdentifierExpressionBackreference) { + VisitIdentifierExpressionBackreference((IdentifierExpressionBackreference)childNode); + } else if (childNode is Choice) { + VisitChoice((Choice)childNode); + } else if (childNode is AnyNode) { + VisitAnyNode((AnyNode)childNode); + } else if (childNode is Backreference) { + VisitBackreference((Backreference)childNode); + } else if (childNode is NamedNode) { + VisitNamedNode((NamedNode)childNode); + } else if (childNode is OptionalNode) { + VisitOptionalNode((OptionalNode)childNode); + } else if (childNode is Repeat) { + VisitRepeat((Repeat)childNode); + } else { + TextWriterTokenWriter.PrintPrimitiveValue(childNode); + } + } + #endregion + + #region Documentation Reference + public void VisitDocumentationReference(DocumentationReference documentationReference) + { + StartNode(documentationReference); + if (!documentationReference.DeclaringType.IsNull) { + documentationReference.DeclaringType.AcceptVisitor(this); + if (documentationReference.SymbolKind != SymbolKind.TypeDefinition) { + WriteToken(Roles.Dot); + } + } + switch (documentationReference.SymbolKind) { + case SymbolKind.TypeDefinition: + // we already printed the DeclaringType + break; + case SymbolKind.Indexer: + WriteKeyword(IndexerDeclaration.ThisKeywordRole); + break; + case SymbolKind.Operator: + var opType = documentationReference.OperatorType; + if (opType == OperatorType.Explicit) { + WriteKeyword(OperatorDeclaration.ExplicitRole); + } else if (opType == OperatorType.Implicit) { + WriteKeyword(OperatorDeclaration.ImplicitRole); + } + WriteKeyword(OperatorDeclaration.OperatorKeywordRole); + Space(); + if (opType == OperatorType.Explicit || opType == OperatorType.Implicit) { + documentationReference.ConversionOperatorReturnType.AcceptVisitor(this); + } else { + WriteToken(OperatorDeclaration.GetToken(opType), OperatorDeclaration.GetRole(opType)); + } + break; + default: + WriteIdentifier(documentationReference.GetChildByRole(Roles.Identifier)); + break; + } + WriteTypeArguments(documentationReference.TypeArguments); + if (documentationReference.HasParameterList) { + Space(policy.SpaceBeforeMethodDeclarationParentheses); + if (documentationReference.SymbolKind == SymbolKind.Indexer) { + WriteCommaSeparatedListInBrackets(documentationReference.Parameters, policy.SpaceWithinMethodDeclarationParentheses); + } else { + WriteCommaSeparatedListInParenthesis(documentationReference.Parameters, policy.SpaceWithinMethodDeclarationParentheses); + } + } + EndNode(documentationReference); + } + #endregion + + /// + /// Converts special characters to escape sequences within the given string. + /// + public static string ConvertString(string text) + { + return TextWriterTokenWriter.ConvertString(text); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/CodeDomConvertVisitor.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/CodeDomConvertVisitor.cs new file mode 100644 index 000000000..bd82827d8 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/CodeDomConvertVisitor.cs @@ -0,0 +1,1429 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// 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.CodeDom; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using ICSharpCode.NRefactory.CSharp.Refactoring; +using ICSharpCode.NRefactory.CSharp.Resolver; +using ICSharpCode.NRefactory.CSharp.TypeSystem; +using ICSharpCode.NRefactory.PatternMatching; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Converts from C# AST to CodeDom. + /// + /// + /// The conversion is intended for use in the SharpDevelop forms designer. + /// + public class CodeDomConvertVisitor : IAstVisitor + { + CSharpAstResolver resolver; + + /// + /// Gets/Sets whether the visitor should convert short type names into + /// fully qualified type names. + /// The default is false. + /// + public bool UseFullyQualifiedTypeNames { get; set; } + + /// + /// Gets whether the visitor is allowed to produce snippet nodes for + /// code that cannot be converted. + /// The default is true. If this property is set to false, + /// unconvertible code will throw a NotSupportedException. + /// + public bool AllowSnippetNodes { get; set; } + + public CodeDomConvertVisitor() + { + this.AllowSnippetNodes = true; + } + + /// + /// Converts a syntax tree to CodeDom. + /// + /// The input syntax tree. + /// The current compilation. + /// CSharpUnresolvedFile, used for resolving. + /// Converted CodeCompileUnit + /// + /// This conversion process requires a resolver because it needs to distinguish field/property/event references etc. + /// + public CodeCompileUnit Convert(ICompilation compilation, SyntaxTree syntaxTree, CSharpUnresolvedFile unresolvedFile) + { + if (syntaxTree == null) + throw new ArgumentNullException("syntaxTree"); + if (compilation == null) + throw new ArgumentNullException("compilation"); + + CSharpAstResolver resolver = new CSharpAstResolver(compilation, syntaxTree, unresolvedFile); + return (CodeCompileUnit)Convert(syntaxTree, resolver); + } + + /// + /// Converts a C# AST node to CodeDom. + /// + /// The input node. + /// The AST resolver. + /// The node converted into CodeDom + /// + /// This conversion process requires a resolver because it needs to distinguish field/property/event references etc. + /// + public CodeObject Convert(AstNode node, CSharpAstResolver resolver) + { + if (node == null) + throw new ArgumentNullException("node"); + if (resolver == null) + throw new ArgumentNullException("resolver"); + try { + this.resolver = resolver; + return node.AcceptVisitor(this); + } finally { + this.resolver = null; + } + } + + ResolveResult Resolve(AstNode node) + { + if (resolver == null) + return ErrorResolveResult.UnknownError; + else + return resolver.Resolve(node); + } + + CodeExpression Convert(Expression expr) + { + return (CodeExpression)expr.AcceptVisitor(this); + } + + CodeExpression[] Convert(IEnumerable expressions) + { + List result = new List(); + foreach (Expression expr in expressions) { + CodeExpression e = Convert(expr); + if (e != null) + result.Add(e); + } + return result.ToArray(); + } + + CodeTypeReference Convert(AstType type) + { + return (CodeTypeReference)type.AcceptVisitor(this); + } + + CodeTypeReference[] Convert(IEnumerable types) + { + List result = new List(); + foreach (AstType type in types) { + CodeTypeReference e = Convert(type); + if (e != null) + result.Add(e); + } + return result.ToArray(); + } + + public CodeTypeReference Convert(IType type) + { + if (type.Kind == TypeKind.Array) { + ArrayType a = (ArrayType)type; + return new CodeTypeReference(Convert(a.ElementType), a.Dimensions); + } else if (type is ParameterizedType) { + var pt = (ParameterizedType)type; + return new CodeTypeReference(pt.GetDefinition().ReflectionName, pt.TypeArguments.Select(Convert).ToArray()); + } else { + return new CodeTypeReference(type.ReflectionName); + } + } + + CodeStatement Convert(Statement stmt) + { + return (CodeStatement)stmt.AcceptVisitor(this); + } + + CodeStatement[] ConvertBlock(BlockStatement block) + { + List result = new List(); + foreach (Statement stmt in block.Statements) { + if (stmt is EmptyStatement) + continue; + CodeStatement s = Convert(stmt); + if (s != null) + result.Add(s); + } + return result.ToArray(); + } + + CodeStatement[] ConvertEmbeddedStatement(Statement embeddedStatement) + { + BlockStatement block = embeddedStatement as BlockStatement; + if (block != null) { + return ConvertBlock(block); + } else if (embeddedStatement is EmptyStatement) { + return new CodeStatement[0]; + } + CodeStatement s = Convert(embeddedStatement); + if (s != null) + return new CodeStatement[] { s }; + else + return new CodeStatement[0]; + } + + string MakeSnippet(AstNode node) + { + if (!AllowSnippetNodes) + throw new NotSupportedException(); + StringWriter w = new StringWriter(); + CSharpOutputVisitor v = new CSharpOutputVisitor(w, FormattingOptionsFactory.CreateMono ()); + node.AcceptVisitor(v); + return w.ToString(); + } + + /// + /// Converts an expression by storing it as C# snippet. + /// This is used for expressions that cannot be represented in CodeDom. + /// + CodeSnippetExpression MakeSnippetExpression(Expression expr) + { + return new CodeSnippetExpression(MakeSnippet(expr)); + } + + CodeSnippetStatement MakeSnippetStatement(Statement stmt) + { + return new CodeSnippetStatement(MakeSnippet(stmt)); + } + + CodeObject IAstVisitor.VisitNullNode(AstNode nullNode) + { + return null; + } + + CodeObject IAstVisitor.VisitErrorNode(AstNode errorNode) + { + return null; + } + + CodeObject IAstVisitor.VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression) + { + return MakeSnippetExpression(anonymousMethodExpression); + } + + CodeObject IAstVisitor.VisitUndocumentedExpression(UndocumentedExpression undocumentedExpression) + { + return MakeSnippetExpression(undocumentedExpression); + } + + CodeObject IAstVisitor.VisitArrayCreateExpression(ArrayCreateExpression arrayCreateExpression) + { + CodeArrayCreateExpression ace = new CodeArrayCreateExpression(); + int dimensions = arrayCreateExpression.Arguments.Count; + int nestingDepth = arrayCreateExpression.AdditionalArraySpecifiers.Count; + if (dimensions > 0) + nestingDepth++; + if (nestingDepth > 1 || dimensions > 1) { + // CodeDom does not support jagged or multi-dimensional arrays + return MakeSnippetExpression(arrayCreateExpression); + } + if (arrayCreateExpression.Type.IsNull) { + ace.CreateType = Convert(Resolve(arrayCreateExpression).Type); + } else { + ace.CreateType = Convert(arrayCreateExpression.Type); + } + if (arrayCreateExpression.Arguments.Count == 1) { + ace.SizeExpression = Convert(arrayCreateExpression.Arguments.Single()); + } + ace.Initializers.AddRange(Convert(arrayCreateExpression.Initializer.Elements)); + return ace; + } + + CodeObject IAstVisitor.VisitArrayInitializerExpression(ArrayInitializerExpression arrayInitializerExpression) + { + // Array initializers should be handled by the parent node + return MakeSnippetExpression(arrayInitializerExpression); + } + + CodeObject IAstVisitor.VisitAsExpression(AsExpression asExpression) + { + return MakeSnippetExpression(asExpression); + } + + CodeObject IAstVisitor.VisitAssignmentExpression(AssignmentExpression assignmentExpression) + { + // assignments are only supported as statements, not as expressions + return MakeSnippetExpression(assignmentExpression); + } + + CodeObject IAstVisitor.VisitBaseReferenceExpression(BaseReferenceExpression baseReferenceExpression) + { + return new CodeBaseReferenceExpression(); + } + + CodeObject IAstVisitor.VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression) + { + CodeBinaryOperatorType op; + switch (binaryOperatorExpression.Operator) { + case BinaryOperatorType.BitwiseAnd: + op = CodeBinaryOperatorType.BitwiseAnd; + break; + case BinaryOperatorType.BitwiseOr: + op = CodeBinaryOperatorType.BitwiseOr; + break; + case BinaryOperatorType.ConditionalAnd: + op = CodeBinaryOperatorType.BooleanAnd; + break; + case BinaryOperatorType.ConditionalOr: + op = CodeBinaryOperatorType.BooleanOr; + break; + case BinaryOperatorType.GreaterThan: + op = CodeBinaryOperatorType.GreaterThan; + break; + case BinaryOperatorType.GreaterThanOrEqual: + op = CodeBinaryOperatorType.GreaterThanOrEqual; + break; + case BinaryOperatorType.LessThan: + op = CodeBinaryOperatorType.LessThan; + break; + case BinaryOperatorType.LessThanOrEqual: + op = CodeBinaryOperatorType.LessThanOrEqual; + break; + case BinaryOperatorType.Add: + op = CodeBinaryOperatorType.Add; + break; + case BinaryOperatorType.Subtract: + op = CodeBinaryOperatorType.Subtract; + break; + case BinaryOperatorType.Multiply: + op = CodeBinaryOperatorType.Multiply; + break; + case BinaryOperatorType.Divide: + op = CodeBinaryOperatorType.Divide; + break; + case BinaryOperatorType.Modulus: + op = CodeBinaryOperatorType.Modulus; + break; + case BinaryOperatorType.Equality: + case BinaryOperatorType.InEquality: + OperatorResolveResult rr = Resolve(binaryOperatorExpression) as OperatorResolveResult; + if (rr != null && rr.GetChildResults().Any(cr => cr.Type.IsReferenceType == true)) { + if (binaryOperatorExpression.Operator == BinaryOperatorType.Equality) + op = CodeBinaryOperatorType.IdentityEquality; + else + op = CodeBinaryOperatorType.IdentityInequality; + } else { + if (binaryOperatorExpression.Operator == BinaryOperatorType.Equality) { + op = CodeBinaryOperatorType.ValueEquality; + } else { + // CodeDom is retarded and does not support ValueInequality, so we'll simulate it using + // ValueEquality and Not... but CodeDom doesn't have Not either, so we use + // '(a == b) == false' + return new CodeBinaryOperatorExpression( + new CodeBinaryOperatorExpression( + Convert(binaryOperatorExpression.Left), + CodeBinaryOperatorType.ValueEquality, + Convert(binaryOperatorExpression.Right) + ), + CodeBinaryOperatorType.ValueEquality, + new CodePrimitiveExpression(false) + ); + } + } + break; + default: + // not supported: xor, shift, null coalescing + return MakeSnippetExpression(binaryOperatorExpression); + } + return new CodeBinaryOperatorExpression(Convert(binaryOperatorExpression.Left), op, Convert(binaryOperatorExpression.Right)); + } + + CodeObject IAstVisitor.VisitCastExpression(CastExpression castExpression) + { + return new CodeCastExpression(Convert(castExpression.Type), Convert(castExpression.Expression)); + } + + CodeObject IAstVisitor.VisitCheckedExpression(CheckedExpression checkedExpression) + { + return MakeSnippetExpression(checkedExpression); + } + + CodeObject IAstVisitor.VisitConditionalExpression(ConditionalExpression conditionalExpression) + { + return MakeSnippetExpression(conditionalExpression); + } + + CodeObject IAstVisitor.VisitDefaultValueExpression(DefaultValueExpression defaultValueExpression) + { + return new CodeDefaultValueExpression(Convert(defaultValueExpression.Type)); + } + + CodeObject IAstVisitor.VisitDirectionExpression(DirectionExpression directionExpression) + { + System.CodeDom.FieldDirection direction; + if (directionExpression.FieldDirection == FieldDirection.Out) { + direction = System.CodeDom.FieldDirection.Out; + } else { + direction = System.CodeDom.FieldDirection.Ref; + } + return new CodeDirectionExpression(direction, Convert(directionExpression.Expression)); + } + + CodeObject IAstVisitor.VisitIdentifierExpression(IdentifierExpression identifierExpression) + { + ResolveResult rr = Resolve(identifierExpression); + LocalResolveResult lrr = rr as LocalResolveResult; + if (lrr != null && lrr.IsParameter) { + if (lrr.Variable.Name == "value" && identifierExpression.Ancestors.Any(a => a is Accessor)) { + return new CodePropertySetValueReferenceExpression(); + } else { + return new CodeArgumentReferenceExpression(lrr.Variable.Name); + } + } + MemberResolveResult mrr = rr as MemberResolveResult; + if (mrr != null) { + return HandleMemberReference(null, identifierExpression.Identifier, identifierExpression.TypeArguments, mrr); + } + TypeResolveResult trr = rr as TypeResolveResult; + if (trr != null) { + CodeTypeReference typeRef; + if (UseFullyQualifiedTypeNames) { + typeRef = Convert(trr.Type); + } else { + typeRef = new CodeTypeReference(identifierExpression.Identifier); + typeRef.TypeArguments.AddRange(Convert(identifierExpression.TypeArguments)); + } + return new CodeTypeReferenceExpression(typeRef); + } + MethodGroupResolveResult mgrr = rr as MethodGroupResolveResult; + if (mgrr != null || identifierExpression.TypeArguments.Any()) { + return new CodeMethodReferenceExpression(new CodeThisReferenceExpression(), identifierExpression.Identifier, Convert(identifierExpression.TypeArguments)); + } + return new CodeVariableReferenceExpression(identifierExpression.Identifier); + } + + CodeObject IAstVisitor.VisitIndexerExpression(IndexerExpression indexerExpression) + { + if (Resolve(indexerExpression) is ArrayAccessResolveResult) + return new CodeArrayIndexerExpression(Convert(indexerExpression.Target), Convert(indexerExpression.Arguments)); + else + return new CodeIndexerExpression(Convert(indexerExpression.Target), Convert(indexerExpression.Arguments)); + } + + CodeObject IAstVisitor.VisitInvocationExpression(InvocationExpression invocationExpression) + { + MemberResolveResult rr = Resolve(invocationExpression) as MemberResolveResult; + CSharpInvocationResolveResult csRR = rr as CSharpInvocationResolveResult; + if (csRR != null && csRR.IsDelegateInvocation) { + return new CodeDelegateInvokeExpression(Convert(invocationExpression.Target), Convert(invocationExpression.Arguments)); + } + + Expression methodExpr = invocationExpression.Target; + while (methodExpr is ParenthesizedExpression) + methodExpr = ((ParenthesizedExpression)methodExpr).Expression; + CodeMethodReferenceExpression mr = null; + MemberReferenceExpression mre = methodExpr as MemberReferenceExpression; + if (mre != null) { + mr = new CodeMethodReferenceExpression(Convert(mre.Target), mre.MemberName, Convert(mre.TypeArguments)); + } + IdentifierExpression id = methodExpr as IdentifierExpression; + if (id != null) { + CodeExpression target; + if (rr != null && rr.Member.IsStatic) + target = new CodeTypeReferenceExpression(Convert(rr.Member.DeclaringType)); + else + target = new CodeThisReferenceExpression(); + + mr = new CodeMethodReferenceExpression(target, id.Identifier, Convert(id.TypeArguments)); + } + if (mr != null) + return new CodeMethodInvokeExpression(mr, Convert(invocationExpression.Arguments)); + else + return MakeSnippetExpression(invocationExpression); + } + + CodeObject IAstVisitor.VisitIsExpression(IsExpression isExpression) + { + return MakeSnippetExpression(isExpression); + } + + CodeObject IAstVisitor.VisitLambdaExpression(LambdaExpression lambdaExpression) + { + return MakeSnippetExpression(lambdaExpression); + } + + CodeObject IAstVisitor.VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression) + { + CodeExpression target = Convert(memberReferenceExpression.Target); + ResolveResult rr = Resolve(memberReferenceExpression); + MemberResolveResult mrr = rr as MemberResolveResult; + TypeResolveResult trr = rr as TypeResolveResult; + if (mrr != null) { + return HandleMemberReference(target, memberReferenceExpression.MemberName, memberReferenceExpression.TypeArguments, mrr); + } else if (trr != null) { + return new CodeTypeReferenceExpression(Convert(trr.Type)); + } else { + if (memberReferenceExpression.TypeArguments.Any() || rr is MethodGroupResolveResult) { + return new CodeMethodReferenceExpression(target, memberReferenceExpression.MemberName, Convert(memberReferenceExpression.TypeArguments)); + } else { + return new CodePropertyReferenceExpression(target, memberReferenceExpression.MemberName); + } + } + } + + CodeExpression HandleMemberReference(CodeExpression target, string identifier, AstNodeCollection typeArguments, MemberResolveResult mrr) + { + if (target == null) { + if (mrr.Member.IsStatic) + target = new CodeTypeReferenceExpression(Convert(mrr.Member.DeclaringType)); + else + target = new CodeThisReferenceExpression(); + } + if (mrr.Member is IField) { + return new CodeFieldReferenceExpression(target, identifier); + } else if (mrr.Member is IMethod) { + return new CodeMethodReferenceExpression(target, identifier, Convert(typeArguments)); + } else if (mrr.Member is IEvent) { + return new CodeEventReferenceExpression(target, identifier); + } else { + return new CodePropertyReferenceExpression(target, identifier); + } + } + + CodeObject IAstVisitor.VisitNamedArgumentExpression(NamedArgumentExpression namedArgumentExpression) + { + return MakeSnippetExpression(namedArgumentExpression); + } + + CodeObject IAstVisitor.VisitNamedExpression(NamedExpression namedExpression) + { + return MakeSnippetExpression(namedExpression); + } + + CodeObject IAstVisitor.VisitNullReferenceExpression(NullReferenceExpression nullReferenceExpression) + { + return new CodePrimitiveExpression(null); + } + + CodeObject IAstVisitor.VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression) + { + if (!objectCreateExpression.Initializer.IsNull) + return MakeSnippetExpression(objectCreateExpression); + return new CodeObjectCreateExpression(Convert(objectCreateExpression.Type), Convert(objectCreateExpression.Arguments)); + } + + CodeObject IAstVisitor.VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression) + { + return MakeSnippetExpression(anonymousTypeCreateExpression); + } + + CodeObject IAstVisitor.VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression) + { + // CodeDom generators will insert parentheses where necessary + return Convert(parenthesizedExpression.Expression); + } + + CodeObject IAstVisitor.VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression) + { + return MakeSnippetExpression(pointerReferenceExpression); + } + + CodeObject IAstVisitor.VisitPrimitiveExpression(PrimitiveExpression primitiveExpression) + { + return new CodePrimitiveExpression(primitiveExpression.Value); + } + + CodeObject IAstVisitor.VisitSizeOfExpression(SizeOfExpression sizeOfExpression) + { + return MakeSnippetExpression(sizeOfExpression); + } + + CodeObject IAstVisitor.VisitStackAllocExpression(StackAllocExpression stackAllocExpression) + { + return MakeSnippetExpression(stackAllocExpression); + } + + CodeObject IAstVisitor.VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression) + { + return new CodeThisReferenceExpression(); + } + + CodeObject IAstVisitor.VisitTypeOfExpression(TypeOfExpression typeOfExpression) + { + return new CodeTypeOfExpression(Convert(typeOfExpression.Type)); + } + + CodeObject IAstVisitor.VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression) + { + return new CodeTypeReferenceExpression(Convert(typeReferenceExpression.Type)); + } + + CodeObject IAstVisitor.VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression) + { + switch (unaryOperatorExpression.Operator) { + case UnaryOperatorType.Not: + return new CodeBinaryOperatorExpression( + Convert(unaryOperatorExpression.Expression), + CodeBinaryOperatorType.ValueEquality, + new CodePrimitiveExpression(false)); + case UnaryOperatorType.Minus: + return new CodeBinaryOperatorExpression( + new CodePrimitiveExpression(0), + CodeBinaryOperatorType.Subtract, + Convert(unaryOperatorExpression.Expression)); + case UnaryOperatorType.Plus: + return Convert(unaryOperatorExpression.Expression); + default: + return MakeSnippetExpression(unaryOperatorExpression); + } + } + + CodeObject IAstVisitor.VisitUncheckedExpression(UncheckedExpression uncheckedExpression) + { + return MakeSnippetExpression(uncheckedExpression); + } + + CodeObject IAstVisitor.VisitQueryExpression(QueryExpression queryExpression) + { + return MakeSnippetExpression(queryExpression); + } + + CodeObject IAstVisitor.VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause) + { + throw new NotSupportedException(); + } + + CodeObject IAstVisitor.VisitQueryFromClause(QueryFromClause queryFromClause) + { + throw new NotSupportedException(); + } + + CodeObject IAstVisitor.VisitQueryLetClause(QueryLetClause queryLetClause) + { + throw new NotSupportedException(); + } + + CodeObject IAstVisitor.VisitQueryWhereClause(QueryWhereClause queryWhereClause) + { + throw new NotSupportedException(); + } + + CodeObject IAstVisitor.VisitQueryJoinClause(QueryJoinClause queryJoinClause) + { + throw new NotSupportedException(); + } + + CodeObject IAstVisitor.VisitQueryOrderClause(QueryOrderClause queryOrderClause) + { + throw new NotSupportedException(); + } + + CodeObject IAstVisitor.VisitQueryOrdering(QueryOrdering queryOrdering) + { + throw new NotSupportedException(); + } + + CodeObject IAstVisitor.VisitQuerySelectClause(QuerySelectClause querySelectClause) + { + throw new NotSupportedException(); + } + + CodeObject IAstVisitor.VisitQueryGroupClause(QueryGroupClause queryGroupClause) + { + throw new NotSupportedException(); + } + + CodeObject IAstVisitor.VisitAttribute(Attribute attribute) + { + throw new NotSupportedException(); + } + + CodeObject IAstVisitor.VisitAttributeSection(AttributeSection attributeSection) + { + throw new NotSupportedException(); + } + + CodeAttributeDeclaration Convert(Attribute attribute) + { + var attr = new CodeAttributeDeclaration(Convert(attribute.Type)); + foreach (Expression expr in attribute.Arguments) { + NamedExpression ne = expr as NamedExpression; + if (ne != null) + attr.Arguments.Add(new CodeAttributeArgument(ne.Name, Convert(ne.Expression))); + else + attr.Arguments.Add(new CodeAttributeArgument(Convert(expr))); + } + return attr; + } + + CodeAttributeDeclaration[] Convert(IEnumerable attributeSections) + { + List result = new List(); + foreach (AttributeSection section in attributeSections) { + foreach (Attribute attr in section.Attributes) { + CodeAttributeDeclaration attrDecl = Convert(attr); + if (attrDecl != null) + result.Add(attrDecl); + } + } + return result.ToArray(); + } + + CodeObject IAstVisitor.VisitDelegateDeclaration(DelegateDeclaration delegateDeclaration) + { + CodeTypeDelegate d = new CodeTypeDelegate(delegateDeclaration.Name); + d.Attributes = ConvertMemberAttributes(delegateDeclaration.Modifiers, SymbolKind.TypeDefinition); + d.CustomAttributes.AddRange(Convert(delegateDeclaration.Attributes)); + d.ReturnType = Convert(delegateDeclaration.ReturnType); + d.Parameters.AddRange(Convert(delegateDeclaration.Parameters)); + d.TypeParameters.AddRange(ConvertTypeParameters(delegateDeclaration.TypeParameters, delegateDeclaration.Constraints)); + return d; + } + + MemberAttributes ConvertMemberAttributes(Modifiers modifiers, SymbolKind symbolKind) + { + MemberAttributes a = 0; + if ((modifiers & Modifiers.Abstract) != 0) + a |= MemberAttributes.Abstract; + if ((modifiers & Modifiers.Sealed) != 0) + a |= MemberAttributes.Final; + if (symbolKind != SymbolKind.TypeDefinition && (modifiers & (Modifiers.Abstract | Modifiers.Override | Modifiers.Virtual)) == 0) + a |= MemberAttributes.Final; + if ((modifiers & Modifiers.Static) != 0) + a |= MemberAttributes.Static; + if ((modifiers & Modifiers.Override) != 0) + a |= MemberAttributes.Override; + if ((modifiers & Modifiers.Const) != 0) + a |= MemberAttributes.Const; + if ((modifiers & Modifiers.New) != 0) + a |= MemberAttributes.New; + + if ((modifiers & Modifiers.Public) != 0) + a |= MemberAttributes.Public; + else if ((modifiers & (Modifiers.Protected | Modifiers.Internal)) == (Modifiers.Protected | Modifiers.Internal)) + a |= MemberAttributes.FamilyOrAssembly; + else if ((modifiers & Modifiers.Protected) != 0) + a |= MemberAttributes.Family; + else if ((modifiers & Modifiers.Internal) != 0) + a |= MemberAttributes.Assembly; + else if ((modifiers & Modifiers.Private) != 0) + a |= MemberAttributes.Private; + + return a; + } + + CodeObject IAstVisitor.VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration) + { + CodeNamespace ns = new CodeNamespace(namespaceDeclaration.Name); + foreach (AstNode node in namespaceDeclaration.Members) { + CodeObject r = node.AcceptVisitor(this); + + CodeNamespaceImport import = r as CodeNamespaceImport; + if (import != null) + ns.Imports.Add(import); + + CodeTypeDeclaration typeDecl = r as CodeTypeDeclaration; + if (typeDecl != null) + ns.Types.Add(typeDecl); + } + return ns; + } + + Stack typeStack = new Stack(); + + CodeObject IAstVisitor.VisitTypeDeclaration(TypeDeclaration typeDeclaration) + { + //bool isNestedType = typeStack.Count > 0; + CodeTypeDeclaration typeDecl = new CodeTypeDeclaration(typeDeclaration.Name); + typeDecl.Attributes = ConvertMemberAttributes(typeDeclaration.Modifiers, SymbolKind.TypeDefinition); + typeDecl.CustomAttributes.AddRange(Convert(typeDeclaration.Attributes)); + + switch (typeDeclaration.ClassType) { + case ClassType.Struct: + typeDecl.IsStruct = true; + break; + case ClassType.Interface: + typeDecl.IsInterface = true; + break; + case ClassType.Enum: + typeDecl.IsEnum = true; + break; + default: + typeDecl.IsClass = true; + break; + } + typeDecl.IsPartial = (typeDeclaration.Modifiers & Modifiers.Partial) == Modifiers.Partial; + + typeDecl.BaseTypes.AddRange(Convert(typeDeclaration.BaseTypes)); + typeDecl.TypeParameters.AddRange(ConvertTypeParameters(typeDeclaration.TypeParameters, typeDeclaration.Constraints)); + + typeStack.Push(typeDecl); + foreach (var member in typeDeclaration.Members) { + CodeTypeMember m = member.AcceptVisitor(this) as CodeTypeMember; + if (m != null) + typeDecl.Members.Add(m); + } + typeStack.Pop(); + return typeDecl; + } + + void AddTypeMember(CodeTypeMember member) + { + if (typeStack.Count != 0) + typeStack.Peek().Members.Add(member); + } + + CodeObject IAstVisitor.VisitUsingAliasDeclaration(UsingAliasDeclaration usingAliasDeclaration) + { + return new CodeSnippetTypeMember(MakeSnippet(usingAliasDeclaration)); + } + + CodeObject IAstVisitor.VisitUsingDeclaration(UsingDeclaration usingDeclaration) + { + return new CodeNamespaceImport(usingDeclaration.Namespace); + } + + CodeObject IAstVisitor.VisitExternAliasDeclaration(ExternAliasDeclaration externAliasDeclaration) + { + return new CodeSnippetTypeMember(MakeSnippet(externAliasDeclaration)); + } + + CodeObject IAstVisitor.VisitBlockStatement(BlockStatement blockStatement) + { + return new CodeConditionStatement(new CodePrimitiveExpression(true), ConvertBlock(blockStatement)); + } + + CodeObject IAstVisitor.VisitBreakStatement(BreakStatement breakStatement) + { + return MakeSnippetStatement(breakStatement); + } + + CodeObject IAstVisitor.VisitCheckedStatement(CheckedStatement checkedStatement) + { + return MakeSnippetStatement(checkedStatement); + } + + CodeObject IAstVisitor.VisitContinueStatement(ContinueStatement continueStatement) + { + return MakeSnippetStatement(continueStatement); + } + + CodeObject IAstVisitor.VisitDoWhileStatement(DoWhileStatement doWhileStatement) + { + // do { } while (expr); + // + // emulate with: + // for (bool _do = true; _do; _do = expr) {} + string varName = "_do" + doWhileStatement.Ancestors.OfType().Count(); + return new CodeIterationStatement( + new CodeVariableDeclarationStatement(typeof(bool), varName, new CodePrimitiveExpression(true)), + new CodeVariableReferenceExpression(varName), + new CodeAssignStatement(new CodeVariableReferenceExpression(varName), Convert(doWhileStatement.Condition)), + ConvertEmbeddedStatement(doWhileStatement.EmbeddedStatement) + ); + } + + CodeObject IAstVisitor.VisitEmptyStatement(EmptyStatement emptyStatement) + { + return EmptyStatement(); + } + + CodeStatement EmptyStatement() + { + return new CodeExpressionStatement(new CodeObjectCreateExpression(new CodeTypeReference(typeof(object)))); + } + + CodeObject IAstVisitor.VisitExpressionStatement(ExpressionStatement expressionStatement) + { + AssignmentExpression assignment = expressionStatement.Expression as AssignmentExpression; + if (assignment != null && assignment.Operator == AssignmentOperatorType.Assign) { + return new CodeAssignStatement(Convert(assignment.Left), Convert(assignment.Right)); + } else if (assignment != null && CanBeDuplicatedForCompoundAssignment(assignment.Left)) { + CodeBinaryOperatorType op; + switch (assignment.Operator) { + case AssignmentOperatorType.Add: + op = CodeBinaryOperatorType.Add; + break; + case AssignmentOperatorType.Subtract: + op = CodeBinaryOperatorType.Subtract; + break; + case AssignmentOperatorType.Multiply: + op = CodeBinaryOperatorType.Multiply; + break; + case AssignmentOperatorType.Divide: + op = CodeBinaryOperatorType.Divide; + break; + case AssignmentOperatorType.Modulus: + op = CodeBinaryOperatorType.Modulus; + break; + case AssignmentOperatorType.BitwiseAnd: + op = CodeBinaryOperatorType.BitwiseAnd; + break; + case AssignmentOperatorType.BitwiseOr: + op = CodeBinaryOperatorType.BitwiseOr; + break; + default: + return MakeSnippetStatement(expressionStatement); + } + var cboe = new CodeBinaryOperatorExpression(Convert(assignment.Left), op, Convert(assignment.Right)); + return new CodeAssignStatement(Convert(assignment.Left), cboe); + } + UnaryOperatorExpression unary = expressionStatement.Expression as UnaryOperatorExpression; + if (unary != null && CanBeDuplicatedForCompoundAssignment(unary.Expression)) { + var op = unary.Operator; + if (op == UnaryOperatorType.Increment || op == UnaryOperatorType.PostIncrement) { + var cboe = new CodeBinaryOperatorExpression(Convert(unary.Expression), CodeBinaryOperatorType.Add, new CodePrimitiveExpression(1)); + return new CodeAssignStatement(Convert(unary.Expression), cboe); + } else if (op == UnaryOperatorType.Decrement || op == UnaryOperatorType.PostDecrement) { + var cboe = new CodeBinaryOperatorExpression(Convert(unary.Expression), CodeBinaryOperatorType.Subtract, new CodePrimitiveExpression(1)); + return new CodeAssignStatement(Convert(unary.Expression), cboe); + } + } + if (assignment != null && assignment.Operator == AssignmentOperatorType.Add) { + var rr = Resolve(assignment.Left); + if (!rr.IsError && rr.Type.Kind == TypeKind.Delegate) { + var expr = (MemberReferenceExpression)assignment.Left; + var memberRef = (CodeEventReferenceExpression)HandleMemberReference(Convert(expr.Target), expr.MemberName, expr.TypeArguments, (MemberResolveResult)rr); + return new CodeAttachEventStatement(memberRef, Convert(assignment.Right)); + } + } + return new CodeExpressionStatement(Convert(expressionStatement.Expression)); + } + + bool CanBeDuplicatedForCompoundAssignment(Expression expr) + { + return expr is IdentifierExpression; + } + + CodeObject IAstVisitor.VisitFixedStatement(FixedStatement fixedStatement) + { + return MakeSnippetStatement(fixedStatement); + } + + CodeObject IAstVisitor.VisitForeachStatement(ForeachStatement foreachStatement) + { + return MakeSnippetStatement(foreachStatement); + } + + CodeObject IAstVisitor.VisitForStatement(ForStatement forStatement) + { + if (forStatement.Initializers.Count != 1 || forStatement.Iterators.Count != 1) + return MakeSnippetStatement(forStatement); + return new CodeIterationStatement( + Convert(forStatement.Initializers.Single()), + Convert(forStatement.Condition), + Convert(forStatement.Iterators.Single()), + ConvertEmbeddedStatement(forStatement.EmbeddedStatement) + ); + } + + CodeObject IAstVisitor.VisitGotoCaseStatement(GotoCaseStatement gotoCaseStatement) + { + return MakeSnippetStatement(gotoCaseStatement); + } + + CodeObject IAstVisitor.VisitGotoDefaultStatement(GotoDefaultStatement gotoDefaultStatement) + { + return MakeSnippetStatement(gotoDefaultStatement); + } + + CodeObject IAstVisitor.VisitGotoStatement(GotoStatement gotoStatement) + { + return new CodeGotoStatement(gotoStatement.Label); + } + + CodeObject IAstVisitor.VisitIfElseStatement(IfElseStatement ifElseStatement) + { + return new CodeConditionStatement( + Convert(ifElseStatement.Condition), + ConvertEmbeddedStatement(ifElseStatement.TrueStatement), + ConvertEmbeddedStatement(ifElseStatement.FalseStatement)); + } + + CodeObject IAstVisitor.VisitLabelStatement(LabelStatement labelStatement) + { + return new CodeLabeledStatement(labelStatement.Label); + } + + CodeObject IAstVisitor.VisitLockStatement(LockStatement lockStatement) + { + return MakeSnippetStatement(lockStatement); + } + + CodeObject IAstVisitor.VisitReturnStatement(ReturnStatement returnStatement) + { + return new CodeMethodReturnStatement(Convert(returnStatement.Expression)); + } + + CodeObject IAstVisitor.VisitSwitchStatement(SwitchStatement switchStatement) + { + return MakeSnippetStatement(switchStatement); + } + + CodeObject IAstVisitor.VisitSwitchSection(SwitchSection switchSection) + { + throw new NotSupportedException(); + } + + CodeObject IAstVisitor.VisitCaseLabel(CaseLabel caseLabel) + { + throw new NotSupportedException(); + } + + CodeObject IAstVisitor.VisitThrowStatement(ThrowStatement throwStatement) + { + return new CodeThrowExceptionStatement(Convert(throwStatement.Expression)); + } + + CodeObject IAstVisitor.VisitTryCatchStatement(TryCatchStatement tryCatchStatement) + { + List catchClauses = new List(); + foreach (var catchClause in tryCatchStatement.CatchClauses) { + catchClauses.Add(new CodeCatchClause(catchClause.VariableName, Convert(catchClause.Type), ConvertBlock(catchClause.Body))); + } + return new CodeTryCatchFinallyStatement( + ConvertBlock(tryCatchStatement.TryBlock), + catchClauses.ToArray(), + ConvertBlock(tryCatchStatement.FinallyBlock)); + } + + CodeObject IAstVisitor.VisitCatchClause(CatchClause catchClause) + { + throw new NotSupportedException(); + } + + CodeObject IAstVisitor.VisitUncheckedStatement(UncheckedStatement uncheckedStatement) + { + return MakeSnippetStatement(uncheckedStatement); + } + + CodeObject IAstVisitor.VisitUnsafeStatement(UnsafeStatement unsafeStatement) + { + return MakeSnippetStatement(unsafeStatement); + } + + CodeObject IAstVisitor.VisitUsingStatement(UsingStatement usingStatement) + { + return MakeSnippetStatement(usingStatement); + } + + CodeObject IAstVisitor.VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement) + { + if (variableDeclarationStatement.Variables.Count != 1) + return MakeSnippetStatement(variableDeclarationStatement); + VariableInitializer vi = variableDeclarationStatement.Variables.Single(); + return new CodeVariableDeclarationStatement( + Convert(variableDeclarationStatement.Type), + vi.Name, + ConvertVariableInitializer(vi.Initializer, variableDeclarationStatement.Type)); + } + + CodeExpression ConvertVariableInitializer(Expression expr, AstType type) + { + ArrayInitializerExpression aie = expr as ArrayInitializerExpression; + if (aie != null) { + return new CodeArrayCreateExpression(Convert(type), Convert(aie.Elements)); + } else { + return Convert(expr); + } + } + + CodeObject IAstVisitor.VisitWhileStatement(WhileStatement whileStatement) + { + return new CodeIterationStatement(EmptyStatement(), Convert(whileStatement.Condition), EmptyStatement(), ConvertEmbeddedStatement(whileStatement.EmbeddedStatement)); + } + + CodeObject IAstVisitor.VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement) + { + return MakeSnippetStatement(yieldBreakStatement); + } + + CodeObject IAstVisitor.VisitYieldReturnStatement(YieldReturnStatement yieldStatement) + { + return MakeSnippetStatement(yieldStatement); + } + + CodeObject IAstVisitor.VisitAccessor(Accessor accessor) + { + throw new NotSupportedException(); + } + + CodeObject IAstVisitor.VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration) + { + CodeConstructor ctor = new CodeConstructor(); + ctor.Attributes = ConvertMemberAttributes(constructorDeclaration.Modifiers, SymbolKind.Constructor); + ctor.CustomAttributes.AddRange(Convert(constructorDeclaration.Attributes)); + if (constructorDeclaration.Initializer.ConstructorInitializerType == ConstructorInitializerType.This) { + ctor.ChainedConstructorArgs.AddRange(Convert(constructorDeclaration.Initializer.Arguments)); + } else { + ctor.BaseConstructorArgs.AddRange(Convert(constructorDeclaration.Initializer.Arguments)); + } + ctor.Parameters.AddRange(Convert(constructorDeclaration.Parameters)); + + ctor.Statements.AddRange(ConvertBlock(constructorDeclaration.Body)); + return ctor; + } + + CodeObject IAstVisitor.VisitConstructorInitializer(ConstructorInitializer constructorInitializer) + { + throw new NotSupportedException(); + } + + CodeObject IAstVisitor.VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration) + { + return new CodeSnippetTypeMember(MakeSnippet(destructorDeclaration)); + } + + CodeObject IAstVisitor.VisitEnumMemberDeclaration(EnumMemberDeclaration enumMemberDeclaration) + { + TypeDeclaration td = enumMemberDeclaration.Parent as TypeDeclaration; + CodeMemberField f = new CodeMemberField(td != null ? td.Name : "Enum", enumMemberDeclaration.Name); + f.Attributes = MemberAttributes.Public | MemberAttributes.Static; + f.CustomAttributes.AddRange(Convert(enumMemberDeclaration.Attributes)); + f.InitExpression = Convert(enumMemberDeclaration.Initializer); + return f; + } + + CodeObject IAstVisitor.VisitEventDeclaration(EventDeclaration eventDeclaration) + { + foreach (VariableInitializer vi in eventDeclaration.Variables) { + if (!vi.Initializer.IsNull) { + AddTypeMember(new CodeSnippetTypeMember(MakeSnippet(eventDeclaration))); + continue; + } + + CodeMemberEvent e = new CodeMemberEvent(); + e.Attributes = ConvertMemberAttributes(eventDeclaration.Modifiers, SymbolKind.Event); + e.CustomAttributes.AddRange(Convert(eventDeclaration.Attributes)); + e.Name = vi.Name; + e.Type = Convert(eventDeclaration.ReturnType); + AddTypeMember(e); + } + return null; + } + + CodeObject IAstVisitor.VisitCustomEventDeclaration(CustomEventDeclaration customEventDeclaration) + { + return new CodeSnippetTypeMember(MakeSnippet(customEventDeclaration)); + } + + CodeObject IAstVisitor.VisitFieldDeclaration(FieldDeclaration fieldDeclaration) + { + foreach (VariableInitializer vi in fieldDeclaration.Variables) { + CodeMemberField f = new CodeMemberField(Convert(fieldDeclaration.ReturnType), vi.Name); + f.Attributes = ConvertMemberAttributes(fieldDeclaration.Modifiers, SymbolKind.Field); + f.CustomAttributes.AddRange(Convert(fieldDeclaration.Attributes)); + f.InitExpression = ConvertVariableInitializer(vi.Initializer, fieldDeclaration.ReturnType); + AddTypeMember(f); + } + return null; + } + + CodeObject IAstVisitor.VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration) + { + CodeMemberProperty p = new CodeMemberProperty(); + p.Attributes = ConvertMemberAttributes(indexerDeclaration.Modifiers, SymbolKind.Indexer); + p.CustomAttributes.AddRange(Convert(indexerDeclaration.Attributes)); + p.Name = "Items"; + p.PrivateImplementationType = Convert(indexerDeclaration.PrivateImplementationType); + p.Parameters.AddRange(Convert(indexerDeclaration.Parameters)); + p.Type = Convert(indexerDeclaration.ReturnType); + + if (!indexerDeclaration.Getter.IsNull) { + p.HasGet = true; + p.GetStatements.AddRange(ConvertBlock(indexerDeclaration.Getter.Body)); + } + if (!indexerDeclaration.Setter.IsNull) { + p.HasSet = true; + p.SetStatements.AddRange(ConvertBlock(indexerDeclaration.Setter.Body)); + } + return p; + } + + CodeObject IAstVisitor.VisitMethodDeclaration(MethodDeclaration methodDeclaration) + { + CodeMemberMethod m = new CodeMemberMethod(); + m.Attributes = ConvertMemberAttributes(methodDeclaration.Modifiers, SymbolKind.Method); + + m.CustomAttributes.AddRange(Convert(methodDeclaration.Attributes.Where(a => a.AttributeTarget != "return"))); + m.ReturnTypeCustomAttributes.AddRange(Convert(methodDeclaration.Attributes.Where(a => a.AttributeTarget == "return"))); + + m.ReturnType = Convert(methodDeclaration.ReturnType); + m.PrivateImplementationType = Convert(methodDeclaration.PrivateImplementationType); + m.Name = methodDeclaration.Name; + m.TypeParameters.AddRange(ConvertTypeParameters(methodDeclaration.TypeParameters, methodDeclaration.Constraints)); + m.Parameters.AddRange(Convert(methodDeclaration.Parameters)); + + m.Statements.AddRange(ConvertBlock(methodDeclaration.Body)); + return m; + } + + CodeObject IAstVisitor.VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration) + { + CodeMemberMethod m = new CodeMemberMethod(); + m.Attributes = ConvertMemberAttributes(operatorDeclaration.Modifiers, SymbolKind.Method); + + m.CustomAttributes.AddRange(Convert(operatorDeclaration.Attributes.Where(a => a.AttributeTarget != "return"))); + m.ReturnTypeCustomAttributes.AddRange(Convert(operatorDeclaration.Attributes.Where(a => a.AttributeTarget == "return"))); + + m.ReturnType = Convert(operatorDeclaration.ReturnType); + m.Name = operatorDeclaration.Name; + m.Parameters.AddRange(Convert(operatorDeclaration.Parameters)); + + m.Statements.AddRange(ConvertBlock(operatorDeclaration.Body)); + return m; + } + + CodeObject IAstVisitor.VisitParameterDeclaration(ParameterDeclaration parameterDeclaration) + { + var p = new CodeParameterDeclarationExpression(Convert(parameterDeclaration.Type), parameterDeclaration.Name); + p.CustomAttributes.AddRange(Convert(parameterDeclaration.Attributes)); + switch (parameterDeclaration.ParameterModifier) { + case ParameterModifier.Ref: + p.Direction = System.CodeDom.FieldDirection.Ref; + break; + case ParameterModifier.Out: + p.Direction = System.CodeDom.FieldDirection.Out; + break; + } + return p; + } + + CodeParameterDeclarationExpression[] Convert(IEnumerable parameters) + { + List result = new List(); + foreach (ParameterDeclaration pd in parameters) { + CodeParameterDeclarationExpression pde = pd.AcceptVisitor(this) as CodeParameterDeclarationExpression; + if (pde != null) + result.Add(pde); + } + return result.ToArray(); + } + + CodeObject IAstVisitor.VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration) + { + CodeMemberProperty p = new CodeMemberProperty(); + p.Attributes = ConvertMemberAttributes(propertyDeclaration.Modifiers, SymbolKind.Property); + p.CustomAttributes.AddRange(Convert(propertyDeclaration.Attributes)); + p.Name = propertyDeclaration.Name; + p.PrivateImplementationType = Convert(propertyDeclaration.PrivateImplementationType); + p.Type = Convert(propertyDeclaration.ReturnType); + + if (!propertyDeclaration.Getter.IsNull) { + p.HasGet = true; + p.GetStatements.AddRange(ConvertBlock(propertyDeclaration.Getter.Body)); + } + if (!propertyDeclaration.Setter.IsNull) { + p.HasSet = true; + p.SetStatements.AddRange(ConvertBlock(propertyDeclaration.Setter.Body)); + } + return p; + } + + CodeObject IAstVisitor.VisitVariableInitializer(VariableInitializer variableInitializer) + { + throw new NotSupportedException(); // should be handled by the parent node + } + + CodeObject IAstVisitor.VisitFixedFieldDeclaration(FixedFieldDeclaration fixedFieldDeclaration) + { + return new CodeSnippetTypeMember(MakeSnippet(fixedFieldDeclaration)); + } + + CodeObject IAstVisitor.VisitFixedVariableInitializer(FixedVariableInitializer fixedVariableInitializer) + { + throw new NotSupportedException(); // should be handled by the parent node + } + + CodeObject IAstVisitor.VisitSyntaxTree(SyntaxTree syntaxTree) + { + CodeCompileUnit cu = new CodeCompileUnit(); + var globalImports = new List (); + foreach (AstNode node in syntaxTree.Children) { + CodeObject o = node.AcceptVisitor(this); + + CodeNamespace ns = o as CodeNamespace; + if (ns != null) { + cu.Namespaces.Add(ns); + } + CodeTypeDeclaration td = o as CodeTypeDeclaration; + if (td != null) { + cu.Namespaces.Add(new CodeNamespace() { Types = { td } }); + } + + var import = o as CodeNamespaceImport; + if (import != null) + globalImports.Add (import); + } + foreach (var gi in globalImports) { + for (int j = 0; j < cu.Namespaces.Count; j++) { + var cn = cu.Namespaces [j]; + bool found = cn.Imports + .Cast () + .Any (ns => ns.Namespace == gi.Namespace); + if (!found) + cn.Imports.Add (gi); + } + } + return cu; + } + + CodeObject IAstVisitor.VisitSimpleType(SimpleType simpleType) + { + if (UseFullyQualifiedTypeNames) { + IType type = Resolve(simpleType).Type; + if (type.Kind != TypeKind.Unknown) + return Convert(type); + } + var tr = new CodeTypeReference(simpleType.Identifier); + tr.TypeArguments.AddRange(Convert(simpleType.TypeArguments)); + return tr; + } + + CodeObject IAstVisitor.VisitMemberType(MemberType memberType) + { + if (memberType.IsDoubleColon && new SimpleType("global").IsMatch(memberType.Target)) { + var tr = new CodeTypeReference(memberType.MemberName, CodeTypeReferenceOptions.GlobalReference); + tr.TypeArguments.AddRange(Convert(memberType.TypeArguments)); + return tr; + } + if (UseFullyQualifiedTypeNames || memberType.IsDoubleColon) { + IType type = Resolve(memberType).Type; + if (type.Kind != TypeKind.Unknown) + return Convert(type); + } + CodeTypeReference target = Convert(memberType.Target); + if (target == null) + return null; + target.BaseType = target.BaseType + "." + memberType.MemberName; + target.TypeArguments.AddRange(Convert(memberType.TypeArguments)); + return target; + } + + CodeObject IAstVisitor.VisitComposedType(ComposedType composedType) + { + CodeTypeReference typeRef = Convert(composedType.BaseType); + if (typeRef == null) + return null; + if (composedType.HasNullableSpecifier) { + typeRef = new CodeTypeReference("System.Nullable") { TypeArguments = { typeRef } }; + } + foreach (ArraySpecifier s in composedType.ArraySpecifiers.Reverse()) { + typeRef = new CodeTypeReference(typeRef, s.Dimensions); + } + return typeRef; + } + + CodeObject IAstVisitor.VisitArraySpecifier(ArraySpecifier arraySpecifier) + { + throw new NotSupportedException(); // handled by parent node + } + + CodeObject IAstVisitor.VisitPrimitiveType(PrimitiveType primitiveType) + { + KnownTypeCode typeCode = primitiveType.KnownTypeCode; + if (typeCode != KnownTypeCode.None) { + KnownTypeReference ktr = KnownTypeReference.Get(typeCode); + return new CodeTypeReference(ktr.Namespace + "." + ktr.Name); + } + return new CodeTypeReference(primitiveType.Keyword); + } + + CodeObject IAstVisitor.VisitComment (Comment comment) + { + return new CodeComment (comment.Content, comment.CommentType == CommentType.Documentation); + } + + CodeObject IAstVisitor.VisitNewLine(NewLineNode newLineNode) + { + return null; + } + + CodeObject IAstVisitor.VisitWhitespace(WhitespaceNode whitespaceNode) + { + return null; + } + + CodeObject IAstVisitor.VisitText(TextNode textNode) + { + throw new NotSupportedException(); + } + + CodeObject IAstVisitor.VisitPreProcessorDirective (PreProcessorDirective preProcessorDirective) + { + return new CodeComment ("#" + preProcessorDirective.Type.ToString ().ToLowerInvariant ()); + } + + CodeObject IAstVisitor.VisitTypeParameterDeclaration(TypeParameterDeclaration typeParameterDeclaration) + { + throw new NotSupportedException(); // type parameters and constraints are handled together + } + + CodeObject IAstVisitor.VisitConstraint(Constraint constraint) + { + throw new NotSupportedException(); + } + + CodeTypeParameter[] ConvertTypeParameters(IEnumerable typeParameters, IEnumerable constraints) + { + List result = new List(); + foreach (TypeParameterDeclaration tpDecl in typeParameters) { + CodeTypeParameter tp = new CodeTypeParameter(tpDecl.Name); + tp.CustomAttributes.AddRange(Convert(tpDecl.Attributes)); + foreach (Constraint constraint in constraints) { + if (constraint.TypeParameter.Identifier == tp.Name) { + foreach (AstType baseType in constraint.BaseTypes) { + if (baseType is PrimitiveType && ((PrimitiveType)baseType).Keyword == "new") { + tp.HasConstructorConstraint = true; + } else { + CodeTypeReference tr = Convert(baseType); + if (tr != null) + tp.Constraints.Add(tr); + } + } + } + } + result.Add(tp); + } + return result.ToArray(); + } + + CodeObject IAstVisitor.VisitCSharpTokenNode(CSharpTokenNode cSharpTokenNode) + { + return null; + } + + CodeObject IAstVisitor.VisitIdentifier(Identifier identifier) + { + return null; + } + + CodeObject IAstVisitor.VisitPatternPlaceholder(AstNode placeholder, Pattern pattern) + { + return null; + } + + CodeObject IAstVisitor.VisitDocumentationReference(DocumentationReference documentationReference) + { + return null; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/ITokenWriter.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/ITokenWriter.cs new file mode 100644 index 000000000..31b73f987 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/ITokenWriter.cs @@ -0,0 +1,161 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// 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.IO; + +namespace ICSharpCode.NRefactory.CSharp +{ + public abstract class TokenWriter + { + public abstract void StartNode(AstNode node); + public abstract void EndNode(AstNode node); + + /// + /// Writes an identifier. + /// + public abstract void WriteIdentifier(Identifier identifier); + + /// + /// Writes a keyword to the output. + /// + public abstract void WriteKeyword(Role role, string keyword); + + /// + /// Writes a token to the output. + /// + public abstract void WriteToken(Role role, string token); + + /// + /// Writes a primitive/literal value + /// + public abstract void WritePrimitiveValue(object value, string literalValue = null); + + public abstract void WritePrimitiveType(string type); + + public abstract void Space(); + public abstract void Indent(); + public abstract void Unindent(); + public abstract void NewLine(); + + public abstract void WriteComment(CommentType commentType, string content); + public abstract void WritePreProcessorDirective(PreProcessorDirectiveType type, string argument); + + public static TokenWriter Create(TextWriter writer, string indentation = "\t") + { + return new InsertSpecialsDecorator(new InsertRequiredSpacesDecorator(new TextWriterTokenWriter(writer) { IndentationString = indentation })); + } + + public static TokenWriter CreateWriterThatSetsLocationsInAST(TextWriter writer, string indentation = "\t") + { + var target = new TextWriterTokenWriter(writer) { IndentationString = indentation }; + return new InsertSpecialsDecorator(new InsertRequiredSpacesDecorator(new InsertMissingTokensDecorator(target, target))); + } + + public static TokenWriter WrapInWriterThatSetsLocationsInAST(TokenWriter writer) + { + if (!(writer is ILocatable)) + throw new InvalidOperationException("writer does not provide locations!"); + return new InsertSpecialsDecorator(new InsertRequiredSpacesDecorator(new InsertMissingTokensDecorator(writer, (ILocatable)writer))); + } + } + + public interface ILocatable + { + TextLocation Location { get; } + } + + public abstract class DecoratingTokenWriter : TokenWriter + { + TokenWriter decoratedWriter; + + protected DecoratingTokenWriter(TokenWriter decoratedWriter) + { + if (decoratedWriter == null) + throw new ArgumentNullException("decoratedWriter"); + this.decoratedWriter = decoratedWriter; + } + + public override void StartNode(AstNode node) + { + decoratedWriter.StartNode(node); + } + + public override void EndNode(AstNode node) + { + decoratedWriter.EndNode(node); + } + + public override void WriteIdentifier(Identifier identifier) + { + decoratedWriter.WriteIdentifier(identifier); + } + + public override void WriteKeyword(Role role, string keyword) + { + decoratedWriter.WriteKeyword(role, keyword); + } + + public override void WriteToken(Role role, string token) + { + decoratedWriter.WriteToken(role, token); + } + + public override void WritePrimitiveValue(object value, string literalValue = null) + { + decoratedWriter.WritePrimitiveValue(value, literalValue); + } + + public override void WritePrimitiveType(string type) + { + decoratedWriter.WritePrimitiveType(type); + } + + public override void Space() + { + decoratedWriter.Space(); + } + + public override void Indent() + { + decoratedWriter.Indent(); + } + + public override void Unindent() + { + decoratedWriter.Unindent(); + } + + public override void NewLine() + { + decoratedWriter.NewLine(); + } + + public override void WriteComment(CommentType commentType, string content) + { + decoratedWriter.WriteComment(commentType, content); + } + + public override void WritePreProcessorDirective(PreProcessorDirectiveType type, string argument) + { + decoratedWriter.WritePreProcessorDirective(type, argument); + } + } +} + + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/InsertMissingTokensDecorator.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/InsertMissingTokensDecorator.cs new file mode 100644 index 000000000..3f9200145 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/InsertMissingTokensDecorator.cs @@ -0,0 +1,123 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + class InsertMissingTokensDecorator : DecoratingTokenWriter + { + readonly Stack> nodes = new Stack>(); + List currentList; + readonly ILocatable locationProvider; + + public InsertMissingTokensDecorator(TokenWriter writer, ILocatable locationProvider) + : base(writer) + { + this.locationProvider = locationProvider; + currentList = new List(); + } + + public override void StartNode(AstNode node) + { + currentList.Add(node); + nodes.Push(currentList); + currentList = new List(); + base.StartNode(node); + } + + public override void EndNode(AstNode node) + { + System.Diagnostics.Debug.Assert(currentList != null); + foreach (var removable in node.Children.Where(n => n is CSharpTokenNode)) { + removable.Remove(); + } + foreach (var child in currentList) { + System.Diagnostics.Debug.Assert(child.Parent == null || node == child.Parent); + child.Remove(); + node.AddChildWithExistingRole(child); + } + currentList = nodes.Pop(); + base.EndNode(node); + } + + public override void WriteToken(Role role, string token) + { + CSharpTokenNode t = new CSharpTokenNode(locationProvider.Location, (TokenRole)role); + t.Role = role; + EmptyStatement node = nodes.Peek().LastOrDefault() as EmptyStatement; + if (node == null) + currentList.Add(t); + else { + node.Location = locationProvider.Location; + } + base.WriteToken(role, token); + } + + public override void WriteKeyword(Role role, string keyword) + { + TextLocation start = locationProvider.Location; + CSharpTokenNode t = null; + if (role is TokenRole) + t = new CSharpTokenNode(start, (TokenRole)role); + else if (role == EntityDeclaration.ModifierRole) + t = new CSharpModifierToken(start, CSharpModifierToken.GetModifierValue(keyword)); + else if (keyword == "this") { + ThisReferenceExpression node = nodes.Peek().LastOrDefault() as ThisReferenceExpression; + if (node != null) + node.Location = start; + } else if (keyword == "base") { + BaseReferenceExpression node = nodes.Peek().LastOrDefault() as BaseReferenceExpression; + if (node != null) + node.Location = start; + } + if (t != null) currentList.Add(t); + base.WriteKeyword(role, keyword); + } + + public override void WriteIdentifier(Identifier identifier) + { + if (!identifier.IsNull) + identifier.SetStartLocation(locationProvider.Location); + currentList.Add(identifier); + base.WriteIdentifier(identifier); + } + + public override void WritePrimitiveValue(object value, string literalValue = null) + { + Expression node = nodes.Peek().LastOrDefault() as Expression; + if (node is PrimitiveExpression) { + ((PrimitiveExpression)node).SetStartLocation(locationProvider.Location); + } + if (node is NullReferenceExpression) { + ((NullReferenceExpression)node).SetStartLocation(locationProvider.Location); + } + base.WritePrimitiveValue(value, literalValue); + } + + public override void WritePrimitiveType(string type) + { + PrimitiveType node = nodes.Peek().LastOrDefault() as PrimitiveType; + if (node != null) + node.SetStartLocation(locationProvider.Location); + base.WritePrimitiveType(type); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/InsertParenthesesVisitor.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/InsertParenthesesVisitor.cs new file mode 100644 index 000000000..6d622de23 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/InsertParenthesesVisitor.cs @@ -0,0 +1,345 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// 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; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Inserts the parentheses into the AST that are needed to ensure the AST can be printed correctly. + /// For example, if the AST contains + /// BinaryOperatorExpresson(2, Mul, BinaryOperatorExpression(1, Add, 1))); printing that AST + /// would incorrectly result in "2 * 1 + 1". By running InsertParenthesesVisitor, the necessary + /// parentheses are inserted: "2 * (1 + 1)". + /// + public class InsertParenthesesVisitor : DepthFirstAstVisitor + { + /// + /// Gets/Sets whether the visitor should insert parentheses to make the code better looking. + /// If this property is false, it will insert parentheses only where strictly required by the language spec. + /// + public bool InsertParenthesesForReadability { get; set; } + + const int Primary = 16; + const int QueryOrLambda = 15; + const int Unary = 14; + const int RelationalAndTypeTesting = 10; + const int Equality = 9; + const int Conditional = 2; + const int Assignment = 1; + + /// + /// Gets the row number in the C# 4.0 spec operator precedence table. + /// + static int GetPrecedence(Expression expr) + { + // Note: the operator precedence table on MSDN is incorrect + if (expr is QueryExpression) { + // Not part of the table in the C# spec, but we need to ensure that queries within + // primary expressions get parenthesized. + return QueryOrLambda; + } + UnaryOperatorExpression uoe = expr as UnaryOperatorExpression; + if (uoe != null) { + if (uoe.Operator == UnaryOperatorType.PostDecrement || uoe.Operator == UnaryOperatorType.PostIncrement) + return Primary; + else + return Unary; + } + if (expr is CastExpression) + return Unary; + BinaryOperatorExpression boe = expr as BinaryOperatorExpression; + if (boe != null) { + switch (boe.Operator) { + case BinaryOperatorType.Multiply: + case BinaryOperatorType.Divide: + case BinaryOperatorType.Modulus: + return 13; // multiplicative + case BinaryOperatorType.Add: + case BinaryOperatorType.Subtract: + return 12; // additive + case BinaryOperatorType.ShiftLeft: + case BinaryOperatorType.ShiftRight: + return 11; + case BinaryOperatorType.GreaterThan: + case BinaryOperatorType.GreaterThanOrEqual: + case BinaryOperatorType.LessThan: + case BinaryOperatorType.LessThanOrEqual: + return RelationalAndTypeTesting; + case BinaryOperatorType.Equality: + case BinaryOperatorType.InEquality: + return Equality; + case BinaryOperatorType.BitwiseAnd: + return 8; + case BinaryOperatorType.ExclusiveOr: + return 7; + case BinaryOperatorType.BitwiseOr: + return 6; + case BinaryOperatorType.ConditionalAnd: + return 5; + case BinaryOperatorType.ConditionalOr: + return 4; + case BinaryOperatorType.NullCoalescing: + return 3; + default: + throw new NotSupportedException("Invalid value for BinaryOperatorType"); + } + } + if (expr is IsExpression || expr is AsExpression) + return RelationalAndTypeTesting; + if (expr is ConditionalExpression) + return Conditional; + if (expr is AssignmentExpression || expr is LambdaExpression) + return Assignment; + // anything else: primary expression + return Primary; + } + + /// + /// Parenthesizes the expression if it does not have the minimum required precedence. + /// + static void ParenthesizeIfRequired(Expression expr, int minimumPrecedence) + { + if (GetPrecedence(expr) < minimumPrecedence) { + Parenthesize(expr); + } + } + + static void Parenthesize(Expression expr) + { + expr.ReplaceWith(e => new ParenthesizedExpression { Expression = e }); + } + + // Primary expressions + public override void VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression) + { + ParenthesizeIfRequired(memberReferenceExpression.Target, Primary); + base.VisitMemberReferenceExpression(memberReferenceExpression); + } + + public override void VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression) + { + ParenthesizeIfRequired(pointerReferenceExpression.Target, Primary); + base.VisitPointerReferenceExpression(pointerReferenceExpression); + } + + public override void VisitInvocationExpression(InvocationExpression invocationExpression) + { + ParenthesizeIfRequired(invocationExpression.Target, Primary); + base.VisitInvocationExpression(invocationExpression); + } + + public override void VisitIndexerExpression(IndexerExpression indexerExpression) + { + ParenthesizeIfRequired(indexerExpression.Target, Primary); + ArrayCreateExpression ace = indexerExpression.Target as ArrayCreateExpression; + if (ace != null && (InsertParenthesesForReadability || ace.Initializer.IsNull)) { + // require parentheses for "(new int[1])[0]" + Parenthesize(indexerExpression.Target); + } + base.VisitIndexerExpression(indexerExpression); + } + + // Unary expressions + public override void VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression) + { + ParenthesizeIfRequired(unaryOperatorExpression.Expression, GetPrecedence(unaryOperatorExpression)); + UnaryOperatorExpression child = unaryOperatorExpression.Expression as UnaryOperatorExpression; + if (child != null && InsertParenthesesForReadability) + Parenthesize(child); + base.VisitUnaryOperatorExpression(unaryOperatorExpression); + } + + public override void VisitCastExpression(CastExpression castExpression) + { + ParenthesizeIfRequired(castExpression.Expression, InsertParenthesesForReadability ? Primary : Unary); + // There's a nasty issue in the C# grammar: cast expressions including certain operators are ambiguous in some cases + // "(int)-1" is fine, but "(A)-b" is not a cast. + UnaryOperatorExpression uoe = castExpression.Expression as UnaryOperatorExpression; + if (uoe != null && !(uoe.Operator == UnaryOperatorType.BitNot || uoe.Operator == UnaryOperatorType.Not)) { + if (TypeCanBeMisinterpretedAsExpression(castExpression.Type)) { + Parenthesize(castExpression.Expression); + } + } + // The above issue can also happen with PrimitiveExpressions representing negative values: + PrimitiveExpression pe = castExpression.Expression as PrimitiveExpression; + if (pe != null && pe.Value != null && TypeCanBeMisinterpretedAsExpression(castExpression.Type)) { + TypeCode typeCode = Type.GetTypeCode(pe.Value.GetType()); + switch (typeCode) { + case TypeCode.SByte: + if ((sbyte)pe.Value < 0) + Parenthesize(castExpression.Expression); + break; + case TypeCode.Int16: + if ((short)pe.Value < 0) + Parenthesize(castExpression.Expression); + break; + case TypeCode.Int32: + if ((int)pe.Value < 0) + Parenthesize(castExpression.Expression); + break; + case TypeCode.Int64: + if ((long)pe.Value < 0) + Parenthesize(castExpression.Expression); + break; + case TypeCode.Single: + if ((float)pe.Value < 0) + Parenthesize(castExpression.Expression); + break; + case TypeCode.Double: + if ((double)pe.Value < 0) + Parenthesize(castExpression.Expression); + break; + case TypeCode.Decimal: + if ((decimal)pe.Value < 0) + Parenthesize(castExpression.Expression); + break; + } + } + base.VisitCastExpression(castExpression); + } + + static bool TypeCanBeMisinterpretedAsExpression(AstType type) + { + // SimpleTypes can always be misinterpreted as IdentifierExpressions + // MemberTypes can be misinterpreted as MemberReferenceExpressions if they don't use double colon + // PrimitiveTypes or ComposedTypes can never be misinterpreted as expressions. + MemberType mt = type as MemberType; + if (mt != null) + return !mt.IsDoubleColon; + else + return type is SimpleType; + } + + // Binary Operators + public override void VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression) + { + int precedence = GetPrecedence(binaryOperatorExpression); + if (binaryOperatorExpression.Operator == BinaryOperatorType.NullCoalescing) { + if (InsertParenthesesForReadability) { + ParenthesizeIfRequired(binaryOperatorExpression.Left, Primary); + ParenthesizeIfRequired(binaryOperatorExpression.Right, Primary); + } else { + // ?? is right-associative + ParenthesizeIfRequired(binaryOperatorExpression.Left, precedence + 1); + ParenthesizeIfRequired(binaryOperatorExpression.Right, precedence); + } + } else { + if (InsertParenthesesForReadability && precedence < Equality) { + // In readable mode, boost the priority of the left-hand side if the operator + // there isn't the same as the operator on this expression. + if (GetBinaryOperatorType(binaryOperatorExpression.Left) == binaryOperatorExpression.Operator) { + ParenthesizeIfRequired(binaryOperatorExpression.Left, precedence); + } else { + ParenthesizeIfRequired(binaryOperatorExpression.Left, Equality); + } + ParenthesizeIfRequired(binaryOperatorExpression.Right, Equality); + } else { + // all other binary operators are left-associative + ParenthesizeIfRequired(binaryOperatorExpression.Left, precedence); + ParenthesizeIfRequired(binaryOperatorExpression.Right, precedence + 1); + } + } + base.VisitBinaryOperatorExpression(binaryOperatorExpression); + } + + BinaryOperatorType? GetBinaryOperatorType(Expression expr) + { + BinaryOperatorExpression boe = expr as BinaryOperatorExpression; + if (boe != null) + return boe.Operator; + else + return null; + } + + public override void VisitIsExpression(IsExpression isExpression) + { + if (InsertParenthesesForReadability) { + // few people know the precedence of 'is', so always put parentheses in nice-looking mode. + ParenthesizeIfRequired(isExpression.Expression, Primary); + } else { + ParenthesizeIfRequired(isExpression.Expression, RelationalAndTypeTesting); + } + base.VisitIsExpression(isExpression); + } + + public override void VisitAsExpression(AsExpression asExpression) + { + if (InsertParenthesesForReadability) { + // few people know the precedence of 'as', so always put parentheses in nice-looking mode. + ParenthesizeIfRequired(asExpression.Expression, Primary); + } else { + ParenthesizeIfRequired(asExpression.Expression, RelationalAndTypeTesting); + } + base.VisitAsExpression(asExpression); + } + + // Conditional operator + public override void VisitConditionalExpression(ConditionalExpression conditionalExpression) + { + // Associativity here is a bit tricky: + // (a ? b : c ? d : e) == (a ? b : (c ? d : e)) + // (a ? b ? c : d : e) == (a ? (b ? c : d) : e) + // Only ((a ? b : c) ? d : e) strictly needs the additional parentheses + if (InsertParenthesesForReadability) { + // Precedence of ?: can be confusing; so always put parentheses in nice-looking mode. + ParenthesizeIfRequired(conditionalExpression.Condition, Primary); + ParenthesizeIfRequired(conditionalExpression.TrueExpression, Primary); + ParenthesizeIfRequired(conditionalExpression.FalseExpression, Primary); + } else { + ParenthesizeIfRequired(conditionalExpression.Condition, Conditional + 1); + ParenthesizeIfRequired(conditionalExpression.TrueExpression, Conditional); + ParenthesizeIfRequired(conditionalExpression.FalseExpression, Conditional); + } + base.VisitConditionalExpression(conditionalExpression); + } + + public override void VisitAssignmentExpression(AssignmentExpression assignmentExpression) + { + // assignment is right-associative + ParenthesizeIfRequired(assignmentExpression.Left, Assignment + 1); + if (InsertParenthesesForReadability) { + ParenthesizeIfRequired(assignmentExpression.Right, RelationalAndTypeTesting + 1); + } else { + ParenthesizeIfRequired(assignmentExpression.Right, Assignment); + } + base.VisitAssignmentExpression(assignmentExpression); + } + + // don't need to handle lambdas, they have lowest precedence and unambiguous associativity + + public override void VisitQueryExpression(QueryExpression queryExpression) + { + // Query expressions are strange beasts: + // "var a = -from b in c select d;" is valid, so queries bind stricter than unary expressions. + // However, the end of the query is greedy. So their start sort of has a high precedence, + // while their end has a very low precedence. We handle this by checking whether a query is used + // as left part of a binary operator, and parenthesize it if required. + if (queryExpression.Role == BinaryOperatorExpression.LeftRole) + Parenthesize(queryExpression); + if (queryExpression.Parent is IsExpression || queryExpression.Parent is AsExpression) + Parenthesize(queryExpression); + if (InsertParenthesesForReadability) { + // when readability is desired, always parenthesize query expressions within unary or binary operators + if (queryExpression.Parent is UnaryOperatorExpression || queryExpression.Parent is BinaryOperatorExpression) + Parenthesize(queryExpression); + } + base.VisitQueryExpression(queryExpression); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/InsertRequiredSpacesDecorator.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/InsertRequiredSpacesDecorator.cs new file mode 100644 index 000000000..e9aca4bf5 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/InsertRequiredSpacesDecorator.cs @@ -0,0 +1,184 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ICSharpCode.NRefactory.PatternMatching; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.CSharp; + +namespace ICSharpCode.NRefactory.CSharp +{ + class InsertRequiredSpacesDecorator : DecoratingTokenWriter + { + /// + /// Used to insert the minimal amount of spaces so that the lexer recognizes the tokens that were written. + /// + LastWritten lastWritten; + + enum LastWritten + { + Whitespace, + Other, + KeywordOrIdentifier, + Plus, + Minus, + Ampersand, + QuestionMark, + Division + } + + public InsertRequiredSpacesDecorator(TokenWriter writer) + : base(writer) + { + } + + public override void WriteIdentifier(Identifier identifier) + { + if (identifier.IsVerbatim || CSharpOutputVisitor.IsKeyword(identifier.Name, identifier)) { + if (lastWritten == LastWritten.KeywordOrIdentifier) { + // this space is not strictly required, so we call Space() + Space(); + } + } else if (lastWritten == LastWritten.KeywordOrIdentifier) { + // this space is strictly required, so we directly call the formatter + base.Space(); + } + base.WriteIdentifier(identifier); + lastWritten = LastWritten.KeywordOrIdentifier; + } + + public override void WriteKeyword(Role role, string keyword) + { + if (lastWritten == LastWritten.KeywordOrIdentifier) { + Space(); + } + base.WriteKeyword(role, keyword); + lastWritten = LastWritten.KeywordOrIdentifier; + } + + public override void WriteToken(Role role, string token) + { + // Avoid that two +, - or ? tokens are combined into a ++, -- or ?? token. + // Note that we don't need to handle tokens like = because there's no valid + // C# program that contains the single token twice in a row. + // (for +, - and &, this can happen with unary operators; + // for ?, this can happen in "a is int? ? b : c" or "a as int? ?? 0"; + // and for /, this can happen with "1/ *ptr" or "1/ //comment".) + if (lastWritten == LastWritten.Plus && token[0] == '+' || + lastWritten == LastWritten.Minus && token[0] == '-' || + lastWritten == LastWritten.Ampersand && token[0] == '&' || + lastWritten == LastWritten.QuestionMark && token[0] == '?' || + lastWritten == LastWritten.Division && token[0] == '*') { + base.Space(); + } + base.WriteToken(role, token); + if (token == "+") { + lastWritten = LastWritten.Plus; + } else if (token == "-") { + lastWritten = LastWritten.Minus; + } else if (token == "&") { + lastWritten = LastWritten.Ampersand; + } else if (token == "?") { + lastWritten = LastWritten.QuestionMark; + } else if (token == "/") { + lastWritten = LastWritten.Division; + } else { + lastWritten = LastWritten.Other; + } + } + + public override void Space() + { + base.Space(); + lastWritten = LastWritten.Whitespace; + } + + public override void NewLine() + { + base.NewLine(); + lastWritten = LastWritten.Whitespace; + } + + public override void WriteComment(CommentType commentType, string content) + { + if (lastWritten == LastWritten.Division) { + // When there's a comment starting after a division operator + // "1.0 / /*comment*/a", then we need to insert a space in front of the comment. + base.Space(); + } + base.WriteComment(commentType, content); + lastWritten = LastWritten.Whitespace; + } + + public override void WritePreProcessorDirective(PreProcessorDirectiveType type, string argument) + { + base.WritePreProcessorDirective(type, argument); + lastWritten = LastWritten.Whitespace; + } + + public override void WritePrimitiveValue(object value, string literalValue = null) + { + base.WritePrimitiveValue(value, literalValue); + if (value == null || value is bool) + return; + if (value is string) { + lastWritten = LastWritten.Other; + } else if (value is char) { + lastWritten = LastWritten.Other; + } else if (value is decimal) { + lastWritten = LastWritten.Other; + } else if (value is float) { + float f = (float)value; + if (float.IsInfinity(f) || float.IsNaN(f)) return; + lastWritten = LastWritten.Other; + } else if (value is double) { + double f = (double)value; + if (double.IsInfinity(f) || double.IsNaN(f)) return; + // needs space if identifier follows number; + // this avoids mistaking the following identifier as type suffix + lastWritten = LastWritten.KeywordOrIdentifier; + } else if (value is IFormattable) { + // needs space if identifier follows number; + // this avoids mistaking the following identifier as type suffix + lastWritten = LastWritten.KeywordOrIdentifier; + } else { + lastWritten = LastWritten.Other; + } + } + + public override void WritePrimitiveType(string type) + { + if (lastWritten == LastWritten.KeywordOrIdentifier) { + Space(); + } + base.WritePrimitiveType(type); + if (type == "new") { + lastWritten = LastWritten.Other; + } else { + lastWritten = LastWritten.KeywordOrIdentifier; + } + } + } +} \ No newline at end of file diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/InsertSpecialsDecorator.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/InsertSpecialsDecorator.cs new file mode 100644 index 000000000..0fafdeef0 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/InsertSpecialsDecorator.cs @@ -0,0 +1,157 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using ICSharpCode.NRefactory.CSharp; + +namespace ICSharpCode.NRefactory.CSharp +{ + class InsertSpecialsDecorator : DecoratingTokenWriter + { + readonly Stack positionStack = new Stack(); + int visitorWroteNewLine = 0; + + public InsertSpecialsDecorator(TokenWriter writer) : base(writer) + { + } + + public override void StartNode(AstNode node) + { + if (positionStack.Count > 0) { + WriteSpecialsUpToNode(node); + } + positionStack.Push(node.FirstChild); + base.StartNode(node); + } + + public override void EndNode(AstNode node) + { + base.EndNode(node); + AstNode pos = positionStack.Pop(); + Debug.Assert(pos == null || pos.Parent == node); + WriteSpecials(pos, null); + } + + public override void WriteKeyword(Role role, string keyword) + { + if (role != null) { + WriteSpecialsUpToRole(role); + } + base.WriteKeyword(role, keyword); + } + + public override void WriteIdentifier(Identifier identifier) + { + WriteSpecialsUpToRole(identifier.Role ?? Roles.Identifier); + base.WriteIdentifier(identifier); + } + + public override void WriteToken(Role role, string token) + { + WriteSpecialsUpToRole(role); + base.WriteToken(role, token); + } + + public override void NewLine() + { + if (visitorWroteNewLine >= 0) + base.NewLine(); + visitorWroteNewLine++; + } + + #region WriteSpecials + /// + /// Writes all specials from start to end (exclusive). Does not touch the positionStack. + /// + void WriteSpecials(AstNode start, AstNode end) + { + for (AstNode pos = start; pos != end; pos = pos.NextSibling) { + if (pos.Role == Roles.Comment) { + var node = (Comment)pos; + base.WriteComment(node.CommentType, node.Content); + } + // see CSharpOutputVisitor.VisitNewLine() + // if (pos.Role == Roles.NewLine) { + // if (visitorWroteNewLine <= 0) + // base.NewLine(); + // visitorWroteNewLine--; + // } + if (pos.Role == Roles.PreProcessorDirective) { + var node = (PreProcessorDirective)pos; + base.WritePreProcessorDirective(node.Type, node.Argument); + } + } + } + + /// + /// Writes all specials between the current position (in the positionStack) and the next + /// node with the specified role. Advances the current position. + /// + void WriteSpecialsUpToRole(Role role) + { + WriteSpecialsUpToRole(role, null); + } + + void WriteSpecialsUpToRole(Role role, AstNode nextNode) + { + if (positionStack.Count == 0) { + return; + } + // Look for the role between the current position and the nextNode. + for (AstNode pos = positionStack.Peek(); pos != null && pos != nextNode; pos = pos.NextSibling) { + if (pos.Role == role) { + WriteSpecials(positionStack.Pop(), pos); + // Push the next sibling because the node matching the role is not a special, + // and should be considered to be already handled. + positionStack.Push(pos.NextSibling); + // This is necessary for OptionalComma() to work correctly. + break; + } + } + } + + /// + /// Writes all specials between the current position (in the positionStack) and the specified node. + /// Advances the current position. + /// + void WriteSpecialsUpToNode(AstNode node) + { + if (positionStack.Count == 0) { + return; + } + for (AstNode pos = positionStack.Peek(); pos != null; pos = pos.NextSibling) { + if (pos == node) { + WriteSpecials(positionStack.Pop(), pos); + // Push the next sibling because the node itself is not a special, + // and should be considered to be already handled. + positionStack.Push(pos.NextSibling); + // This is necessary for OptionalComma() to work correctly. + break; + } + } + } + #endregion + } +} + + + + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/TextWriterOutputFormatter.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/TextWriterOutputFormatter.cs new file mode 100644 index 000000000..277b1c3e9 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/TextWriterOutputFormatter.cs @@ -0,0 +1,419 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// 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.Globalization; +using System.IO; +using System.Text; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Writes C# code into a TextWriter. + /// + public class TextWriterTokenWriter : TokenWriter, ILocatable + { + readonly TextWriter textWriter; + int indentation; + bool needsIndent = true; + bool isAtStartOfLine = true; + int line, column; + + public int Indentation { + get { return this.indentation; } + set { this.indentation = value; } + } + + public TextLocation Location { + get { return new TextLocation(line, column + (needsIndent ? indentation * IndentationString.Length : 0)); } + } + + public string IndentationString { get; set; } + + public TextWriterTokenWriter(TextWriter textWriter) + { + if (textWriter == null) + throw new ArgumentNullException("textWriter"); + this.textWriter = textWriter; + this.IndentationString = "\t"; + this.line = 1; + this.column = 1; + } + + public override void WriteIdentifier(Identifier identifier) + { + WriteIndentation(); + if (identifier.IsVerbatim || CSharpOutputVisitor.IsKeyword(identifier.Name, identifier)) { + textWriter.Write('@'); + column++; + } + textWriter.Write(identifier.Name); + column += identifier.Name.Length; + isAtStartOfLine = false; + } + + public override void WriteKeyword(Role role, string keyword) + { + WriteIndentation(); + column += keyword.Length; + textWriter.Write(keyword); + isAtStartOfLine = false; + } + + public override void WriteToken(Role role, string token) + { + WriteIndentation(); + column += token.Length; + textWriter.Write(token); + isAtStartOfLine = false; + } + + public override void Space() + { + WriteIndentation(); + column++; + textWriter.Write(' '); + } + + protected void WriteIndentation() + { + if (needsIndent) { + needsIndent = false; + for (int i = 0; i < indentation; i++) { + textWriter.Write(this.IndentationString); + } + column += indentation * IndentationString.Length; + } + } + + public override void NewLine() + { + textWriter.WriteLine(); + column = 1; + line++; + needsIndent = true; + isAtStartOfLine = true; + } + + public override void Indent() + { + indentation++; + } + + public override void Unindent() + { + indentation--; + } + + public override void WriteComment(CommentType commentType, string content) + { + WriteIndentation(); + switch (commentType) { + case CommentType.SingleLine: + textWriter.Write("//"); + textWriter.WriteLine(content); + column += 2 + content.Length; + needsIndent = true; + isAtStartOfLine = true; + break; + case CommentType.MultiLine: + textWriter.Write("/*"); + textWriter.Write(content); + textWriter.Write("*/"); + column += 2; + UpdateEndLocation(content, ref line, ref column); + column += 2; + isAtStartOfLine = false; + break; + case CommentType.Documentation: + textWriter.Write("///"); + textWriter.WriteLine(content); + column += 3 + content.Length; + needsIndent = true; + isAtStartOfLine = true; + break; + case CommentType.MultiLineDocumentation: + textWriter.Write("/**"); + textWriter.Write(content); + textWriter.Write("*/"); + column += 3; + UpdateEndLocation(content, ref line, ref column); + column += 2; + isAtStartOfLine = false; + break; + default: + textWriter.Write(content); + column += content.Length; + break; + } + } + + static void UpdateEndLocation(string content, ref int line, ref int column) + { + if (string.IsNullOrEmpty(content)) + return; + for (int i = 0; i < content.Length; i++) { + char ch = content[i]; + switch (ch) { + case '\r': + if (i + 1 < content.Length && content[i + 1] == '\n') + i++; + goto case '\n'; + case '\n': + line++; + column = 0; + break; + } + column++; + } + } + + public override void WritePreProcessorDirective(PreProcessorDirectiveType type, string argument) + { + // pre-processor directive must start on its own line + if (!isAtStartOfLine) + NewLine(); + WriteIndentation(); + textWriter.Write('#'); + string directive = type.ToString().ToLowerInvariant(); + textWriter.Write(directive); + column += 1 + directive.Length; + if (!string.IsNullOrEmpty(argument)) { + textWriter.Write(' '); + textWriter.Write(argument); + column += 1 + argument.Length; + } + NewLine(); + } + + public static string PrintPrimitiveValue(object value) + { + TextWriter writer = new StringWriter(); + TextWriterTokenWriter tokenWriter = new TextWriterTokenWriter(writer); + tokenWriter.WritePrimitiveValue(value); + return writer.ToString(); + } + + public override void WritePrimitiveValue(object value, string literalValue = null) + { + if (literalValue != null) { + textWriter.Write(literalValue); + column += literalValue.Length; + return; + } + + if (value == null) { + // usually NullReferenceExpression should be used for this, but we'll handle it anyways + textWriter.Write("null"); + column += 4; + return; + } + + if (value is bool) { + if ((bool)value) { + textWriter.Write("true"); + column += 4; + } else { + textWriter.Write("false"); + column += 5; + } + return; + } + + if (value is string) { + string tmp = "\"" + ConvertString(value.ToString()) + "\""; + column += tmp.Length; + textWriter.Write(tmp); + } else if (value is char) { + string tmp = "'" + ConvertCharLiteral((char)value) + "'"; + column += tmp.Length; + textWriter.Write(tmp); + } else if (value is decimal) { + string str = ((decimal)value).ToString(NumberFormatInfo.InvariantInfo) + "m"; + column += str.Length; + textWriter.Write(str); + } else if (value is float) { + float f = (float)value; + if (float.IsInfinity(f) || float.IsNaN(f)) { + // Strictly speaking, these aren't PrimitiveExpressions; + // but we still support writing these to make life easier for code generators. + textWriter.Write("float"); + column += 5; + WriteToken(Roles.Dot, "."); + if (float.IsPositiveInfinity(f)) { + textWriter.Write("PositiveInfinity"); + column += "PositiveInfinity".Length; + } else if (float.IsNegativeInfinity(f)) { + textWriter.Write("NegativeInfinity"); + column += "NegativeInfinity".Length; + } else { + textWriter.Write("NaN"); + column += 3; + } + return; + } + if (f == 0 && 1 / f == float.NegativeInfinity) { + // negative zero is a special case + // (again, not a primitive expression, but it's better to handle + // the special case here than to do it in all code generators) + textWriter.Write("-"); + column++; + } + var str = f.ToString("R", NumberFormatInfo.InvariantInfo) + "f"; + column += str.Length; + textWriter.Write(str); + } else if (value is double) { + double f = (double)value; + if (double.IsInfinity(f) || double.IsNaN(f)) { + // Strictly speaking, these aren't PrimitiveExpressions; + // but we still support writing these to make life easier for code generators. + textWriter.Write("double"); + column += 6; + WriteToken(Roles.Dot, "."); + if (double.IsPositiveInfinity(f)) { + textWriter.Write("PositiveInfinity"); + column += "PositiveInfinity".Length; + } else if (double.IsNegativeInfinity(f)) { + textWriter.Write("NegativeInfinity"); + column += "NegativeInfinity".Length; + } else { + textWriter.Write("NaN"); + column += 3; + } + return; + } + if (f == 0 && 1 / f == double.NegativeInfinity) { + // negative zero is a special case + // (again, not a primitive expression, but it's better to handle + // the special case here than to do it in all code generators) + textWriter.Write("-"); + } + string number = f.ToString("R", NumberFormatInfo.InvariantInfo); + if (number.IndexOf('.') < 0 && number.IndexOf('E') < 0) { + number += ".0"; + } + textWriter.Write(number); + } else if (value is IFormattable) { + StringBuilder b = new StringBuilder (); +// if (primitiveExpression.LiteralFormat == LiteralFormat.HexadecimalNumber) { +// b.Append("0x"); +// b.Append(((IFormattable)val).ToString("x", NumberFormatInfo.InvariantInfo)); +// } else { + b.Append(((IFormattable)value).ToString(null, NumberFormatInfo.InvariantInfo)); +// } + if (value is uint || value is ulong) { + b.Append("u"); + } + if (value is long || value is ulong) { + b.Append("L"); + } + textWriter.Write(b.ToString()); + column += b.Length; + } else { + textWriter.Write(value.ToString()); + column += value.ToString().Length; + } + } + + /// + /// Gets the escape sequence for the specified character within a char literal. + /// Does not include the single quotes surrounding the char literal. + /// + public static string ConvertCharLiteral(char ch) + { + if (ch == '\'') { + return "\\'"; + } + return ConvertChar(ch); + } + + /// + /// Gets the escape sequence for the specified character. + /// + /// This method does not convert ' or ". + static string ConvertChar(char ch) + { + switch (ch) { + case '\\': + return "\\\\"; + case '\0': + return "\\0"; + case '\a': + return "\\a"; + case '\b': + return "\\b"; + case '\f': + return "\\f"; + case '\n': + return "\\n"; + case '\r': + return "\\r"; + case '\t': + return "\\t"; + case '\v': + return "\\v"; + default: + if (char.IsControl(ch) || char.IsSurrogate(ch) || + // print all uncommon white spaces as numbers + (char.IsWhiteSpace(ch) && ch != ' ')) { + return "\\u" + ((int)ch).ToString("x4"); + } else { + return ch.ToString(); + } + } + } + + /// + /// Converts special characters to escape sequences within the given string. + /// + public static string ConvertString(string str) + { + StringBuilder sb = new StringBuilder (); + foreach (char ch in str) { + if (ch == '"') { + sb.Append("\\\""); + } else { + sb.Append(ConvertChar(ch)); + } + } + return sb.ToString(); + } + + public override void WritePrimitiveType(string type) + { + textWriter.Write(type); + column += type.Length; + if (type == "new") { + textWriter.Write("()"); + column += 2; + } + } + + public override void StartNode(AstNode node) + { + // Write out the indentation, so that overrides of this method + // can rely use the current output length to identify the position of the node + // in the output. + WriteIndentation(); + } + + public override void EndNode(AstNode node) + { + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/CSharpParser.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/CSharpParser.cs new file mode 100644 index 000000000..158b86ce3 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/CSharpParser.cs @@ -0,0 +1,4096 @@ +// +// CSharpParser.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.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.Linq; +using System.Collections.Generic; +using System.IO; +using ICSharpCode.NRefactory.Editor; +using Mono.CSharp; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp +{ + public class CSharpParser + { + CompilerSettings compilerSettings; + + class ConversionVisitor : StructuralVisitor + { + SyntaxTree unit = new SyntaxTree(); + internal bool convertTypeSystemMode; + + public SyntaxTree Unit { + get { + return unit; + } + set { + unit = value; + } + } + + public LocationsBag LocationsBag { + get; + private set; + } + + public ConversionVisitor(bool convertTypeSystemMode, LocationsBag locationsBag) + { + this.convertTypeSystemMode = convertTypeSystemMode; + this.LocationsBag = locationsBag; + } + + public static TextLocation Convert(Location loc) + { + return new TextLocation(loc.Row, loc.Column); + } + + public override void Visit(ModuleContainer mc) + { + bool first = true; + foreach (var container in mc.Containers) { + var nspace = container as NamespaceContainer; + if (nspace == null) { + container.Accept(this); + continue; + } + NamespaceDeclaration nDecl = null; + var loc = LocationsBag.GetLocations(nspace); + + if (nspace.NS != null && !string.IsNullOrEmpty(nspace.NS.Name)) { + nDecl = new NamespaceDeclaration(); + if (loc != null) { + nDecl.AddChild(new CSharpTokenNode(Convert(loc [0]), Roles.NamespaceKeyword), Roles.NamespaceKeyword); + } + nDecl.AddChild(ConvertNamespaceName(nspace.RealMemberName), NamespaceDeclaration.NamespaceNameRole); + if (loc != null && loc.Count > 1) { + nDecl.AddChild(new CSharpTokenNode(Convert(loc [1]), Roles.LBrace), Roles.LBrace); + } + AddToNamespace(nDecl); + namespaceStack.Push(nDecl); + } + + if (nspace.Usings != null) { + foreach (var us in nspace.Usings) { + us.Accept(this); + } + } + + if (first) { + first = false; + if (mc.OptAttributes != null) { + foreach (var attr in mc.OptAttributes.Sections) { + var section = ConvertAttributeSection(attr); + if (section != null) + unit.AddChild(section, SyntaxTree.MemberRole); + } + } + } + + if (nspace.Containers != null) { + foreach (var subContainer in nspace.Containers) { + subContainer.Accept(this); + } + } + if (nDecl != null) { + AddAttributeSection(nDecl, nspace.UnattachedAttributes, EntityDeclaration.UnattachedAttributeRole); + if (loc != null && loc.Count > 2) + nDecl.AddChild(new CSharpTokenNode(Convert(loc [2]), Roles.RBrace), Roles.RBrace); + if (loc != null && loc.Count > 3) + nDecl.AddChild(new CSharpTokenNode(Convert(loc [3]), Roles.Semicolon), Roles.Semicolon); + + namespaceStack.Pop(); + } else { + AddAttributeSection(unit, nspace.UnattachedAttributes, EntityDeclaration.UnattachedAttributeRole); + } + } + AddAttributeSection(unit, mc.UnattachedAttributes, EntityDeclaration.UnattachedAttributeRole); + } + + #region Global + + readonly Stack namespaceStack = new Stack(); + + void AddTypeArguments(ATypeNameExpression texpr, AstType result) + { + var unbound = texpr.TypeArguments as UnboundTypeArguments; + if (unbound != null) { + var loc2 = LocationsBag.GetLocations(texpr.TypeArguments); + if (loc2 == null) + return; + int j = 0; + if (j < loc2.Count) + result.AddChild(new CSharpTokenNode(Convert(loc2 [j++]), Roles.LChevron), Roles.LChevron); + while (j < loc2.Count - 1) { + result.AddChild (new SimpleType (), Roles.TypeArgument); + result.AddChild(new CSharpTokenNode(Convert(loc2 [j++]), Roles.LChevron), Roles.Comma); + } + if (j < loc2.Count) { + result.AddChild (new SimpleType (), Roles.TypeArgument); + result.AddChild(new CSharpTokenNode(Convert(loc2 [j++]), Roles.RChevron), Roles.RChevron); + } + return; + } + if (texpr.TypeArguments == null || texpr.TypeArguments.Args == null) + return; + var loc = LocationsBag.GetLocations(texpr.TypeArguments); + if (loc != null && loc.Count >= 2) + result.AddChild(new CSharpTokenNode(Convert(loc [loc.Count - 2]), Roles.LChevron), Roles.LChevron); + int i = 0; + foreach (var arg in texpr.TypeArguments.Args) { + result.AddChild(ConvertToType(arg), Roles.TypeArgument); + if (loc != null && i < loc.Count - 2) + result.AddChild(new CSharpTokenNode(Convert(loc [i++]), Roles.Comma), Roles.Comma); + } + if (loc != null && loc.Count >= 2) + result.AddChild(new CSharpTokenNode(Convert(loc [loc.Count - 1]), Roles.RChevron), Roles.RChevron); + } + + static AstType ConvertToType(TypeParameter spec) + { + AstType result; + result = new SimpleType { IdentifierToken = Identifier.Create(spec.Name, Convert(spec.Location)) }; + return result; + } + + AstType ConvertToType(MemberName memberName) + { + AstType result; + if (memberName.Left != null) { + result = new MemberType(); + result.AddChild(ConvertToType(memberName.Left), MemberType.TargetRole); + var loc = LocationsBag.GetLocations(memberName); + if (loc != null) + result.AddChild(new CSharpTokenNode(Convert(loc [0]), Roles.Dot), Roles.Dot); + result.AddChild(Identifier.Create(memberName.Name, Convert(memberName.Location)), Roles.Identifier); + } else { + result = new SimpleType { IdentifierToken = Identifier.Create(memberName.Name, Convert(memberName.Location)) }; + } + if (memberName.TypeParameters != null) { + var chevronLocs = LocationsBag.GetLocations(memberName.TypeParameters); + if (chevronLocs != null) + result.AddChild(new CSharpTokenNode(Convert(chevronLocs [chevronLocs.Count - 2]), Roles.LChevron), Roles.LChevron); + for (int i = 0; i < memberName.TypeParameters.Count; i++) { + var param = memberName.TypeParameters [i]; + result.AddChild(new SimpleType(Identifier.Create(param.Name, Convert(param.Location))), Roles.TypeArgument); + if (chevronLocs != null && i < chevronLocs.Count - 2) + result.AddChild(new CSharpTokenNode(Convert(chevronLocs [i]), Roles.Comma), Roles.Comma); + } + if (chevronLocs != null) + result.AddChild(new CSharpTokenNode(Convert(chevronLocs [chevronLocs.Count - 1]), Roles.RChevron), Roles.RChevron); + } + return result; + } + + AstType ConvertToType(Mono.CSharp.Expression typeName) + { + if (typeName == null) // may happen in typeof(Generic<,,,,>) + return new SimpleType(); + + var typeExpr = typeName as TypeExpression; + if (typeExpr != null) { + return new PrimitiveType(typeExpr.GetSignatureForError(), Convert(typeExpr.Location)); + } + + var qam = typeName as QualifiedAliasMember; + if (qam != null) { + var loc = LocationsBag.GetLocations(typeName); + var memberType = new MemberType(); + memberType.Target = new SimpleType(qam.alias, Convert(qam.Location)); + memberType.IsDoubleColon = true; + + if (loc != null && loc.Count > 0) + memberType.AddChild(new CSharpTokenNode(Convert(loc [0]), Roles.DoubleColon), Roles.DoubleColon); + + memberType.MemberNameToken = Identifier.Create(qam.Name, loc != null ? Convert(loc [1]) : TextLocation.Empty); + AddTypeArguments(qam, memberType); + return memberType; + } + + var ma = typeName as MemberAccess; + if (ma != null) { + var memberType = new MemberType(); + memberType.AddChild(ConvertToType(ma.LeftExpression), MemberType.TargetRole); + var loc = LocationsBag.GetLocations(ma); + if (loc != null) + memberType.AddChild(new CSharpTokenNode(Convert(loc [0]), Roles.Dot), Roles.Dot); + + memberType.MemberNameToken = Identifier.Create(ma.Name, Convert(ma.Location)); + + AddTypeArguments(ma, memberType); + return memberType; + } + + var sn = typeName as SimpleName; + if (sn != null) { + var result = new SimpleType(sn.Name, Convert(sn.Location)); + AddTypeArguments(sn, result); + return result; + } + + var cc = typeName as ComposedCast; + if (cc != null) { + var baseType = ConvertToType(cc.Left); + var result = new ComposedType { BaseType = baseType }; + var ccSpec = cc.Spec; + while (ccSpec != null) { + if (ccSpec.IsNullable) { + result.AddChild(new CSharpTokenNode(Convert(ccSpec.Location), ComposedType.NullableRole), ComposedType.NullableRole); + } else if (ccSpec.IsPointer) { + result.AddChild(new CSharpTokenNode(Convert(ccSpec.Location), ComposedType.PointerRole), ComposedType.PointerRole); + } else { + var location = LocationsBag.GetLocations(ccSpec); + var spec = new ArraySpecifier { Dimensions = ccSpec.Dimension }; + spec.AddChild(new CSharpTokenNode(Convert(ccSpec.Location), Roles.LBracket), Roles.LBracket); + if (location != null) + spec.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.RBracket), Roles.RBracket); + + result.ArraySpecifiers.Add(spec); + } + ccSpec = ccSpec.Next; + } + return result; + } + + var sce = typeName as SpecialContraintExpr; + if (sce != null) { + switch (sce.Constraint) { + case SpecialConstraint.Class: + return new PrimitiveType("class", Convert(sce.Location)); + case SpecialConstraint.Struct: + return new PrimitiveType("struct", Convert(sce.Location)); + case SpecialConstraint.Constructor: + return new PrimitiveType("new", Convert(sce.Location)); + } + } + return new SimpleType("unknown"); + } + + IEnumerable GetAttributes(IEnumerable optAttributes) + { + if (optAttributes == null) + yield break; + foreach (var attr in optAttributes) { + var result = new Attribute(); + result.Type = ConvertToType(attr.TypeNameExpression); + var loc = LocationsBag.GetLocations(attr); + result.HasArgumentList = loc != null; + int pos = 0; + if (loc != null) + result.AddChild(new CSharpTokenNode(Convert(loc [pos++]), Roles.LPar), Roles.LPar); + + if (attr.PositionalArguments != null) { + foreach (var arg in attr.PositionalArguments) { + if (arg == null) + continue; + var na = arg as NamedArgument; + if (na != null) { + var newArg = new NamedArgumentExpression(); + newArg.AddChild(Identifier.Create(na.Name, Convert(na.Location)), Roles.Identifier); + + var argLoc = LocationsBag.GetLocations(na); + if (argLoc != null) + newArg.AddChild(new CSharpTokenNode(Convert(argLoc [0]), Roles.Colon), Roles.Colon); + if (na.Expr != null) + newArg.AddChild((Expression)na.Expr.Accept(this), Roles.Expression); + result.AddChild(newArg, Roles.Argument); + } else { + if (arg.Expr != null) + result.AddChild((Expression)arg.Expr.Accept(this), Roles.Argument); + } + if (loc != null && pos + 1 < loc.Count) + result.AddChild(new CSharpTokenNode(Convert(loc [pos++]), Roles.Comma), Roles.Comma); + } + } + if (attr.NamedArguments != null) { + foreach (var arg in attr.NamedArguments) { + var na = (NamedArgument)arg; + var newArg = new NamedExpression(); + newArg.AddChild(Identifier.Create(na.Name, Convert(na.Location)), Roles.Identifier); + + var argLoc = LocationsBag.GetLocations(na); + if (argLoc != null) + newArg.AddChild(new CSharpTokenNode(Convert(argLoc [0]), Roles.Assign), Roles.Assign); + if (na.Expr != null) + newArg.AddChild((Expression)na.Expr.Accept(this), Roles.Expression); + result.AddChild(newArg, Roles.Argument); + if (loc != null && pos + 1 < loc.Count) + result.AddChild(new CSharpTokenNode(Convert(loc [pos++]), Roles.Comma), Roles.Comma); + } + } + if (loc != null && pos < loc.Count) + result.AddChild(new CSharpTokenNode(Convert(loc [pos++]), Roles.RPar), Roles.RPar); + + yield return result; + } + } + + AttributeSection ConvertAttributeSection(IEnumerable optAttributes) + { + if (optAttributes == null) + return null; + var result = new AttributeSection(); + var loc = LocationsBag.GetLocations(optAttributes); + int pos = 0; + if (loc != null) + result.AddChild(new CSharpTokenNode(Convert(loc [pos++]), Roles.LBracket), Roles.LBracket); + var first = optAttributes.FirstOrDefault(); + string target = first != null ? first.ExplicitTarget : null; + + if (!string.IsNullOrEmpty(target)) { + if (loc != null && pos < loc.Count - 1) { + result.AddChild(Identifier.Create(target, Convert(loc [pos++])), Roles.Identifier); + } else { + result.AddChild(Identifier.Create(target), Roles.Identifier); + } + if (loc != null && pos < loc.Count) + result.AddChild(new CSharpTokenNode(Convert(loc [pos++]), Roles.Colon), Roles.Colon); + } + + int attributeCount = 0; + foreach (var attr in GetAttributes (optAttributes)) { + result.AddChild(attr, Roles.Attribute); + if (loc != null && pos + 1 < loc.Count) + result.AddChild(new CSharpTokenNode(Convert(loc [pos++]), Roles.Comma), Roles.Comma); + + attributeCount++; + } + if (attributeCount == 0) + return null; + // Left and right bracket + commas between the attributes + int locCount = 2 + attributeCount - 1; + // optional comma + if (loc != null && pos < loc.Count - 1 && loc.Count == locCount + 1) + result.AddChild(new CSharpTokenNode(Convert(loc [pos++]), Roles.Comma), Roles.Comma); + if (loc != null && pos < loc.Count) + result.AddChild(new CSharpTokenNode(Convert(loc [pos++]), Roles.RBracket), Roles.RBracket); + return result; + } + + public override void Visit(NamespaceContainer ns) + { + NamespaceDeclaration nDecl = null; + var loc = LocationsBag.GetLocations(ns); + // is caused by the parser - see Bug 12383 - [AST] Non existing namespaces generated + if (ns.NS != null && !string.IsNullOrEmpty(ns.NS.Name) && !ns.NS.Name.EndsWith("", StringComparison.Ordinal)) { + nDecl = new NamespaceDeclaration(); + if (loc != null) { + nDecl.AddChild(new CSharpTokenNode(Convert(loc [0]), Roles.NamespaceKeyword), Roles.NamespaceKeyword); + } + nDecl.AddChild(ConvertNamespaceName(ns.RealMemberName), NamespaceDeclaration.NamespaceNameRole); + if (loc != null && loc.Count > 1) { + nDecl.AddChild(new CSharpTokenNode(Convert(loc [1]), Roles.LBrace), Roles.LBrace); + } + + AddToNamespace(nDecl); + namespaceStack.Push(nDecl); + } + + if (ns.Usings != null) { + foreach (var us in ns.Usings) { + us.Accept(this); + } + } + + if (ns.Containers != null) { + foreach (var container in ns.Containers) { + container.Accept(this); + } + } + + if (nDecl != null) { + AddAttributeSection(nDecl, ns.UnattachedAttributes, EntityDeclaration.UnattachedAttributeRole); + if (loc != null && loc.Count > 2) + nDecl.AddChild(new CSharpTokenNode(Convert(loc [2]), Roles.RBrace), Roles.RBrace); + if (loc != null && loc.Count > 3) + nDecl.AddChild(new CSharpTokenNode(Convert(loc [3]), Roles.Semicolon), Roles.Semicolon); + + namespaceStack.Pop(); + } + } + // public override void Visit (UsingsBag.Namespace nspace) + // { + // + // + // VisitNamespaceUsings (nspace); + // VisitNamespaceBody (nspace); + // + // } + // + AstType ConvertNamespaceName(MemberName memberName) + { + // HACK for a parser 'bug' - sometimes it generates "" identifiers in namespace names (on certain bugs in the input file) + if (memberName.Name == "") + return AstType.Null; + return ConvertToType(memberName); + } + + public override void Visit(UsingNamespace un) + { + var ud = new UsingDeclaration(); + var loc = LocationsBag.GetLocations(un); + ud.AddChild(new CSharpTokenNode(Convert(un.Location), UsingDeclaration.UsingKeywordRole), UsingDeclaration.UsingKeywordRole); + if (un.NamespaceExpression != null) + ud.AddChild(ConvertToType(un.NamespaceExpression), UsingDeclaration.ImportRole); + if (loc != null) + ud.AddChild(new CSharpTokenNode(Convert(loc [0]), Roles.Semicolon), Roles.Semicolon); + AddToNamespace(ud); + } + + public override void Visit(UsingAliasNamespace uan) + { + var ud = new UsingAliasDeclaration(); + var loc = LocationsBag.GetLocations(uan); + + ud.AddChild(new CSharpTokenNode(Convert(uan.Location), UsingAliasDeclaration.UsingKeywordRole), UsingAliasDeclaration.UsingKeywordRole); + ud.AddChild(Identifier.Create(uan.Alias.Value, Convert(uan.Alias.Location)), UsingAliasDeclaration.AliasRole); + if (loc != null) + ud.AddChild(new CSharpTokenNode(Convert(loc [0]), Roles.Assign), Roles.Assign); + if (uan.NamespaceExpression != null) + ud.AddChild(ConvertToType(uan.NamespaceExpression), UsingAliasDeclaration.ImportRole); + if (loc != null && loc.Count > 1) + ud.AddChild(new CSharpTokenNode(Convert(loc [1]), Roles.Semicolon), Roles.Semicolon); + AddToNamespace(ud); + } + + public override void Visit(UsingExternAlias uea) + { + var ud = new ExternAliasDeclaration(); + var loc = LocationsBag.GetLocations(uea); + ud.AddChild(new CSharpTokenNode(Convert(uea.Location), Roles.ExternKeyword), Roles.ExternKeyword); + if (loc != null) + ud.AddChild(new CSharpTokenNode(Convert(loc [0]), Roles.AliasKeyword), Roles.AliasKeyword); + ud.AddChild(Identifier.Create(uea.Alias.Value, Convert(uea.Alias.Location)), Roles.Identifier); + if (loc != null && loc.Count > 1) + ud.AddChild(new CSharpTokenNode(Convert(loc [1]), Roles.Semicolon), Roles.Semicolon); + AddToNamespace(ud); + } + + AstType ConvertImport(MemberName memberName) + { + if (memberName.Left != null) { + // left.name + var t = new MemberType(); +// t.IsDoubleColon = memberName.IsDoubleColon; + t.AddChild(ConvertImport(memberName.Left), MemberType.TargetRole); + var loc = LocationsBag.GetLocations(memberName); + if (loc != null) + t.AddChild(new CSharpTokenNode(Convert(loc [0]), Roles.Dot), Roles.Dot); + + t.AddChild(Identifier.Create(memberName.Name, Convert(memberName.Location)), Roles.Identifier); + AddTypeArguments(t, memberName); + return t; + } else { + var t = new SimpleType(); + t.AddChild(Identifier.Create(memberName.Name, Convert(memberName.Location)), Roles.Identifier); + AddTypeArguments(t, memberName); + return t; + } + } + + public override void Visit(MemberCore member) + { + Console.WriteLine("Unknown member:"); + Console.WriteLine(member.GetType() + "-> Member {0}", member.GetSignatureForError()); + } + + readonly Stack typeStack = new Stack(); + + public override void Visit(Class c) + { + var newType = new TypeDeclaration(); + newType.ClassType = ClassType.Class; + AddAttributeSection(newType, c); + var location = LocationsBag.GetMemberLocation(c); + AddModifiers(newType, location); + int curLoc = 0; + if (location != null && location.Count > 0) + newType.AddChild(new CSharpTokenNode(Convert(location [curLoc++]), Roles.ClassKeyword), Roles.ClassKeyword); + + newType.AddChild(Identifier.Create(c.MemberName.Name, Convert(c.MemberName.Location)), Roles.Identifier); + AddTypeParameters(newType, c.MemberName); + + if (c.TypeBaseExpressions != null) { + if (location != null && curLoc < location.Count) + newType.AddChild(new CSharpTokenNode(Convert(location [curLoc++]), Roles.Colon), Roles.Colon); + + var commaLocations = LocationsBag.GetLocations(c.TypeBaseExpressions); + int i = 0; + foreach (var baseTypes in c.TypeBaseExpressions) { + newType.AddChild(ConvertToType(baseTypes), Roles.BaseType); + if (commaLocations != null && i < commaLocations.Count) { + newType.AddChild(new CSharpTokenNode(Convert(commaLocations [i]), Roles.Comma), Roles.Comma); + i++; + } + } + } + + AddConstraints(newType, c.CurrentTypeParameters); + if (location != null && curLoc < location.Count) + newType.AddChild(new CSharpTokenNode(Convert(location [curLoc++]), Roles.LBrace), Roles.LBrace); + typeStack.Push(newType); + base.Visit(c); + AddAttributeSection(newType, c.UnattachedAttributes, EntityDeclaration.UnattachedAttributeRole); + + if (location != null && curLoc < location.Count) { + newType.AddChild(new CSharpTokenNode(Convert(location [curLoc++]), Roles.RBrace), Roles.RBrace); + + if (location != null && curLoc < location.Count) + newType.AddChild(new CSharpTokenNode(Convert(location [curLoc++]), Roles.Semicolon), Roles.Semicolon); + + } else { + // parser error, set end node to max value. + newType.AddChild(new ErrorNode(), Roles.Error); + } + typeStack.Pop(); + AddType(newType); + } + + public override void Visit(Struct s) + { + var newType = new TypeDeclaration(); + newType.ClassType = ClassType.Struct; + AddAttributeSection(newType, s); + var location = LocationsBag.GetMemberLocation(s); + AddModifiers(newType, location); + int curLoc = 0; + if (location != null && location.Count > 0) + newType.AddChild(new CSharpTokenNode(Convert(location [curLoc++]), Roles.StructKeyword), Roles.StructKeyword); + newType.AddChild(Identifier.Create(s.MemberName.Name, Convert(s.MemberName.Location)), Roles.Identifier); + AddTypeParameters(newType, s.MemberName); + + if (s.TypeBaseExpressions != null) { + if (location != null && curLoc < location.Count) + newType.AddChild(new CSharpTokenNode(Convert(location [curLoc++]), Roles.Colon), Roles.Colon); + var commaLocations = LocationsBag.GetLocations(s.TypeBaseExpressions); + int i = 0; + foreach (var baseTypes in s.TypeBaseExpressions) { + newType.AddChild(ConvertToType(baseTypes), Roles.BaseType); + if (commaLocations != null && i < commaLocations.Count) { + newType.AddChild(new CSharpTokenNode(Convert(commaLocations [i]), Roles.Comma), Roles.Comma); + i++; + } + } + } + + AddConstraints(newType, s.CurrentTypeParameters); + if (location != null && curLoc < location.Count) + newType.AddChild(new CSharpTokenNode(Convert(location [curLoc++]), Roles.LBrace), Roles.LBrace); + typeStack.Push(newType); + base.Visit(s); + if (location != null && location.Count > 2) { + if (location != null && curLoc < location.Count) + newType.AddChild(new CSharpTokenNode(Convert(location [curLoc++]), Roles.RBrace), Roles.RBrace); + if (location != null && curLoc < location.Count) + newType.AddChild(new CSharpTokenNode(Convert(location [curLoc++]), Roles.Semicolon), Roles.Semicolon); + } else { + // parser error, set end node to max value. + newType.AddChild(new ErrorNode(), Roles.Error); + } + typeStack.Pop(); + AddType(newType); + } + + public override void Visit(Interface i) + { + var newType = new TypeDeclaration(); + newType.ClassType = ClassType.Interface; + AddAttributeSection(newType, i); + var location = LocationsBag.GetMemberLocation(i); + AddModifiers(newType, location); + int curLoc = 0; + if (location != null && location.Count > 0) + newType.AddChild(new CSharpTokenNode(Convert(location [curLoc++]), Roles.InterfaceKeyword), Roles.InterfaceKeyword); + newType.AddChild(Identifier.Create(i.MemberName.Name, Convert(i.MemberName.Location)), Roles.Identifier); + AddTypeParameters(newType, i.MemberName); + + if (i.TypeBaseExpressions != null) { + if (location != null && curLoc < location.Count) + newType.AddChild(new CSharpTokenNode(Convert(location [curLoc++]), Roles.Colon), Roles.Colon); + var commaLocations = LocationsBag.GetLocations(i.TypeBaseExpressions); + int j = 0; + foreach (var baseTypes in i.TypeBaseExpressions) { + newType.AddChild(ConvertToType(baseTypes), Roles.BaseType); + if (commaLocations != null && j < commaLocations.Count) { + newType.AddChild(new CSharpTokenNode(Convert(commaLocations [j]), Roles.Comma), Roles.Comma); + j++; + } + } + } + + AddConstraints(newType, i.CurrentTypeParameters); + if (location != null && curLoc < location.Count) + newType.AddChild(new CSharpTokenNode(Convert(location [curLoc++]), Roles.LBrace), Roles.LBrace); + typeStack.Push(newType); + base.Visit(i); + if (location != null && location.Count > 2) { + if (location != null && curLoc < location.Count) + newType.AddChild(new CSharpTokenNode(Convert(location [curLoc++]), Roles.RBrace), Roles.RBrace); + if (location != null && curLoc < location.Count) + newType.AddChild(new CSharpTokenNode(Convert(location [curLoc++]), Roles.Semicolon), Roles.Semicolon); + } else { + // parser error, set end node to max value. + newType.AddChild(new ErrorNode(), Roles.Error); + } + typeStack.Pop(); + AddType(newType); + } + + public override void Visit(Mono.CSharp.Delegate d) + { + var newDelegate = new DelegateDeclaration(); + var location = LocationsBag.GetMemberLocation(d); + AddAttributeSection(newDelegate, d); + AddModifiers(newDelegate, location); + if (location != null && location.Count > 0) { + newDelegate.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.DelegateKeyword), Roles.DelegateKeyword); + } + if (d.ReturnType != null) + newDelegate.AddChild(ConvertToType(d.ReturnType), Roles.Type); + newDelegate.AddChild(Identifier.Create(d.MemberName.Name, Convert(d.MemberName.Location)), Roles.Identifier); + AddTypeParameters(newDelegate, d.MemberName); + + if (location != null && location.Count > 1) + newDelegate.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.LPar), Roles.LPar); + AddParameter(newDelegate, d.Parameters); + + if (location != null && location.Count > 2) { + newDelegate.AddChild(new CSharpTokenNode(Convert(location [2]), Roles.RPar), Roles.RPar); + } + AddConstraints(newDelegate, d.CurrentTypeParameters); + if (location != null && location.Count > 3) { + newDelegate.AddChild(new CSharpTokenNode(Convert(location [3]), Roles.Semicolon), Roles.Semicolon); + } + AddType(newDelegate); + } + + void AddType(EntityDeclaration child) + { + if (typeStack.Count > 0) { + typeStack.Peek().AddChild(child, Roles.TypeMemberRole); + } else { + AddToNamespace(child); + } + } + + void AddToNamespace(AstNode child) + { + if (namespaceStack.Count > 0) { + namespaceStack.Peek().AddChild(child, NamespaceDeclaration.MemberRole); + } else { + unit.AddChild(child, SyntaxTree.MemberRole); + } + } + + public override void Visit(Mono.CSharp.Enum e) + { + var newType = new TypeDeclaration(); + newType.ClassType = ClassType.Enum; + AddAttributeSection(newType, e); + var location = LocationsBag.GetMemberLocation(e); + + AddModifiers(newType, location); + int curLoc = 0; + if (location != null && location.Count > 0) + newType.AddChild(new CSharpTokenNode(Convert(location [curLoc++]), Roles.EnumKeyword), Roles.EnumKeyword); + newType.AddChild(Identifier.Create(e.MemberName.Name, Convert(e.MemberName.Location)), Roles.Identifier); + + if (e.BaseTypeExpression != null) { + if (location != null && curLoc < location.Count) + newType.AddChild(new CSharpTokenNode(Convert(location [curLoc++]), Roles.Colon), Roles.Colon); + newType.AddChild(ConvertToType(e.BaseTypeExpression), Roles.BaseType); + } + + if (location != null && curLoc < location.Count) + newType.AddChild(new CSharpTokenNode(Convert(location [curLoc++]), Roles.LBrace), Roles.LBrace); + typeStack.Push(newType); + + foreach (var m in e.Members) { + var member = m as EnumMember; + if (member == null) { + Console.WriteLine("WARNING - ENUM MEMBER: " + m); + continue; + } + Visit(member); + if (location != null && curLoc < location.Count - 1) //last one is closing brace + newType.AddChild(new CSharpTokenNode(Convert(location [curLoc++]), Roles.Comma), Roles.Comma); + } + + if (location != null && location.Count > 2) { + if (location != null && curLoc < location.Count) + newType.AddChild(new CSharpTokenNode(Convert(location [curLoc++]), Roles.RBrace), Roles.RBrace); + if (location != null && curLoc < location.Count) + newType.AddChild(new CSharpTokenNode(Convert(location [curLoc++]), Roles.Semicolon), Roles.Semicolon); + } else { + // parser error, set end node to max value. + newType.AddChild(new ErrorNode(), Roles.Error); + } + + AddAttributeSection(newType, e.UnattachedAttributes, EntityDeclaration.UnattachedAttributeRole); + typeStack.Pop(); + AddType(newType); + } + + public override void Visit(EnumMember em) + { + var newField = new EnumMemberDeclaration(); + AddAttributeSection(newField, em); + newField.AddChild(Identifier.Create(em.Name, Convert(em.Location)), Roles.Identifier); + if (em.Initializer != null) { + newField.AddChild(new CSharpTokenNode(Convert(em.Initializer.Location), Roles.Assign), Roles.Assign); + newField.AddChild((Expression)em.Initializer.Accept(this), EnumMemberDeclaration.InitializerRole); + } + //Console.WriteLine (newField.StartLocation +"-" + newField.EndLocation); + + typeStack.Peek().AddChild(newField, Roles.TypeMemberRole); + } + + #endregion + + #region Type members + + public override void Visit(FixedField f) + { + var location = LocationsBag.GetMemberLocation(f); + int locationIdx = 0; + + var newField = new FixedFieldDeclaration(); + AddAttributeSection(newField, f); + AddModifiers(newField, location); + if (location != null && location.Count > 0) + newField.AddChild(new CSharpTokenNode(Convert(location [locationIdx++]), FixedFieldDeclaration.FixedKeywordRole), FixedFieldDeclaration.FixedKeywordRole); + + if (f.TypeExpression != null) + newField.AddChild(ConvertToType(f.TypeExpression), Roles.Type); + + var variable = new FixedVariableInitializer(); + variable.AddChild(Identifier.Create(f.MemberName.Name, Convert(f.MemberName.Location)), Roles.Identifier); + if (f.Initializer != null && !f.Initializer.IsNull) { + variable.AddChild(new CSharpTokenNode(Convert(f.Initializer.Location), Roles.LBracket), Roles.LBracket); + + variable.AddChild((Expression)f.Initializer.Accept(this), Roles.Expression); + var bracketLocations = LocationsBag.GetLocations(f.Initializer); + if (bracketLocations != null) + variable.AddChild(new CSharpTokenNode(Convert(bracketLocations [0]), Roles.RBracket), Roles.RBracket); + } + newField.AddChild(variable, FixedFieldDeclaration.VariableRole); + + if (f.Declarators != null) { + foreach (var decl in f.Declarators) { + var declLoc = LocationsBag.GetLocations(decl); + if (declLoc != null) + newField.AddChild(new CSharpTokenNode(Convert(declLoc [0]), Roles.Comma), Roles.Comma); + + variable = new FixedVariableInitializer(); + variable.AddChild(Identifier.Create(decl.Name.Value, Convert(decl.Name.Location)), Roles.Identifier); + variable.AddChild(new CSharpTokenNode(Convert(decl.Initializer.Location), Roles.LBracket), Roles.LBracket); + variable.AddChild((Expression)decl.Initializer.Accept(this), Roles.Expression); + var bracketLocations = LocationsBag.GetLocations(decl.Initializer); + if (bracketLocations != null) + variable.AddChild(new CSharpTokenNode(Convert(bracketLocations [0]), Roles.RBracket), Roles.RBracket); + + newField.AddChild(variable, FixedFieldDeclaration.VariableRole); + } + } + if (location != null && location.Count > locationIdx) + newField.AddChild(new CSharpTokenNode(Convert(location [locationIdx]), Roles.Semicolon), Roles.Semicolon); + typeStack.Peek().AddChild(newField, Roles.TypeMemberRole); + + } + + public override void Visit(Field f) + { + var location = LocationsBag.GetMemberLocation(f); + + var newField = new FieldDeclaration(); + AddAttributeSection(newField, f); + AddModifiers(newField, location); + newField.AddChild(ConvertToType(f.TypeExpression), Roles.Type); + + var variable = new VariableInitializer(); + variable.AddChild(Identifier.Create(f.MemberName.Name, Convert(f.MemberName.Location)), Roles.Identifier); + int locationIdx = 0; + if (f.Initializer != null) { + if (location != null) + variable.AddChild(new CSharpTokenNode(Convert(location [locationIdx++]), Roles.Assign), Roles.Assign); + variable.AddChild((Expression)f.Initializer.Accept(this), Roles.Expression); + } + newField.AddChild(variable, Roles.Variable); + if (f.Declarators != null) { + foreach (var decl in f.Declarators) { + var declLoc = LocationsBag.GetLocations(decl); + if (declLoc != null) + newField.AddChild(new CSharpTokenNode(Convert(declLoc [0]), Roles.Comma), Roles.Comma); + + variable = new VariableInitializer(); + variable.AddChild(Identifier.Create(decl.Name.Value, Convert(decl.Name.Location)), Roles.Identifier); + if (decl.Initializer != null) { + if (declLoc != null) + variable.AddChild(new CSharpTokenNode(Convert(declLoc [1]), Roles.Assign), Roles.Assign); + variable.AddChild((Expression)decl.Initializer.Accept(this), Roles.Expression); + } + newField.AddChild(variable, Roles.Variable); + } + } + if (location != null && location.Count > locationIdx) + newField.AddChild(new CSharpTokenNode(Convert(location [locationIdx++]), Roles.Semicolon), Roles.Semicolon); + + typeStack.Peek().AddChild(newField, Roles.TypeMemberRole); + } + + public override void Visit(Const c) + { + var location = LocationsBag.GetMemberLocation(c); + + var newField = new FieldDeclaration(); + AddAttributeSection(newField, c); + AddModifiers(newField, location); + if (location != null) + newField.AddChild(new CSharpModifierToken(Convert(location [0]), Modifiers.Const), EntityDeclaration.ModifierRole); + newField.AddChild(ConvertToType(c.TypeExpression), Roles.Type); + + var variable = new VariableInitializer(); + variable.AddChild(Identifier.Create(c.MemberName.Name, Convert(c.MemberName.Location)), Roles.Identifier); + + if (c.Initializer != null) { + variable.AddChild(new CSharpTokenNode(Convert(c.Initializer.Location), Roles.Assign), Roles.Assign); + variable.AddChild((Expression)c.Initializer.Accept(this), Roles.Expression); + } + newField.AddChild(variable, Roles.Variable); + if (c.Declarators != null) { + foreach (var decl in c.Declarators) { + var declLoc = LocationsBag.GetLocations(decl); + if (declLoc != null) + newField.AddChild(new CSharpTokenNode(Convert(declLoc [0]), Roles.Comma), Roles.Comma); + + variable = new VariableInitializer(); + variable.AddChild(Identifier.Create(decl.Name.Value, Convert(decl.Name.Location)), Roles.Identifier); + if (decl.Initializer != null) { + variable.AddChild(new CSharpTokenNode(Convert(decl.Initializer.Location), Roles.Assign), Roles.Assign); + variable.AddChild((Expression)decl.Initializer.Accept(this), Roles.Expression); + } + newField.AddChild(variable, Roles.Variable); + } + } + if (location != null) + newField.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.Semicolon), Roles.Semicolon); + + typeStack.Peek().AddChild(newField, Roles.TypeMemberRole); + + + } + + public override void Visit(Operator o) + { + var newOperator = new OperatorDeclaration(); + newOperator.OperatorType = (OperatorType)o.OperatorType; + + var location = LocationsBag.GetMemberLocation(o); + AddAttributeSection(newOperator, o); + AddModifiers(newOperator, location); + + + if (o.OperatorType == Operator.OpType.Implicit) { + if (location != null && location.Count > 0) { + newOperator.AddChild(new CSharpTokenNode(Convert(location [0]), OperatorDeclaration.ImplicitRole), OperatorDeclaration.ImplicitRole); + if (location.Count > 1) + newOperator.AddChild(new CSharpTokenNode(Convert(location [1]), OperatorDeclaration.OperatorKeywordRole), OperatorDeclaration.OperatorKeywordRole); + } + newOperator.AddChild(ConvertToType(o.TypeExpression), Roles.Type); + } else if (o.OperatorType == Operator.OpType.Explicit) { + if (location != null && location.Count > 0) { + newOperator.AddChild(new CSharpTokenNode(Convert(location [0]), OperatorDeclaration.ExplicitRole), OperatorDeclaration.ExplicitRole); + if (location.Count > 1) + newOperator.AddChild(new CSharpTokenNode(Convert(location [1]), OperatorDeclaration.OperatorKeywordRole), OperatorDeclaration.OperatorKeywordRole); + } + newOperator.AddChild(ConvertToType(o.TypeExpression), Roles.Type); + } else { + newOperator.AddChild(ConvertToType(o.TypeExpression), Roles.Type); + + if (location != null && location.Count > 0) + newOperator.AddChild(new CSharpTokenNode(Convert(location [0]), OperatorDeclaration.OperatorKeywordRole), OperatorDeclaration.OperatorKeywordRole); + + if (location != null && location.Count > 1) { + var r = OperatorDeclaration.GetRole(newOperator.OperatorType); + newOperator.AddChild(new CSharpTokenNode(Convert(location [1]), r), r); + } + } + if (location != null && location.Count > 2) + newOperator.AddChild(new CSharpTokenNode(Convert(location [2]), Roles.LPar), Roles.LPar); + AddParameter(newOperator, o.ParameterInfo); + if (location != null && location.Count > 3) + newOperator.AddChild(new CSharpTokenNode(Convert(location [3]), Roles.RPar), Roles.RPar); + + if (o.Block != null) { + newOperator.AddChild((BlockStatement)o.Block.Accept(this), Roles.Body); + } else { + if (location != null && location.Count >= 5) + newOperator.AddChild(new CSharpTokenNode(Convert(location [4]), Roles.Semicolon), Roles.Semicolon); + } + typeStack.Peek().AddChild(newOperator, Roles.TypeMemberRole); + } + + public void AddAttributeSection(AstNode parent, Attributable a) + { + if (a == null || a.OptAttributes == null) + return; + AddAttributeSection(parent, a.OptAttributes); + } + + public void AddAttributeSection(AstNode parent, Attributes attrs, Role role) + { + if (attrs == null) + return; + foreach (var attr in attrs.Sections) { + var section = ConvertAttributeSection(attr); + if (section == null) + continue; + parent.AddChild(section, role); + } + } + + public void AddAttributeSection(AstNode parent, Attributes attrs) + { + AddAttributeSection(parent, attrs, EntityDeclaration.AttributeRole); + } + + public override void Visit(Indexer i) + { + var newIndexer = new IndexerDeclaration(); + AddAttributeSection(newIndexer, i); + var location = LocationsBag.GetMemberLocation(i); + AddModifiers(newIndexer, location); + newIndexer.AddChild(ConvertToType(i.TypeExpression), Roles.Type); + AddExplicitInterface(newIndexer, i.MemberName); + var name = i.MemberName; + newIndexer.AddChild(new CSharpTokenNode(Convert(name.Location), IndexerDeclaration.ThisKeywordRole), IndexerDeclaration.ThisKeywordRole); + + if (location != null && location.Count > 0) + newIndexer.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LBracket), Roles.LBracket); + AddParameter(newIndexer, i.ParameterInfo); + if (location != null && location.Count > 1) + newIndexer.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RBracket), Roles.RBracket); + + if (location != null && location.Count > 2) + newIndexer.AddChild(new CSharpTokenNode(Convert(location [2]), Roles.LBrace), Roles.LBrace); + if (i.Get != null) { + var getAccessor = new Accessor(); + var getLocation = LocationsBag.GetMemberLocation(i.Get); + AddAttributeSection(getAccessor, i.Get); + AddModifiers(getAccessor, getLocation); + if (getLocation != null) + getAccessor.AddChild(new CSharpTokenNode(Convert(i.Get.Location), PropertyDeclaration.GetKeywordRole), PropertyDeclaration.GetKeywordRole); + if (i.Get.Block != null) { + getAccessor.AddChild((BlockStatement)i.Get.Block.Accept(this), Roles.Body); + } else { + if (getLocation != null && getLocation.Count > 0) + newIndexer.AddChild(new CSharpTokenNode(Convert(getLocation [0]), Roles.Semicolon), Roles.Semicolon); + } + newIndexer.AddChild(getAccessor, PropertyDeclaration.GetterRole); + } + + if (i.Set != null) { + var setAccessor = new Accessor(); + var setLocation = LocationsBag.GetMemberLocation(i.Set); + AddAttributeSection(setAccessor, i.Set); + AddModifiers(setAccessor, setLocation); + if (setLocation != null) + setAccessor.AddChild(new CSharpTokenNode(Convert(i.Set.Location), PropertyDeclaration.SetKeywordRole), PropertyDeclaration.SetKeywordRole); + + if (i.Set.Block != null) { + setAccessor.AddChild((BlockStatement)i.Set.Block.Accept(this), Roles.Body); + } else { + if (setLocation != null && setLocation.Count > 0) + newIndexer.AddChild(new CSharpTokenNode(Convert(setLocation [0]), Roles.Semicolon), Roles.Semicolon); + } + newIndexer.AddChild(setAccessor, PropertyDeclaration.SetterRole); + } + + if (location != null) { + if (location.Count > 3) + newIndexer.AddChild(new CSharpTokenNode(Convert(location [3]), Roles.RBrace), Roles.RBrace); + } else { + // parser error, set end node to max value. + newIndexer.AddChild(new ErrorNode(), Roles.Error); + } + typeStack.Peek().AddChild(newIndexer, Roles.TypeMemberRole); + } + + public override void Visit(Method m) + { + var newMethod = new MethodDeclaration(); + AddAttributeSection(newMethod, m); + var location = LocationsBag.GetMemberLocation(m); + AddModifiers(newMethod, location); + newMethod.AddChild(ConvertToType(m.TypeExpression), Roles.Type); + AddExplicitInterface(newMethod, m.MethodName); + newMethod.AddChild(Identifier.Create(m.MethodName.Name, Convert(m.Location)), Roles.Identifier); + + AddTypeParameters(newMethod, m.MemberName); + + if (location != null && location.Count > 0) + newMethod.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + AddParameter(newMethod, m.ParameterInfo); + + if (location != null && location.Count > 1) + newMethod.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + + AddConstraints(newMethod, m.CurrentTypeParameters); + + if (m.Block != null) { + var bodyBlock = (BlockStatement)m.Block.Accept(this); +// if (m.Block is ToplevelBlock) { +// newMethod.AddChild (bodyBlock.FirstChild.NextSibling, Roles.Body); +// } else { + newMethod.AddChild(bodyBlock, Roles.Body); +// } + } else if (location != null) { + if (location.Count < 3) { + // parser error, set end node to max value. + newMethod.AddChild(new ErrorNode(), Roles.Error); + } else { + newMethod.AddChild(new CSharpTokenNode(Convert(location [2]), Roles.Semicolon), Roles.Semicolon); + } + } + typeStack.Peek().AddChild(newMethod, Roles.TypeMemberRole); + } + + static readonly Dictionary modifierTable = new Dictionary(); + static readonly string[] keywordTable; + + static ConversionVisitor() + { + modifierTable [Mono.CSharp.Modifiers.NEW] = Modifiers.New; + modifierTable [Mono.CSharp.Modifiers.PUBLIC] = Modifiers.Public; + modifierTable [Mono.CSharp.Modifiers.PROTECTED] = Modifiers.Protected; + modifierTable [Mono.CSharp.Modifiers.PRIVATE] = Modifiers.Private; + modifierTable [Mono.CSharp.Modifiers.INTERNAL] = Modifiers.Internal; + modifierTable [Mono.CSharp.Modifiers.ABSTRACT] = Modifiers.Abstract; + modifierTable [Mono.CSharp.Modifiers.VIRTUAL] = Modifiers.Virtual; + modifierTable [Mono.CSharp.Modifiers.SEALED] = Modifiers.Sealed; + modifierTable [Mono.CSharp.Modifiers.STATIC] = Modifiers.Static; + modifierTable [Mono.CSharp.Modifiers.OVERRIDE] = Modifiers.Override; + modifierTable [Mono.CSharp.Modifiers.READONLY] = Modifiers.Readonly; + modifierTable [Mono.CSharp.Modifiers.PARTIAL] = Modifiers.Partial; + modifierTable [Mono.CSharp.Modifiers.EXTERN] = Modifiers.Extern; + modifierTable [Mono.CSharp.Modifiers.VOLATILE] = Modifiers.Volatile; + modifierTable [Mono.CSharp.Modifiers.UNSAFE] = Modifiers.Unsafe; + modifierTable [Mono.CSharp.Modifiers.ASYNC] = Modifiers.Async; + + keywordTable = new string[255]; + for (int i = 0; i< keywordTable.Length; i++) + keywordTable [i] = "unknown"; + + keywordTable [(int)BuiltinTypeSpec.Type.Other] = "void"; + keywordTable [(int)BuiltinTypeSpec.Type.String] = "string"; + keywordTable [(int)BuiltinTypeSpec.Type.Int] = "int"; + keywordTable [(int)BuiltinTypeSpec.Type.Object] = "object"; + keywordTable [(int)BuiltinTypeSpec.Type.Float] = "float"; + keywordTable [(int)BuiltinTypeSpec.Type.Double] = "double"; + keywordTable [(int)BuiltinTypeSpec.Type.Long] = "long"; + keywordTable [(int)BuiltinTypeSpec.Type.Byte] = "byte"; + keywordTable [(int)BuiltinTypeSpec.Type.UInt] = "uint"; + keywordTable [(int)BuiltinTypeSpec.Type.ULong] = "ulong"; + keywordTable [(int)BuiltinTypeSpec.Type.Short] = "short"; + keywordTable [(int)BuiltinTypeSpec.Type.UShort] = "ushort"; + keywordTable [(int)BuiltinTypeSpec.Type.SByte] = "sbyte"; + keywordTable [(int)BuiltinTypeSpec.Type.Decimal] = "decimal"; + keywordTable [(int)BuiltinTypeSpec.Type.Char] = "char"; + keywordTable [(int)BuiltinTypeSpec.Type.Bool] = "bool"; + } + + static void AddModifiers(EntityDeclaration parent, LocationsBag.MemberLocations location) + { + if (location == null || location.Modifiers == null) + return; + foreach (var modifier in location.Modifiers) { + Modifiers mod; + if (!modifierTable.TryGetValue(modifier.Item1, out mod)) { + Console.WriteLine("modifier " + modifier.Item1 + " can't be converted,"); + } + + parent.AddChild(new CSharpModifierToken(Convert(modifier.Item2), mod), EntityDeclaration.ModifierRole); + } + } + + public override void Visit(Property p) + { + var newProperty = new PropertyDeclaration(); + AddAttributeSection(newProperty, p); + var location = LocationsBag.GetMemberLocation(p); + AddModifiers(newProperty, location); + newProperty.AddChild(ConvertToType(p.TypeExpression), Roles.Type); + AddExplicitInterface(newProperty, p.MemberName); + newProperty.AddChild(Identifier.Create(p.MemberName.Name, Convert(p.Location)), Roles.Identifier); + + if (location != null && location.Count > 0) + newProperty.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LBrace), Roles.LBrace); + + Accessor getAccessor = null; + if (p.Get != null) { + getAccessor = new Accessor(); + AddAttributeSection(getAccessor, p.Get); + var getLocation = LocationsBag.GetMemberLocation(p.Get); + AddModifiers(getAccessor, getLocation); + getAccessor.AddChild(new CSharpTokenNode(Convert(p.Get.Location), PropertyDeclaration.GetKeywordRole), PropertyDeclaration.GetKeywordRole); + + if (p.Get.Block != null) { + getAccessor.AddChild((BlockStatement)p.Get.Block.Accept(this), Roles.Body); + } else { + if (getLocation != null && getLocation.Count > 0) + getAccessor.AddChild(new CSharpTokenNode(Convert(getLocation [0]), Roles.Semicolon), Roles.Semicolon); + } + } + + Accessor setAccessor = null; + if (p.Set != null) { + setAccessor = new Accessor(); + AddAttributeSection(setAccessor, p.Set); + var setLocation = LocationsBag.GetMemberLocation(p.Set); + AddModifiers(setAccessor, setLocation); + setAccessor.AddChild(new CSharpTokenNode(Convert(p.Set.Location), PropertyDeclaration.SetKeywordRole), PropertyDeclaration.SetKeywordRole); + + if (p.Set.Block != null) { + setAccessor.AddChild((BlockStatement)p.Set.Block.Accept(this), Roles.Body); + } else { + if (setLocation != null && setLocation.Count > 0) + setAccessor.AddChild(new CSharpTokenNode(Convert(setLocation [0]), Roles.Semicolon), Roles.Semicolon); + } + } + if (getAccessor != null && setAccessor != null) { + if (getAccessor.StartLocation < setAccessor.StartLocation) { + newProperty.AddChild(getAccessor, PropertyDeclaration.GetterRole); + newProperty.AddChild(setAccessor, PropertyDeclaration.SetterRole); + } else { + newProperty.AddChild(setAccessor, PropertyDeclaration.SetterRole); + newProperty.AddChild(getAccessor, PropertyDeclaration.GetterRole); + } + } else { + if (getAccessor != null) + newProperty.AddChild(getAccessor, PropertyDeclaration.GetterRole); + if (setAccessor != null) + newProperty.AddChild(setAccessor, PropertyDeclaration.SetterRole); + } + + if (location != null && location.Count > 1) { + newProperty.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RBrace), Roles.RBrace); + } else { + // parser error, set end node to max value. + newProperty.AddChild(new ErrorNode(), Roles.Error); + } + + typeStack.Peek().AddChild(newProperty, Roles.TypeMemberRole); + } + + public override void Visit(Constructor c) + { + var newConstructor = new ConstructorDeclaration(); + AddAttributeSection(newConstructor, c); + var location = LocationsBag.GetMemberLocation(c); + AddModifiers(newConstructor, location); + newConstructor.AddChild(Identifier.Create(c.MemberName.Name, Convert(c.MemberName.Location)), Roles.Identifier); + if (location != null && location.Count > 0) + newConstructor.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + + AddParameter(newConstructor, c.ParameterInfo); + if (location != null && location.Count > 1) + newConstructor.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + + if (c.Initializer != null) { + var initializer = new ConstructorInitializer(); + initializer.ConstructorInitializerType = c.Initializer is ConstructorBaseInitializer ? ConstructorInitializerType.Base : ConstructorInitializerType.This; + var initializerLocation = LocationsBag.GetLocations(c.Initializer); + + if (initializerLocation != null) + newConstructor.AddChild(new CSharpTokenNode(Convert(initializerLocation [0]), Roles.Colon), Roles.Colon); + + if (initializerLocation != null && initializerLocation.Count > 1) { + // this and base has the same length + var r = initializer.ConstructorInitializerType == ConstructorInitializerType.This ? ConstructorInitializer.ThisKeywordRole : ConstructorInitializer.BaseKeywordRole; + initializer.AddChild(new CSharpTokenNode(Convert(c.Initializer.Location), r), r); + initializer.AddChild(new CSharpTokenNode(Convert(initializerLocation [1]), Roles.LPar), Roles.LPar); + AddArguments(initializer, c.Initializer.Arguments); + initializer.AddChild(new CSharpTokenNode(Convert(initializerLocation [2]), Roles.RPar), Roles.RPar); + newConstructor.AddChild(initializer, ConstructorDeclaration.InitializerRole); + } + } + + if (c.Block != null) + newConstructor.AddChild((BlockStatement)c.Block.Accept(this), Roles.Body); + typeStack.Peek().AddChild(newConstructor, Roles.TypeMemberRole); + } + + public override void Visit(Destructor d) + { + var newDestructor = new DestructorDeclaration(); + AddAttributeSection(newDestructor, d); + var location = LocationsBag.GetMemberLocation(d); + AddModifiers(newDestructor, location); + if (location != null && location.Count > 0) + newDestructor.AddChild(new CSharpTokenNode(Convert(location [0]), DestructorDeclaration.TildeRole), DestructorDeclaration.TildeRole); + newDestructor.AddChild(Identifier.Create(d.Identifier, Convert(d.MemberName.Location)), Roles.Identifier); + + if (location != null && location.Count > 1) { + newDestructor.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.LPar), Roles.LPar); + + if (location.Count > 2) + newDestructor.AddChild(new CSharpTokenNode(Convert(location [2]), Roles.RPar), Roles.RPar); + } + + if (d.Block != null) + newDestructor.AddChild((BlockStatement)d.Block.Accept(this), Roles.Body); + + typeStack.Peek().AddChild(newDestructor, Roles.TypeMemberRole); + } + + public override void Visit(EventField e) + { + var newEvent = new EventDeclaration(); + AddAttributeSection(newEvent, e); + var location = LocationsBag.GetMemberLocation(e); + int l = 0; + AddModifiers(newEvent, location); + + if (location != null && location.Count > 0) + newEvent.AddChild(new CSharpTokenNode(Convert(location [l++]), EventDeclaration.EventKeywordRole), EventDeclaration.EventKeywordRole); + newEvent.AddChild(ConvertToType(e.TypeExpression), Roles.Type); + + var variable = new VariableInitializer(); + variable.AddChild(Identifier.Create(e.MemberName.Name, Convert(e.MemberName.Location)), Roles.Identifier); + + if (e.Initializer != null) { + if (location != null && location.Count > l) + variable.AddChild(new CSharpTokenNode(Convert(location [l++]), Roles.Assign), Roles.Assign); + variable.AddChild((Expression)e.Initializer.Accept(this), Roles.Expression); + } + newEvent.AddChild(variable, Roles.Variable); + if (e.Declarators != null) { + foreach (var decl in e.Declarators) { + var declLoc = LocationsBag.GetLocations(decl); + if (declLoc != null) + newEvent.AddChild(new CSharpTokenNode(Convert(declLoc [0]), Roles.Comma), Roles.Comma); + + variable = new VariableInitializer(); + variable.AddChild(Identifier.Create(decl.Name.Value, Convert(decl.Name.Location)), Roles.Identifier); + + if (decl.Initializer != null) { + if (declLoc != null) + variable.AddChild(new CSharpTokenNode(Convert(declLoc [1]), Roles.Assign), Roles.Assign); + variable.AddChild((Expression)decl.Initializer.Accept(this), Roles.Expression); + } + newEvent.AddChild(variable, Roles.Variable); + } + } + + if (location != null && location.Count > l) + newEvent.AddChild(new CSharpTokenNode(Convert(location [l++]), Roles.Semicolon), Roles.Semicolon); + + typeStack.Peek().AddChild(newEvent, Roles.TypeMemberRole); + } + + void AddExplicitInterface(AstNode parent, MemberName memberName) + { + if (memberName == null || memberName.ExplicitInterface == null) + return; + + parent.AddChild(ConvertToType(memberName.ExplicitInterface), EntityDeclaration.PrivateImplementationTypeRole); + var privateImplTypeLoc = LocationsBag.GetLocations(memberName.ExplicitInterface); + if (privateImplTypeLoc != null) + parent.AddChild(new CSharpTokenNode(Convert(privateImplTypeLoc [0]), Roles.Dot), Roles.Dot); + } + + public override void Visit(EventProperty ep) + { + var newEvent = new CustomEventDeclaration(); + AddAttributeSection(newEvent, ep); + var location = LocationsBag.GetMemberLocation(ep); + AddModifiers(newEvent, location); + + if (location != null && location.Count > 0) + newEvent.AddChild(new CSharpTokenNode(Convert(location [0]), CustomEventDeclaration.EventKeywordRole), CustomEventDeclaration.EventKeywordRole); + newEvent.AddChild(ConvertToType(ep.TypeExpression), Roles.Type); + + AddExplicitInterface(newEvent, ep.MemberName); + + newEvent.AddChild(Identifier.Create(ep.MemberName.Name, Convert(ep.Location)), Roles.Identifier); + + if (location != null && location.Count >= 2) + newEvent.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.LBrace), Roles.LBrace); + + if (ep.Add != null) { + var addAccessor = new Accessor(); + AddAttributeSection(addAccessor, ep.Add); + var addLocation = LocationsBag.GetMemberLocation(ep.Add); + AddModifiers(addAccessor, addLocation); + addAccessor.AddChild(new CSharpTokenNode(Convert(ep.Add.Location), CustomEventDeclaration.AddKeywordRole), CustomEventDeclaration.AddKeywordRole); + if (ep.Add.Block != null) + addAccessor.AddChild((BlockStatement)ep.Add.Block.Accept(this), Roles.Body); + newEvent.AddChild(addAccessor, CustomEventDeclaration.AddAccessorRole); + } + + if (ep.Remove != null) { + var removeAccessor = new Accessor(); + AddAttributeSection(removeAccessor, ep.Remove); + var removeLocation = LocationsBag.GetMemberLocation(ep.Remove); + AddModifiers(removeAccessor, removeLocation); + removeAccessor.AddChild(new CSharpTokenNode(Convert(ep.Remove.Location), CustomEventDeclaration.RemoveKeywordRole), CustomEventDeclaration.RemoveKeywordRole); + + if (ep.Remove.Block != null) + removeAccessor.AddChild((BlockStatement)ep.Remove.Block.Accept(this), Roles.Body); + newEvent.AddChild(removeAccessor, CustomEventDeclaration.RemoveAccessorRole); + } + if (location != null && location.Count >= 3) { + newEvent.AddChild(new CSharpTokenNode(Convert(location [2]), Roles.RBrace), Roles.RBrace); + } else { + // parser error, set end node to max value. + newEvent.AddChild(new ErrorNode(), Roles.Error); + } + + typeStack.Peek().AddChild(newEvent, Roles.TypeMemberRole); + } + + #endregion + + #region Statements + + public override object Visit(Mono.CSharp.Statement stmt) + { + Console.WriteLine("unknown statement:" + stmt); + return null; + } + + public override object Visit(BlockVariable blockVariableDeclaration) + { + var result = new VariableDeclarationStatement(); + result.AddChild(ConvertToType(blockVariableDeclaration.TypeExpression), Roles.Type); + + var varInit = new VariableInitializer(); + var location = LocationsBag.GetLocations(blockVariableDeclaration); + varInit.AddChild(Identifier.Create(blockVariableDeclaration.Variable.Name, Convert(blockVariableDeclaration.Variable.Location)), Roles.Identifier); + if (blockVariableDeclaration.Initializer != null) { + if (location != null && location.Count > 0) + varInit.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.Assign), Roles.Assign); + varInit.AddChild((Expression)blockVariableDeclaration.Initializer.Accept(this), Roles.Expression); + } + + result.AddChild(varInit, Roles.Variable); + + if (blockVariableDeclaration.Declarators != null) { + foreach (var decl in blockVariableDeclaration.Declarators) { + var loc = LocationsBag.GetLocations(decl); + var init = new VariableInitializer(); + if (loc != null && loc.Count > 0) + result.AddChild(new CSharpTokenNode(Convert(loc [0]), Roles.Comma), Roles.Comma); + init.AddChild(Identifier.Create(decl.Variable.Name, Convert(decl.Variable.Location)), Roles.Identifier); + if (decl.Initializer != null) { + if (loc != null && loc.Count > 1) + init.AddChild(new CSharpTokenNode(Convert(loc [1]), Roles.Assign), Roles.Assign); + init.AddChild((Expression)decl.Initializer.Accept(this), Roles.Expression); + } + result.AddChild(init, Roles.Variable); + } + } + if (location != null && (blockVariableDeclaration.Initializer == null || location.Count > 1)) + result.AddChild(new CSharpTokenNode(Convert(location [location.Count - 1]), Roles.Semicolon), Roles.Semicolon); + return result; + } + + public override object Visit(BlockConstant blockConstantDeclaration) + { + var result = new VariableDeclarationStatement(); + + var location = LocationsBag.GetLocations(blockConstantDeclaration); + if (location != null && location.Count > 0) + result.AddChild(new CSharpModifierToken(Convert(location [0]), Modifiers.Const), VariableDeclarationStatement.ModifierRole); + + result.AddChild(ConvertToType(blockConstantDeclaration.TypeExpression), Roles.Type); + + var varInit = new VariableInitializer(); + varInit.AddChild(Identifier.Create(blockConstantDeclaration.Variable.Name, Convert(blockConstantDeclaration.Variable.Location)), Roles.Identifier); + if (blockConstantDeclaration.Initializer != null) { + if (location != null && location.Count > 1) + varInit.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.Assign), Roles.Assign); + varInit.AddChild((Expression)blockConstantDeclaration.Initializer.Accept(this), Roles.Expression); + } + + result.AddChild(varInit, Roles.Variable); + + if (blockConstantDeclaration.Declarators != null) { + foreach (var decl in blockConstantDeclaration.Declarators) { + var loc = LocationsBag.GetLocations(decl); + var init = new VariableInitializer(); + init.AddChild(Identifier.Create(decl.Variable.Name, Convert(decl.Variable.Location)), Roles.Identifier); + if (decl.Initializer != null) { + if (loc != null) + init.AddChild(new CSharpTokenNode(Convert(loc [0]), Roles.Assign), Roles.Assign); + init.AddChild((Expression)decl.Initializer.Accept(this), Roles.Expression); + if (loc != null && loc.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(loc [1]), Roles.Comma), Roles.Comma); + } else { + if (loc != null && loc.Count > 0) + result.AddChild(new CSharpTokenNode(Convert(loc [0]), Roles.Comma), Roles.Comma); + } + result.AddChild(init, Roles.Variable); + } + } + if (location != null) { + result.AddChild(new CSharpTokenNode(Convert(location [location.Count - 1]), Roles.Semicolon), Roles.Semicolon); + } else { + // parser error, set end node to max value. + result.AddChild(new ErrorNode(), Roles.Error); + } + return result; + } + + public override object Visit(Mono.CSharp.EmptyStatement emptyStatement) + { + var result = new EmptyStatement(); + result.Location = Convert(emptyStatement.loc); + return result; + } + + public override object Visit(Mono.CSharp.ErrorExpression errorExpression) + { + return new ErrorExpression(Convert(errorExpression.Location)); + } + + public override object Visit(EmptyExpressionStatement emptyExpressionStatement) + { + // Should never happen. + throw new NotSupportedException(); + } + + public override object Visit(If ifStatement) + { + var result = new IfElseStatement(); + + var location = LocationsBag.GetLocations(ifStatement); + + result.AddChild(new CSharpTokenNode(Convert(ifStatement.loc), IfElseStatement.IfKeywordRole), IfElseStatement.IfKeywordRole); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + if (ifStatement.Expr != null) + result.AddChild((Expression)ifStatement.Expr.Accept(this), Roles.Condition); + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + + if (ifStatement.TrueStatement != null) + result.AddChild((Statement)ifStatement.TrueStatement.Accept(this), IfElseStatement.TrueRole); + + if (ifStatement.FalseStatement != null) { + if (location != null && location.Count > 2) + result.AddChild(new CSharpTokenNode(Convert(location [2]), IfElseStatement.ElseKeywordRole), IfElseStatement.ElseKeywordRole); + result.AddChild((Statement)ifStatement.FalseStatement.Accept(this), IfElseStatement.FalseRole); + } + + return result; + } + + public override object Visit(Do doStatement) + { + var result = new DoWhileStatement(); + var location = LocationsBag.GetLocations(doStatement); + result.AddChild(new CSharpTokenNode(Convert(doStatement.loc), DoWhileStatement.DoKeywordRole), DoWhileStatement.DoKeywordRole); + if (doStatement.Statement != null) + result.AddChild((Statement)doStatement.Statement.Accept(this), Roles.EmbeddedStatement); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), DoWhileStatement.WhileKeywordRole), DoWhileStatement.WhileKeywordRole); + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.LPar), Roles.LPar); + if (doStatement.expr != null) + result.AddChild((Expression)doStatement.expr.Accept(this), Roles.Condition); + if (location != null && location.Count > 2) { + result.AddChild(new CSharpTokenNode(Convert(location [2]), Roles.RPar), Roles.RPar); + if (location.Count > 3) + result.AddChild(new CSharpTokenNode(Convert(location [3]), Roles.Semicolon), Roles.Semicolon); + } + + return result; + } + + public override object Visit(While whileStatement) + { + var result = new WhileStatement(); + var location = LocationsBag.GetLocations(whileStatement); + result.AddChild(new CSharpTokenNode(Convert(whileStatement.loc), WhileStatement.WhileKeywordRole), WhileStatement.WhileKeywordRole); + + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + if (whileStatement.expr != null) + result.AddChild((Expression)whileStatement.expr.Accept(this), Roles.Condition); + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + if (whileStatement.Statement != null) + result.AddChild((Statement)whileStatement.Statement.Accept(this), Roles.EmbeddedStatement); + return result; + } + + void AddStatementOrList(ForStatement forStatement, Mono.CSharp.Statement init, Role role) + { + if (init == null) + return; + var stmtList = init as StatementList; + if (stmtList != null) { + foreach (var stmt in stmtList.Statements) { + forStatement.AddChild((Statement)stmt.Accept(this), role); + } + } else if (init is Mono.CSharp.EmptyStatement) { + + } else { + forStatement.AddChild((Statement)init.Accept(this), role); + } + } + + public override object Visit(For forStatement) + { + var result = new ForStatement(); + + var location = LocationsBag.GetLocations(forStatement); + + result.AddChild(new CSharpTokenNode(Convert(forStatement.loc), ForStatement.ForKeywordRole), ForStatement.ForKeywordRole); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + + AddStatementOrList(result, forStatement.Initializer, ForStatement.InitializerRole); + + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.Semicolon), Roles.Semicolon); + if (forStatement.Condition != null) + result.AddChild((Expression)forStatement.Condition.Accept(this), Roles.Condition); + if (location != null && location.Count >= 3) + result.AddChild(new CSharpTokenNode(Convert(location [2]), Roles.Semicolon), Roles.Semicolon); + + AddStatementOrList(result, forStatement.Iterator, ForStatement.IteratorRole); + + if (location != null && location.Count >= 4) + result.AddChild(new CSharpTokenNode(Convert(location [3]), Roles.RPar), Roles.RPar); + + if (forStatement.Statement != null) + result.AddChild((Statement)forStatement.Statement.Accept(this), Roles.EmbeddedStatement); + + return result; + } + + public override object Visit(StatementExpression statementExpression) + { + var result = new ExpressionStatement(); + var expr = statementExpression.Expr.Accept(this) as Expression; + if (expr != null) + result.AddChild(expr, Roles.Expression); + var location = LocationsBag.GetLocations(statementExpression); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.Semicolon), Roles.Semicolon); + return result; + } + + public override object Visit(StatementErrorExpression errorStatement) + { + var result = new ExpressionStatement(); + var expr = errorStatement.Expr.Accept(this) as Expression; + if (expr != null) + result.AddChild(expr, Roles.Expression); + var location = LocationsBag.GetLocations(errorStatement); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.Semicolon), Roles.Semicolon); + return result; + } + + public override object Visit(InvalidStatementExpression invalidStatementExpression) + { + var result = new ExpressionStatement(); + if (invalidStatementExpression.Expression == null) + return result; + var expr = invalidStatementExpression.Expression.Accept(this) as Expression; + if (expr != null) + result.AddChild(expr, Roles.Expression); + var location = LocationsBag.GetLocations(invalidStatementExpression); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.Semicolon), Roles.Semicolon); + return result; + } + + public override object Visit(Return returnStatement) + { + var result = new ReturnStatement(); + + result.AddChild(new CSharpTokenNode(Convert(returnStatement.loc), ReturnStatement.ReturnKeywordRole), ReturnStatement.ReturnKeywordRole); + if (returnStatement.Expr != null) + result.AddChild((Expression)returnStatement.Expr.Accept(this), Roles.Expression); + + var location = LocationsBag.GetLocations(returnStatement); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.Semicolon), Roles.Semicolon); + + return result; + } + + public override object Visit(Goto gotoStatement) + { + var result = new GotoStatement(); + var location = LocationsBag.GetLocations(gotoStatement); + result.AddChild(new CSharpTokenNode(Convert(gotoStatement.loc), GotoStatement.GotoKeywordRole), GotoStatement.GotoKeywordRole); + var loc = location != null ? Convert(location [0]) : TextLocation.Empty; + result.AddChild(Identifier.Create(gotoStatement.Target, loc), Roles.Identifier); + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.Semicolon), Roles.Semicolon); + + return result; + } + + public override object Visit(LabeledStatement labeledStatement) + { + var result = new LabelStatement(); + result.AddChild(Identifier.Create(labeledStatement.Name, Convert(labeledStatement.loc)), Roles.Identifier); + var location = LocationsBag.GetLocations(labeledStatement); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.Colon), Roles.Colon); + return result; + } + + public override object Visit(GotoDefault gotoDefault) + { + var result = new GotoDefaultStatement(); + result.AddChild(new CSharpTokenNode(Convert(gotoDefault.loc), GotoDefaultStatement.GotoKeywordRole), GotoDefaultStatement.GotoKeywordRole); + var location = LocationsBag.GetLocations(gotoDefault); + if (location != null) { + result.AddChild(new CSharpTokenNode(Convert(location [0]), GotoDefaultStatement.DefaultKeywordRole), GotoDefaultStatement.DefaultKeywordRole); + if (location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.Semicolon), Roles.Semicolon); + } + + return result; + } + + public override object Visit(GotoCase gotoCase) + { + var result = new GotoCaseStatement(); + result.AddChild(new CSharpTokenNode(Convert(gotoCase.loc), GotoCaseStatement.GotoKeywordRole), GotoCaseStatement.GotoKeywordRole); + + var location = LocationsBag.GetLocations(gotoCase); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), GotoCaseStatement.CaseKeywordRole), GotoCaseStatement.CaseKeywordRole); + if (gotoCase.Expr != null) + result.AddChild((Expression)gotoCase.Expr.Accept(this), Roles.Expression); + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.Semicolon), Roles.Semicolon); + return result; + } + + public override object Visit(Throw throwStatement) + { + var result = new ThrowStatement(); + var location = LocationsBag.GetLocations(throwStatement); + + result.AddChild(new CSharpTokenNode(Convert(throwStatement.loc), ThrowStatement.ThrowKeywordRole), ThrowStatement.ThrowKeywordRole); + if (throwStatement.Expr != null) + result.AddChild((Expression)throwStatement.Expr.Accept(this), Roles.Expression); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.Semicolon), Roles.Semicolon); + return result; + } + + public override object Visit(Break breakStatement) + { + var result = new BreakStatement(); + var location = LocationsBag.GetLocations(breakStatement); + + result.AddChild(new CSharpTokenNode(Convert(breakStatement.loc), BreakStatement.BreakKeywordRole), BreakStatement.BreakKeywordRole); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.Semicolon), Roles.Semicolon); + return result; + } + + public override object Visit(Continue continueStatement) + { + var result = new ContinueStatement(); + var location = LocationsBag.GetLocations(continueStatement); + result.AddChild(new CSharpTokenNode(Convert(continueStatement.loc), ContinueStatement.ContinueKeywordRole), ContinueStatement.ContinueKeywordRole); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.Semicolon), Roles.Semicolon); + return result; + } + + public static bool IsLower(Location left, Location right) + { + return left.Row < right.Row || left.Row == right.Row && left.Column < right.Column; + } + + public UsingStatement CreateUsingStatement(Block blockStatement) + { + var usingResult = new UsingStatement(); + Mono.CSharp.Statement cur = blockStatement.Statements [0]; + var u = cur as Using; + if (u != null) { + usingResult.AddChild(new CSharpTokenNode(Convert(u.loc), UsingStatement.UsingKeywordRole), UsingStatement.UsingKeywordRole); + usingResult.AddChild(new CSharpTokenNode(Convert(blockStatement.StartLocation), Roles.LPar), Roles.LPar); + if (u.Variables != null) { + var initializer = new VariableInitializer { + NameToken = Identifier.Create(u.Variables.Variable.Name, Convert(u.Variables.Variable.Location)), + }; + + var loc = LocationsBag.GetLocations(u.Variables); + if (loc != null) + initializer.AddChild(new CSharpTokenNode(Convert(loc [0]), Roles.Assign), Roles.Assign); + if (u.Variables.Initializer != null) + initializer.Initializer = u.Variables.Initializer.Accept(this) as Expression; + + + var varDec = new VariableDeclarationStatement { + Type = ConvertToType(u.Variables.TypeExpression), + Variables = { initializer } + }; + + if (u.Variables.Declarators != null) { + foreach (var decl in u.Variables.Declarators) { + var declLoc = LocationsBag.GetLocations(decl); + var init = new VariableInitializer(); + if (declLoc != null && declLoc.Count > 0) + varDec.AddChild(new CSharpTokenNode(Convert(declLoc [0]), Roles.Comma), Roles.Comma); + init.AddChild(Identifier.Create(decl.Variable.Name, Convert(decl.Variable.Location)), Roles.Identifier); + if (decl.Initializer != null) { + if (declLoc != null && declLoc.Count > 1) + init.AddChild(new CSharpTokenNode(Convert(declLoc [1]), Roles.Assign), Roles.Assign); + init.AddChild((Expression)decl.Initializer.Accept(this), Roles.Expression); + } + varDec.AddChild(init, Roles.Variable); + } + } + usingResult.AddChild(varDec, UsingStatement.ResourceAcquisitionRole); + } + cur = u.Statement; + usingResult.AddChild(new CSharpTokenNode(Convert(blockStatement.EndLocation), Roles.RPar), Roles.RPar); + if (cur != null) + usingResult.AddChild((Statement)cur.Accept(this), Roles.EmbeddedStatement); + } + return usingResult; + } + + void AddBlockChildren(BlockStatement result, Block blockStatement, ref int curLocal) + { + if (convertTypeSystemMode) { + return; + } + foreach (Mono.CSharp.Statement stmt in blockStatement.Statements) { + if (stmt == null) + continue; + /* if (curLocal < localVariables.Count && IsLower (localVariables[curLocal].Location, stmt.loc)) { + result.AddChild (CreateVariableDeclaration (localVariables[curLocal]), Roles.Statement); + curLocal++; + }*/ + if (stmt is Block && !(stmt is ToplevelBlock || stmt is ExplicitBlock)) { + AddBlockChildren(result, (Block)stmt, ref curLocal); + } else { + result.AddChild((Statement)stmt.Accept(this), BlockStatement.StatementRole); + } + } + } + + public override object Visit(Block blockStatement) + { + if (blockStatement.IsCompilerGenerated && blockStatement.Statements.Any()) { + if (blockStatement.Statements.First() is Using) + return CreateUsingStatement(blockStatement); + return blockStatement.Statements.Last().Accept(this); + } + var result = new BlockStatement(); + result.AddChild(new CSharpTokenNode(Convert(blockStatement.StartLocation), Roles.LBrace), Roles.LBrace); + int curLocal = 0; + AddBlockChildren(result, blockStatement, ref curLocal); + + result.AddChild(new CSharpTokenNode(Convert(blockStatement.EndLocation), Roles.RBrace), Roles.RBrace); + return result; + } + + public override object Visit(Switch switchStatement) + { + var result = new SwitchStatement(); + + var location = LocationsBag.GetLocations(switchStatement); + result.AddChild(new CSharpTokenNode(Convert(switchStatement.loc), SwitchStatement.SwitchKeywordRole), SwitchStatement.SwitchKeywordRole); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + if (switchStatement.Expr != null) + result.AddChild((Expression)switchStatement.Expr.Accept(this), Roles.Expression); + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + if (location != null && location.Count > 2) + result.AddChild(new CSharpTokenNode(Convert(location [2]), Roles.LBrace), Roles.LBrace); + SwitchSection newSection = null; + bool lastWasCase = false, added = true; + if (switchStatement.Block != null) { + foreach (var child in switchStatement.Block.Statements) { + var statement = child.Accept(this); + var caseLabel = statement as CaseLabel; + if (caseLabel != null) { + if (!lastWasCase) { + newSection = new SwitchSection(); + added = false; + } + newSection.AddChild(caseLabel, SwitchSection.CaseLabelRole); + lastWasCase = true; + } else { + if (lastWasCase) { + result.AddChild(newSection, SwitchStatement.SwitchSectionRole); + lastWasCase = false; + added = true; + } + newSection.AddChild((Statement)statement, Roles.EmbeddedStatement); + } + } + } + if (!added) + result.AddChild(newSection, SwitchStatement.SwitchSectionRole); + + if (location != null && location.Count > 3) { + result.AddChild(new CSharpTokenNode(Convert(location [3]), Roles.RBrace), Roles.RBrace); + } else { + // parser error, set end node to max value. + result.AddChild(new ErrorNode(), Roles.Error); + } + + return result; + } + + public override object Visit(SwitchLabel switchLabel) + { + var newLabel = new CaseLabel(); + if (!switchLabel.IsDefault) { + newLabel.AddChild(new CSharpTokenNode(Convert(switchLabel.Location), CaseLabel.CaseKeywordRole), CaseLabel.CaseKeywordRole); + if (switchLabel.Label != null) + newLabel.AddChild((Expression)switchLabel.Label.Accept(this), Roles.Expression); + var colonLocation = LocationsBag.GetLocations(switchLabel); + if (colonLocation != null) + newLabel.AddChild(new CSharpTokenNode(Convert(colonLocation [0]), Roles.Colon), Roles.Colon); + } else { + newLabel.AddChild(new CSharpTokenNode(Convert(switchLabel.Location), CaseLabel.DefaultKeywordRole), CaseLabel.DefaultKeywordRole); + newLabel.AddChild(new CSharpTokenNode(new TextLocation(switchLabel.Location.Row, switchLabel.Location.Column + "default".Length), Roles.Colon), Roles.Colon); + } + return newLabel; + } + + public override object Visit(Lock lockStatement) + { + var result = new LockStatement(); + var location = LocationsBag.GetLocations(lockStatement); + result.AddChild(new CSharpTokenNode(Convert(lockStatement.loc), LockStatement.LockKeywordRole), LockStatement.LockKeywordRole); + + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + if (lockStatement.Expr != null) + result.AddChild((Expression)lockStatement.Expr.Accept(this), Roles.Expression); + + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + if (lockStatement.Statement != null) + result.AddChild((Statement)lockStatement.Statement.Accept(this), Roles.EmbeddedStatement); + + return result; + } + + public override object Visit(Unchecked uncheckedStatement) + { + var result = new UncheckedStatement(); + result.AddChild(new CSharpTokenNode(Convert(uncheckedStatement.loc), UncheckedStatement.UncheckedKeywordRole), UncheckedStatement.UncheckedKeywordRole); + if (uncheckedStatement.Block != null) + result.AddChild((BlockStatement)uncheckedStatement.Block.Accept(this), Roles.Body); + return result; + } + + public override object Visit(Checked checkedStatement) + { + var result = new CheckedStatement(); + result.AddChild(new CSharpTokenNode(Convert(checkedStatement.loc), CheckedStatement.CheckedKeywordRole), CheckedStatement.CheckedKeywordRole); + if (checkedStatement.Block != null) + result.AddChild((BlockStatement)checkedStatement.Block.Accept(this), Roles.Body); + return result; + } + + public override object Visit(Unsafe unsafeStatement) + { + var result = new UnsafeStatement(); + result.AddChild(new CSharpTokenNode(Convert(unsafeStatement.loc), UnsafeStatement.UnsafeKeywordRole), UnsafeStatement.UnsafeKeywordRole); + if (unsafeStatement.Block != null) + result.AddChild((BlockStatement)unsafeStatement.Block.Accept(this), Roles.Body); + return result; + } + + public override object Visit(Fixed fixedStatement) + { + var result = new FixedStatement(); + var location = LocationsBag.GetLocations(fixedStatement); + + result.AddChild(new CSharpTokenNode(Convert(fixedStatement.loc), FixedStatement.FixedKeywordRole), FixedStatement.FixedKeywordRole); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + + if (fixedStatement.Variables != null) { + var blockVariableDeclaration = fixedStatement.Variables; + result.AddChild(ConvertToType(blockVariableDeclaration.TypeExpression), Roles.Type); + var varInit = new VariableInitializer(); + var initLocation = LocationsBag.GetLocations(blockVariableDeclaration); + varInit.AddChild(Identifier.Create(blockVariableDeclaration.Variable.Name, Convert(blockVariableDeclaration.Variable.Location)), Roles.Identifier); + if (blockVariableDeclaration.Initializer != null) { + if (initLocation != null) + varInit.AddChild(new CSharpTokenNode(Convert(initLocation [0]), Roles.Assign), Roles.Assign); + varInit.AddChild((Expression)blockVariableDeclaration.Initializer.Accept(this), Roles.Expression); + } + + result.AddChild(varInit, Roles.Variable); + + if (blockVariableDeclaration.Declarators != null) { + foreach (var decl in blockVariableDeclaration.Declarators) { + var loc = LocationsBag.GetLocations(decl); + var init = new VariableInitializer(); + if (loc != null && loc.Count > 0) + result.AddChild(new CSharpTokenNode(Convert(loc [0]), Roles.Comma), Roles.Comma); + init.AddChild(Identifier.Create(decl.Variable.Name, Convert(decl.Variable.Location)), Roles.Identifier); + if (decl.Initializer != null) { + if (loc != null && loc.Count > 1) + init.AddChild(new CSharpTokenNode(Convert(loc [1]), Roles.Assign), Roles.Assign); + init.AddChild((Expression)decl.Initializer.Accept(this), Roles.Expression); + } + result.AddChild(init, Roles.Variable); + } + } + } + + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + if (fixedStatement.Statement != null) + result.AddChild((Statement)fixedStatement.Statement.Accept(this), Roles.EmbeddedStatement); + return result; + } + + public override object Visit(TryFinally tryFinallyStatement) + { + TryCatchStatement result; + var location = LocationsBag.GetLocations(tryFinallyStatement); + + if (tryFinallyStatement.Stmt is TryCatch) { + result = (TryCatchStatement)tryFinallyStatement.Stmt.Accept(this); + } else { + result = new TryCatchStatement(); + result.AddChild(new CSharpTokenNode(Convert(tryFinallyStatement.loc), TryCatchStatement.TryKeywordRole), TryCatchStatement.TryKeywordRole); + if (tryFinallyStatement.Stmt != null) + result.AddChild((BlockStatement)tryFinallyStatement.Stmt.Accept(this), TryCatchStatement.TryBlockRole); + } + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), TryCatchStatement.FinallyKeywordRole), TryCatchStatement.FinallyKeywordRole); + if (tryFinallyStatement.Fini != null) + result.AddChild((BlockStatement)tryFinallyStatement.Fini.Accept(this), TryCatchStatement.FinallyBlockRole); + + return result; + } + + CatchClause ConvertCatch(Catch ctch) + { + var result = new CatchClause(); + var location = LocationsBag.GetLocations(ctch); + result.AddChild(new CSharpTokenNode(Convert(ctch.loc), CatchClause.CatchKeywordRole), CatchClause.CatchKeywordRole); + if (ctch.TypeExpression != null) { + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + + if (ctch.TypeExpression != null) + result.AddChild(ConvertToType(ctch.TypeExpression), Roles.Type); + if (ctch.Variable != null && !string.IsNullOrEmpty(ctch.Variable.Name)) + result.AddChild(Identifier.Create(ctch.Variable.Name, Convert(ctch.Variable.Location)), Roles.Identifier); + + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + } + + if (ctch.Block != null) + result.AddChild((BlockStatement)ctch.Block.Accept(this), Roles.Body); + + return result; + } + + public override object Visit(TryCatch tryCatchStatement) + { + var result = new TryCatchStatement(); + result.AddChild(new CSharpTokenNode(Convert(tryCatchStatement.loc), TryCatchStatement.TryKeywordRole), TryCatchStatement.TryKeywordRole); + if (tryCatchStatement.Block != null) + result.AddChild((BlockStatement)tryCatchStatement.Block.Accept(this), TryCatchStatement.TryBlockRole); + if (tryCatchStatement.Clauses != null) { + foreach (var ctch in tryCatchStatement.Clauses) { + result.AddChild(ConvertCatch(ctch), TryCatchStatement.CatchClauseRole); + } + } +// if (tryCatchStatement.General != null) +// result.AddChild (ConvertCatch (tryCatchStatement.General), TryCatchStatement.CatchClauseRole); + + return result; + } + + public override object Visit(Using usingStatement) + { + var result = new UsingStatement(); + var location = LocationsBag.GetLocations(usingStatement); + + result.AddChild(new CSharpTokenNode(Convert(usingStatement.loc), UsingStatement.UsingKeywordRole), UsingStatement.UsingKeywordRole); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + if (usingStatement.Expr != null) + result.AddChild((AstNode)usingStatement.Expr.Accept(this), UsingStatement.ResourceAcquisitionRole); + + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + + if (usingStatement.Statement != null) + result.AddChild((Statement)usingStatement.Statement.Accept(this), Roles.EmbeddedStatement); + return result; + } + + public override object Visit(Foreach foreachStatement) + { + var result = new ForeachStatement(); + + var location = LocationsBag.GetLocations(foreachStatement); + + result.AddChild(new CSharpTokenNode(Convert(foreachStatement.loc), ForeachStatement.ForeachKeywordRole), ForeachStatement.ForeachKeywordRole); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + + if (foreachStatement.TypeExpression != null) + result.AddChild(ConvertToType(foreachStatement.TypeExpression), Roles.Type); + + if (foreachStatement.Variable != null) + result.AddChild(Identifier.Create(foreachStatement.Variable.Name, Convert(foreachStatement.Variable.Location)), Roles.Identifier); + + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), ForeachStatement.InKeywordRole), ForeachStatement.InKeywordRole); + + if (foreachStatement.Expr != null) + result.AddChild((Expression)foreachStatement.Expr.Accept(this), Roles.Expression); + + if (location != null && location.Count > 2) + result.AddChild(new CSharpTokenNode(Convert(location [2]), Roles.RPar), Roles.RPar); + + if (foreachStatement.Statement != null) + result.AddChild((Statement)foreachStatement.Statement.Accept(this), Roles.EmbeddedStatement); + + return result; + } + + public override object Visit(Yield yieldStatement) + { + var result = new YieldReturnStatement(); + var location = LocationsBag.GetLocations(yieldStatement); + + result.AddChild(new CSharpTokenNode(Convert(yieldStatement.loc), YieldReturnStatement.YieldKeywordRole), YieldReturnStatement.YieldKeywordRole); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), YieldReturnStatement.ReturnKeywordRole), YieldReturnStatement.ReturnKeywordRole); + if (yieldStatement.Expr != null) + result.AddChild((Expression)yieldStatement.Expr.Accept(this), Roles.Expression); + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.Semicolon), Roles.Semicolon); + + return result; + } + + public override object Visit(YieldBreak yieldBreakStatement) + { + var result = new YieldBreakStatement(); + var location = LocationsBag.GetLocations(yieldBreakStatement); + result.AddChild(new CSharpTokenNode(Convert(yieldBreakStatement.loc), YieldBreakStatement.YieldKeywordRole), YieldBreakStatement.YieldKeywordRole); + if (location != null) { + result.AddChild(new CSharpTokenNode(Convert(location [0]), YieldBreakStatement.BreakKeywordRole), YieldBreakStatement.BreakKeywordRole); + if (location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.Semicolon), Roles.Semicolon); + } + return result; + } + + #endregion + + #region Expression + + public override object Visit(Mono.CSharp.Expression expression) + { + Console.WriteLine("Visit unknown expression:" + expression); + Console.WriteLine(Environment.StackTrace); + return null; + } + + public override object Visit(DefaultParameterValueExpression defaultParameterValueExpression) + { + return defaultParameterValueExpression.Child.Accept(this); + } + + public override object Visit(TypeExpression typeExpression) + { + return new TypeReferenceExpression(new PrimitiveType(keywordTable [(int)typeExpression.Type.BuiltinType], Convert(typeExpression.Location))); + } + + public override object Visit(LocalVariableReference localVariableReference) + { + return Identifier.Create(localVariableReference.Name, Convert(localVariableReference.Location)); + } + + public override object Visit(MemberAccess memberAccess) + { + Expression result; + var ind = memberAccess.LeftExpression as Indirection; + if (ind != null) { + result = new PointerReferenceExpression(); + result.AddChild((Expression)ind.Expr.Accept(this), Roles.TargetExpression); + result.AddChild(new CSharpTokenNode(Convert(ind.Location), PointerReferenceExpression.ArrowRole), PointerReferenceExpression.ArrowRole); + } else { + result = new MemberReferenceExpression(); + if (memberAccess.LeftExpression != null) { + var leftExpr = memberAccess.LeftExpression.Accept(this); + result.AddChild((Expression)leftExpr, Roles.TargetExpression); + } + var loc = LocationsBag.GetLocations(memberAccess); + + if (loc != null) { + result.AddChild(new CSharpTokenNode(Convert(loc [0]), Roles.Dot), Roles.Dot); + } + } + + result.AddChild(Identifier.Create(memberAccess.Name, Convert(memberAccess.Location)), Roles.Identifier); + + AddTypeArguments(result, memberAccess); + return result; + } + + public override object Visit(QualifiedAliasMember qualifiedAliasMember) + { + var result = new MemberType(); + result.Target = new SimpleType(qualifiedAliasMember.alias, Convert(qualifiedAliasMember.Location)); + result.IsDoubleColon = true; + var location = LocationsBag.GetLocations(qualifiedAliasMember); + if (location != null && location.Count > 0) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.DoubleColon), Roles.DoubleColon); + + AddTypeArguments(result, qualifiedAliasMember); + result.AddChild(Identifier.Create(qualifiedAliasMember.Name, location != null && location.Count > 1 ? Convert(location [1]) : TextLocation.Empty), Roles.Identifier); + return new TypeReferenceExpression { Type = result }; + } + + public override object Visit(Constant constant) + { + if (constant.GetValue() == null) + return new NullReferenceExpression(Convert(constant.Location)); + string literalValue; + var literalConstant = constant as ILiteralConstant; + literalValue = literalConstant != null ? new string(literalConstant.ParsedValue) : constant.GetValueAsLiteral(); + object val = constant.GetValue(); + if (val is bool) + literalValue = (bool)val ? "true" : "false"; + var result = new PrimitiveExpression(val, Convert(constant.Location), literalValue); + return result; + } + + public override object Visit(SimpleName simpleName) + { + var result = new IdentifierExpression(); + result.AddChild(Identifier.Create(simpleName.Name, Convert(simpleName.Location)), Roles.Identifier); + AddTypeArguments(result, simpleName); + return result; + } + + public override object Visit(BooleanExpression booleanExpression) + { + return booleanExpression.Expr.Accept(this); + } + + public override object Visit(Mono.CSharp.ParenthesizedExpression parenthesizedExpression) + { + var result = new ParenthesizedExpression(); + var location = LocationsBag.GetLocations(parenthesizedExpression); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + if (parenthesizedExpression.Expr != null) + result.AddChild((Expression)parenthesizedExpression.Expr.Accept(this), Roles.Expression); + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + return result; + } + + public override object Visit(Unary unaryExpression) + { + var result = new UnaryOperatorExpression(); + switch (unaryExpression.Oper) { + case Unary.Operator.UnaryPlus: + result.Operator = UnaryOperatorType.Plus; + break; + case Unary.Operator.UnaryNegation: + result.Operator = UnaryOperatorType.Minus; + break; + case Unary.Operator.LogicalNot: + result.Operator = UnaryOperatorType.Not; + break; + case Unary.Operator.OnesComplement: + result.Operator = UnaryOperatorType.BitNot; + break; + case Unary.Operator.AddressOf: + result.Operator = UnaryOperatorType.AddressOf; + break; + } + var r = UnaryOperatorExpression.GetOperatorRole(result.Operator); + result.AddChild(new CSharpTokenNode(Convert(unaryExpression.Location), r), r); + if (unaryExpression.Expr != null) + result.AddChild((Expression)unaryExpression.Expr.Accept(this), Roles.Expression); + return result; + } + + public override object Visit(UnaryMutator unaryMutatorExpression) + { + var result = new UnaryOperatorExpression(); + if (unaryMutatorExpression.Expr == null) + return result; + var expression = (Expression)unaryMutatorExpression.Expr.Accept(this); + switch (unaryMutatorExpression.UnaryMutatorMode) { + case UnaryMutator.Mode.PostDecrement: + result.Operator = UnaryOperatorType.PostDecrement; + result.AddChild(expression, Roles.Expression); + result.AddChild(new CSharpTokenNode(Convert(unaryMutatorExpression.Location), UnaryOperatorExpression.DecrementRole), UnaryOperatorExpression.DecrementRole); + break; + case UnaryMutator.Mode.PostIncrement: + result.Operator = UnaryOperatorType.PostIncrement; + result.AddChild(expression, Roles.Expression); + result.AddChild(new CSharpTokenNode(Convert(unaryMutatorExpression.Location), UnaryOperatorExpression.IncrementRole), UnaryOperatorExpression.IncrementRole); + break; + + case UnaryMutator.Mode.PreIncrement: + result.Operator = UnaryOperatorType.Increment; + result.AddChild(new CSharpTokenNode(Convert(unaryMutatorExpression.Location), UnaryOperatorExpression.IncrementRole), UnaryOperatorExpression.IncrementRole); + result.AddChild(expression, Roles.Expression); + break; + case UnaryMutator.Mode.PreDecrement: + result.Operator = UnaryOperatorType.Decrement; + result.AddChild(new CSharpTokenNode(Convert(unaryMutatorExpression.Location), UnaryOperatorExpression.DecrementRole), UnaryOperatorExpression.DecrementRole); + result.AddChild(expression, Roles.Expression); + break; + } + + return result; + } + + public override object Visit(Indirection indirectionExpression) + { + var result = new UnaryOperatorExpression(); + result.Operator = UnaryOperatorType.Dereference; + result.AddChild(new CSharpTokenNode(Convert(indirectionExpression.Location), UnaryOperatorExpression.DereferenceRole), UnaryOperatorExpression.DereferenceRole); + if (indirectionExpression.Expr != null) + result.AddChild((Expression)indirectionExpression.Expr.Accept(this), Roles.Expression); + return result; + } + + public override object Visit(Is isExpression) + { + var result = new IsExpression(); + if (isExpression.Expr != null) + result.AddChild((Expression)isExpression.Expr.Accept(this), Roles.Expression); + result.AddChild(new CSharpTokenNode(Convert(isExpression.Location), IsExpression.IsKeywordRole), IsExpression.IsKeywordRole); + + if (isExpression.ProbeType != null) + result.AddChild(ConvertToType(isExpression.ProbeType), Roles.Type); + return result; + } + + public override object Visit(As asExpression) + { + var result = new AsExpression(); + if (asExpression.Expr != null) + result.AddChild((Expression)asExpression.Expr.Accept(this), Roles.Expression); + result.AddChild(new CSharpTokenNode(Convert(asExpression.Location), AsExpression.AsKeywordRole), AsExpression.AsKeywordRole); + if (asExpression.ProbeType != null) + result.AddChild(ConvertToType(asExpression.ProbeType), Roles.Type); + return result; + } + + public override object Visit(Cast castExpression) + { + var result = new CastExpression(); + var location = LocationsBag.GetLocations(castExpression); + + result.AddChild(new CSharpTokenNode(Convert(castExpression.Location), Roles.LPar), Roles.LPar); + if (castExpression.TargetType != null) + result.AddChild(ConvertToType(castExpression.TargetType), Roles.Type); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.RPar), Roles.RPar); + if (castExpression.Expr != null) + result.AddChild((Expression)castExpression.Expr.Accept(this), Roles.Expression); + return result; + } + + public override object Visit(ComposedCast composedCast) + { + var result = new ComposedType(); + result.AddChild(ConvertToType(composedCast.Left), Roles.Type); + + var spec = composedCast.Spec; + while (spec != null) { + if (spec.IsNullable) { + result.AddChild(new CSharpTokenNode(Convert(spec.Location), ComposedType.NullableRole), ComposedType.NullableRole); + } else if (spec.IsPointer) { + result.AddChild(new CSharpTokenNode(Convert(spec.Location), ComposedType.PointerRole), ComposedType.PointerRole); + } else { + var aSpec = new ArraySpecifier(); + aSpec.AddChild(new CSharpTokenNode(Convert(spec.Location), Roles.LBracket), Roles.LBracket); + var location = LocationsBag.GetLocations(spec); + if (location != null) + aSpec.AddChild(new CSharpTokenNode(Convert(spec.Location), Roles.RBracket), Roles.RBracket); + result.AddChild(aSpec, ComposedType.ArraySpecifierRole); + } + spec = spec.Next; + } + + return result; + } + + public override object Visit(Mono.CSharp.DefaultValueExpression defaultValueExpression) + { + var result = new DefaultValueExpression(); + result.AddChild(new CSharpTokenNode(Convert(defaultValueExpression.Location), DefaultValueExpression.DefaultKeywordRole), DefaultValueExpression.DefaultKeywordRole); + var location = LocationsBag.GetLocations(defaultValueExpression); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + result.AddChild(ConvertToType(defaultValueExpression.Expr), Roles.Type); + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + return result; + } + + public override object Visit(Binary binaryExpression) + { + var result = new BinaryOperatorExpression(); + switch (binaryExpression.Oper) { + case Binary.Operator.Multiply: + result.Operator = BinaryOperatorType.Multiply; + break; + case Binary.Operator.Division: + result.Operator = BinaryOperatorType.Divide; + break; + case Binary.Operator.Modulus: + result.Operator = BinaryOperatorType.Modulus; + break; + case Binary.Operator.Addition: + result.Operator = BinaryOperatorType.Add; + break; + case Binary.Operator.Subtraction: + result.Operator = BinaryOperatorType.Subtract; + break; + case Binary.Operator.LeftShift: + result.Operator = BinaryOperatorType.ShiftLeft; + break; + case Binary.Operator.RightShift: + result.Operator = BinaryOperatorType.ShiftRight; + break; + case Binary.Operator.LessThan: + result.Operator = BinaryOperatorType.LessThan; + break; + case Binary.Operator.GreaterThan: + result.Operator = BinaryOperatorType.GreaterThan; + break; + case Binary.Operator.LessThanOrEqual: + result.Operator = BinaryOperatorType.LessThanOrEqual; + break; + case Binary.Operator.GreaterThanOrEqual: + result.Operator = BinaryOperatorType.GreaterThanOrEqual; + break; + case Binary.Operator.Equality: + result.Operator = BinaryOperatorType.Equality; + break; + case Binary.Operator.Inequality: + result.Operator = BinaryOperatorType.InEquality; + break; + case Binary.Operator.BitwiseAnd: + result.Operator = BinaryOperatorType.BitwiseAnd; + break; + case Binary.Operator.ExclusiveOr: + result.Operator = BinaryOperatorType.ExclusiveOr; + break; + case Binary.Operator.BitwiseOr: + result.Operator = BinaryOperatorType.BitwiseOr; + break; + case Binary.Operator.LogicalAnd: + result.Operator = BinaryOperatorType.ConditionalAnd; + break; + case Binary.Operator.LogicalOr: + result.Operator = BinaryOperatorType.ConditionalOr; + break; + } + + if (binaryExpression.Left != null) + result.AddChild((Expression)binaryExpression.Left.Accept(this), BinaryOperatorExpression.LeftRole); + var location = LocationsBag.GetLocations(binaryExpression); + if (location != null) { + var r = BinaryOperatorExpression.GetOperatorRole(result.Operator); + result.AddChild(new CSharpTokenNode(Convert(location [0]), r), r); + } + if (binaryExpression.Right != null) + result.AddChild((Expression)binaryExpression.Right.Accept(this), BinaryOperatorExpression.RightRole); + return result; + } + + public override object Visit(Mono.CSharp.Nullable.NullCoalescingOperator nullCoalescingOperator) + { + var result = new BinaryOperatorExpression(); + result.Operator = BinaryOperatorType.NullCoalescing; + if (nullCoalescingOperator.LeftExpression != null) + result.AddChild((Expression)nullCoalescingOperator.LeftExpression.Accept(this), BinaryOperatorExpression.LeftRole); + var location = LocationsBag.GetLocations(nullCoalescingOperator); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), BinaryOperatorExpression.NullCoalescingRole), BinaryOperatorExpression.NullCoalescingRole); + if (nullCoalescingOperator.RightExpression != null) + result.AddChild((Expression)nullCoalescingOperator.RightExpression.Accept(this), BinaryOperatorExpression.RightRole); + return result; + } + + public override object Visit(Conditional conditionalExpression) + { + var result = new ConditionalExpression(); + + if (conditionalExpression.Expr != null) + result.AddChild((Expression)conditionalExpression.Expr.Accept(this), Roles.Condition); + var location = LocationsBag.GetLocations(conditionalExpression); + + result.AddChild(new CSharpTokenNode(Convert(conditionalExpression.Location), ConditionalExpression.QuestionMarkRole), ConditionalExpression.QuestionMarkRole); + if (conditionalExpression.TrueExpr != null) + result.AddChild((Expression)conditionalExpression.TrueExpr.Accept(this), ConditionalExpression.TrueRole); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), ConditionalExpression.ColonRole), ConditionalExpression.ColonRole); + if (conditionalExpression.FalseExpr != null) + result.AddChild((Expression)conditionalExpression.FalseExpr.Accept(this), ConditionalExpression.FalseRole); + return result; + } + + void AddParameter(AstNode parent, AParametersCollection parameters) + { + if (parameters == null) + return; + var paramLocation = LocationsBag.GetLocations(parameters); + + for (int i = 0; i < parameters.Count; i++) { + var p = (Parameter)parameters.FixedParameters [i]; + if (p == null) + continue; + var location = LocationsBag.GetLocations(p); + var parameterDeclarationExpression = new ParameterDeclaration(); + AddAttributeSection(parameterDeclarationExpression, p); + switch (p.ModFlags) { + case Parameter.Modifier.OUT: + parameterDeclarationExpression.ParameterModifier = ParameterModifier.Out; + if (location != null) + parameterDeclarationExpression.AddChild(new CSharpTokenNode(Convert(location [0]), ParameterDeclaration.OutModifierRole), ParameterDeclaration.OutModifierRole); + break; + case Parameter.Modifier.REF: + parameterDeclarationExpression.ParameterModifier = ParameterModifier.Ref; + if (location != null) + parameterDeclarationExpression.AddChild(new CSharpTokenNode(Convert(location [0]), ParameterDeclaration.RefModifierRole), ParameterDeclaration.RefModifierRole); + break; + case Parameter.Modifier.PARAMS: + parameterDeclarationExpression.ParameterModifier = ParameterModifier.Params; + if (location != null) + parameterDeclarationExpression.AddChild(new CSharpTokenNode(Convert(location [0]), ParameterDeclaration.ParamsModifierRole), ParameterDeclaration.ParamsModifierRole); + break; + default: + if (p.HasExtensionMethodModifier) { + parameterDeclarationExpression.ParameterModifier = ParameterModifier.This; + if (location != null) { + parameterDeclarationExpression.AddChild(new CSharpTokenNode(Convert(location [0]), ParameterDeclaration.ThisModifierRole), ParameterDeclaration.ThisModifierRole); + } + } + break; + } + if (p.TypeExpression != null) // lambdas may have no types (a, b) => ... + parameterDeclarationExpression.AddChild(ConvertToType(p.TypeExpression), Roles.Type); + if (p.Name != null) + parameterDeclarationExpression.AddChild(Identifier.Create(p.Name, Convert(p.Location)), Roles.Identifier); + if (p.HasDefaultValue) { + if (location != null && location.Count > 1) + parameterDeclarationExpression.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.Assign), Roles.Assign); + parameterDeclarationExpression.AddChild((Expression)p.DefaultValue.Accept(this), Roles.Expression); + } + parent.AddChild(parameterDeclarationExpression, Roles.Parameter); + if (paramLocation != null && i < paramLocation.Count) { + parent.AddChild(new CSharpTokenNode(Convert(paramLocation [i]), Roles.Comma), Roles.Comma); + } + } + } + + void AddTypeParameters(AstNode parent, MemberName memberName) + { + if (memberName == null || memberName.TypeParameters == null) + return; + var chevronLocs = LocationsBag.GetLocations(memberName.TypeParameters); + if (chevronLocs != null) + parent.AddChild(new CSharpTokenNode(Convert(chevronLocs [chevronLocs.Count - 2]), Roles.LChevron), Roles.LChevron); + for (int i = 0; i < memberName.TypeParameters.Count; i++) { + if (chevronLocs != null && i > 0 && i - 1 < chevronLocs.Count) + parent.AddChild(new CSharpTokenNode(Convert(chevronLocs [i - 1]), Roles.Comma), Roles.Comma); + var arg = memberName.TypeParameters [i]; + if (arg == null) + continue; + var tp = new TypeParameterDeclaration(); + + List varianceLocation; + switch (arg.Variance) { + case Variance.Contravariant: + tp.Variance = VarianceModifier.Contravariant; + varianceLocation = LocationsBag.GetLocations(arg); + if (varianceLocation != null) + tp.AddChild(new CSharpTokenNode(Convert(varianceLocation [0]), TypeParameterDeclaration.InVarianceKeywordRole), TypeParameterDeclaration.InVarianceKeywordRole); + break; + case Variance.Covariant: + tp.Variance = VarianceModifier.Covariant; + varianceLocation = LocationsBag.GetLocations(arg); + if (varianceLocation != null) + tp.AddChild(new CSharpTokenNode(Convert(varianceLocation [0]), TypeParameterDeclaration.OutVarianceKeywordRole), TypeParameterDeclaration.OutVarianceKeywordRole); + break; + default: + tp.Variance = VarianceModifier.Invariant; + break; + + } + + AddAttributeSection(tp, arg.OptAttributes); + + switch (arg.Variance) { + case Variance.Covariant: + tp.Variance = VarianceModifier.Covariant; + break; + case Variance.Contravariant: + tp.Variance = VarianceModifier.Contravariant; + break; + } + tp.AddChild(Identifier.Create(arg.Name, Convert(arg.Location)), Roles.Identifier); + parent.AddChild(tp, Roles.TypeParameter); + } + if (chevronLocs != null) + parent.AddChild(new CSharpTokenNode(Convert(chevronLocs [chevronLocs.Count - 1]), Roles.RChevron), Roles.RChevron); + } + + void AddTypeArguments(AstNode parent, MemberName memberName) + { + if (memberName == null || memberName.TypeParameters == null) + return; + var chevronLocs = LocationsBag.GetLocations(memberName.TypeParameters); + if (chevronLocs != null) + parent.AddChild(new CSharpTokenNode(Convert(chevronLocs [chevronLocs.Count - 2]), Roles.LChevron), Roles.LChevron); + + for (int i = 0; i < memberName.TypeParameters.Count; i++) { + var arg = memberName.TypeParameters [i]; + if (arg == null) + continue; + parent.AddChild(ConvertToType(arg), Roles.TypeArgument); + if (chevronLocs != null && i < chevronLocs.Count - 2) + parent.AddChild(new CSharpTokenNode(Convert(chevronLocs [i]), Roles.Comma), Roles.Comma); + } + + if (chevronLocs != null) + parent.AddChild(new CSharpTokenNode(Convert(chevronLocs [chevronLocs.Count - 1]), Roles.RChevron), Roles.RChevron); + } + + void AddTypeArguments(AstNode parent, ATypeNameExpression memberName) + { + if (memberName == null || !memberName.HasTypeArguments) + return; + var chevronLocs = LocationsBag.GetLocations(memberName.TypeArguments); + if (chevronLocs != null) + parent.AddChild(new CSharpTokenNode(Convert(chevronLocs [chevronLocs.Count - 2]), Roles.LChevron), Roles.LChevron); + + for (int i = 0; i < memberName.TypeArguments.Count; i++) { + var arg = memberName.TypeArguments.Args [i]; + if (arg == null) + continue; + parent.AddChild(ConvertToType(arg), Roles.TypeArgument); + if (chevronLocs != null && i < chevronLocs.Count - 2) + parent.AddChild(new CSharpTokenNode(Convert(chevronLocs [i]), Roles.Comma), Roles.Comma); + } + + if (chevronLocs != null) + parent.AddChild(new CSharpTokenNode(Convert(chevronLocs [chevronLocs.Count - 1]), Roles.RChevron), Roles.RChevron); + } + + void AddConstraints(AstNode parent, TypeParameters d) + { + if (d == null) + return; + for (int i = 0; i < d.Count; i++) { + var typeParameter = d [i]; + if (typeParameter == null) + continue; + var c = typeParameter.Constraints; + if (c == null) + continue; + var location = LocationsBag.GetLocations(c); + var constraint = new Constraint(); + constraint.AddChild(new CSharpTokenNode(Convert(c.Location), Roles.WhereKeyword), Roles.WhereKeyword); + constraint.AddChild(new SimpleType(Identifier.Create(c.TypeParameter.Value, Convert(c.TypeParameter.Location))), Roles.ConstraintTypeParameter); + if (location != null) + constraint.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.Colon), Roles.Colon); + var commaLocs = LocationsBag.GetLocations(c.ConstraintExpressions); + int curComma = 0; + if (c.ConstraintExpressions != null) { + foreach (var expr in c.ConstraintExpressions) { + constraint.AddChild(ConvertToType(expr), Roles.BaseType); + var sce = expr as SpecialContraintExpr; + if (sce != null) { + switch (sce.Constraint) { + case SpecialConstraint.Class: + break; + case SpecialConstraint.Struct: + break; + case SpecialConstraint.Constructor: + var bl = LocationsBag.GetLocations(expr); + if (bl != null) { + constraint.AddChild(new CSharpTokenNode(Convert(bl [0]), Roles.LPar), Roles.LPar); + constraint.AddChild(new CSharpTokenNode(Convert(bl [1]), Roles.RPar), Roles.RPar); + } + break; + } + } + + if (commaLocs != null && curComma < commaLocs.Count) + constraint.AddChild(new CSharpTokenNode(Convert(commaLocs [curComma++]), Roles.Comma), Roles.Comma); + } + } + + // We need to sort the constraints by position; as they might be in a different order than the type parameters + AstNode prevSibling = parent.LastChild; + while (prevSibling.StartLocation > constraint.StartLocation && prevSibling.PrevSibling != null) + prevSibling = prevSibling.PrevSibling; + parent.InsertChildAfter(prevSibling, constraint, Roles.Constraint); + } + } + + Expression ConvertArgument(Argument arg) + { + var na = arg as NamedArgument; + if (na != null) { + var newArg = new NamedArgumentExpression(); + newArg.AddChild(Identifier.Create(na.Name, Convert(na.Location)), Roles.Identifier); + + var loc = LocationsBag.GetLocations(na); + if (loc != null) + newArg.AddChild(new CSharpTokenNode(Convert(loc [0]), Roles.Colon), Roles.Colon); + + if (arg.ArgType == Argument.AType.Out || arg.ArgType == Argument.AType.Ref) { + var direction = new DirectionExpression(); + direction.FieldDirection = arg.ArgType == Argument.AType.Out ? FieldDirection.Out : FieldDirection.Ref; + var argLocation = LocationsBag.GetLocations(arg); + if (argLocation != null) { + var r = arg.ArgType == Argument.AType.Out ? DirectionExpression.OutKeywordRole : DirectionExpression.RefKeywordRole; + direction.AddChild(new CSharpTokenNode(Convert(argLocation [0]), r), r); + } + direction.AddChild((Expression)arg.Expr.Accept(this), Roles.Expression); + newArg.AddChild(direction, Roles.Expression); + } else { + newArg.AddChild(na.Expr != null ? (Expression)na.Expr.Accept(this) : new ErrorExpression("Named argument expression parse error"), Roles.Expression); + } + return newArg; + } + + if (arg.ArgType == Argument.AType.Out || arg.ArgType == Argument.AType.Ref) { + var direction = new DirectionExpression(); + direction.FieldDirection = arg.ArgType == Argument.AType.Out ? FieldDirection.Out : FieldDirection.Ref; + var argLocation = LocationsBag.GetLocations(arg); + if (argLocation != null) { + var r = arg.ArgType == Argument.AType.Out ? DirectionExpression.OutKeywordRole : DirectionExpression.RefKeywordRole; + direction.AddChild(new CSharpTokenNode(Convert(argLocation [0]), r), r); + } + direction.AddChild((Expression)arg.Expr.Accept(this), Roles.Expression); + return direction; + } + + return (Expression)arg.Expr.Accept(this); + } + + void AddArguments(AstNode parent, Arguments args) + { + if (args == null) + return; + + var commaLocations = LocationsBag.GetLocations(args); + for (int i = 0; i < args.Count; i++) { + parent.AddChild(ConvertArgument(args [i]), Roles.Argument); + if (commaLocations != null && i < commaLocations.Count) { + parent.AddChild(new CSharpTokenNode(Convert(commaLocations [i]), Roles.Comma), Roles.Comma); + } + } + if (commaLocations != null && commaLocations.Count > args.Count) + parent.AddChild(new CSharpTokenNode(Convert(commaLocations [args.Count]), Roles.Comma), Roles.Comma); + } + + public override object Visit(Invocation invocationExpression) + { + var result = new InvocationExpression(); + var location = LocationsBag.GetLocations(invocationExpression); + if (invocationExpression.Exp != null) + result.AddChild((Expression)invocationExpression.Exp.Accept(this), Roles.TargetExpression); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + AddArguments(result, invocationExpression.Arguments); + + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + return result; + } + + public override object Visit(New newExpression) + { + var result = new ObjectCreateExpression(); + var location = LocationsBag.GetLocations(newExpression); + result.AddChild(new CSharpTokenNode(Convert(newExpression.Location), ObjectCreateExpression.NewKeywordRole), ObjectCreateExpression.NewKeywordRole); + + if (newExpression.TypeRequested != null) + result.AddChild(ConvertToType(newExpression.TypeRequested), Roles.Type); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + AddArguments(result, newExpression.Arguments); + + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + + return result; + } + + public override object Visit(NewAnonymousType newAnonymousType) + { + var result = new AnonymousTypeCreateExpression(); + var location = LocationsBag.GetLocations(newAnonymousType); + result.AddChild(new CSharpTokenNode(Convert(newAnonymousType.Location), ObjectCreateExpression.NewKeywordRole), ObjectCreateExpression.NewKeywordRole); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LBrace), Roles.LBrace); + if (newAnonymousType.Parameters != null) { + foreach (var par in newAnonymousType.Parameters) { + if (par == null) + continue; + var parLocation = LocationsBag.GetLocations(par); + + if (parLocation == null) { + if (par.Expr != null) + result.AddChild((Expression)par.Expr.Accept(this), Roles.Expression); + } else { + var namedExpression = new NamedExpression(); + namedExpression.AddChild(Identifier.Create(par.Name, Convert(par.Location)), Roles.Identifier); + namedExpression.AddChild(new CSharpTokenNode(Convert(parLocation [0]), Roles.Assign), Roles.Assign); + if (par.Expr != null) + namedExpression.AddChild((Expression)par.Expr.Accept(this), Roles.Expression); + result.AddChild(namedExpression, Roles.Expression); + } + } + } + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RBrace), Roles.RBrace); + return result; + } + + ArrayInitializerExpression ConvertCollectionOrObjectInitializers(CollectionOrObjectInitializers minit) + { + if (minit == null) + return null; + var init = new ArrayInitializerExpression(); + AddConvertCollectionOrObjectInitializers(init, minit); + return init; + } + + void AddConvertCollectionOrObjectInitializers(Expression init, CollectionOrObjectInitializers minit) + { + var initLoc = LocationsBag.GetLocations(minit); + var commaLoc = LocationsBag.GetLocations(minit.Initializers); + int curComma = 0; + init.AddChild(new CSharpTokenNode(Convert(minit.Location), Roles.LBrace), Roles.LBrace); + foreach (var expr in minit.Initializers) { + var collectionInit = expr as CollectionElementInitializer; + if (collectionInit != null) { + AstNode parent; + // For ease of use purposes in the resolver the ast representation + // of { a, b, c } is { {a}, {b}, {c} } - but the generated ArrayInitializerExpression + // can be identified by expr.IsSingleElement. + if (!collectionInit.IsSingle) { + parent = new ArrayInitializerExpression(); + parent.AddChild(new CSharpTokenNode(Convert(collectionInit.Location), Roles.LBrace), Roles.LBrace); + } else { + parent = ArrayInitializerExpression.CreateSingleElementInitializer(); + } + + if (collectionInit.Arguments != null) { + for (int i = 0; i < collectionInit.Arguments.Count; i++) { + var arg = collectionInit.Arguments [i] as CollectionElementInitializer.ElementInitializerArgument; + if (arg == null || arg.Expr == null) + continue; + parent.AddChild( + (Expression)arg.Expr.Accept(this), + Roles.Expression + ); + } + } + + if (!collectionInit.IsSingle) { + var braceLocs = LocationsBag.GetLocations(expr); + if (braceLocs != null) + parent.AddChild(new CSharpTokenNode(Convert(braceLocs [0]), Roles.RBrace), Roles.RBrace); + } + init.AddChild((ArrayInitializerExpression)parent, Roles.Expression); + } else { + var eleInit = expr as ElementInitializer; + if (eleInit != null) { + var nexpr = new NamedExpression(); + nexpr.AddChild( + Identifier.Create(eleInit.Name, Convert(eleInit.Location)), + Roles.Identifier + ); + var assignLoc = LocationsBag.GetLocations(eleInit); + if (assignLoc != null) + nexpr.AddChild(new CSharpTokenNode(Convert(assignLoc [0]), Roles.Assign), Roles.Assign); + if (eleInit.Source != null) { + var colInit = eleInit.Source as CollectionOrObjectInitializers; + if (colInit != null) { + var arrInit = new ArrayInitializerExpression(); + AddConvertCollectionOrObjectInitializers( + arrInit, + colInit + ); + nexpr.AddChild(arrInit, Roles.Expression); + } else { + nexpr.AddChild((Expression)eleInit.Source.Accept(this), Roles.Expression); + } + } + + init.AddChild(nexpr, Roles.Expression); + } + } + if (commaLoc != null && curComma < commaLoc.Count) + init.AddChild(new CSharpTokenNode(Convert(commaLoc [curComma++]), Roles.Comma), Roles.Comma); + } + + if (initLoc != null) { + if (initLoc.Count == 2) // optional comma + init.AddChild(new CSharpTokenNode(Convert(initLoc [0]), Roles.Comma), Roles.Comma); + init.AddChild(new CSharpTokenNode(Convert(initLoc [initLoc.Count - 1]), Roles.RBrace), Roles.RBrace); + } + } + + public override object Visit(NewInitialize newInitializeExpression) + { + var result = new ObjectCreateExpression(); + result.AddChild(new CSharpTokenNode(Convert(newInitializeExpression.Location), ObjectCreateExpression.NewKeywordRole), ObjectCreateExpression.NewKeywordRole); + + if (newInitializeExpression.TypeRequested != null) + result.AddChild(ConvertToType(newInitializeExpression.TypeRequested), Roles.Type); + + var location = LocationsBag.GetLocations(newInitializeExpression); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + AddArguments(result, newInitializeExpression.Arguments); + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + + var init = ConvertCollectionOrObjectInitializers(newInitializeExpression.Initializers); + if (init != null) + result.AddChild(init, ObjectCreateExpression.InitializerRole); + + return result; + } + + public override object Visit(ArrayCreation arrayCreationExpression) + { + var result = new ArrayCreateExpression(); + + var location = LocationsBag.GetLocations(arrayCreationExpression); + result.AddChild(new CSharpTokenNode(Convert(arrayCreationExpression.Location), ArrayCreateExpression.NewKeywordRole), ArrayCreateExpression.NewKeywordRole); + if (arrayCreationExpression.TypeExpression != null) + result.AddChild(ConvertToType(arrayCreationExpression.TypeExpression), Roles.Type); + + var next = arrayCreationExpression.Rank; + if (arrayCreationExpression.Arguments != null) { + // skip first array rank. + next = next.Next; + + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LBracket), Roles.LBracket); + + var commaLocations = LocationsBag.GetLocations(arrayCreationExpression.Arguments); + for (int i = 0; i < arrayCreationExpression.Arguments.Count; i++) { + var arg = arrayCreationExpression.Arguments [i]; + if (arg != null) + result.AddChild((Expression)arg.Accept(this), Roles.Argument); + if (commaLocations != null && i < commaLocations.Count) + result.AddChild(new CSharpTokenNode(Convert(commaLocations [i]), Roles.Comma), Roles.Comma); + } + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RBracket), Roles.RBracket); + + } + + while (next != null) { + var spec = new ArraySpecifier(next.Dimension); + var loc = LocationsBag.GetLocations(next); + spec.AddChild(new CSharpTokenNode(Convert(next.Location), Roles.LBracket), Roles.LBracket); + result.AddChild(spec, ArrayCreateExpression.AdditionalArraySpecifierRole); + if (loc != null) + result.AddChild(new CSharpTokenNode(Convert(loc [0]), Roles.RBracket), Roles.RBracket); + next = next.Next; + } + + if (arrayCreationExpression.Initializers != null) { + var initLocation = LocationsBag.GetLocations(arrayCreationExpression.Initializers); + var initializer = new ArrayInitializerExpression(); + + initializer.AddChild(new CSharpTokenNode(Convert(arrayCreationExpression.Initializers.Location), Roles.LBrace), Roles.LBrace); + var commaLocations = LocationsBag.GetLocations(arrayCreationExpression.Initializers.Elements); + for (int i = 0; i < arrayCreationExpression.Initializers.Count; i++) { + var init = arrayCreationExpression.Initializers [i]; + if (init == null) + continue; + initializer.AddChild((Expression)init.Accept(this), Roles.Expression); + if (commaLocations != null && i < commaLocations.Count) { + initializer.AddChild(new CSharpTokenNode(Convert(commaLocations [i]), Roles.Comma), Roles.Comma); + } + } + if (initLocation != null) { + if (initLocation.Count == 2) // optional comma + initializer.AddChild(new CSharpTokenNode(Convert(initLocation [0]), Roles.Comma), Roles.Comma); + initializer.AddChild(new CSharpTokenNode(Convert(initLocation [initLocation.Count - 1]), Roles.RBrace), Roles.RBrace); + } + result.AddChild(initializer, ArrayCreateExpression.InitializerRole); + } + + return result; + } + + public override object Visit(This thisExpression) + { + var result = new ThisReferenceExpression(); + result.Location = Convert(thisExpression.Location); + return result; + } + + public override object Visit(ArglistAccess argListAccessExpression) + { + var result = new UndocumentedExpression { + UndocumentedExpressionType = UndocumentedExpressionType.ArgListAccess + }; + result.AddChild(new CSharpTokenNode(Convert(argListAccessExpression.Location), UndocumentedExpression.ArglistKeywordRole), UndocumentedExpression.ArglistKeywordRole); + return result; + } + + #region Undocumented expressions + + public override object Visit(Arglist argListExpression) + { + var result = new UndocumentedExpression { UndocumentedExpressionType = UndocumentedExpressionType.ArgList }; + result.AddChild(new CSharpTokenNode(Convert(argListExpression.Location), UndocumentedExpression.ArglistKeywordRole), UndocumentedExpression.ArglistKeywordRole); + var location = LocationsBag.GetLocations(argListExpression); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + + AddArguments(result, argListExpression.Arguments); + + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + return result; + } + + public override object Visit(MakeRefExpr makeRefExpr) + { + var result = new UndocumentedExpression { UndocumentedExpressionType = UndocumentedExpressionType.MakeRef }; + result.AddChild(new CSharpTokenNode(Convert(makeRefExpr.Location), UndocumentedExpression.MakerefKeywordRole), UndocumentedExpression.MakerefKeywordRole); + var location = LocationsBag.GetLocations(makeRefExpr); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + if (makeRefExpr.Expr != null) + result.AddChild((Expression)makeRefExpr.Expr.Accept(this), Roles.Argument); + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + return result; + } + + public override object Visit(RefTypeExpr refTypeExpr) + { + var result = new UndocumentedExpression { UndocumentedExpressionType = UndocumentedExpressionType.RefType }; + result.AddChild(new CSharpTokenNode(Convert(refTypeExpr.Location), UndocumentedExpression.ReftypeKeywordRole), UndocumentedExpression.ReftypeKeywordRole); + var location = LocationsBag.GetLocations(refTypeExpr); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + + if (refTypeExpr.Expr != null) + result.AddChild((Expression)refTypeExpr.Expr.Accept(this), Roles.Argument); + + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + return result; + } + + public override object Visit(RefValueExpr refValueExpr) + { + var result = new UndocumentedExpression { UndocumentedExpressionType = UndocumentedExpressionType.RefValue }; + result.AddChild(new CSharpTokenNode(Convert(refValueExpr.Location), UndocumentedExpression.RefvalueKeywordRole), UndocumentedExpression.RefvalueKeywordRole); + var location = LocationsBag.GetLocations(refValueExpr); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + + + if (refValueExpr.Expr != null) + result.AddChild((Expression)refValueExpr.Expr.Accept(this), Roles.Argument); + + if (refValueExpr.FullNamedExpression != null) + result.AddChild((Expression)refValueExpr.FullNamedExpression.Accept(this), Roles.Argument); + + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + return result; + } + + #endregion + + public override object Visit(TypeOf typeOfExpression) + { + var result = new TypeOfExpression(); + var location = LocationsBag.GetLocations(typeOfExpression); + result.AddChild(new CSharpTokenNode(Convert(typeOfExpression.Location), TypeOfExpression.TypeofKeywordRole), TypeOfExpression.TypeofKeywordRole); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + if (typeOfExpression.TypeExpression != null) + result.AddChild(ConvertToType(typeOfExpression.TypeExpression), Roles.Type); + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + return result; + } + + public override object Visit(SizeOf sizeOfExpression) + { + var result = new SizeOfExpression(); + var location = LocationsBag.GetLocations(sizeOfExpression); + result.AddChild(new CSharpTokenNode(Convert(sizeOfExpression.Location), SizeOfExpression.SizeofKeywordRole), SizeOfExpression.SizeofKeywordRole); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + if (sizeOfExpression.TypeExpression != null) + result.AddChild(ConvertToType(sizeOfExpression.TypeExpression), Roles.Type); + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + return result; + } + + public override object Visit(CheckedExpr checkedExpression) + { + var result = new CheckedExpression(); + var location = LocationsBag.GetLocations(checkedExpression); + result.AddChild(new CSharpTokenNode(Convert(checkedExpression.Location), CheckedExpression.CheckedKeywordRole), CheckedExpression.CheckedKeywordRole); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + if (checkedExpression.Expr != null) + result.AddChild((Expression)checkedExpression.Expr.Accept(this), Roles.Expression); + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + return result; + } + + public override object Visit(UnCheckedExpr uncheckedExpression) + { + var result = new UncheckedExpression(); + var location = LocationsBag.GetLocations(uncheckedExpression); + result.AddChild(new CSharpTokenNode(Convert(uncheckedExpression.Location), UncheckedExpression.UncheckedKeywordRole), UncheckedExpression.UncheckedKeywordRole); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.LPar), Roles.LPar); + if (uncheckedExpression.Expr != null) + result.AddChild((Expression)uncheckedExpression.Expr.Accept(this), Roles.Expression); + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.RPar), Roles.RPar); + return result; + } + + public override object Visit(ElementAccess elementAccessExpression) + { + var result = new IndexerExpression(); + var location = LocationsBag.GetLocations(elementAccessExpression); + + if (elementAccessExpression.Expr != null) + result.AddChild((Expression)elementAccessExpression.Expr.Accept(this), Roles.TargetExpression); + result.AddChild(new CSharpTokenNode(Convert(elementAccessExpression.Location), Roles.LBracket), Roles.LBracket); + AddArguments(result, elementAccessExpression.Arguments); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.RBracket), Roles.RBracket); + return result; + } + + public override object Visit(BaseThis baseAccessExpression) + { + var result = new BaseReferenceExpression(); + result.Location = Convert(baseAccessExpression.Location); + return result; + } + + public override object Visit(StackAlloc stackAllocExpression) + { + var result = new StackAllocExpression(); + + var location = LocationsBag.GetLocations(stackAllocExpression); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), StackAllocExpression.StackallocKeywordRole), StackAllocExpression.StackallocKeywordRole); + if (stackAllocExpression.TypeExpression != null) + result.AddChild(ConvertToType(stackAllocExpression.TypeExpression), Roles.Type); + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), Roles.LBracket), Roles.LBracket); + if (stackAllocExpression.CountExpression != null) + result.AddChild((Expression)stackAllocExpression.CountExpression.Accept(this), Roles.Expression); + if (location != null && location.Count > 2) + result.AddChild(new CSharpTokenNode(Convert(location [2]), Roles.RBracket), Roles.RBracket); + return result; + } + + public override object Visit(SimpleAssign simpleAssign) + { + var result = new AssignmentExpression(); + + result.Operator = AssignmentOperatorType.Assign; + if (simpleAssign.Target != null) + result.AddChild((Expression)simpleAssign.Target.Accept(this), AssignmentExpression.LeftRole); + var location = LocationsBag.GetLocations(simpleAssign); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), AssignmentExpression.AssignRole), AssignmentExpression.AssignRole); + if (simpleAssign.Source != null) { + result.AddChild((Expression)simpleAssign.Source.Accept(this), AssignmentExpression.RightRole); + } + return result; + } + + public override object Visit(CompoundAssign compoundAssign) + { + var result = new AssignmentExpression(); + switch (compoundAssign.Op) { + case Binary.Operator.Multiply: + result.Operator = AssignmentOperatorType.Multiply; + break; + case Binary.Operator.Division: + result.Operator = AssignmentOperatorType.Divide; + break; + case Binary.Operator.Modulus: + result.Operator = AssignmentOperatorType.Modulus; + break; + case Binary.Operator.Addition: + result.Operator = AssignmentOperatorType.Add; + break; + case Binary.Operator.Subtraction: + result.Operator = AssignmentOperatorType.Subtract; + break; + case Binary.Operator.LeftShift: + result.Operator = AssignmentOperatorType.ShiftLeft; + break; + case Binary.Operator.RightShift: + result.Operator = AssignmentOperatorType.ShiftRight; + break; + case Binary.Operator.BitwiseAnd: + result.Operator = AssignmentOperatorType.BitwiseAnd; + break; + case Binary.Operator.BitwiseOr: + result.Operator = AssignmentOperatorType.BitwiseOr; + break; + case Binary.Operator.ExclusiveOr: + result.Operator = AssignmentOperatorType.ExclusiveOr; + break; + } + + if (compoundAssign.Target != null) + result.AddChild((Expression)compoundAssign.Target.Accept(this), AssignmentExpression.LeftRole); + var location = LocationsBag.GetLocations(compoundAssign); + if (location != null) { + var r = AssignmentExpression.GetOperatorRole(result.Operator); + result.AddChild(new CSharpTokenNode(Convert(location [0]), r), r); + } + if (compoundAssign.Source != null) + result.AddChild((Expression)compoundAssign.Source.Accept(this), AssignmentExpression.RightRole); + return result; + } + + public override object Visit(Mono.CSharp.AnonymousMethodExpression anonymousMethodExpression) + { + var result = new AnonymousMethodExpression(); + var location = LocationsBag.GetLocations(anonymousMethodExpression); + int l = 0; + if (anonymousMethodExpression.IsAsync) { + result.IsAsync = true; + result.AddChild(new CSharpTokenNode(Convert(location [l++]), AnonymousMethodExpression.AsyncModifierRole), AnonymousMethodExpression.AsyncModifierRole); + } + if (location != null) { + result.AddChild(new CSharpTokenNode(Convert(location [l++]), AnonymousMethodExpression.DelegateKeywordRole), AnonymousMethodExpression.DelegateKeywordRole); + + if (location.Count > l) { + result.HasParameterList = true; + result.AddChild(new CSharpTokenNode(Convert(location [l++]), Roles.LPar), Roles.LPar); + AddParameter(result, anonymousMethodExpression.Parameters); + result.AddChild(new CSharpTokenNode(Convert(location [l++]), Roles.RPar), Roles.RPar); + } + } + if (anonymousMethodExpression.Block != null) + result.AddChild((BlockStatement)anonymousMethodExpression.Block.Accept(this), Roles.Body); + return result; + } + + public override object Visit(Mono.CSharp.LambdaExpression lambdaExpression) + { + var result = new LambdaExpression(); + var location = LocationsBag.GetLocations(lambdaExpression); + int l = 0; + if (lambdaExpression.IsAsync) { + result.IsAsync = true; + result.AddChild(new CSharpTokenNode(Convert(location [l++]), LambdaExpression.AsyncModifierRole), LambdaExpression.AsyncModifierRole); + } + if (location == null || location.Count == l + 1) { + if (lambdaExpression.Block != null) + AddParameter(result, lambdaExpression.Parameters); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [l++]), LambdaExpression.ArrowRole), LambdaExpression.ArrowRole); + } else { + result.AddChild(new CSharpTokenNode(Convert(location [l++]), Roles.LPar), Roles.LPar); + if (lambdaExpression.Block != null) + AddParameter(result, lambdaExpression.Parameters); + if (location != null) { + result.AddChild(new CSharpTokenNode(Convert(location [l++]), Roles.RPar), Roles.RPar); + result.AddChild(new CSharpTokenNode(Convert(location [l++]), LambdaExpression.ArrowRole), LambdaExpression.ArrowRole); + } + } + if (lambdaExpression.Block != null) { + if (lambdaExpression.Block.IsCompilerGenerated) { + var generatedReturn = (ContextualReturn)lambdaExpression.Block.Statements [0]; + result.AddChild((AstNode)generatedReturn.Expr.Accept(this), LambdaExpression.BodyRole); + } else { + result.AddChild((AstNode)lambdaExpression.Block.Accept(this), LambdaExpression.BodyRole); + } + } + return result; + } + + public override object Visit(ConstInitializer constInitializer) + { + return constInitializer.Expr.Accept(this); + } + + public override object Visit(ArrayInitializer arrayInitializer) + { + var result = new ArrayInitializerExpression(); + var location = LocationsBag.GetLocations(arrayInitializer); + result.AddChild(new CSharpTokenNode(Convert(arrayInitializer.Location), Roles.LBrace), Roles.LBrace); + var commaLocations = LocationsBag.GetLocations(arrayInitializer.Elements); + for (int i = 0; i < arrayInitializer.Count; i++) { + var init = arrayInitializer [i]; + if (init == null) + continue; + result.AddChild((Expression)init.Accept(this), Roles.Expression); + if (commaLocations != null && i < commaLocations.Count) + result.AddChild(new CSharpTokenNode(Convert(commaLocations [i]), Roles.Comma), Roles.Comma); + } + + if (location != null) { + if (location.Count == 2) // optional comma + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.Comma), Roles.Comma); + result.AddChild(new CSharpTokenNode(Convert(location [location.Count - 1]), Roles.RBrace), Roles.RBrace); + } + return result; + } + + #endregion + + #region LINQ expressions + + QueryOrderClause currentQueryOrderClause; + + public override object Visit(Mono.CSharp.Linq.QueryExpression queryExpression) + { + var oldQueryOrderClause = currentQueryOrderClause; + try { + currentQueryOrderClause = null; + var result = new QueryExpression(); + + var currentClause = queryExpression.next; + + while (currentClause != null) { + var clause = (QueryClause)currentClause.Accept(this); + if (clause is QueryContinuationClause) { + // insert preceding query at beginning of QueryContinuationClause + clause.InsertChildAfter(null, result, QueryContinuationClause.PrecedingQueryRole); + // create a new QueryExpression for the remaining query + result = new QueryExpression(); + } + if (clause != null) { + result.AddChild(clause, QueryExpression.ClauseRole); + } + currentClause = currentClause.next; + } + + return result; + } finally { + currentQueryOrderClause = oldQueryOrderClause; + } + } + + public override object Visit(Mono.CSharp.Linq.QueryStartClause queryExpression) + { + if (queryExpression.Expr == null) { + var intoClause = new QueryContinuationClause(); + intoClause.AddChild(new CSharpTokenNode(Convert(queryExpression.Location), QueryContinuationClause.IntoKeywordRole), QueryContinuationClause.IntoKeywordRole); + intoClause.AddChild(Identifier.Create(queryExpression.IntoVariable.Name, Convert(queryExpression.IntoVariable.Location)), Roles.Identifier); + return intoClause; + } + + var fromClause = new QueryFromClause(); + + fromClause.AddChild(new CSharpTokenNode(Convert(queryExpression.Location), QueryFromClause.FromKeywordRole), QueryFromClause.FromKeywordRole); + + if (queryExpression.IdentifierType != null) + fromClause.AddChild(ConvertToType(queryExpression.IdentifierType), Roles.Type); + + fromClause.AddChild(Identifier.Create(queryExpression.IntoVariable.Name, Convert(queryExpression.IntoVariable.Location)), Roles.Identifier); + + var location = LocationsBag.GetLocations(queryExpression); + if (location != null) + fromClause.AddChild(new CSharpTokenNode(Convert(location [0]), QueryFromClause.InKeywordRole), QueryFromClause.InKeywordRole); + + if (queryExpression.Expr != null) + fromClause.AddChild((Expression)queryExpression.Expr.Accept(this), Roles.Expression); + return fromClause; + } + + public override object Visit(Mono.CSharp.Linq.SelectMany selectMany) + { + var fromClause = new QueryFromClause(); + + fromClause.AddChild(new CSharpTokenNode(Convert(selectMany.Location), QueryFromClause.FromKeywordRole), QueryFromClause.FromKeywordRole); + + if (selectMany.IdentifierType != null) + fromClause.AddChild(ConvertToType(selectMany.IdentifierType), Roles.Type); + + fromClause.AddChild(Identifier.Create(selectMany.IntoVariable.Name, Convert(selectMany.IntoVariable.Location)), Roles.Identifier); + + var location = LocationsBag.GetLocations(selectMany); + if (location != null) + fromClause.AddChild(new CSharpTokenNode(Convert(location [0]), QueryFromClause.InKeywordRole), QueryFromClause.InKeywordRole); + + if (selectMany.Expr != null) + fromClause.AddChild((Expression)selectMany.Expr.Accept(this), Roles.Expression); + return fromClause; + } + + public override object Visit(Mono.CSharp.Linq.Select select) + { + var result = new QuerySelectClause(); + result.AddChild(new CSharpTokenNode(Convert(select.Location), QuerySelectClause.SelectKeywordRole), QuerySelectClause.SelectKeywordRole); + if (select.Expr != null) + result.AddChild((Expression)select.Expr.Accept(this), Roles.Expression); + return result; + } + + public override object Visit(Mono.CSharp.Linq.GroupBy groupBy) + { + var result = new QueryGroupClause(); + var location = LocationsBag.GetLocations(groupBy); + result.AddChild(new CSharpTokenNode(Convert(groupBy.Location), QueryGroupClause.GroupKeywordRole), QueryGroupClause.GroupKeywordRole); + if (groupBy.ElementSelector != null) + result.AddChild((Expression)groupBy.ElementSelector.Accept(this), QueryGroupClause.ProjectionRole); + if (location != null) { + var byLoc = Convert(location[0]); + if (byLoc.Line > 1 || byLoc.Column > 1) + result.AddChild(new CSharpTokenNode(byLoc, QueryGroupClause.ByKeywordRole), QueryGroupClause.ByKeywordRole); + } + if (groupBy.Expr != null) + result.AddChild((Expression)groupBy.Expr.Accept(this), QueryGroupClause.KeyRole); + return result; + } + + public override object Visit(Mono.CSharp.Linq.Let let) + { + var result = new QueryLetClause(); + var location = LocationsBag.GetLocations(let); + + result.AddChild(new CSharpTokenNode(Convert(let.Location), QueryLetClause.LetKeywordRole), QueryLetClause.LetKeywordRole); + result.AddChild(Identifier.Create(let.IntoVariable.Name, Convert(let.IntoVariable.Location)), Roles.Identifier); + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), Roles.Assign), Roles.Assign); + if (let.Expr != null) + result.AddChild((Expression)let.Expr.Accept(this), Roles.Expression); + return result; + } + + public override object Visit(Mono.CSharp.Linq.Where where) + { + var result = new QueryWhereClause(); + result.AddChild(new CSharpTokenNode(Convert(where.Location), QueryWhereClause.WhereKeywordRole), QueryWhereClause.WhereKeywordRole); + if (where.Expr != null) + result.AddChild((Expression)where.Expr.Accept(this), Roles.Condition); + return result; + } + + public override object Visit(Mono.CSharp.Linq.Join join) + { + var result = new QueryJoinClause(); + var location = LocationsBag.GetLocations(join); + result.AddChild(new CSharpTokenNode(Convert(join.Location), QueryJoinClause.JoinKeywordRole), QueryJoinClause.JoinKeywordRole); + + if (join.IdentifierType != null) + result.AddChild(ConvertToType(join.IdentifierType), QueryJoinClause.TypeRole); + + result.AddChild(Identifier.Create(join.JoinVariable.Name, Convert(join.JoinVariable.Location)), QueryJoinClause.JoinIdentifierRole); + + if (join.IdentifierType != null) + result.AddChild(ConvertToType(join.IdentifierType), QueryJoinClause.TypeRole); + + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), QueryJoinClause.InKeywordRole), QueryJoinClause.InKeywordRole); + + if (join.Expr != null) + result.AddChild((Expression)join.Expr.Accept(this), QueryJoinClause.InExpressionRole); + + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), QueryJoinClause.OnKeywordRole), QueryJoinClause.OnKeywordRole); + + var outer = join.OuterSelector.Statements.FirstOrDefault() as ContextualReturn; + if (outer != null) + result.AddChild((Expression)outer.Expr.Accept(this), QueryJoinClause.OnExpressionRole); + + if (location != null && location.Count > 2) + result.AddChild(new CSharpTokenNode(Convert(location [2]), QueryJoinClause.EqualsKeywordRole), QueryJoinClause.EqualsKeywordRole); + + var inner = join.InnerSelector.Statements.FirstOrDefault() as ContextualReturn; + if (inner != null) + result.AddChild((Expression)inner.Expr.Accept(this), QueryJoinClause.EqualsExpressionRole); + + return result; + } + + public override object Visit(Mono.CSharp.Linq.GroupJoin groupJoin) + { + var result = new QueryJoinClause(); + var location = LocationsBag.GetLocations(groupJoin); + result.AddChild(new CSharpTokenNode(Convert(groupJoin.Location), QueryJoinClause.JoinKeywordRole), QueryJoinClause.JoinKeywordRole); + + // mcs seems to have swapped IntoVariable with JoinVariable, so we'll swap it back here + result.AddChild(Identifier.Create(groupJoin.IntoVariable.Name, Convert(groupJoin.IntoVariable.Location)), QueryJoinClause.JoinIdentifierRole); + + if (location != null) + result.AddChild(new CSharpTokenNode(Convert(location [0]), QueryJoinClause.InKeywordRole), QueryJoinClause.InKeywordRole); + + if (groupJoin.Expr != null) + result.AddChild((Expression)groupJoin.Expr.Accept(this), QueryJoinClause.InExpressionRole); + + if (location != null && location.Count > 1) + result.AddChild(new CSharpTokenNode(Convert(location [1]), QueryJoinClause.OnKeywordRole), QueryJoinClause.OnKeywordRole); + + var outer = groupJoin.OuterSelector.Statements.FirstOrDefault() as ContextualReturn; + if (outer != null) + result.AddChild((Expression)outer.Expr.Accept(this), QueryJoinClause.OnExpressionRole); + + + if (location != null && location.Count > 2) + result.AddChild(new CSharpTokenNode(Convert(location [2]), QueryJoinClause.EqualsKeywordRole), QueryJoinClause.EqualsKeywordRole); + var inner = groupJoin.InnerSelector.Statements.FirstOrDefault() as ContextualReturn; + if (inner != null) + result.AddChild((Expression)inner.Expr.Accept(this), QueryJoinClause.EqualsExpressionRole); + + if (location != null && location.Count > 3) + result.AddChild(new CSharpTokenNode(Convert(location [3]), QueryJoinClause.IntoKeywordRole), QueryJoinClause.IntoKeywordRole); + + result.AddChild(Identifier.Create(groupJoin.JoinVariable.Name, Convert(groupJoin.JoinVariable.Location)), QueryJoinClause.IntoIdentifierRole); + return result; + } + + public override object Visit(Mono.CSharp.Linq.OrderByAscending orderByAscending) + { + currentQueryOrderClause = new QueryOrderClause(); + var location2 = LocationsBag.GetLocations(orderByAscending.block); + if (location2 != null) + currentQueryOrderClause.AddChild(new CSharpTokenNode(Convert(location2 [0]), QueryOrderClause.OrderbyKeywordRole), QueryOrderClause.OrderbyKeywordRole); + var ordering = new QueryOrdering(); + if (orderByAscending.Expr != null) + ordering.AddChild((Expression)orderByAscending.Expr.Accept(this), Roles.Expression); + var location = LocationsBag.GetLocations(orderByAscending); + if (location != null) { + ordering.Direction = QueryOrderingDirection.Ascending; + ordering.AddChild(new CSharpTokenNode(Convert(location [0]), QueryOrdering.AscendingKeywordRole), QueryOrdering.AscendingKeywordRole); + } + currentQueryOrderClause.AddChild(ordering, QueryOrderClause.OrderingRole); + return currentQueryOrderClause; + } + + public override object Visit(Mono.CSharp.Linq.OrderByDescending orderByDescending) + { + currentQueryOrderClause = new QueryOrderClause(); + + var ordering = new QueryOrdering(); + if (orderByDescending.Expr != null) + ordering.AddChild((Expression)orderByDescending.Expr.Accept(this), Roles.Expression); + var location = LocationsBag.GetLocations(orderByDescending); + if (location != null) { + ordering.Direction = QueryOrderingDirection.Descending; + ordering.AddChild(new CSharpTokenNode(Convert(location [0]), QueryOrdering.DescendingKeywordRole), QueryOrdering.DescendingKeywordRole); + } + currentQueryOrderClause.AddChild(ordering, QueryOrderClause.OrderingRole); + return currentQueryOrderClause; + } + + public override object Visit(Mono.CSharp.Linq.ThenByAscending thenByAscending) + { + var ordering = new QueryOrdering(); + if (thenByAscending.Expr != null) + ordering.AddChild((Expression)thenByAscending.Expr.Accept(this), Roles.Expression); + var location = LocationsBag.GetLocations(thenByAscending); + if (location != null) { + ordering.Direction = QueryOrderingDirection.Ascending; + ordering.AddChild(new CSharpTokenNode(Convert(location [0]), QueryOrdering.AscendingKeywordRole), QueryOrdering.AscendingKeywordRole); + } + currentQueryOrderClause.AddChild(ordering, QueryOrderClause.OrderingRole); + return null; + } + + public override object Visit(Mono.CSharp.Linq.ThenByDescending thenByDescending) + { + var ordering = new QueryOrdering(); + if (thenByDescending.Expr != null) + ordering.AddChild((Expression)thenByDescending.Expr.Accept(this), Roles.Expression); + var location = LocationsBag.GetLocations(thenByDescending); + if (location != null) { + ordering.Direction = QueryOrderingDirection.Descending; + ordering.AddChild(new CSharpTokenNode(Convert(location [0]), QueryOrdering.DescendingKeywordRole), QueryOrdering.DescendingKeywordRole); + } + currentQueryOrderClause.AddChild(ordering, QueryOrderClause.OrderingRole); + return null; + } + + public override object Visit(Await awaitExpr) + { + var result = new UnaryOperatorExpression(); + result.Operator = UnaryOperatorType.Await; + result.AddChild(new CSharpTokenNode(Convert(awaitExpr.Location), UnaryOperatorExpression.AwaitRole), UnaryOperatorExpression.AwaitRole); + if (awaitExpr.Expression != null) + result.AddChild((Expression)awaitExpr.Expression.Accept(this), Roles.Expression); + return result; + } + + #endregion + + #region XmlDoc + + public DocumentationReference ConvertXmlDoc(DocumentationBuilder doc) + { + var result = new DocumentationReference(); + if (doc.ParsedName != null) { + if (doc.ParsedName.Name == "") { + result.SymbolKind = SymbolKind.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.SymbolKind = SymbolKind.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.SymbolKind = SymbolKind.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) + { + var 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() + { + compilerSettings = new CompilerSettings(); + } + + public CSharpParser(CompilerSettings args) + { + compilerSettings = args ?? new CompilerSettings(); + } + + void InsertComments(CompilerCompilationUnit top, ConversionVisitor conversionVisitor) + { + AstNode insertionPoint = conversionVisitor.Unit.FirstChild; + foreach (var special in top.SpecialsBag.Specials) { + AstNode newLeaf = null; + Role role = null; + bool isDocumentationComment = false; + var comment = special as SpecialsBag.Comment; + if (comment != null) { + // HACK: multiline documentation comment detection; better move this logic into the mcs tokenizer + bool isMultilineDocumentationComment = (comment.CommentType == SpecialsBag.CommentType.Multi && comment.Content.StartsWith("*", StringComparison.Ordinal) && !comment.Content.StartsWith("**", StringComparison.Ordinal)); + isDocumentationComment = comment.CommentType == SpecialsBag.CommentType.Documentation || isMultilineDocumentationComment; + if (conversionVisitor.convertTypeSystemMode && !isDocumentationComment) + continue; + var type = isMultilineDocumentationComment ? CommentType.MultiLineDocumentation : (CommentType)comment.CommentType; + var start = new TextLocation(comment.Line, comment.Col); + var end = new TextLocation(comment.EndLine, comment.EndCol); + newLeaf = new Comment(type, start, end) { + StartsLine = comment.StartsLine, + Content = isMultilineDocumentationComment ? comment.Content.Substring(1) : comment.Content + }; + role = Roles.Comment; + } else if (!GenerateTypeSystemMode) { + var pragmaDirective = special as SpecialsBag.PragmaPreProcessorDirective; + if (pragmaDirective != null) { + var pragma = new PragmaWarningPreprocessorDirective(new TextLocation(pragmaDirective.Line, pragmaDirective.Col), new TextLocation(pragmaDirective.EndLine, pragmaDirective.EndCol)); + pragma.AddChild(new CSharpTokenNode(new TextLocation(pragmaDirective.Line, pragmaDirective.Col), PragmaWarningPreprocessorDirective.PragmaKeywordRole), PragmaWarningPreprocessorDirective.PragmaKeywordRole); + pragma.AddChild(new CSharpTokenNode(new TextLocation(pragmaDirective.Line, pragmaDirective.WarningColumn), PragmaWarningPreprocessorDirective.WarningKeywordRole), PragmaWarningPreprocessorDirective.WarningKeywordRole); + var pragmaRole = pragmaDirective.Disalbe ? PragmaWarningPreprocessorDirective.DisableKeywordRole : PragmaWarningPreprocessorDirective.RestoreKeywordRole; + pragma.AddChild(new CSharpTokenNode(new TextLocation(pragmaDirective.Line, pragmaDirective.DisableRestoreColumn), pragmaRole), pragmaRole); + foreach (var code in pragmaDirective.Codes) { + pragma.AddChild((PrimitiveExpression)conversionVisitor.Visit(code), PragmaWarningPreprocessorDirective.WarningRole); + } + newLeaf = pragma; + role = Roles.PreProcessorDirective; + goto end; + } + var lineDirective = special as SpecialsBag.LineProcessorDirective; + if (lineDirective != null) { + var pragma = new LinePreprocessorDirective(new TextLocation(lineDirective.Line, lineDirective.Col), new TextLocation(lineDirective.EndLine, lineDirective.EndCol)); + pragma.LineNumber = lineDirective.LineNumber; + pragma.FileName = lineDirective.FileName; + newLeaf = pragma; + role = Roles.PreProcessorDirective; + goto end; + } + var directive = special as SpecialsBag.PreProcessorDirective; + if (directive != null) { + newLeaf = new PreProcessorDirective((PreProcessorDirectiveType)((int)directive.Cmd & 0xF), new TextLocation(directive.Line, directive.Col), new TextLocation(directive.EndLine, directive.EndCol)) { + Argument = directive.Arg, + Take = directive.Take + }; + role = Roles.PreProcessorDirective; + } + end: + ; + } + if (newLeaf != null) { + InsertComment(ref insertionPoint, newLeaf, role, isDocumentationComment, conversionVisitor.Unit); + } + } + if (!GenerateTypeSystemMode) { + // We cannot insert newlines in the same loop as comments/preprocessor directives + // because they are not correctly ordered in the specials bag + insertionPoint = conversionVisitor.Unit.FirstChild; + for (int i = 0; i < top.SpecialsBag.Specials.Count; i++) { + var newLine = top.SpecialsBag.Specials [i] as SpecialsBag.NewLineToken; + if (newLine != null) { + var newLeaf = new NewLineNode(new TextLocation(newLine.Line, newLine.Col + 1)); + newLeaf.NewLineType = newLine.NewLine == SpecialsBag.NewLine.Unix ? UnicodeNewline.LF : UnicodeNewline.CRLF; + InsertComment(ref insertionPoint, newLeaf, Roles.NewLine, false, conversionVisitor.Unit); + } + } + } + } + + static void InsertComment(ref AstNode insertionPoint, AstNode newNode, Role role, bool isDocumentationComment, AstNode rootNode) + { + TextLocation insertAt = newNode.StartLocation; + // Advance insertionPoint to the first node that has a start location >= insertAt + while (insertionPoint != null && insertionPoint.StartLocation < insertAt) { + // Enter the current node if insertAt is within + while (insertAt < insertionPoint.EndLocation && insertionPoint.FirstChild != null) { + insertionPoint = insertionPoint.FirstChild; + } + // Go to next node (insertionPoint.NextSibling if it exists; otherwise the next sibling of the parent node etc.) + insertionPoint = insertionPoint.GetNextNode(); + } + // As a special case, XmlDoc gets inserted at the beginning of the entity declaration + if (isDocumentationComment && insertionPoint is EntityDeclaration && insertionPoint.FirstChild != null) { + insertionPoint = insertionPoint.FirstChild; + } + if (insertionPoint == null) { + // we're at the end of the compilation unit + rootNode.AddChildUnsafe(newNode, role); + } else { + insertionPoint.Parent.InsertChildBeforeUnsafe(insertionPoint, newNode, role); + } + } + + public class ErrorReportPrinter : ReportPrinter + { + readonly string fileName; + public readonly List Errors = new List(); + + public ErrorReportPrinter(string fileName) + { + this.fileName = fileName; + } + + public override void Print(AbstractMessage msg, bool showFullPath) + { + base.Print(msg, showFullPath); + var newError = new Error(msg.IsWarning ? ErrorType.Warning : ErrorType.Error, msg.Text, new DomRegion(fileName, msg.Location.Row, msg.Location.Column)); + Errors.Add(newError); + } + } + + ErrorReportPrinter errorReportPrinter = new ErrorReportPrinter(null); + + [Obsolete("Use the Errors/Warnings/ErrorsAndWarnings properties instead")] + public ErrorReportPrinter ErrorPrinter { + get { + return errorReportPrinter; + } + } + + public bool HasErrors { + get { + return errorReportPrinter.ErrorsCount > 0; + } + } + + public bool HasWarnings { + get { + return errorReportPrinter.WarningsCount > 0; + } + } + + public IEnumerable Errors { + get { + return errorReportPrinter.Errors.Where(e => e.ErrorType == ErrorType.Error); + } + } + + public IEnumerable Warnings { + get { + return errorReportPrinter.Errors.Where(e => e.ErrorType == ErrorType.Warning); + } + } + + public IEnumerable ErrorsAndWarnings { + get { return errorReportPrinter.Errors; } + } + + /// + /// Parses a C# code file. + /// + /// The source code to parse. + /// The file name. Used to identify the file (e.g. when building a type system). + /// This can be an arbitrary identifier, NRefactory never tries to access the file on disk. + /// Returns the syntax tree. + public SyntaxTree Parse(string program, string fileName = "") + { + return Parse(new StringTextSource(program), fileName); + } + + /// + /// Parses a C# code file. + /// + /// The text reader containing the source code to parse. + /// The file name. Used to identify the file (e.g. when building a type system). + /// This can be an arbitrary identifier, NRefactory never tries to access the file on disk. + /// Returns the syntax tree. + public SyntaxTree Parse(TextReader reader, string fileName = "") + { + return Parse(new StringTextSource(reader.ReadToEnd()), fileName); + } + + /// + /// Converts a Mono.CSharp syntax tree into an NRefactory syntax tree. + /// + public SyntaxTree Parse(CompilerCompilationUnit top, string fileName) + { + if (top == null) { + return null; + } + CSharpParser.ConversionVisitor conversionVisitor = new ConversionVisitor(GenerateTypeSystemMode, top.LocationsBag); + top.ModuleCompiled.Accept(conversionVisitor); + InsertComments(top, conversionVisitor); + if (CompilationUnitCallback != null) { + CompilationUnitCallback(top); + } + var expr = top.LastYYValue as Mono.CSharp.Expression; + if (expr != null) + conversionVisitor.Unit.TopExpression = expr.Accept(conversionVisitor) as AstNode; + + conversionVisitor.Unit.FileName = fileName; + var conditionals = new List(); + foreach (var settings in compilerSettings.ConditionalSymbols) { + if (top.Conditionals.ContainsKey(settings) && !top.Conditionals [settings]) + continue; + conditionals.Add(settings); + } + foreach (var kv in top.Conditionals) { + if (!kv.Value || compilerSettings.ConditionalSymbols.Contains(kv.Key)) + continue; + conditionals.Add(kv.Key); + } + conversionVisitor.Unit.ConditionalSymbols = conditionals; + return conversionVisitor.Unit; + } + + public CompilerSettings CompilerSettings { + get { return compilerSettings; } + set { + if (value == null) + throw new ArgumentNullException(); + compilerSettings = value; + } + } + + /// + /// Callback that gets called with the Mono.CSharp syntax tree whenever some code is parsed. + /// + public Action CompilationUnitCallback { + get; + set; + } + + /// + /// Specifies whether to run the parser in a special mode for generating the type system. + /// If this property is true, the syntax tree will only contain nodes relevant for the + /// call and might be missing other nodes (e.g. method bodies). + /// The default is false. + /// + public bool GenerateTypeSystemMode { + get; + set; + } + + TextLocation initialLocation = new TextLocation(1, 1); + + /// + /// Specifies the text location where parsing starts. + /// This property can be used when parsing a part of a file to make the locations of the AstNodes + /// refer to the position in the whole file. + /// The default is (1,1). + /// + public TextLocation InitialLocation { + get { return initialLocation; } + set { initialLocation = value; } + } + + internal static object parseLock = new object(); + + /// + /// Parses a C# code file. + /// + /// The stream containing the source code to parse. + /// The file name. Used to identify the file (e.g. when building a type system). + /// This can be an arbitrary identifier, NRefactory never tries to access the file on disk. + /// Returns the syntax tree. + public SyntaxTree Parse(Stream stream, string fileName = "") + { + return Parse(new StreamReader(stream), fileName); + } + + /// + /// Parses a C# code file. + /// + /// The source code to parse. + /// The file name. Used to identify the file (e.g. when building a type system). + /// This can be an arbitrary identifier, NRefactory never tries to access the file on disk. + /// Returns the syntax tree. + public SyntaxTree Parse(ITextSource program, string fileName = "") + { + return Parse(program, fileName, initialLocation.Line, initialLocation.Column); + } + + SyntaxTree Parse(ITextSource program, string fileName, int initialLine, int initialColumn) + { + lock (parseLock) { + errorReportPrinter = new ErrorReportPrinter(""); + var ctx = new CompilerContext(compilerSettings.ToMono(), errorReportPrinter); + ctx.Settings.TabSize = 1; + var reader = new SeekableStreamReader(program); + var file = new SourceFile(fileName, fileName, 0); + Location.Initialize(new List(new [] { file })); + var module = new ModuleContainer(ctx); + var session = new ParserSession(); + session.LocationsBag = new LocationsBag(); + var report = new Report(ctx, errorReportPrinter); + var parser = Driver.Parse(reader, file, module, session, report, initialLine - 1, initialColumn - 1); + var top = new CompilerCompilationUnit { + ModuleCompiled = module, + LocationsBag = session.LocationsBag, + SpecialsBag = parser.Lexer.sbag, + Conditionals = parser.Lexer.SourceFile.Conditionals + }; + var unit = Parse(top, fileName); + unit.Errors.AddRange(errorReportPrinter.Errors); + CompilerCallableEntryPoint.Reset(); + return unit; + } + } + + public IEnumerable ParseTypeMembers(string code) + { + return ParseTypeMembers(code, initialLocation.Line, initialLocation.Column); + } + + IEnumerable ParseTypeMembers(string code, int initialLine, int initialColumn) + { + const string prefix = "unsafe partial class MyClass { "; + var syntaxTree = Parse(new StringTextSource(prefix + code + "}"), "parsed.cs", initialLine, initialColumn - prefix.Length); + if (syntaxTree == null) + return Enumerable.Empty(); + var td = syntaxTree.FirstChild as TypeDeclaration; + if (td != null) { + var members = td.Members.ToArray(); + // detach members from parent + foreach (var m in members) + m.Remove(); + return members; + } + return Enumerable.Empty(); + } + + public IEnumerable ParseStatements(string code) + { + return ParseStatements(code, initialLocation.Line, initialLocation.Column); + } + + IEnumerable ParseStatements(string code, int initialLine, int initialColumn) + { + // the dummy method is async so that 'await' expressions are parsed as expected + const string prefix = "async void M() { "; + var members = ParseTypeMembers(prefix + code + "}", initialLine, initialColumn - prefix.Length); + var method = members.FirstOrDefault() as MethodDeclaration; + 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(); + } + + public AstType ParseTypeReference(string code) + { + var members = ParseTypeMembers(code + " a;"); + var field = members.FirstOrDefault() as FieldDeclaration; + if (field != null) { + AstType type = field.ReturnType; + type.Remove(); + return type; + } + return AstType.Null; + } + + public Expression ParseExpression(string code) + { + const string prefix = "tmp = "; + var statements = ParseStatements(prefix + code + ";", initialLocation.Line, initialLocation.Column - prefix.Length); + var es = statements.FirstOrDefault() as ExpressionStatement; + if (es != null) { + var ae = es.Expression as AssignmentExpression; + if (ae != null) { + Expression expr = ae.Right; + expr.Remove(); + return expr; + } + } + return Expression.Null; + } + /* + /// + /// Parses a file snippet; guessing what the code snippet represents (whole file, type members, block, type reference, expression). + /// + public AstNode ParseSnippet (string code) + { + // TODO: add support for parsing a part of a file + throw new NotImplementedException (); + } + */ + 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('}', '>'); + + lock (parseLock) { + errorReportPrinter = new ErrorReportPrinter(""); + var ctx = new CompilerContext(compilerSettings.ToMono(), errorReportPrinter); + ctx.Settings.TabSize = 1; + var reader = new SeekableStreamReader(new StringTextSource(cref)); + var file = new SourceFile("", "", 0); + Location.Initialize(new List(new [] { file })); + var module = new ModuleContainer(ctx); + module.DocumentationBuilder = new DocumentationBuilder(module); + var source_file = new CompilationSourceFile(module); + var report = new Report(ctx, errorReportPrinter); + var session = new ParserSession(); + session.LocationsBag = new LocationsBag(); + var parser = new Mono.CSharp.CSharpParser(reader, source_file, report, session); + parser.Lexer.Line += initialLocation.Line - 1; + parser.Lexer.Column += initialLocation.Column - 1; + 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); + } + + var conversionVisitor = new ConversionVisitor(false, session.LocationsBag); + var docRef = conversionVisitor.ConvertXmlDoc(module.DocumentationBuilder); + CompilerCallableEntryPoint.Reset(); + return docRef; + } + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/CompilerSettings.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/CompilerSettings.cs new file mode 100644 index 000000000..1146ff85e --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/CompilerSettings.cs @@ -0,0 +1,151 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// 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.Implementation; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// C# compiler settings. + /// + [Serializable] + public class CompilerSettings : AbstractFreezable + { + protected override void FreezeInternal() + { + conditionalSymbols = FreezableHelper.FreezeList(conditionalSymbols); + specificWarningsAsErrors = FreezableHelper.FreezeList(specificWarningsAsErrors); + disabledWarnings = FreezableHelper.FreezeList(disabledWarnings); + base.FreezeInternal(); + } + + /// + /// Creates a new CompilerSettings instance. + /// + public CompilerSettings() + { + } + + bool allowUnsafeBlocks = true; + + /// + /// Gets/Sets whether unsafe code is allowed. + /// The default is true. If set to false, parsing unsafe code will result in parser errors. + /// + public bool AllowUnsafeBlocks { + get { return allowUnsafeBlocks; } + set { + FreezableHelper.ThrowIfFrozen(this); + allowUnsafeBlocks = value; + } + } + + bool checkForOverflow; + + /// + /// Gets/Sets whether overflow checking is enabled. + /// The default is false. This setting effects semantic analysis. + /// + public bool CheckForOverflow { + get { return checkForOverflow; } + set { checkForOverflow = value; } + } + + Version languageVersion = new Version((int)Mono.CSharp.LanguageVersion.Default, 0); + + /// + /// Gets/Sets the language version used by the parser. + /// Using language constructs newer than the supplied version will result in parser errors. + /// + public Version LanguageVersion { + get { return languageVersion; } + set { + FreezableHelper.ThrowIfFrozen(this); + if (value == null) + throw new ArgumentNullException(); + languageVersion = value; + } + } + + IList conditionalSymbols = new List(); + + /// + /// Gets/Sets the list of conditional symbols that are defined project-wide. + /// + public IList ConditionalSymbols { + get { return conditionalSymbols; } + } + + bool treatWarningsAsErrors; + + public bool TreatWarningsAsErrors { + get { return treatWarningsAsErrors; } + set { + FreezableHelper.ThrowIfFrozen(this); + treatWarningsAsErrors = value; + } + } + + IList specificWarningsAsErrors = new List(); + + /// + /// Allows treating specific warnings as errors without setting to true. + /// + public IList SpecificWarningsAsErrors { + get { return specificWarningsAsErrors; } + } + + int warningLevel = 4; + + public int WarningLevel { + get { return warningLevel; } + set { + FreezableHelper.ThrowIfFrozen(this); + warningLevel = value; + } + } + + IList disabledWarnings = new List(); + + /// + /// Disables the specified warnings. + /// + public IList DisabledWarnings { + get { return disabledWarnings; } + } + + internal Mono.CSharp.CompilerSettings ToMono() + { + var s = new Mono.CSharp.CompilerSettings(); + s.Unsafe = allowUnsafeBlocks; + s.Checked = checkForOverflow; + s.Version = (Mono.CSharp.LanguageVersion)languageVersion.Major; + s.WarningsAreErrors = treatWarningsAsErrors; + s.WarningLevel = warningLevel; + foreach (int code in disabledWarnings) + s.SetIgnoreWarning(code); + foreach (int code in specificWarningsAsErrors) + s.AddWarningAsError(code); + foreach (string sym in conditionalSymbols) + s.AddConditionalSymbol(sym); + return s; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/SeekableStreamReader.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/SeekableStreamReader.cs new file mode 100644 index 000000000..5a853c54e --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/SeekableStreamReader.cs @@ -0,0 +1,103 @@ +// +// SeekableStreamReader.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 ICSharpCode.NRefactory.Editor; +using System.IO; +using System.Text; + +namespace Mono.CSharp +{ + public class SeekableStreamReader : IDisposable + { + public const int DefaultReadAheadSize = 2048; + + readonly ITextSource textSource; + + int pos; + + static string GetAllText(Stream stream, Encoding encoding) { + using (var rdr = new StreamReader(stream, encoding)) { + return rdr.ReadToEnd(); + } + } + + public SeekableStreamReader (Stream stream, Encoding encoding, char[] sharedBuffer = null) : this(new StringTextSource(GetAllText(stream, encoding))) + { + } + + public SeekableStreamReader (ITextSource source) + { + this.textSource = source; + } + + + public void Dispose () + { + } + + /// + /// This value corresponds to the current position in a stream of characters. + /// The StreamReader hides its manipulation of the underlying byte stream and all + /// character set/decoding issues. Thus, we cannot use this position to guess at + /// the corresponding position in the underlying byte stream even though there is + /// a correlation between them. + /// + public int Position { + get { + return pos; + } + + set { + pos = value; + } + } + + public char GetChar (int position) + { + return textSource.GetCharAt (position); + } + + public char[] ReadChars (int fromPosition, int toPosition) + { + return textSource.GetText (fromPosition, toPosition - fromPosition).ToCharArray (); + } + + public int Peek () + { + if (pos >= textSource.TextLength) + return -1; + return textSource.GetCharAt (pos); + } + + public int Read () + { + if (pos >= textSource.TextLength) + return -1; + return textSource.GetCharAt (pos++); + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/CryptoConvert.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/CryptoConvert.cs new file mode 100644 index 000000000..a56e94d01 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/CryptoConvert.cs @@ -0,0 +1,754 @@ +// +// CryptoConvert.cs - Crypto Convertion Routines +// +// Author: +// Sebastien Pouliot +// +// (C) 2003 Motus Technologies Inc. (http://www.motus.com) +// Copyright (C) 2004-2006 Novell Inc. (http://www.novell.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.Globalization; +using System.Security.Cryptography; +using System.Text; + +namespace Mono.Security.Cryptography { + +#if INSIDE_CORLIB + internal +#else + public +#endif + sealed class CryptoConvert { + + private CryptoConvert () + { + } + + static private int ToInt32LE (byte [] bytes, int offset) + { + return (bytes [offset+3] << 24) | (bytes [offset+2] << 16) | (bytes [offset+1] << 8) | bytes [offset]; + } + + static private uint ToUInt32LE (byte [] bytes, int offset) + { + return (uint)((bytes [offset+3] << 24) | (bytes [offset+2] << 16) | (bytes [offset+1] << 8) | bytes [offset]); + } + + static private byte [] GetBytesLE (int val) + { + return new byte [] { + (byte) (val & 0xff), + (byte) ((val >> 8) & 0xff), + (byte) ((val >> 16) & 0xff), + (byte) ((val >> 24) & 0xff) + }; + } + + static private byte[] Trim (byte[] array) + { + for (int i=0; i < array.Length; i++) { + if (array [i] != 0x00) { + byte[] result = new byte [array.Length - i]; + Buffer.BlockCopy (array, i, result, 0, result.Length); + return result; + } + } + return null; + } + + // convert the key from PRIVATEKEYBLOB to RSA + // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/security/Security/private_key_blobs.asp + // e.g. SNK files, PVK files + static public RSA FromCapiPrivateKeyBlob (byte[] blob) + { + return FromCapiPrivateKeyBlob (blob, 0); + } + + static public RSA FromCapiPrivateKeyBlob (byte[] blob, int offset) + { + if (blob == null) + throw new ArgumentNullException ("blob"); + if (offset >= blob.Length) + throw new ArgumentException ("blob is too small."); + + RSAParameters rsap = new RSAParameters (); + try { + if ((blob [offset] != 0x07) || // PRIVATEKEYBLOB (0x07) + (blob [offset+1] != 0x02) || // Version (0x02) + (blob [offset+2] != 0x00) || // Reserved (word) + (blob [offset+3] != 0x00) || + (ToUInt32LE (blob, offset+8) != 0x32415352)) // DWORD magic = RSA2 + throw new CryptographicException ("Invalid blob header"); + + // ALGID (CALG_RSA_SIGN, CALG_RSA_KEYX, ...) + // int algId = ToInt32LE (blob, offset+4); + + // DWORD bitlen + int bitLen = ToInt32LE (blob, offset+12); + + // DWORD public exponent + byte[] exp = new byte [4]; + Buffer.BlockCopy (blob, offset+16, exp, 0, 4); + Array.Reverse (exp); + rsap.Exponent = Trim (exp); + + int pos = offset+20; + // BYTE modulus[rsapubkey.bitlen/8]; + int byteLen = (bitLen >> 3); + rsap.Modulus = new byte [byteLen]; + Buffer.BlockCopy (blob, pos, rsap.Modulus, 0, byteLen); + Array.Reverse (rsap.Modulus); + pos += byteLen; + + // BYTE prime1[rsapubkey.bitlen/16]; + int byteHalfLen = (byteLen >> 1); + rsap.P = new byte [byteHalfLen]; + Buffer.BlockCopy (blob, pos, rsap.P, 0, byteHalfLen); + Array.Reverse (rsap.P); + pos += byteHalfLen; + + // BYTE prime2[rsapubkey.bitlen/16]; + rsap.Q = new byte [byteHalfLen]; + Buffer.BlockCopy (blob, pos, rsap.Q, 0, byteHalfLen); + Array.Reverse (rsap.Q); + pos += byteHalfLen; + + // BYTE exponent1[rsapubkey.bitlen/16]; + rsap.DP = new byte [byteHalfLen]; + Buffer.BlockCopy (blob, pos, rsap.DP, 0, byteHalfLen); + Array.Reverse (rsap.DP); + pos += byteHalfLen; + + // BYTE exponent2[rsapubkey.bitlen/16]; + rsap.DQ = new byte [byteHalfLen]; + Buffer.BlockCopy (blob, pos, rsap.DQ, 0, byteHalfLen); + Array.Reverse (rsap.DQ); + pos += byteHalfLen; + + // BYTE coefficient[rsapubkey.bitlen/16]; + rsap.InverseQ = new byte [byteHalfLen]; + Buffer.BlockCopy (blob, pos, rsap.InverseQ, 0, byteHalfLen); + Array.Reverse (rsap.InverseQ); + pos += byteHalfLen; + + // ok, this is hackish but CryptoAPI support it so... + // note: only works because CRT is used by default + // http://bugzilla.ximian.com/show_bug.cgi?id=57941 + rsap.D = new byte [byteLen]; // must be allocated + if (pos + byteLen + offset <= blob.Length) { + // BYTE privateExponent[rsapubkey.bitlen/8]; + Buffer.BlockCopy (blob, pos, rsap.D, 0, byteLen); + Array.Reverse (rsap.D); + } + } + catch (Exception e) { + throw new CryptographicException ("Invalid blob.", e); + } + +#if NET_2_1 + RSA rsa = RSA.Create (); + rsa.ImportParameters (rsap); +#else + RSA rsa = null; + try { + rsa = RSA.Create (); + rsa.ImportParameters (rsap); + } + catch (CryptographicException ce) { + // this may cause problem when this code is run under + // the SYSTEM identity on Windows (e.g. ASP.NET). See + // http://bugzilla.ximian.com/show_bug.cgi?id=77559 + try { + CspParameters csp = new CspParameters (); + csp.Flags = CspProviderFlags.UseMachineKeyStore; + rsa = new RSACryptoServiceProvider (csp); + rsa.ImportParameters (rsap); + } + catch { + // rethrow original, not the later, exception if this fails + throw ce; + } + } +#endif + return rsa; + } + + static public DSA FromCapiPrivateKeyBlobDSA (byte[] blob) + { + return FromCapiPrivateKeyBlobDSA (blob, 0); + } + + static public DSA FromCapiPrivateKeyBlobDSA (byte[] blob, int offset) + { + if (blob == null) + throw new ArgumentNullException ("blob"); + if (offset >= blob.Length) + throw new ArgumentException ("blob is too small."); + + DSAParameters dsap = new DSAParameters (); + try { + if ((blob [offset] != 0x07) || // PRIVATEKEYBLOB (0x07) + (blob [offset + 1] != 0x02) || // Version (0x02) + (blob [offset + 2] != 0x00) || // Reserved (word) + (blob [offset + 3] != 0x00) || + (ToUInt32LE (blob, offset + 8) != 0x32535344)) // DWORD magic + throw new CryptographicException ("Invalid blob header"); + + int bitlen = ToInt32LE (blob, offset + 12); + int bytelen = bitlen >> 3; + int pos = offset + 16; + + dsap.P = new byte [bytelen]; + Buffer.BlockCopy (blob, pos, dsap.P, 0, bytelen); + Array.Reverse (dsap.P); + pos += bytelen; + + dsap.Q = new byte [20]; + Buffer.BlockCopy (blob, pos, dsap.Q, 0, 20); + Array.Reverse (dsap.Q); + pos += 20; + + dsap.G = new byte [bytelen]; + Buffer.BlockCopy (blob, pos, dsap.G, 0, bytelen); + Array.Reverse (dsap.G); + pos += bytelen; + + dsap.X = new byte [20]; + Buffer.BlockCopy (blob, pos, dsap.X, 0, 20); + Array.Reverse (dsap.X); + pos += 20; + + dsap.Counter = ToInt32LE (blob, pos); + pos += 4; + + dsap.Seed = new byte [20]; + Buffer.BlockCopy (blob, pos, dsap.Seed, 0, 20); + Array.Reverse (dsap.Seed); + pos += 20; + } + catch (Exception e) { + throw new CryptographicException ("Invalid blob.", e); + } + +#if NET_2_1 + DSA dsa = (DSA)DSA.Create (); + dsa.ImportParameters (dsap); +#else + DSA dsa = null; + try { + dsa = (DSA)DSA.Create (); + dsa.ImportParameters (dsap); + } + catch (CryptographicException ce) { + // this may cause problem when this code is run under + // the SYSTEM identity on Windows (e.g. ASP.NET). See + // http://bugzilla.ximian.com/show_bug.cgi?id=77559 + try { + CspParameters csp = new CspParameters (); + csp.Flags = CspProviderFlags.UseMachineKeyStore; + dsa = new DSACryptoServiceProvider (csp); + dsa.ImportParameters (dsap); + } + catch { + // rethrow original, not the later, exception if this fails + throw ce; + } + } +#endif + return dsa; + } + + static public byte[] ToCapiPrivateKeyBlob (RSA rsa) + { + RSAParameters p = rsa.ExportParameters (true); + int keyLength = p.Modulus.Length; // in bytes + byte[] blob = new byte [20 + (keyLength << 2) + (keyLength >> 1)]; + + blob [0] = 0x07; // Type - PRIVATEKEYBLOB (0x07) + blob [1] = 0x02; // Version - Always CUR_BLOB_VERSION (0x02) + // [2], [3] // RESERVED - Always 0 + blob [5] = 0x24; // ALGID - Always 00 24 00 00 (for CALG_RSA_SIGN) + blob [8] = 0x52; // Magic - RSA2 (ASCII in hex) + blob [9] = 0x53; + blob [10] = 0x41; + blob [11] = 0x32; + + byte[] bitlen = GetBytesLE (keyLength << 3); + blob [12] = bitlen [0]; // bitlen + blob [13] = bitlen [1]; + blob [14] = bitlen [2]; + blob [15] = bitlen [3]; + + // public exponent (DWORD) + int pos = 16; + int n = p.Exponent.Length; + while (n > 0) + blob [pos++] = p.Exponent [--n]; + // modulus + pos = 20; + byte[] part = p.Modulus; + int len = part.Length; + Array.Reverse (part, 0, len); + Buffer.BlockCopy (part, 0, blob, pos, len); + pos += len; + // private key + part = p.P; + len = part.Length; + Array.Reverse (part, 0, len); + Buffer.BlockCopy (part, 0, blob, pos, len); + pos += len; + + part = p.Q; + len = part.Length; + Array.Reverse (part, 0, len); + Buffer.BlockCopy (part, 0, blob, pos, len); + pos += len; + + part = p.DP; + len = part.Length; + Array.Reverse (part, 0, len); + Buffer.BlockCopy (part, 0, blob, pos, len); + pos += len; + + part = p.DQ; + len = part.Length; + Array.Reverse (part, 0, len); + Buffer.BlockCopy (part, 0, blob, pos, len); + pos += len; + + part = p.InverseQ; + len = part.Length; + Array.Reverse (part, 0, len); + Buffer.BlockCopy (part, 0, blob, pos, len); + pos += len; + + part = p.D; + len = part.Length; + Array.Reverse (part, 0, len); + Buffer.BlockCopy (part, 0, blob, pos, len); + + return blob; + } + + static public byte[] ToCapiPrivateKeyBlob (DSA dsa) + { + DSAParameters p = dsa.ExportParameters (true); + int keyLength = p.P.Length; // in bytes + + // header + P + Q + G + X + count + seed + byte[] blob = new byte [16 + keyLength + 20 + keyLength + 20 + 4 + 20]; + + blob [0] = 0x07; // Type - PRIVATEKEYBLOB (0x07) + blob [1] = 0x02; // Version - Always CUR_BLOB_VERSION (0x02) + // [2], [3] // RESERVED - Always 0 + blob [5] = 0x22; // ALGID + blob [8] = 0x44; // Magic + blob [9] = 0x53; + blob [10] = 0x53; + blob [11] = 0x32; + + byte[] bitlen = GetBytesLE (keyLength << 3); + blob [12] = bitlen [0]; + blob [13] = bitlen [1]; + blob [14] = bitlen [2]; + blob [15] = bitlen [3]; + + int pos = 16; + byte[] part = p.P; + Array.Reverse (part); + Buffer.BlockCopy (part, 0, blob, pos, keyLength); + pos += keyLength; + + part = p.Q; + Array.Reverse (part); + Buffer.BlockCopy (part, 0, blob, pos, 20); + pos += 20; + + part = p.G; + Array.Reverse (part); + Buffer.BlockCopy (part, 0, blob, pos, keyLength); + pos += keyLength; + + part = p.X; + Array.Reverse (part); + Buffer.BlockCopy (part, 0, blob, pos, 20); + pos += 20; + + Buffer.BlockCopy (GetBytesLE (p.Counter), 0, blob, pos, 4); + pos += 4; + + part = p.Seed; + Array.Reverse (part); + Buffer.BlockCopy (part, 0, blob, pos, 20); + + return blob; + } + + static public RSA FromCapiPublicKeyBlob (byte[] blob) + { + return FromCapiPublicKeyBlob (blob, 0); + } + + static public RSA FromCapiPublicKeyBlob (byte[] blob, int offset) + { + if (blob == null) + throw new ArgumentNullException ("blob"); + if (offset >= blob.Length) + throw new ArgumentException ("blob is too small."); + + try { + if ((blob [offset] != 0x06) || // PUBLICKEYBLOB (0x06) + (blob [offset+1] != 0x02) || // Version (0x02) + (blob [offset+2] != 0x00) || // Reserved (word) + (blob [offset+3] != 0x00) || + (ToUInt32LE (blob, offset+8) != 0x31415352)) // DWORD magic = RSA1 + throw new CryptographicException ("Invalid blob header"); + + // ALGID (CALG_RSA_SIGN, CALG_RSA_KEYX, ...) + // int algId = ToInt32LE (blob, offset+4); + + // DWORD bitlen + int bitLen = ToInt32LE (blob, offset+12); + + // DWORD public exponent + RSAParameters rsap = new RSAParameters (); + rsap.Exponent = new byte [3]; + rsap.Exponent [0] = blob [offset+18]; + rsap.Exponent [1] = blob [offset+17]; + rsap.Exponent [2] = blob [offset+16]; + + int pos = offset+20; + // BYTE modulus[rsapubkey.bitlen/8]; + int byteLen = (bitLen >> 3); + rsap.Modulus = new byte [byteLen]; + Buffer.BlockCopy (blob, pos, rsap.Modulus, 0, byteLen); + Array.Reverse (rsap.Modulus); +#if NET_2_1 + RSA rsa = RSA.Create (); + rsa.ImportParameters (rsap); +#else + RSA rsa = null; + try { + rsa = RSA.Create (); + rsa.ImportParameters (rsap); + } + catch (CryptographicException) { + // this may cause problem when this code is run under + // the SYSTEM identity on Windows (e.g. ASP.NET). See + // http://bugzilla.ximian.com/show_bug.cgi?id=77559 + CspParameters csp = new CspParameters (); + csp.Flags = CspProviderFlags.UseMachineKeyStore; + rsa = new RSACryptoServiceProvider (csp); + rsa.ImportParameters (rsap); + } +#endif + return rsa; + } + catch (Exception e) { + throw new CryptographicException ("Invalid blob.", e); + } + } + + static public DSA FromCapiPublicKeyBlobDSA (byte[] blob) + { + return FromCapiPublicKeyBlobDSA (blob, 0); + } + + static public DSA FromCapiPublicKeyBlobDSA (byte[] blob, int offset) + { + if (blob == null) + throw new ArgumentNullException ("blob"); + if (offset >= blob.Length) + throw new ArgumentException ("blob is too small."); + + try { + if ((blob [offset] != 0x06) || // PUBLICKEYBLOB (0x06) + (blob [offset + 1] != 0x02) || // Version (0x02) + (blob [offset + 2] != 0x00) || // Reserved (word) + (blob [offset + 3] != 0x00) || + (ToUInt32LE (blob, offset + 8) != 0x31535344)) // DWORD magic + throw new CryptographicException ("Invalid blob header"); + + int bitlen = ToInt32LE (blob, offset + 12); + DSAParameters dsap = new DSAParameters (); + int bytelen = bitlen >> 3; + int pos = offset + 16; + + dsap.P = new byte [bytelen]; + Buffer.BlockCopy (blob, pos, dsap.P, 0, bytelen); + Array.Reverse (dsap.P); + pos += bytelen; + + dsap.Q = new byte [20]; + Buffer.BlockCopy (blob, pos, dsap.Q, 0, 20); + Array.Reverse (dsap.Q); + pos += 20; + + dsap.G = new byte [bytelen]; + Buffer.BlockCopy (blob, pos, dsap.G, 0, bytelen); + Array.Reverse (dsap.G); + pos += bytelen; + + dsap.Y = new byte [bytelen]; + Buffer.BlockCopy (blob, pos, dsap.Y, 0, bytelen); + Array.Reverse (dsap.Y); + pos += bytelen; + + dsap.Counter = ToInt32LE (blob, pos); + pos += 4; + + dsap.Seed = new byte [20]; + Buffer.BlockCopy (blob, pos, dsap.Seed, 0, 20); + Array.Reverse (dsap.Seed); + pos += 20; + + DSA dsa = (DSA)DSA.Create (); + dsa.ImportParameters (dsap); + return dsa; + } + catch (Exception e) { + throw new CryptographicException ("Invalid blob.", e); + } + } + + static public byte[] ToCapiPublicKeyBlob (RSA rsa) + { + RSAParameters p = rsa.ExportParameters (false); + int keyLength = p.Modulus.Length; // in bytes + byte[] blob = new byte [20 + keyLength]; + + blob [0] = 0x06; // Type - PUBLICKEYBLOB (0x06) + blob [1] = 0x02; // Version - Always CUR_BLOB_VERSION (0x02) + // [2], [3] // RESERVED - Always 0 + blob [5] = 0x24; // ALGID - Always 00 24 00 00 (for CALG_RSA_SIGN) + blob [8] = 0x52; // Magic - RSA1 (ASCII in hex) + blob [9] = 0x53; + blob [10] = 0x41; + blob [11] = 0x31; + + byte[] bitlen = GetBytesLE (keyLength << 3); + blob [12] = bitlen [0]; // bitlen + blob [13] = bitlen [1]; + blob [14] = bitlen [2]; + blob [15] = bitlen [3]; + + // public exponent (DWORD) + int pos = 16; + int n = p.Exponent.Length; + while (n > 0) + blob [pos++] = p.Exponent [--n]; + // modulus + pos = 20; + byte[] part = p.Modulus; + int len = part.Length; + Array.Reverse (part, 0, len); + Buffer.BlockCopy (part, 0, blob, pos, len); + pos += len; + return blob; + } + + static public byte[] ToCapiPublicKeyBlob (DSA dsa) + { + DSAParameters p = dsa.ExportParameters (false); + int keyLength = p.P.Length; // in bytes + + // header + P + Q + G + Y + count + seed + byte[] blob = new byte [16 + keyLength + 20 + keyLength + keyLength + 4 + 20]; + + blob [0] = 0x06; // Type - PUBLICKEYBLOB (0x06) + blob [1] = 0x02; // Version - Always CUR_BLOB_VERSION (0x02) + // [2], [3] // RESERVED - Always 0 + blob [5] = 0x22; // ALGID + blob [8] = 0x44; // Magic + blob [9] = 0x53; + blob [10] = 0x53; + blob [11] = 0x31; + + byte[] bitlen = GetBytesLE (keyLength << 3); + blob [12] = bitlen [0]; + blob [13] = bitlen [1]; + blob [14] = bitlen [2]; + blob [15] = bitlen [3]; + + int pos = 16; + byte[] part; + + part = p.P; + Array.Reverse (part); + Buffer.BlockCopy (part, 0, blob, pos, keyLength); + pos += keyLength; + + part = p.Q; + Array.Reverse (part); + Buffer.BlockCopy (part, 0, blob, pos, 20); + pos += 20; + + part = p.G; + Array.Reverse (part); + Buffer.BlockCopy (part, 0, blob, pos, keyLength); + pos += keyLength; + + part = p.Y; + Array.Reverse (part); + Buffer.BlockCopy (part, 0, blob, pos, keyLength); + pos += keyLength; + + Buffer.BlockCopy (GetBytesLE (p.Counter), 0, blob, pos, 4); + pos += 4; + + part = p.Seed; + Array.Reverse (part); + Buffer.BlockCopy (part, 0, blob, pos, 20); + + return blob; + } + + // PRIVATEKEYBLOB + // PUBLICKEYBLOB + static public RSA FromCapiKeyBlob (byte[] blob) + { + return FromCapiKeyBlob (blob, 0); + } + + static public RSA FromCapiKeyBlob (byte[] blob, int offset) + { + if (blob == null) + throw new ArgumentNullException ("blob"); + if (offset >= blob.Length) + throw new ArgumentException ("blob is too small."); + + switch (blob [offset]) { + case 0x00: + // this could be a public key inside an header + // like "sn -e" would produce + if (blob [offset + 12] == 0x06) { + return FromCapiPublicKeyBlob (blob, offset + 12); + } + break; + case 0x06: + return FromCapiPublicKeyBlob (blob, offset); + case 0x07: + return FromCapiPrivateKeyBlob (blob, offset); + } + throw new CryptographicException ("Unknown blob format."); + } + + static public DSA FromCapiKeyBlobDSA (byte[] blob) + { + return FromCapiKeyBlobDSA (blob, 0); + } + + static public DSA FromCapiKeyBlobDSA (byte[] blob, int offset) + { + if (blob == null) + throw new ArgumentNullException ("blob"); + if (offset >= blob.Length) + throw new ArgumentException ("blob is too small."); + + switch (blob [offset]) { + case 0x06: + return FromCapiPublicKeyBlobDSA (blob, offset); + case 0x07: + return FromCapiPrivateKeyBlobDSA (blob, offset); + } + throw new CryptographicException ("Unknown blob format."); + } + + static public byte[] ToCapiKeyBlob (AsymmetricAlgorithm keypair, bool includePrivateKey) + { + if (keypair == null) + throw new ArgumentNullException ("keypair"); + + // check between RSA and DSA (and potentially others like DH) + if (keypair is RSA) + return ToCapiKeyBlob ((RSA)keypair, includePrivateKey); + else if (keypair is DSA) + return ToCapiKeyBlob ((DSA)keypair, includePrivateKey); + else + return null; // TODO + } + + static public byte[] ToCapiKeyBlob (RSA rsa, bool includePrivateKey) + { + if (rsa == null) + throw new ArgumentNullException ("rsa"); + + if (includePrivateKey) + return ToCapiPrivateKeyBlob (rsa); + else + return ToCapiPublicKeyBlob (rsa); + } + + static public byte[] ToCapiKeyBlob (DSA dsa, bool includePrivateKey) + { + if (dsa == null) + throw new ArgumentNullException ("dsa"); + + if (includePrivateKey) + return ToCapiPrivateKeyBlob (dsa); + else + return ToCapiPublicKeyBlob (dsa); + } + + static public string ToHex (byte[] input) + { + if (input == null) + return null; + + StringBuilder sb = new StringBuilder (input.Length * 2); + foreach (byte b in input) { + sb.Append (b.ToString ("X2", CultureInfo.InvariantCulture)); + } + return sb.ToString (); + } + + static private byte FromHexChar (char c) + { + if ((c >= 'a') && (c <= 'f')) + return (byte) (c - 'a' + 10); + if ((c >= 'A') && (c <= 'F')) + return (byte) (c - 'A' + 10); + if ((c >= '0') && (c <= '9')) + return (byte) (c - '0'); + throw new ArgumentException ("invalid hex char"); + } + + static public byte[] FromHex (string hex) + { + if (hex == null) + return null; + if ((hex.Length & 0x1) == 0x1) + throw new ArgumentException ("Length must be a multiple of 2"); + + byte[] result = new byte [hex.Length >> 1]; + int n = 0; + int i = 0; + while (n < result.Length) { + result [n] = (byte) (FromHexChar (hex [i++]) << 4); + result [n++] += FromHexChar (hex [i++]); + } + return result; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/MonoSymbolFile.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/MonoSymbolFile.cs new file mode 100644 index 000000000..8431c70a4 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/MonoSymbolFile.cs @@ -0,0 +1,637 @@ +// +// MonoSymbolFile.cs +// +// Authors: +// Martin Baulig (martin@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// (C) 2003 Ximian, Inc. http://www.ximian.com +// Copyright (C) 2012 Xamarin Inc (http://www.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.Reflection; +using System.Collections.Generic; +using System.IO; + +namespace Mono.CompilerServices.SymbolWriter +{ + public class MonoSymbolFileException : Exception + { + public MonoSymbolFileException () + : base () + { } + + public MonoSymbolFileException (string message, params object[] args) + : base (String.Format (message, args)) + { + } + + public MonoSymbolFileException (string message, Exception innerException) + : base (message, innerException) + { + } + } + + sealed class MyBinaryWriter : BinaryWriter + { + public MyBinaryWriter (Stream stream) + : base (stream) + { } + + public void WriteLeb128 (int value) + { + base.Write7BitEncodedInt (value); + } + } + + internal class MyBinaryReader : BinaryReader + { + public MyBinaryReader (Stream stream) + : base (stream) + { } + + public int ReadLeb128 () + { + return base.Read7BitEncodedInt (); + } + + public string ReadString (int offset) + { + long old_pos = BaseStream.Position; + BaseStream.Position = offset; + + string text = ReadString (); + + BaseStream.Position = old_pos; + return text; + } + } + + public interface ISourceFile + { + SourceFileEntry Entry { + get; + } + } + + public interface ICompileUnit + { + CompileUnitEntry Entry { + get; + } + } + + public interface IMethodDef + { + string Name { + get; + } + + int Token { + get; + } + } + + public class MonoSymbolFile : IDisposable + { + List methods = new List (); + List sources = new List (); + List comp_units = new List (); + Dictionary anonymous_scopes; + + OffsetTable ot; + int last_type_index; + int last_method_index; + int last_namespace_index; + + public readonly int MajorVersion = OffsetTable.MajorVersion; + public readonly int MinorVersion = OffsetTable.MinorVersion; + + public int NumLineNumbers; + + public MonoSymbolFile () + { + ot = new OffsetTable (); + } + + public int AddSource (SourceFileEntry source) + { + sources.Add (source); + return sources.Count; + } + + public int AddCompileUnit (CompileUnitEntry entry) + { + comp_units.Add (entry); + return comp_units.Count; + } + + public void AddMethod (MethodEntry entry) + { + methods.Add (entry); + } + + public MethodEntry DefineMethod (CompileUnitEntry comp_unit, int token, + ScopeVariable[] scope_vars, LocalVariableEntry[] locals, + LineNumberEntry[] lines, CodeBlockEntry[] code_blocks, + string real_name, MethodEntry.Flags flags, + int namespace_id) + { + if (reader != null) + throw new InvalidOperationException (); + + MethodEntry method = new MethodEntry ( + this, comp_unit, token, scope_vars, locals, lines, code_blocks, + real_name, flags, namespace_id); + AddMethod (method); + return method; + } + + internal void DefineAnonymousScope (int id) + { + if (reader != null) + throw new InvalidOperationException (); + + if (anonymous_scopes == null) + anonymous_scopes = new Dictionary (); + + anonymous_scopes.Add (id, new AnonymousScopeEntry (id)); + } + + internal void DefineCapturedVariable (int scope_id, string name, string captured_name, + CapturedVariable.CapturedKind kind) + { + if (reader != null) + throw new InvalidOperationException (); + + AnonymousScopeEntry scope = anonymous_scopes [scope_id]; + scope.AddCapturedVariable (name, captured_name, kind); + } + + internal void DefineCapturedScope (int scope_id, int id, string captured_name) + { + if (reader != null) + throw new InvalidOperationException (); + + AnonymousScopeEntry scope = anonymous_scopes [scope_id]; + scope.AddCapturedScope (id, captured_name); + } + + internal int GetNextTypeIndex () + { + return ++last_type_index; + } + + internal int GetNextMethodIndex () + { + return ++last_method_index; + } + + internal int GetNextNamespaceIndex () + { + return ++last_namespace_index; + } + + void Write (MyBinaryWriter bw, Guid guid) + { + // Magic number and file version. + bw.Write (OffsetTable.Magic); + bw.Write (MajorVersion); + bw.Write (MinorVersion); + + bw.Write (guid.ToByteArray ()); + + // + // Offsets of file sections; we must write this after we're done + // writing the whole file, so we just reserve the space for it here. + // + long offset_table_offset = bw.BaseStream.Position; + ot.Write (bw, MajorVersion, MinorVersion); + + // + // Sort the methods according to their tokens and update their index. + // + methods.Sort (); + for (int i = 0; i < methods.Count; i++) + methods [i].Index = i + 1; + + // + // Write data sections. + // + ot.DataSectionOffset = (int) bw.BaseStream.Position; + foreach (SourceFileEntry source in sources) + source.WriteData (bw); + foreach (CompileUnitEntry comp_unit in comp_units) + comp_unit.WriteData (bw); + foreach (MethodEntry method in methods) + method.WriteData (this, bw); + ot.DataSectionSize = (int) bw.BaseStream.Position - ot.DataSectionOffset; + + // + // Write the method index table. + // + ot.MethodTableOffset = (int) bw.BaseStream.Position; + for (int i = 0; i < methods.Count; i++) { + MethodEntry entry = methods [i]; + entry.Write (bw); + } + ot.MethodTableSize = (int) bw.BaseStream.Position - ot.MethodTableOffset; + + // + // Write source table. + // + ot.SourceTableOffset = (int) bw.BaseStream.Position; + for (int i = 0; i < sources.Count; i++) { + SourceFileEntry source = sources [i]; + source.Write (bw); + } + ot.SourceTableSize = (int) bw.BaseStream.Position - ot.SourceTableOffset; + + // + // Write compilation unit table. + // + ot.CompileUnitTableOffset = (int) bw.BaseStream.Position; + for (int i = 0; i < comp_units.Count; i++) { + CompileUnitEntry unit = comp_units [i]; + unit.Write (bw); + } + ot.CompileUnitTableSize = (int) bw.BaseStream.Position - ot.CompileUnitTableOffset; + + // + // Write anonymous scope table. + // + ot.AnonymousScopeCount = anonymous_scopes != null ? anonymous_scopes.Count : 0; + ot.AnonymousScopeTableOffset = (int) bw.BaseStream.Position; + if (anonymous_scopes != null) { + foreach (AnonymousScopeEntry scope in anonymous_scopes.Values) + scope.Write (bw); + } + ot.AnonymousScopeTableSize = (int) bw.BaseStream.Position - ot.AnonymousScopeTableOffset; + + // + // Fixup offset table. + // + ot.TypeCount = last_type_index; + ot.MethodCount = methods.Count; + ot.SourceCount = sources.Count; + ot.CompileUnitCount = comp_units.Count; + + // + // Write offset table. + // + ot.TotalFileSize = (int) bw.BaseStream.Position; + bw.Seek ((int) offset_table_offset, SeekOrigin.Begin); + ot.Write (bw, MajorVersion, MinorVersion); + bw.Seek (0, SeekOrigin.End); + +#if false + Console.WriteLine ("TOTAL: {0} line numbes, {1} bytes, extended {2} bytes, " + + "{3} methods.", NumLineNumbers, LineNumberSize, + ExtendedLineNumberSize, methods.Count); +#endif + } + + public void CreateSymbolFile (Guid guid, FileStream fs) + { + if (reader != null) + throw new InvalidOperationException (); + + Write (new MyBinaryWriter (fs), guid); + } + + MyBinaryReader reader; + Dictionary source_file_hash; + Dictionary compile_unit_hash; + + List method_list; + Dictionary method_token_hash; + Dictionary source_name_hash; + + Guid guid; + + MonoSymbolFile (Stream stream) + { + reader = new MyBinaryReader (stream); + + try { + long magic = reader.ReadInt64 (); + int major_version = reader.ReadInt32 (); + int minor_version = reader.ReadInt32 (); + + if (magic != OffsetTable.Magic) + throw new MonoSymbolFileException ("Symbol file is not a valid"); + if (major_version != OffsetTable.MajorVersion) + throw new MonoSymbolFileException ( + "Symbol file has version {0} but expected {1}", major_version, OffsetTable.MajorVersion); + if (minor_version != OffsetTable.MinorVersion) + throw new MonoSymbolFileException ("Symbol file has version {0}.{1} but expected {2}.{3}", + major_version, minor_version, + OffsetTable.MajorVersion, OffsetTable.MinorVersion); + + MajorVersion = major_version; + MinorVersion = minor_version; + guid = new Guid (reader.ReadBytes (16)); + + ot = new OffsetTable (reader, major_version, minor_version); + } catch (Exception e) { + throw new MonoSymbolFileException ("Cannot read symbol file", e); + } + + source_file_hash = new Dictionary (); + compile_unit_hash = new Dictionary (); + } + + public static MonoSymbolFile ReadSymbolFile (Assembly assembly) + { + string filename = assembly.Location; + string name = filename + ".mdb"; + + Module[] modules = assembly.GetModules (); + Guid assembly_guid = modules[0].ModuleVersionId; + + return ReadSymbolFile (name, assembly_guid); + } + + public static MonoSymbolFile ReadSymbolFile (string mdbFilename) + { + return ReadSymbolFile (new FileStream (mdbFilename, FileMode.Open, FileAccess.Read)); + } + + public static MonoSymbolFile ReadSymbolFile (string mdbFilename, Guid assemblyGuid) + { + var sf = ReadSymbolFile (mdbFilename); + if (assemblyGuid != sf.guid) + throw new MonoSymbolFileException ("Symbol file `{0}' does not match assembly", mdbFilename); + + return sf; + } + + public static MonoSymbolFile ReadSymbolFile (Stream stream) + { + return new MonoSymbolFile (stream); + } + + public int CompileUnitCount { + get { return ot.CompileUnitCount; } + } + + public int SourceCount { + get { return ot.SourceCount; } + } + + public int MethodCount { + get { return ot.MethodCount; } + } + + public int TypeCount { + get { return ot.TypeCount; } + } + + public int AnonymousScopeCount { + get { return ot.AnonymousScopeCount; } + } + + public int NamespaceCount { + get { return last_namespace_index; } + } + + public Guid Guid { + get { return guid; } + } + + public OffsetTable OffsetTable { + get { return ot; } + } + + internal int LineNumberCount = 0; + internal int LocalCount = 0; + internal int StringSize = 0; + + internal int LineNumberSize = 0; + internal int ExtendedLineNumberSize = 0; + + public SourceFileEntry GetSourceFile (int index) + { + if ((index < 1) || (index > ot.SourceCount)) + throw new ArgumentException (); + if (reader == null) + throw new InvalidOperationException (); + + lock (this) { + SourceFileEntry source; + if (source_file_hash.TryGetValue (index, out source)) + return source; + + long old_pos = reader.BaseStream.Position; + + reader.BaseStream.Position = ot.SourceTableOffset + + SourceFileEntry.Size * (index - 1); + source = new SourceFileEntry (this, reader); + source_file_hash.Add (index, source); + + reader.BaseStream.Position = old_pos; + return source; + } + } + + public SourceFileEntry[] Sources { + get { + if (reader == null) + throw new InvalidOperationException (); + + SourceFileEntry[] retval = new SourceFileEntry [SourceCount]; + for (int i = 0; i < SourceCount; i++) + retval [i] = GetSourceFile (i + 1); + return retval; + } + } + + public CompileUnitEntry GetCompileUnit (int index) + { + if ((index < 1) || (index > ot.CompileUnitCount)) + throw new ArgumentException (); + if (reader == null) + throw new InvalidOperationException (); + + lock (this) { + CompileUnitEntry unit; + if (compile_unit_hash.TryGetValue (index, out unit)) + return unit; + + long old_pos = reader.BaseStream.Position; + + reader.BaseStream.Position = ot.CompileUnitTableOffset + + CompileUnitEntry.Size * (index - 1); + unit = new CompileUnitEntry (this, reader); + compile_unit_hash.Add (index, unit); + + reader.BaseStream.Position = old_pos; + return unit; + } + } + + public CompileUnitEntry[] CompileUnits { + get { + if (reader == null) + throw new InvalidOperationException (); + + CompileUnitEntry[] retval = new CompileUnitEntry [CompileUnitCount]; + for (int i = 0; i < CompileUnitCount; i++) + retval [i] = GetCompileUnit (i + 1); + return retval; + } + } + + void read_methods () + { + lock (this) { + if (method_token_hash != null) + return; + + method_token_hash = new Dictionary (); + method_list = new List (); + + long old_pos = reader.BaseStream.Position; + reader.BaseStream.Position = ot.MethodTableOffset; + + for (int i = 0; i < MethodCount; i++) { + MethodEntry entry = new MethodEntry (this, reader, i + 1); + method_token_hash.Add (entry.Token, entry); + method_list.Add (entry); + } + + reader.BaseStream.Position = old_pos; + } + } + + public MethodEntry GetMethodByToken (int token) + { + if (reader == null) + throw new InvalidOperationException (); + + lock (this) { + read_methods (); + MethodEntry me; + method_token_hash.TryGetValue (token, out me); + return me; + } + } + + public MethodEntry GetMethod (int index) + { + if ((index < 1) || (index > ot.MethodCount)) + throw new ArgumentException (); + if (reader == null) + throw new InvalidOperationException (); + + lock (this) { + read_methods (); + return method_list [index - 1]; + } + } + + public MethodEntry[] Methods { + get { + if (reader == null) + throw new InvalidOperationException (); + + lock (this) { + read_methods (); + MethodEntry[] retval = new MethodEntry [MethodCount]; + method_list.CopyTo (retval, 0); + return retval; + } + } + } + + public int FindSource (string file_name) + { + if (reader == null) + throw new InvalidOperationException (); + + lock (this) { + if (source_name_hash == null) { + source_name_hash = new Dictionary (); + + for (int i = 0; i < ot.SourceCount; i++) { + SourceFileEntry source = GetSourceFile (i + 1); + source_name_hash.Add (source.FileName, i); + } + } + + int value; + if (!source_name_hash.TryGetValue (file_name, out value)) + return -1; + return value; + } + } + + public AnonymousScopeEntry GetAnonymousScope (int id) + { + if (reader == null) + throw new InvalidOperationException (); + + AnonymousScopeEntry scope; + lock (this) { + if (anonymous_scopes != null) { + anonymous_scopes.TryGetValue (id, out scope); + return scope; + } + + anonymous_scopes = new Dictionary (); + reader.BaseStream.Position = ot.AnonymousScopeTableOffset; + for (int i = 0; i < ot.AnonymousScopeCount; i++) { + scope = new AnonymousScopeEntry (reader); + anonymous_scopes.Add (scope.ID, scope); + } + + return anonymous_scopes [id]; + } + } + + internal MyBinaryReader BinaryReader { + get { + if (reader == null) + throw new InvalidOperationException (); + + return reader; + } + } + + public void Dispose () + { + Dispose (true); + } + + protected virtual void Dispose (bool disposing) + { + if (disposing) { + if (reader != null) { + reader.Close (); + reader = null; + } + } + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/MonoSymbolTable.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/MonoSymbolTable.cs new file mode 100644 index 000000000..277f25a7f --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/MonoSymbolTable.cs @@ -0,0 +1,1437 @@ +// +// Mono.CSharp.Debugger/MonoSymbolTable.cs +// +// Author: +// Martin Baulig (martin@ximian.com) +// +// (C) 2002 Ximian, Inc. http://www.ximian.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.Security.Cryptography; +using System.Collections.Generic; +using System.Text; +using System.IO; + +// +// Parts which are actually written into the symbol file are marked with +// +// #region This is actually written to the symbol file +// #endregion +// +// Please do not modify these regions without previously talking to me. +// +// All changes to the file format must be synchronized in several places: +// +// a) The fields in these regions (and their order) must match the actual +// contents of the symbol file. +// +// This helps people to understand the symbol file format without reading +// too much source code, ie. you look at the appropriate region and then +// you know what's actually in the file. +// +// It is also required to help me enforce b). +// +// b) The regions must be kept in sync with the unmanaged code in +// mono/metadata/debug-mono-symfile.h +// +// When making changes to the file format, you must also increase two version +// numbers: +// +// i) OffsetTable.Version in this file. +// ii) MONO_SYMBOL_FILE_VERSION in mono/metadata/debug-mono-symfile.h +// +// After doing so, recompile everything, including the debugger. Symbol files +// with different versions are incompatible to each other and the debugger and +// the runtime enfore this, so you need to recompile all your assemblies after +// changing the file format. +// + +namespace Mono.CompilerServices.SymbolWriter +{ + public class OffsetTable + { + public const int MajorVersion = 50; + public const int MinorVersion = 0; + public const long Magic = 0x45e82623fd7fa614; + + #region This is actually written to the symbol file + public int TotalFileSize; + public int DataSectionOffset; + public int DataSectionSize; + public int CompileUnitCount; + public int CompileUnitTableOffset; + public int CompileUnitTableSize; + public int SourceCount; + public int SourceTableOffset; + public int SourceTableSize; + public int MethodCount; + public int MethodTableOffset; + public int MethodTableSize; + public int TypeCount; + public int AnonymousScopeCount; + public int AnonymousScopeTableOffset; + public int AnonymousScopeTableSize; + + [Flags] + public enum Flags + { + IsAspxSource = 1, + WindowsFileNames = 2 + } + + public Flags FileFlags; + + public int LineNumberTable_LineBase = LineNumberTable.Default_LineBase; + public int LineNumberTable_LineRange = LineNumberTable.Default_LineRange; + public int LineNumberTable_OpcodeBase = LineNumberTable.Default_OpcodeBase; + #endregion + + internal OffsetTable () + { + int platform = (int) Environment.OSVersion.Platform; + if ((platform != 4) && (platform != 128)) + FileFlags |= Flags.WindowsFileNames; + } + + internal OffsetTable (BinaryReader reader, int major_version, int minor_version) + { + TotalFileSize = reader.ReadInt32 (); + DataSectionOffset = reader.ReadInt32 (); + DataSectionSize = reader.ReadInt32 (); + CompileUnitCount = reader.ReadInt32 (); + CompileUnitTableOffset = reader.ReadInt32 (); + CompileUnitTableSize = reader.ReadInt32 (); + SourceCount = reader.ReadInt32 (); + SourceTableOffset = reader.ReadInt32 (); + SourceTableSize = reader.ReadInt32 (); + MethodCount = reader.ReadInt32 (); + MethodTableOffset = reader.ReadInt32 (); + MethodTableSize = reader.ReadInt32 (); + TypeCount = reader.ReadInt32 (); + + AnonymousScopeCount = reader.ReadInt32 (); + AnonymousScopeTableOffset = reader.ReadInt32 (); + AnonymousScopeTableSize = reader.ReadInt32 (); + + LineNumberTable_LineBase = reader.ReadInt32 (); + LineNumberTable_LineRange = reader.ReadInt32 (); + LineNumberTable_OpcodeBase = reader.ReadInt32 (); + + FileFlags = (Flags) reader.ReadInt32 (); + } + + internal void Write (BinaryWriter bw, int major_version, int minor_version) + { + bw.Write (TotalFileSize); + bw.Write (DataSectionOffset); + bw.Write (DataSectionSize); + bw.Write (CompileUnitCount); + bw.Write (CompileUnitTableOffset); + bw.Write (CompileUnitTableSize); + bw.Write (SourceCount); + bw.Write (SourceTableOffset); + bw.Write (SourceTableSize); + bw.Write (MethodCount); + bw.Write (MethodTableOffset); + bw.Write (MethodTableSize); + bw.Write (TypeCount); + + bw.Write (AnonymousScopeCount); + bw.Write (AnonymousScopeTableOffset); + bw.Write (AnonymousScopeTableSize); + + bw.Write (LineNumberTable_LineBase); + bw.Write (LineNumberTable_LineRange); + bw.Write (LineNumberTable_OpcodeBase); + + bw.Write ((int) FileFlags); + } + + public override string ToString () + { + return String.Format ( + "OffsetTable [{0} - {1}:{2} - {3}:{4}:{5} - {6}:{7}:{8} - {9}]", + TotalFileSize, DataSectionOffset, DataSectionSize, SourceCount, + SourceTableOffset, SourceTableSize, MethodCount, MethodTableOffset, + MethodTableSize, TypeCount); + } + } + + public class LineNumberEntry + { + #region This is actually written to the symbol file + public readonly int Row; + public int Column; + public int EndRow, EndColumn; + public readonly int File; + public readonly int Offset; + public readonly bool IsHidden; // Obsolete is never used + #endregion + + public sealed class LocationComparer : IComparer + { + public static readonly LocationComparer Default = new LocationComparer (); + + public int Compare (LineNumberEntry l1, LineNumberEntry l2) + { + return l1.Row == l2.Row ? + l1.Column.CompareTo (l2.Column) : + l1.Row.CompareTo (l2.Row); + } + } + + public static readonly LineNumberEntry Null = new LineNumberEntry (0, 0, 0, 0); + + public LineNumberEntry (int file, int row, int column, int offset) + : this (file, row, offset, column, false) + { + } + + public LineNumberEntry (int file, int row, int offset) + : this (file, row, -1, offset, false) + { + } + + public LineNumberEntry (int file, int row, int column, int offset, bool is_hidden) + : this (file, row, column, -1, -1, offset, is_hidden) + { + } + + public LineNumberEntry (int file, int row, int column, int end_row, int end_column, int offset, bool is_hidden) + { + this.File = file; + this.Row = row; + this.Column = column; + this.EndRow = end_row; + this.EndColumn = end_column; + this.Offset = offset; + this.IsHidden = is_hidden; + } + + public override string ToString () + { + return String.Format ("[Line {0}:{1,2}-{3,4}:{5}]", File, Row, Column, EndRow, EndColumn, Offset); + } + } + + public class CodeBlockEntry + { + public int Index; + #region This is actually written to the symbol file + public int Parent; + public Type BlockType; + public int StartOffset; + public int EndOffset; + #endregion + + public enum Type { + Lexical = 1, + CompilerGenerated = 2, + IteratorBody = 3, + IteratorDispatcher = 4 + } + + public CodeBlockEntry (int index, int parent, Type type, int start_offset) + { + this.Index = index; + this.Parent = parent; + this.BlockType = type; + this.StartOffset = start_offset; + } + + internal CodeBlockEntry (int index, MyBinaryReader reader) + { + this.Index = index; + int type_flag = reader.ReadLeb128 (); + BlockType = (Type) (type_flag & 0x3f); + this.Parent = reader.ReadLeb128 (); + this.StartOffset = reader.ReadLeb128 (); + this.EndOffset = reader.ReadLeb128 (); + + /* Reserved for future extensions. */ + if ((type_flag & 0x40) != 0) { + int data_size = reader.ReadInt16 (); + reader.BaseStream.Position += data_size; + } + } + + public void Close (int end_offset) + { + this.EndOffset = end_offset; + } + + internal void Write (MyBinaryWriter bw) + { + bw.WriteLeb128 ((int) BlockType); + bw.WriteLeb128 (Parent); + bw.WriteLeb128 (StartOffset); + bw.WriteLeb128 (EndOffset); + } + + public override string ToString () + { + return String.Format ("[CodeBlock {0}:{1}:{2}:{3}:{4}]", + Index, Parent, BlockType, StartOffset, EndOffset); + } + } + + public struct LocalVariableEntry + { + #region This is actually written to the symbol file + public readonly int Index; + public readonly string Name; + public readonly int BlockIndex; + #endregion + + public LocalVariableEntry (int index, string name, int block) + { + this.Index = index; + this.Name = name; + this.BlockIndex = block; + } + + internal LocalVariableEntry (MonoSymbolFile file, MyBinaryReader reader) + { + Index = reader.ReadLeb128 (); + Name = reader.ReadString (); + BlockIndex = reader.ReadLeb128 (); + } + + internal void Write (MonoSymbolFile file, MyBinaryWriter bw) + { + bw.WriteLeb128 (Index); + bw.Write (Name); + bw.WriteLeb128 (BlockIndex); + } + + public override string ToString () + { + return String.Format ("[LocalVariable {0}:{1}:{2}]", + Name, Index, BlockIndex - 1); + } + } + + public struct CapturedVariable + { + #region This is actually written to the symbol file + public readonly string Name; + public readonly string CapturedName; + public readonly CapturedKind Kind; + #endregion + + public enum CapturedKind : byte + { + Local, + Parameter, + This + } + + public CapturedVariable (string name, string captured_name, + CapturedKind kind) + { + this.Name = name; + this.CapturedName = captured_name; + this.Kind = kind; + } + + internal CapturedVariable (MyBinaryReader reader) + { + Name = reader.ReadString (); + CapturedName = reader.ReadString (); + Kind = (CapturedKind) reader.ReadByte (); + } + + internal void Write (MyBinaryWriter bw) + { + bw.Write (Name); + bw.Write (CapturedName); + bw.Write ((byte) Kind); + } + + public override string ToString () + { + return String.Format ("[CapturedVariable {0}:{1}:{2}]", + Name, CapturedName, Kind); + } + } + + public struct CapturedScope + { + #region This is actually written to the symbol file + public readonly int Scope; + public readonly string CapturedName; + #endregion + + public CapturedScope (int scope, string captured_name) + { + this.Scope = scope; + this.CapturedName = captured_name; + } + + internal CapturedScope (MyBinaryReader reader) + { + Scope = reader.ReadLeb128 (); + CapturedName = reader.ReadString (); + } + + internal void Write (MyBinaryWriter bw) + { + bw.WriteLeb128 (Scope); + bw.Write (CapturedName); + } + + public override string ToString () + { + return String.Format ("[CapturedScope {0}:{1}]", + Scope, CapturedName); + } + } + + public struct ScopeVariable + { + #region This is actually written to the symbol file + public readonly int Scope; + public readonly int Index; + #endregion + + public ScopeVariable (int scope, int index) + { + this.Scope = scope; + this.Index = index; + } + + internal ScopeVariable (MyBinaryReader reader) + { + Scope = reader.ReadLeb128 (); + Index = reader.ReadLeb128 (); + } + + internal void Write (MyBinaryWriter bw) + { + bw.WriteLeb128 (Scope); + bw.WriteLeb128 (Index); + } + + public override string ToString () + { + return String.Format ("[ScopeVariable {0}:{1}]", Scope, Index); + } + } + + public class AnonymousScopeEntry + { + #region This is actually written to the symbol file + public readonly int ID; + #endregion + + List captured_vars = new List (); + List captured_scopes = new List (); + + public AnonymousScopeEntry (int id) + { + this.ID = id; + } + + internal AnonymousScopeEntry (MyBinaryReader reader) + { + ID = reader.ReadLeb128 (); + + int num_captured_vars = reader.ReadLeb128 (); + for (int i = 0; i < num_captured_vars; i++) + captured_vars.Add (new CapturedVariable (reader)); + + int num_captured_scopes = reader.ReadLeb128 (); + for (int i = 0; i < num_captured_scopes; i++) + captured_scopes.Add (new CapturedScope (reader)); + } + + internal void AddCapturedVariable (string name, string captured_name, + CapturedVariable.CapturedKind kind) + { + captured_vars.Add (new CapturedVariable (name, captured_name, kind)); + } + + public CapturedVariable[] CapturedVariables { + get { + CapturedVariable[] retval = new CapturedVariable [captured_vars.Count]; + captured_vars.CopyTo (retval, 0); + return retval; + } + } + + internal void AddCapturedScope (int scope, string captured_name) + { + captured_scopes.Add (new CapturedScope (scope, captured_name)); + } + + public CapturedScope[] CapturedScopes { + get { + CapturedScope[] retval = new CapturedScope [captured_scopes.Count]; + captured_scopes.CopyTo (retval, 0); + return retval; + } + } + + internal void Write (MyBinaryWriter bw) + { + bw.WriteLeb128 (ID); + + bw.WriteLeb128 (captured_vars.Count); + foreach (CapturedVariable cv in captured_vars) + cv.Write (bw); + + bw.WriteLeb128 (captured_scopes.Count); + foreach (CapturedScope cs in captured_scopes) + cs.Write (bw); + } + + public override string ToString () + { + return String.Format ("[AnonymousScope {0}]", ID); + } + } + + public class CompileUnitEntry : ICompileUnit + { + #region This is actually written to the symbol file + public readonly int Index; + int DataOffset; + #endregion + + MonoSymbolFile file; + SourceFileEntry source; + List include_files; + List namespaces; + + bool creating; + + public static int Size { + get { return 8; } + } + + CompileUnitEntry ICompileUnit.Entry { + get { return this; } + } + + public CompileUnitEntry (MonoSymbolFile file, SourceFileEntry source) + { + this.file = file; + this.source = source; + + this.Index = file.AddCompileUnit (this); + + creating = true; + namespaces = new List (); + } + + public void AddFile (SourceFileEntry file) + { + if (!creating) + throw new InvalidOperationException (); + + if (include_files == null) + include_files = new List (); + + include_files.Add (file); + } + + public SourceFileEntry SourceFile { + get { + if (creating) + return source; + + ReadData (); + return source; + } + } + + public int DefineNamespace (string name, string[] using_clauses, int parent) + { + if (!creating) + throw new InvalidOperationException (); + + int index = file.GetNextNamespaceIndex (); + NamespaceEntry ns = new NamespaceEntry (name, index, using_clauses, parent); + namespaces.Add (ns); + return index; + } + + internal void WriteData (MyBinaryWriter bw) + { + DataOffset = (int) bw.BaseStream.Position; + bw.WriteLeb128 (source.Index); + + int count_includes = include_files != null ? include_files.Count : 0; + bw.WriteLeb128 (count_includes); + if (include_files != null) { + foreach (SourceFileEntry entry in include_files) + bw.WriteLeb128 (entry.Index); + } + + bw.WriteLeb128 (namespaces.Count); + foreach (NamespaceEntry ns in namespaces) + ns.Write (file, bw); + } + + internal void Write (BinaryWriter bw) + { + bw.Write (Index); + bw.Write (DataOffset); + } + + internal CompileUnitEntry (MonoSymbolFile file, MyBinaryReader reader) + { + this.file = file; + + Index = reader.ReadInt32 (); + DataOffset = reader.ReadInt32 (); + } + + public void ReadAll () + { + ReadData (); + } + + void ReadData () + { + if (creating) + throw new InvalidOperationException (); + + lock (file) { + if (namespaces != null) + return; + + MyBinaryReader reader = file.BinaryReader; + int old_pos = (int) reader.BaseStream.Position; + + reader.BaseStream.Position = DataOffset; + + int source_idx = reader.ReadLeb128 (); + source = file.GetSourceFile (source_idx); + + int count_includes = reader.ReadLeb128 (); + if (count_includes > 0) { + include_files = new List (); + for (int i = 0; i < count_includes; i++) + include_files.Add (file.GetSourceFile (reader.ReadLeb128 ())); + } + + int count_ns = reader.ReadLeb128 (); + namespaces = new List (); + for (int i = 0; i < count_ns; i ++) + namespaces.Add (new NamespaceEntry (file, reader)); + + reader.BaseStream.Position = old_pos; + } + } + + public NamespaceEntry[] Namespaces { + get { + ReadData (); + NamespaceEntry[] retval = new NamespaceEntry [namespaces.Count]; + namespaces.CopyTo (retval, 0); + return retval; + } + } + + public SourceFileEntry[] IncludeFiles { + get { + ReadData (); + if (include_files == null) + return new SourceFileEntry [0]; + + SourceFileEntry[] retval = new SourceFileEntry [include_files.Count]; + include_files.CopyTo (retval, 0); + return retval; + } + } + } + + public class SourceFileEntry + { + #region This is actually written to the symbol file + public readonly int Index; + int DataOffset; + #endregion + + MonoSymbolFile file; + string file_name; + byte[] guid; + byte[] hash; + bool creating; + bool auto_generated; + + public static int Size { + get { return 8; } + } + + public SourceFileEntry (MonoSymbolFile file, string file_name) + { + this.file = file; + this.file_name = file_name; + this.Index = file.AddSource (this); + + creating = true; + } + + public SourceFileEntry (MonoSymbolFile file, string file_name, byte[] guid, byte[] checksum) + : this (file, file_name) + { + this.guid = guid; + this.hash = checksum; + } + + public byte[] Checksum { + get { + return hash; + } + } + + internal void WriteData (MyBinaryWriter bw) + { + DataOffset = (int) bw.BaseStream.Position; + bw.Write (file_name); + + if (guid == null) + guid = new byte[16]; + + if (hash == null) { + try { + using (FileStream fs = new FileStream (file_name, FileMode.Open, FileAccess.Read)) { + MD5 md5 = MD5.Create (); + hash = md5.ComputeHash (fs); + } + } catch { + hash = new byte [16]; + } + } + + bw.Write (guid); + bw.Write (hash); + bw.Write ((byte) (auto_generated ? 1 : 0)); + } + + internal void Write (BinaryWriter bw) + { + bw.Write (Index); + bw.Write (DataOffset); + } + + internal SourceFileEntry (MonoSymbolFile file, MyBinaryReader reader) + { + this.file = file; + + Index = reader.ReadInt32 (); + DataOffset = reader.ReadInt32 (); + + int old_pos = (int) reader.BaseStream.Position; + reader.BaseStream.Position = DataOffset; + + file_name = reader.ReadString (); + guid = reader.ReadBytes (16); + hash = reader.ReadBytes (16); + auto_generated = reader.ReadByte () == 1; + + reader.BaseStream.Position = old_pos; + } + + public string FileName { + get { return file_name; } + set { file_name = value; } + } + + public bool AutoGenerated { + get { return auto_generated; } + } + + public void SetAutoGenerated () + { + if (!creating) + throw new InvalidOperationException (); + + auto_generated = true; + file.OffsetTable.FileFlags |= OffsetTable.Flags.IsAspxSource; + } + + public bool CheckChecksum () + { + try { + using (FileStream fs = new FileStream (file_name, FileMode.Open)) { + MD5 md5 = MD5.Create (); + byte[] data = md5.ComputeHash (fs); + for (int i = 0; i < 16; i++) + if (data [i] != hash [i]) + return false; + return true; + } + } catch { + return false; + } + } + + public override string ToString () + { + return String.Format ("SourceFileEntry ({0}:{1})", Index, DataOffset); + } + } + + public class LineNumberTable + { + protected LineNumberEntry[] _line_numbers; + public LineNumberEntry[] LineNumbers { + get { return _line_numbers; } + } + + public readonly int LineBase; + public readonly int LineRange; + public readonly byte OpcodeBase; + public readonly int MaxAddressIncrement; + +#region Configurable constants + public const int Default_LineBase = -1; + public const int Default_LineRange = 8; + public const byte Default_OpcodeBase = 9; + +#endregion + + public const byte DW_LNS_copy = 1; + public const byte DW_LNS_advance_pc = 2; + public const byte DW_LNS_advance_line = 3; + public const byte DW_LNS_set_file = 4; + public const byte DW_LNS_const_add_pc = 8; + + public const byte DW_LNE_end_sequence = 1; + + // MONO extensions. + public const byte DW_LNE_MONO_negate_is_hidden = 0x40; + + internal const byte DW_LNE_MONO__extensions_start = 0x40; + internal const byte DW_LNE_MONO__extensions_end = 0x7f; + + protected LineNumberTable (MonoSymbolFile file) + { + this.LineBase = file.OffsetTable.LineNumberTable_LineBase; + this.LineRange = file.OffsetTable.LineNumberTable_LineRange; + this.OpcodeBase = (byte) file.OffsetTable.LineNumberTable_OpcodeBase; + this.MaxAddressIncrement = (255 - OpcodeBase) / LineRange; + } + + internal LineNumberTable (MonoSymbolFile file, LineNumberEntry[] lines) + : this (file) + { + this._line_numbers = lines; + } + + internal void Write (MonoSymbolFile file, MyBinaryWriter bw, bool hasColumnsInfo, bool hasEndInfo) + { + int start = (int) bw.BaseStream.Position; + + bool last_is_hidden = false; + int last_line = 1, last_offset = 0, last_file = 1; + for (int i = 0; i < LineNumbers.Length; i++) { + int line_inc = LineNumbers [i].Row - last_line; + int offset_inc = LineNumbers [i].Offset - last_offset; + + if (LineNumbers [i].File != last_file) { + bw.Write (DW_LNS_set_file); + bw.WriteLeb128 (LineNumbers [i].File); + last_file = LineNumbers [i].File; + } + + if (LineNumbers [i].IsHidden != last_is_hidden) { + bw.Write ((byte) 0); + bw.Write ((byte) 1); + bw.Write (DW_LNE_MONO_negate_is_hidden); + last_is_hidden = LineNumbers [i].IsHidden; + } + + if (offset_inc >= MaxAddressIncrement) { + if (offset_inc < 2 * MaxAddressIncrement) { + bw.Write (DW_LNS_const_add_pc); + offset_inc -= MaxAddressIncrement; + } else { + bw.Write (DW_LNS_advance_pc); + bw.WriteLeb128 (offset_inc); + offset_inc = 0; + } + } + + if ((line_inc < LineBase) || (line_inc >= LineBase + LineRange)) { + bw.Write (DW_LNS_advance_line); + bw.WriteLeb128 (line_inc); + if (offset_inc != 0) { + bw.Write (DW_LNS_advance_pc); + bw.WriteLeb128 (offset_inc); + } + bw.Write (DW_LNS_copy); + } else { + byte opcode; + opcode = (byte) (line_inc - LineBase + (LineRange * offset_inc) + + OpcodeBase); + bw.Write (opcode); + } + + last_line = LineNumbers [i].Row; + last_offset = LineNumbers [i].Offset; + } + + bw.Write ((byte) 0); + bw.Write ((byte) 1); + bw.Write (DW_LNE_end_sequence); + + if (hasColumnsInfo) { + for (int i = 0; i < LineNumbers.Length; i++) { + var ln = LineNumbers [i]; + if (ln.Row >= 0) + bw.WriteLeb128 (ln.Column); + } + } + + if (hasEndInfo) { + for (int i = 0; i < LineNumbers.Length; i++) { + var ln = LineNumbers [i]; + if (ln.EndRow == -1 || ln.EndColumn == -1 || ln.Row > ln.EndRow) { + bw.WriteLeb128 (0xffffff); + } else { + bw.WriteLeb128 (ln.EndRow - ln.Row); + bw.WriteLeb128 (ln.EndColumn); + } + } + } + + file.ExtendedLineNumberSize += (int) bw.BaseStream.Position - start; + } + + internal static LineNumberTable Read (MonoSymbolFile file, MyBinaryReader br, bool readColumnsInfo, bool readEndInfo) + { + LineNumberTable lnt = new LineNumberTable (file); + lnt.DoRead (file, br, readColumnsInfo, readEndInfo); + return lnt; + } + + void DoRead (MonoSymbolFile file, MyBinaryReader br, bool includesColumns, bool includesEnds) + { + var lines = new List (); + + bool is_hidden = false, modified = false; + int stm_line = 1, stm_offset = 0, stm_file = 1; + while (true) { + byte opcode = br.ReadByte (); + + if (opcode == 0) { + byte size = br.ReadByte (); + long end_pos = br.BaseStream.Position + size; + opcode = br.ReadByte (); + + if (opcode == DW_LNE_end_sequence) { + if (modified) + lines.Add (new LineNumberEntry ( + stm_file, stm_line, -1, stm_offset, is_hidden)); + break; + } else if (opcode == DW_LNE_MONO_negate_is_hidden) { + is_hidden = !is_hidden; + modified = true; + } else if ((opcode >= DW_LNE_MONO__extensions_start) && + (opcode <= DW_LNE_MONO__extensions_end)) { + ; // reserved for future extensions + } else { + throw new MonoSymbolFileException ("Unknown extended opcode {0:x}", opcode); + } + + br.BaseStream.Position = end_pos; + continue; + } else if (opcode < OpcodeBase) { + switch (opcode) { + case DW_LNS_copy: + lines.Add (new LineNumberEntry ( + stm_file, stm_line, -1, stm_offset, is_hidden)); + modified = false; + break; + case DW_LNS_advance_pc: + stm_offset += br.ReadLeb128 (); + modified = true; + break; + case DW_LNS_advance_line: + stm_line += br.ReadLeb128 (); + modified = true; + break; + case DW_LNS_set_file: + stm_file = br.ReadLeb128 (); + modified = true; + break; + case DW_LNS_const_add_pc: + stm_offset += MaxAddressIncrement; + modified = true; + break; + default: + throw new MonoSymbolFileException ( + "Unknown standard opcode {0:x} in LNT", + opcode); + } + } else { + opcode -= OpcodeBase; + + stm_offset += opcode / LineRange; + stm_line += LineBase + (opcode % LineRange); + lines.Add (new LineNumberEntry ( + stm_file, stm_line, -1, stm_offset, is_hidden)); + modified = false; + } + } + + _line_numbers = lines.ToArray (); + + if (includesColumns) { + for (int i = 0; i < _line_numbers.Length; ++i) { + var ln = _line_numbers[i]; + if (ln.Row >= 0) + ln.Column = br.ReadLeb128 (); + } + } + if (includesEnds) { + for (int i = 0; i < _line_numbers.Length; ++i) { + var ln = _line_numbers[i]; + + int row = br.ReadLeb128 (); + if (row == 0xffffff) { + ln.EndRow = -1; + ln.EndColumn = -1; + } else { + ln.EndRow = ln.Row + row; + ln.EndColumn = br.ReadLeb128 (); + } + } + } + } + + public bool GetMethodBounds (out LineNumberEntry start, out LineNumberEntry end) + { + if (_line_numbers.Length > 1) { + start = _line_numbers [0]; + end = _line_numbers [_line_numbers.Length - 1]; + return true; + } + + start = LineNumberEntry.Null; + end = LineNumberEntry.Null; + return false; + } + } + + public class MethodEntry : IComparable + { + #region This is actually written to the symbol file + public readonly int CompileUnitIndex; + public readonly int Token; + public readonly int NamespaceID; + + int DataOffset; + int LocalVariableTableOffset; + int LineNumberTableOffset; + int CodeBlockTableOffset; + int ScopeVariableTableOffset; + int RealNameOffset; + Flags flags; + #endregion + + int index; + + public Flags MethodFlags { + get { return flags; } + } + + public readonly CompileUnitEntry CompileUnit; + + LocalVariableEntry[] locals; + CodeBlockEntry[] code_blocks; + ScopeVariable[] scope_vars; + LineNumberTable lnt; + string real_name; + + public readonly MonoSymbolFile SymbolFile; + + public int Index { + get { return index; } + set { index = value; } + } + + [Flags] + public enum Flags + { + LocalNamesAmbiguous = 1, + ColumnsInfoIncluded = 1 << 1, + EndInfoIncluded = 1 << 2 + } + + public const int Size = 12; + + internal MethodEntry (MonoSymbolFile file, MyBinaryReader reader, int index) + { + this.SymbolFile = file; + this.index = index; + + Token = reader.ReadInt32 (); + DataOffset = reader.ReadInt32 (); + LineNumberTableOffset = reader.ReadInt32 (); + + long old_pos = reader.BaseStream.Position; + reader.BaseStream.Position = DataOffset; + + CompileUnitIndex = reader.ReadLeb128 (); + LocalVariableTableOffset = reader.ReadLeb128 (); + NamespaceID = reader.ReadLeb128 (); + + CodeBlockTableOffset = reader.ReadLeb128 (); + ScopeVariableTableOffset = reader.ReadLeb128 (); + + RealNameOffset = reader.ReadLeb128 (); + + flags = (Flags) reader.ReadLeb128 (); + + reader.BaseStream.Position = old_pos; + + CompileUnit = file.GetCompileUnit (CompileUnitIndex); + } + + internal MethodEntry (MonoSymbolFile file, CompileUnitEntry comp_unit, + int token, ScopeVariable[] scope_vars, + LocalVariableEntry[] locals, LineNumberEntry[] lines, + CodeBlockEntry[] code_blocks, string real_name, + Flags flags, int namespace_id) + { + this.SymbolFile = file; + this.real_name = real_name; + this.locals = locals; + this.code_blocks = code_blocks; + this.scope_vars = scope_vars; + this.flags = flags; + + index = -1; + + Token = token; + CompileUnitIndex = comp_unit.Index; + CompileUnit = comp_unit; + NamespaceID = namespace_id; + + CheckLineNumberTable (lines); + lnt = new LineNumberTable (file, lines); + file.NumLineNumbers += lines.Length; + + int num_locals = locals != null ? locals.Length : 0; + + if (num_locals <= 32) { + // Most of the time, the O(n^2) factor is actually + // less than the cost of allocating the hash table, + // 32 is a rough number obtained through some testing. + + for (int i = 0; i < num_locals; i ++) { + string nm = locals [i].Name; + + for (int j = i + 1; j < num_locals; j ++) { + if (locals [j].Name == nm) { + flags |= Flags.LocalNamesAmbiguous; + goto locals_check_done; + } + } + } + locals_check_done : + ; + } else { + var local_names = new Dictionary (); + foreach (LocalVariableEntry local in locals) { + if (local_names.ContainsKey (local.Name)) { + flags |= Flags.LocalNamesAmbiguous; + break; + } + local_names.Add (local.Name, local); + } + } + } + + static void CheckLineNumberTable (LineNumberEntry[] line_numbers) + { + int last_offset = -1; + int last_row = -1; + + if (line_numbers == null) + return; + + for (int i = 0; i < line_numbers.Length; i++) { + LineNumberEntry line = line_numbers [i]; + + if (line.Equals (LineNumberEntry.Null)) + throw new MonoSymbolFileException (); + + if (line.Offset < last_offset) + throw new MonoSymbolFileException (); + + if (line.Offset > last_offset) { + last_row = line.Row; + last_offset = line.Offset; + } else if (line.Row > last_row) { + last_row = line.Row; + } + } + } + + internal void Write (MyBinaryWriter bw) + { + if ((index <= 0) || (DataOffset == 0)) + throw new InvalidOperationException (); + + bw.Write (Token); + bw.Write (DataOffset); + bw.Write (LineNumberTableOffset); + } + + internal void WriteData (MonoSymbolFile file, MyBinaryWriter bw) + { + if (index <= 0) + throw new InvalidOperationException (); + + LocalVariableTableOffset = (int) bw.BaseStream.Position; + int num_locals = locals != null ? locals.Length : 0; + bw.WriteLeb128 (num_locals); + for (int i = 0; i < num_locals; i++) + locals [i].Write (file, bw); + file.LocalCount += num_locals; + + CodeBlockTableOffset = (int) bw.BaseStream.Position; + int num_code_blocks = code_blocks != null ? code_blocks.Length : 0; + bw.WriteLeb128 (num_code_blocks); + for (int i = 0; i < num_code_blocks; i++) + code_blocks [i].Write (bw); + + ScopeVariableTableOffset = (int) bw.BaseStream.Position; + int num_scope_vars = scope_vars != null ? scope_vars.Length : 0; + bw.WriteLeb128 (num_scope_vars); + for (int i = 0; i < num_scope_vars; i++) + scope_vars [i].Write (bw); + + if (real_name != null) { + RealNameOffset = (int) bw.BaseStream.Position; + bw.Write (real_name); + } + + foreach (var lne in lnt.LineNumbers) { + if (lne.EndRow != -1 || lne.EndColumn != -1) + flags |= Flags.EndInfoIncluded; + } + + LineNumberTableOffset = (int) bw.BaseStream.Position; + lnt.Write (file, bw, (flags & Flags.ColumnsInfoIncluded) != 0, (flags & Flags.EndInfoIncluded) != 0); + + DataOffset = (int) bw.BaseStream.Position; + + bw.WriteLeb128 (CompileUnitIndex); + bw.WriteLeb128 (LocalVariableTableOffset); + bw.WriteLeb128 (NamespaceID); + + bw.WriteLeb128 (CodeBlockTableOffset); + bw.WriteLeb128 (ScopeVariableTableOffset); + + bw.WriteLeb128 (RealNameOffset); + bw.WriteLeb128 ((int) flags); + } + + public void ReadAll () + { + GetLineNumberTable (); + GetLocals (); + GetCodeBlocks (); + GetScopeVariables (); + GetRealName (); + } + + public LineNumberTable GetLineNumberTable () + { + lock (SymbolFile) { + if (lnt != null) + return lnt; + + if (LineNumberTableOffset == 0) + return null; + + MyBinaryReader reader = SymbolFile.BinaryReader; + long old_pos = reader.BaseStream.Position; + reader.BaseStream.Position = LineNumberTableOffset; + + lnt = LineNumberTable.Read (SymbolFile, reader, (flags & Flags.ColumnsInfoIncluded) != 0, (flags & Flags.EndInfoIncluded) != 0); + + reader.BaseStream.Position = old_pos; + return lnt; + } + } + + public LocalVariableEntry[] GetLocals () + { + lock (SymbolFile) { + if (locals != null) + return locals; + + if (LocalVariableTableOffset == 0) + return null; + + MyBinaryReader reader = SymbolFile.BinaryReader; + long old_pos = reader.BaseStream.Position; + reader.BaseStream.Position = LocalVariableTableOffset; + + int num_locals = reader.ReadLeb128 (); + locals = new LocalVariableEntry [num_locals]; + + for (int i = 0; i < num_locals; i++) + locals [i] = new LocalVariableEntry (SymbolFile, reader); + + reader.BaseStream.Position = old_pos; + return locals; + } + } + + public CodeBlockEntry[] GetCodeBlocks () + { + lock (SymbolFile) { + if (code_blocks != null) + return code_blocks; + + if (CodeBlockTableOffset == 0) + return null; + + MyBinaryReader reader = SymbolFile.BinaryReader; + long old_pos = reader.BaseStream.Position; + reader.BaseStream.Position = CodeBlockTableOffset; + + int num_code_blocks = reader.ReadLeb128 (); + code_blocks = new CodeBlockEntry [num_code_blocks]; + + for (int i = 0; i < num_code_blocks; i++) + code_blocks [i] = new CodeBlockEntry (i, reader); + + reader.BaseStream.Position = old_pos; + return code_blocks; + } + } + + public ScopeVariable[] GetScopeVariables () + { + lock (SymbolFile) { + if (scope_vars != null) + return scope_vars; + + if (ScopeVariableTableOffset == 0) + return null; + + MyBinaryReader reader = SymbolFile.BinaryReader; + long old_pos = reader.BaseStream.Position; + reader.BaseStream.Position = ScopeVariableTableOffset; + + int num_scope_vars = reader.ReadLeb128 (); + scope_vars = new ScopeVariable [num_scope_vars]; + + for (int i = 0; i < num_scope_vars; i++) + scope_vars [i] = new ScopeVariable (reader); + + reader.BaseStream.Position = old_pos; + return scope_vars; + } + } + + public string GetRealName () + { + lock (SymbolFile) { + if (real_name != null) + return real_name; + + if (RealNameOffset == 0) + return null; + + real_name = SymbolFile.BinaryReader.ReadString (RealNameOffset); + return real_name; + } + } + + public int CompareTo (object obj) + { + MethodEntry method = (MethodEntry) obj; + + if (method.Token < Token) + return 1; + else if (method.Token > Token) + return -1; + else + return 0; + } + + public override string ToString () + { + return String.Format ("[Method {0}:{1:x}:{2}:{3}]", + index, Token, CompileUnitIndex, CompileUnit); + } + } + + public struct NamespaceEntry + { + #region This is actually written to the symbol file + public readonly string Name; + public readonly int Index; + public readonly int Parent; + public readonly string[] UsingClauses; + #endregion + + public NamespaceEntry (string name, int index, string[] using_clauses, int parent) + { + this.Name = name; + this.Index = index; + this.Parent = parent; + this.UsingClauses = using_clauses != null ? using_clauses : new string [0]; + } + + internal NamespaceEntry (MonoSymbolFile file, MyBinaryReader reader) + { + Name = reader.ReadString (); + Index = reader.ReadLeb128 (); + Parent = reader.ReadLeb128 (); + + int count = reader.ReadLeb128 (); + UsingClauses = new string [count]; + for (int i = 0; i < count; i++) + UsingClauses [i] = reader.ReadString (); + } + + internal void Write (MonoSymbolFile file, MyBinaryWriter bw) + { + bw.Write (Name); + bw.WriteLeb128 (Index); + bw.WriteLeb128 (Parent); + bw.WriteLeb128 (UsingClauses.Length); + foreach (string uc in UsingClauses) + bw.Write (uc); + } + + public override string ToString () + { + return String.Format ("[Namespace {0}:{1}:{2}]", Name, Index, Parent); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/MonoSymbolWriter.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/MonoSymbolWriter.cs new file mode 100644 index 000000000..b2c2afdba --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/MonoSymbolWriter.cs @@ -0,0 +1,238 @@ +// +// Mono.CSharp.Debugger/MonoSymbolWriter.cs +// +// Author: +// Martin Baulig (martin@ximian.com) +// +// This is the default implementation of the System.Diagnostics.SymbolStore.ISymbolWriter +// interface. +// +// (C) 2002 Ximian, Inc. http://www.ximian.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.Runtime.CompilerServices; +using System.Collections.Generic; +using System.IO; + +namespace Mono.CompilerServices.SymbolWriter +{ + public class MonoSymbolWriter + { + List methods; + List sources; + List comp_units; + protected readonly MonoSymbolFile file; + string filename; + + private SourceMethodBuilder current_method; + Stack current_method_stack = new Stack (); + + public MonoSymbolWriter (string filename) + { + this.methods = new List (); + this.sources = new List (); + this.comp_units = new List (); + this.file = new MonoSymbolFile (); + + this.filename = filename + ".mdb"; + } + + public MonoSymbolFile SymbolFile { + get { return file; } + } + + public void CloseNamespace () + { } + + public void DefineLocalVariable (int index, string name) + { + if (current_method == null) + return; + + current_method.AddLocal (index, name); + } + + public void DefineCapturedLocal (int scope_id, string name, string captured_name) + { + file.DefineCapturedVariable (scope_id, name, captured_name, + CapturedVariable.CapturedKind.Local); + } + + public void DefineCapturedParameter (int scope_id, string name, string captured_name) + { + file.DefineCapturedVariable (scope_id, name, captured_name, + CapturedVariable.CapturedKind.Parameter); + } + + public void DefineCapturedThis (int scope_id, string captured_name) + { + file.DefineCapturedVariable (scope_id, "this", captured_name, + CapturedVariable.CapturedKind.This); + } + + public void DefineCapturedScope (int scope_id, int id, string captured_name) + { + file.DefineCapturedScope (scope_id, id, captured_name); + } + + public void DefineScopeVariable (int scope, int index) + { + if (current_method == null) + return; + + current_method.AddScopeVariable (scope, index); + } + + public void MarkSequencePoint (int offset, SourceFileEntry file, int line, int column, + bool is_hidden) + { + if (current_method == null) + return; + + current_method.MarkSequencePoint (offset, file, line, column, is_hidden); + } + + public SourceMethodBuilder OpenMethod (ICompileUnit file, int ns_id, IMethodDef method) + { + SourceMethodBuilder builder = new SourceMethodBuilder (file, ns_id, method); + current_method_stack.Push (current_method); + current_method = builder; + methods.Add (current_method); + return builder; + } + + public void CloseMethod () + { + current_method = (SourceMethodBuilder) current_method_stack.Pop (); + } + + public SourceFileEntry DefineDocument (string url) + { + SourceFileEntry entry = new SourceFileEntry (file, url); + sources.Add (entry); + return entry; + } + + public SourceFileEntry DefineDocument (string url, byte[] guid, byte[] checksum) + { + SourceFileEntry entry = new SourceFileEntry (file, url, guid, checksum); + sources.Add (entry); + return entry; + } + + public CompileUnitEntry DefineCompilationUnit (SourceFileEntry source) + { + CompileUnitEntry entry = new CompileUnitEntry (file, source); + comp_units.Add (entry); + return entry; + } + + public int DefineNamespace (string name, CompileUnitEntry unit, + string[] using_clauses, int parent) + { + if ((unit == null) || (using_clauses == null)) + throw new NullReferenceException (); + + return unit.DefineNamespace (name, using_clauses, parent); + } + + public int OpenScope (int start_offset) + { + if (current_method == null) + return 0; + + current_method.StartBlock (CodeBlockEntry.Type.Lexical, start_offset); + return 0; + } + + public void CloseScope (int end_offset) + { + if (current_method == null) + return; + + current_method.EndBlock (end_offset); + } + + public void OpenCompilerGeneratedBlock (int start_offset) + { + if (current_method == null) + return; + + current_method.StartBlock (CodeBlockEntry.Type.CompilerGenerated, + start_offset); + } + + public void CloseCompilerGeneratedBlock (int end_offset) + { + if (current_method == null) + return; + + current_method.EndBlock (end_offset); + } + + public void StartIteratorBody (int start_offset) + { + current_method.StartBlock (CodeBlockEntry.Type.IteratorBody, + start_offset); + } + + public void EndIteratorBody (int end_offset) + { + current_method.EndBlock (end_offset); + } + + public void StartIteratorDispatcher (int start_offset) + { + current_method.StartBlock (CodeBlockEntry.Type.IteratorDispatcher, + start_offset); + } + + public void EndIteratorDispatcher (int end_offset) + { + current_method.EndBlock (end_offset); + } + + public void DefineAnonymousScope (int id) + { + file.DefineAnonymousScope (id); + } + + public void WriteSymbolFile (Guid guid) + { + foreach (SourceMethodBuilder method in methods) + method.DefineMethod (file); + + try { + // We mmap the file, so unlink the previous version since it may be in use + File.Delete (filename); + } catch { + // We can safely ignore + } + using (FileStream fs = new FileStream (filename, FileMode.Create, FileAccess.Write)) { + file.CreateSymbolFile (guid, fs); + } + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/SourceMethodBuilder.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/SourceMethodBuilder.cs new file mode 100644 index 000000000..bd801f657 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/SourceMethodBuilder.cs @@ -0,0 +1,190 @@ +// +// SourceMethodBuilder.cs +// +// Authors: +// Martin Baulig (martin@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// (C) 2002 Ximian, Inc. http://www.ximian.com +// Copyright (C) 2012 Xamarin Inc (http://www.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.Collections.Generic; + +namespace Mono.CompilerServices.SymbolWriter +{ + public class SourceMethodBuilder + { + List _locals; + List _blocks; + List _scope_vars; + Stack _block_stack; + readonly List method_lines; + + readonly ICompileUnit _comp_unit; + readonly int ns_id; + readonly IMethodDef method; + + public SourceMethodBuilder (ICompileUnit comp_unit) + { + this._comp_unit = comp_unit; + method_lines = new List (); + } + + public SourceMethodBuilder (ICompileUnit comp_unit, int ns_id, IMethodDef method) + : this (comp_unit) + { + this.ns_id = ns_id; + this.method = method; + } + + public void MarkSequencePoint (int offset, SourceFileEntry file, int line, int column, bool is_hidden) + { + MarkSequencePoint (offset, file, line, column, -1, -1, is_hidden); + } + + public void MarkSequencePoint (int offset, SourceFileEntry file, int line, int column, int end_line, int end_column, bool is_hidden) + { + int file_idx = file != null ? file.Index : 0; + var lne = new LineNumberEntry (file_idx, line, column, end_line, end_column, offset, is_hidden); + + if (method_lines.Count > 0) { + var prev = method_lines[method_lines.Count - 1]; + + // + // Same offset cannot be used for multiple lines + // + if (prev.Offset == offset) { + // + // Use the new location because debugger will adjust + // the breakpoint to next line with sequence point + // + if (LineNumberEntry.LocationComparer.Default.Compare (lne, prev) > 0) + method_lines[method_lines.Count - 1] = lne; + + return; + } + } + + method_lines.Add (lne); + } + + public void StartBlock (CodeBlockEntry.Type type, int start_offset) + { + if (_block_stack == null) { + _block_stack = new Stack (); + } + + if (_blocks == null) + _blocks = new List (); + + int parent = CurrentBlock != null ? CurrentBlock.Index : -1; + + CodeBlockEntry block = new CodeBlockEntry ( + _blocks.Count + 1, parent, type, start_offset); + + _block_stack.Push (block); + _blocks.Add (block); + } + + public void EndBlock (int end_offset) + { + CodeBlockEntry block = (CodeBlockEntry) _block_stack.Pop (); + block.Close (end_offset); + } + + public CodeBlockEntry[] Blocks { + get { + if (_blocks == null) + return new CodeBlockEntry [0]; + + CodeBlockEntry[] retval = new CodeBlockEntry [_blocks.Count]; + _blocks.CopyTo (retval, 0); + return retval; + } + } + + public CodeBlockEntry CurrentBlock { + get { + if ((_block_stack != null) && (_block_stack.Count > 0)) + return (CodeBlockEntry) _block_stack.Peek (); + else + return null; + } + } + + public LocalVariableEntry[] Locals { + get { + if (_locals == null) + return new LocalVariableEntry [0]; + else { + return _locals.ToArray (); + } + } + } + + public ICompileUnit SourceFile { + get { + return _comp_unit; + } + } + + public void AddLocal (int index, string name) + { + if (_locals == null) + _locals = new List (); + int block_idx = CurrentBlock != null ? CurrentBlock.Index : 0; + _locals.Add (new LocalVariableEntry (index, name, block_idx)); + } + + public ScopeVariable[] ScopeVariables { + get { + if (_scope_vars == null) + return new ScopeVariable [0]; + + return _scope_vars.ToArray (); + } + } + + public void AddScopeVariable (int scope, int index) + { + if (_scope_vars == null) + _scope_vars = new List (); + _scope_vars.Add ( + new ScopeVariable (scope, index)); + } + + public void DefineMethod (MonoSymbolFile file) + { + DefineMethod (file, method.Token); + } + + public void DefineMethod (MonoSymbolFile file, int token) + { + MethodEntry entry = new MethodEntry ( + file, _comp_unit.Entry, token, ScopeVariables, + Locals, method_lines.ToArray (), Blocks, null, MethodEntry.Flags.ColumnsInfoIncluded, ns_id); + + file.AddMethod (entry); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/anonymous.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/anonymous.cs new file mode 100644 index 000000000..e7f003784 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/anonymous.cs @@ -0,0 +1,2292 @@ +// +// anonymous.cs: Support for anonymous methods and types +// +// Author: +// Miguel de Icaza (miguel@ximain.com) +// Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// Copyright 2003-2011 Novell, Inc. +// Copyright 2011 Xamarin Inc +// + +using System; +using System.Collections.Generic; +using Mono.CompilerServices.SymbolWriter; +using System.Diagnostics; + +#if STATIC +using IKVM.Reflection; +using IKVM.Reflection.Emit; +using System.Diagnostics; +#else +using System.Reflection; +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp { + + public abstract class CompilerGeneratedContainer : ClassOrStruct + { + protected CompilerGeneratedContainer (TypeContainer parent, MemberName name, Modifiers mod) + : this (parent, name, mod, MemberKind.Class) + { + } + + protected CompilerGeneratedContainer (TypeContainer parent, MemberName name, Modifiers mod, MemberKind kind) + : base (parent, name, null, kind) + { + Debug.Assert ((mod & Modifiers.AccessibilityMask) != 0); + + ModFlags = mod | Modifiers.COMPILER_GENERATED | Modifiers.SEALED; + spec = new TypeSpec (Kind, null, this, null, ModFlags); + } + + protected void CheckMembersDefined () + { + if (HasMembersDefined) + throw new InternalErrorException ("Helper class already defined!"); + } + + protected override bool DoDefineMembers () + { + if (Kind == MemberKind.Class && !IsStatic && !PartialContainer.HasInstanceConstructor) { + DefineDefaultConstructor (false); + } + + return base.DoDefineMembers (); + } + + protected static MemberName MakeMemberName (MemberBase host, string name, int unique_id, TypeParameters tparams, Location loc) + { + string host_name = host == null ? null : host is InterfaceMemberBase ? ((InterfaceMemberBase)host).GetFullName (host.MemberName) : host.MemberName.Name; + string tname = MakeName (host_name, "c", name, unique_id); + TypeParameters args = null; + if (tparams != null) { + args = new TypeParameters (tparams.Count); + + // Type parameters will be filled later when we have TypeContainer + // instance, for now we need only correct arity to create valid name + for (int i = 0; i < tparams.Count; ++i) + args.Add ((TypeParameter) null); + } + + return new MemberName (tname, args, loc); + } + + public static string MakeName (string host, string typePrefix, string name, int id) + { + return "<" + host + ">" + typePrefix + "__" + name + id.ToString ("X"); + } + + protected override TypeSpec[] ResolveBaseTypes (out FullNamedExpression base_class) + { + base_type = Compiler.BuiltinTypes.Object; + + base_class = null; + return null; + } + } + + public class HoistedStoreyClass : CompilerGeneratedContainer + { + public sealed class HoistedField : Field + { + public HoistedField (HoistedStoreyClass parent, FullNamedExpression type, Modifiers mod, string name, + Attributes attrs, Location loc) + : base (parent, type, mod, new MemberName (name, loc), attrs) + { + } + + protected override bool ResolveMemberType () + { + if (!base.ResolveMemberType ()) + return false; + + HoistedStoreyClass parent = ((HoistedStoreyClass) Parent).GetGenericStorey (); + if (parent != null && parent.Mutator != null) + member_type = parent.Mutator.Mutate (MemberType); + + return true; + } + } + + protected TypeParameterMutator mutator; + + public HoistedStoreyClass (TypeDefinition parent, MemberName name, TypeParameters tparams, Modifiers mods, MemberKind kind) + : base (parent, name, mods | Modifiers.PRIVATE, kind) + { + + if (tparams != null) { + var type_params = name.TypeParameters; + var src = new TypeParameterSpec[tparams.Count]; + var dst = new TypeParameterSpec[tparams.Count]; + + for (int i = 0; i < tparams.Count; ++i) { + type_params[i] = tparams[i].CreateHoistedCopy (spec); + + src[i] = tparams[i].Type; + dst[i] = type_params[i].Type; + } + + // A copy is not enough, inflate any type parameter constraints + // using a new type parameters + var inflator = new TypeParameterInflator (this, null, src, dst); + for (int i = 0; i < tparams.Count; ++i) { + src[i].InflateConstraints (inflator, dst[i]); + } + + mutator = new TypeParameterMutator (tparams, type_params); + } + } + + #region Properties + + public TypeParameterMutator Mutator { + get { + return mutator; + } + set { + mutator = value; + } + } + + #endregion + + public HoistedStoreyClass GetGenericStorey () + { + TypeContainer storey = this; + while (storey != null && storey.CurrentTypeParameters == null) + storey = storey.Parent; + + return storey as HoistedStoreyClass; + } + } + + + // + // Anonymous method storey is created when an anonymous method uses + // variable or parameter from outer scope. They are then hoisted to + // anonymous method storey (captured) + // + public class AnonymousMethodStorey : HoistedStoreyClass + { + struct StoreyFieldPair + { + public readonly AnonymousMethodStorey Storey; + public readonly Field Field; + + public StoreyFieldPair (AnonymousMethodStorey storey, Field field) + { + this.Storey = storey; + this.Field = field; + } + } + + // + // Needed to delay hoisted _this_ initialization. When an anonymous + // method is used inside ctor and _this_ is hoisted, base ctor has to + // be called first, otherwise _this_ will be initialized with + // uninitialized value. + // + sealed class ThisInitializer : Statement + { + readonly HoistedThis hoisted_this; + readonly AnonymousMethodStorey parent; + + public ThisInitializer (HoistedThis hoisted_this, AnonymousMethodStorey parent) + { + this.hoisted_this = hoisted_this; + this.parent = parent; + } + + protected override void DoEmit (EmitContext ec) + { + Expression source; + + if (parent == null) + source = new CompilerGeneratedThis (ec.CurrentType, loc); + else { + source = new FieldExpr (parent.HoistedThis.Field, Location.Null) { + InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, Location.Null) + }; + } + + hoisted_this.EmitAssign (ec, source, false, false); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + return false; + } + + protected override void CloneTo (CloneContext clonectx, Statement target) + { + // Nothing to clone + } + } + + // Unique storey ID + public readonly int ID; + + public readonly ExplicitBlock OriginalSourceBlock; + + // A list of StoreyFieldPair with local field keeping parent storey instance + List used_parent_storeys; + List children_references; + + // A list of hoisted parameters + protected List hoisted_params; + List hoisted_local_params; + protected List hoisted_locals; + + // Hoisted this + protected HoistedThis hoisted_this; + + // Local variable which holds this storey instance + public Expression Instance; + + bool initialize_hoisted_this; + AnonymousMethodStorey hoisted_this_parent; + + public AnonymousMethodStorey (ExplicitBlock block, TypeDefinition parent, MemberBase host, TypeParameters tparams, string name, MemberKind kind) + : base (parent, MakeMemberName (host, name, parent.PartialContainer.CounterAnonymousContainers, tparams, block.StartLocation), + tparams, 0, kind) + { + OriginalSourceBlock = block; + ID = parent.PartialContainer.CounterAnonymousContainers++; + } + + public void AddCapturedThisField (EmitContext ec, AnonymousMethodStorey parent) + { + TypeExpr type_expr = new TypeExpression (ec.CurrentType, Location); + Field f = AddCompilerGeneratedField ("$this", type_expr); + hoisted_this = new HoistedThis (this, f); + + initialize_hoisted_this = true; + hoisted_this_parent = parent; + } + + public Field AddCapturedVariable (string name, TypeSpec type) + { + CheckMembersDefined (); + + FullNamedExpression field_type = new TypeExpression (type, Location); + if (!spec.IsGenericOrParentIsGeneric) + return AddCompilerGeneratedField (name, field_type); + + const Modifiers mod = Modifiers.INTERNAL | Modifiers.COMPILER_GENERATED; + Field f = new HoistedField (this, field_type, mod, name, null, Location); + AddField (f); + return f; + } + + protected Field AddCompilerGeneratedField (string name, FullNamedExpression type) + { + return AddCompilerGeneratedField (name, type, false); + } + + protected Field AddCompilerGeneratedField (string name, FullNamedExpression type, bool privateAccess) + { + Modifiers mod = Modifiers.COMPILER_GENERATED | (privateAccess ? Modifiers.PRIVATE : Modifiers.INTERNAL); + Field f = new Field (this, type, mod, new MemberName (name, Location), null); + AddField (f); + return f; + } + + // + // Creates a link between hoisted variable block and the anonymous method storey + // + // An anonymous method can reference variables from any outer block, but they are + // hoisted in their own ExplicitBlock. When more than one block is referenced we + // need to create another link between those variable storeys + // + public void AddReferenceFromChildrenBlock (ExplicitBlock block) + { + if (children_references == null) + children_references = new List (); + + if (!children_references.Contains (block)) + children_references.Add (block); + } + + public void AddParentStoreyReference (EmitContext ec, AnonymousMethodStorey storey) + { + CheckMembersDefined (); + + if (used_parent_storeys == null) + used_parent_storeys = new List (); + else if (used_parent_storeys.Exists (i => i.Storey == storey)) + return; + + TypeExpr type_expr = storey.CreateStoreyTypeExpression (ec); + Field f = AddCompilerGeneratedField ("<>f__ref$" + storey.ID, type_expr); + used_parent_storeys.Add (new StoreyFieldPair (storey, f)); + } + + public void CaptureLocalVariable (ResolveContext ec, LocalVariable localVariable) + { + if (this is StateMachine) { + if (ec.CurrentBlock.ParametersBlock != localVariable.Block.ParametersBlock) + ec.CurrentBlock.Explicit.HasCapturedVariable = true; + } else { + ec.CurrentBlock.Explicit.HasCapturedVariable = true; + } + + var hoisted = localVariable.HoistedVariant; + if (hoisted != null && hoisted.Storey != this && hoisted.Storey is StateMachine) { + // + // Variable is already hoisted but we need it in storey which can be shared + // + hoisted.Storey.hoisted_locals.Remove (hoisted); + hoisted.Storey.Members.Remove (hoisted.Field); + hoisted = null; + } + + if (hoisted == null) { + hoisted = new HoistedLocalVariable (this, localVariable, GetVariableMangledName (localVariable)); + localVariable.HoistedVariant = hoisted; + + if (hoisted_locals == null) + hoisted_locals = new List (); + + hoisted_locals.Add (hoisted); + } + + if (ec.CurrentBlock.Explicit != localVariable.Block.Explicit && !(hoisted.Storey is StateMachine)) + hoisted.Storey.AddReferenceFromChildrenBlock (ec.CurrentBlock.Explicit); + } + + public void CaptureParameter (ResolveContext ec, ParametersBlock.ParameterInfo parameterInfo, ParameterReference parameterReference) + { + if (!(this is StateMachine)) { + ec.CurrentBlock.Explicit.HasCapturedVariable = true; + } + + var hoisted = parameterInfo.Parameter.HoistedVariant; + + if (parameterInfo.Block.StateMachine != null) { + // + // Another storey in same block exists but state machine does not + // have parameter captured. We need to add it there as well to + // proxy parameter value correctly. + // + if (hoisted == null && parameterInfo.Block.StateMachine != this) { + var storey = parameterInfo.Block.StateMachine; + + hoisted = new HoistedParameter (storey, parameterReference); + parameterInfo.Parameter.HoistedVariant = hoisted; + + if (storey.hoisted_params == null) + storey.hoisted_params = new List (); + + storey.hoisted_params.Add (hoisted); + } + + // + // Lift captured parameter from value type storey to reference type one. Otherwise + // any side effects would be done on a copy + // + if (hoisted != null && hoisted.Storey != this && hoisted.Storey is StateMachine) { + if (hoisted_local_params == null) + hoisted_local_params = new List (); + + hoisted_local_params.Add (hoisted); + hoisted = null; + } + } + + if (hoisted == null) { + hoisted = new HoistedParameter (this, parameterReference); + parameterInfo.Parameter.HoistedVariant = hoisted; + + if (hoisted_params == null) + hoisted_params = new List (); + + hoisted_params.Add (hoisted); + } + + // + // Register link between current block and parameter storey. It will + // be used when setting up storey definition to deploy storey reference + // when parameters are used from multiple blocks + // + if (ec.CurrentBlock.Explicit != parameterInfo.Block) { + hoisted.Storey.AddReferenceFromChildrenBlock (ec.CurrentBlock.Explicit); + } + } + + TypeExpr CreateStoreyTypeExpression (EmitContext ec) + { + // + // Create an instance of storey type + // + TypeExpr storey_type_expr; + if (CurrentTypeParameters != null) { + // + // Use current method type parameter (MVAR) for top level storey only. All + // nested storeys use class type parameter (VAR) + // + var tparams = ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null ? + ec.CurrentAnonymousMethod.Storey.CurrentTypeParameters : + ec.CurrentTypeParameters; + + TypeArguments targs = new TypeArguments (); + + // + // Use type parameter name instead of resolved type parameter + // specification to resolve to correctly nested type parameters + // + for (int i = 0; i < tparams.Count; ++i) + targs.Add (new SimpleName (tparams [i].Name, Location)); // new TypeParameterExpr (tparams[i], Location)); + + storey_type_expr = new GenericTypeExpr (Definition, targs, Location); + } else { + storey_type_expr = new TypeExpression (CurrentType, Location); + } + + return storey_type_expr; + } + + public void SetNestedStoryParent (AnonymousMethodStorey parentStorey) + { + Parent = parentStorey; + spec.IsGeneric = false; + spec.DeclaringType = parentStorey.CurrentType; + MemberName.TypeParameters = null; + } + + protected override bool DoResolveTypeParameters () + { + // Although any storey can have type parameters they are all clones of method type + // parameters therefore have to mutate MVAR references in any of cloned constraints + if (CurrentTypeParameters != null) { + for (int i = 0; i < CurrentTypeParameters.Count; ++i) { + var spec = CurrentTypeParameters[i].Type; + spec.BaseType = mutator.Mutate (spec.BaseType); + if (spec.InterfacesDefined != null) { + var mutated = new TypeSpec[spec.InterfacesDefined.Length]; + for (int ii = 0; ii < mutated.Length; ++ii) { + mutated[ii] = mutator.Mutate (spec.InterfacesDefined[ii]); + } + + spec.InterfacesDefined = mutated; + } + + if (spec.TypeArguments != null) { + spec.TypeArguments = mutator.Mutate (spec.TypeArguments); + } + } + } + + // + // Update parent cache as we most likely passed the point + // where the cache was constructed + // + Parent.CurrentType.MemberCache.AddMember (this.spec); + + return true; + } + + // + // Initializes all hoisted variables + // + public void EmitStoreyInstantiation (EmitContext ec, ExplicitBlock block) + { + // There can be only one instance variable for each storey type + if (Instance != null) + throw new InternalErrorException (); + + // + // Create an instance of this storey + // + ResolveContext rc = new ResolveContext (ec.MemberContext); + rc.CurrentBlock = block; + + var storey_type_expr = CreateStoreyTypeExpression (ec); + var source = new New (storey_type_expr, null, Location).Resolve (rc); + + // + // When the current context is async (or iterator) lift local storey + // instantiation to the currect storey + // + if (ec.CurrentAnonymousMethod is StateMachineInitializer && (block.HasYield || block.HasAwait)) { + // + // Unfortunately, normal capture mechanism could not be used because we are + // too late in the pipeline and standart assign cannot be used either due to + // recursive nature of GetStoreyInstanceExpression + // + var field = ec.CurrentAnonymousMethod.Storey.AddCompilerGeneratedField ( + LocalVariable.GetCompilerGeneratedName (block), storey_type_expr, true); + + field.Define (); + field.Emit (); + + var fexpr = new FieldExpr (field, Location); + fexpr.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, Location); + fexpr.EmitAssign (ec, source, false, false); + Instance = fexpr; + } else { + var local = TemporaryVariableReference.Create (source.Type, block, Location); + if (source.Type.IsStruct) { + local.LocalInfo.CreateBuilder (ec); + } else { + local.EmitAssign (ec, source); + } + + Instance = local; + } + + EmitHoistedFieldsInitialization (rc, ec); + + // TODO: Implement properly + //SymbolWriter.DefineScopeVariable (ID, Instance.Builder); + } + + void EmitHoistedFieldsInitialization (ResolveContext rc, EmitContext ec) + { + // + // Initialize all storey reference fields by using local or hoisted variables + // + if (used_parent_storeys != null) { + foreach (StoreyFieldPair sf in used_parent_storeys) { + // + // Get instance expression of storey field + // + Expression instace_expr = GetStoreyInstanceExpression (ec); + var fs = sf.Field.Spec; + if (TypeManager.IsGenericType (instace_expr.Type)) + fs = MemberCache.GetMember (instace_expr.Type, fs); + + FieldExpr f_set_expr = new FieldExpr (fs, Location); + f_set_expr.InstanceExpression = instace_expr; + + // TODO: CompilerAssign expression + SimpleAssign a = new SimpleAssign (f_set_expr, sf.Storey.GetStoreyInstanceExpression (ec)); + if (a.Resolve (rc) != null) + a.EmitStatement (ec); + } + } + + // + // Initialize hoisted `this' only once, everywhere else will be + // referenced indirectly + // + if (initialize_hoisted_this) { + rc.CurrentBlock.AddScopeStatement (new ThisInitializer (hoisted_this, hoisted_this_parent)); + } + + // + // Setting currect anonymous method to null blocks any further variable hoisting + // + AnonymousExpression ae = ec.CurrentAnonymousMethod; + ec.CurrentAnonymousMethod = null; + + if (hoisted_params != null) { + EmitHoistedParameters (ec, hoisted_params); + } + + ec.CurrentAnonymousMethod = ae; + } + + protected virtual void EmitHoistedParameters (EmitContext ec, List hoisted) + { + foreach (HoistedParameter hp in hoisted) { + if (hp == null) + continue; + + // + // Parameters could be proxied via local fields for value type storey + // + if (hoisted_local_params != null) { + var local_param = hoisted_local_params.Find (l => l.Parameter.Parameter == hp.Parameter.Parameter); + var source = new FieldExpr (local_param.Field, Location); + source.InstanceExpression = new CompilerGeneratedThis (CurrentType, Location); + hp.EmitAssign (ec, source, false, false); + continue; + } + + hp.EmitHoistingAssignment (ec); + } + } + + // + // Returns a field which holds referenced storey instance + // + Field GetReferencedStoreyField (AnonymousMethodStorey storey) + { + if (used_parent_storeys == null) + return null; + + foreach (StoreyFieldPair sf in used_parent_storeys) { + if (sf.Storey == storey) + return sf.Field; + } + + return null; + } + + // + // Creates storey instance expression regardless of currect IP + // + public Expression GetStoreyInstanceExpression (EmitContext ec) + { + AnonymousExpression am = ec.CurrentAnonymousMethod; + + // + // Access from original block -> storey + // + if (am == null) + return Instance; + + // + // Access from anonymous method implemented as a static -> storey + // + if (am.Storey == null) + return Instance; + + Field f = am.Storey.GetReferencedStoreyField (this); + if (f == null) { + if (am.Storey == this) { + // + // Access from inside of same storey (S -> S) + // + return new CompilerGeneratedThis (CurrentType, Location); + } + + // + // External field access + // + return Instance; + } + + // + // Storey was cached to local field + // + FieldExpr f_ind = new FieldExpr (f, Location); + f_ind.InstanceExpression = new CompilerGeneratedThis (CurrentType, Location); + return f_ind; + } + + protected virtual string GetVariableMangledName (LocalVariable local_info) + { + // + // No need to mangle anonymous method hoisted variables cause they + // are hoisted in their own scopes + // + return local_info.Name; + } + + public HoistedThis HoistedThis { + get { + return hoisted_this; + } + set { + hoisted_this = value; + } + } + + public IList ReferencesFromChildrenBlock { + get { return children_references; } + } + } + + public abstract class HoistedVariable + { + // + // Hoisted version of variable references used in expression + // tree has to be delayed until we know its location. The variable + // doesn't know its location until all stories are calculated + // + class ExpressionTreeVariableReference : Expression + { + readonly HoistedVariable hv; + + public ExpressionTreeVariableReference (HoistedVariable hv) + { + this.hv = hv; + } + + public override bool ContainsEmitWithAwait () + { + return false; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + return hv.CreateExpressionTree (); + } + + protected override Expression DoResolve (ResolveContext ec) + { + eclass = ExprClass.Value; + type = ec.Module.PredefinedTypes.Expression.Resolve (); + return this; + } + + public override void Emit (EmitContext ec) + { + ResolveContext rc = new ResolveContext (ec.MemberContext); + Expression e = hv.GetFieldExpression (ec).CreateExpressionTree (rc, false); + // This should never fail + e = e.Resolve (rc); + if (e != null) + e.Emit (ec); + } + } + + protected readonly AnonymousMethodStorey storey; + protected Field field; + Dictionary cached_inner_access; // TODO: Hashtable is too heavyweight + FieldExpr cached_outer_access; + + protected HoistedVariable (AnonymousMethodStorey storey, string name, TypeSpec type) + : this (storey, storey.AddCapturedVariable (name, type)) + { + } + + protected HoistedVariable (AnonymousMethodStorey storey, Field field) + { + this.storey = storey; + this.field = field; + } + + public Field Field { + get { + return field; + } + } + + public AnonymousMethodStorey Storey { + get { + return storey; + } + } + + public void AddressOf (EmitContext ec, AddressOp mode) + { + GetFieldExpression (ec).AddressOf (ec, mode); + } + + public Expression CreateExpressionTree () + { + return new ExpressionTreeVariableReference (this); + } + + public void Emit (EmitContext ec) + { + GetFieldExpression (ec).Emit (ec); + } + + public Expression EmitToField (EmitContext ec) + { + return GetFieldExpression (ec); + } + + // + // Creates field access expression for hoisted variable + // + protected virtual FieldExpr GetFieldExpression (EmitContext ec) + { + if (ec.CurrentAnonymousMethod == null || ec.CurrentAnonymousMethod.Storey == null) { + if (cached_outer_access != null) + return cached_outer_access; + + // + // When setting top-level hoisted variable in generic storey + // change storey generic types to method generic types (VAR -> MVAR) + // + if (storey.Instance.Type.IsGenericOrParentIsGeneric) { + var fs = MemberCache.GetMember (storey.Instance.Type, field.Spec); + cached_outer_access = new FieldExpr (fs, field.Location); + } else { + cached_outer_access = new FieldExpr (field, field.Location); + } + + cached_outer_access.InstanceExpression = storey.GetStoreyInstanceExpression (ec); + return cached_outer_access; + } + + FieldExpr inner_access; + if (cached_inner_access != null) { + if (!cached_inner_access.TryGetValue (ec.CurrentAnonymousMethod, out inner_access)) + inner_access = null; + } else { + inner_access = null; + cached_inner_access = new Dictionary (4); + } + + if (inner_access == null) { + if (field.Parent.IsGenericOrParentIsGeneric) { + var fs = MemberCache.GetMember (field.Parent.CurrentType, field.Spec); + inner_access = new FieldExpr (fs, field.Location); + } else { + inner_access = new FieldExpr (field, field.Location); + } + + inner_access.InstanceExpression = storey.GetStoreyInstanceExpression (ec); + cached_inner_access.Add (ec.CurrentAnonymousMethod, inner_access); + } + + return inner_access; + } + + public void Emit (EmitContext ec, bool leave_copy) + { + GetFieldExpression (ec).Emit (ec, leave_copy); + } + + public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound) + { + GetFieldExpression (ec).EmitAssign (ec, source, leave_copy, false); + } + } + + public class HoistedParameter : HoistedVariable + { + sealed class HoistedFieldAssign : CompilerAssign + { + public HoistedFieldAssign (Expression target, Expression source) + : base (target, source, target.Location) + { + } + + protected override Expression ResolveConversions (ResolveContext ec) + { + // + // Implicit conversion check fails for hoisted type arguments + // as they are of different types (!!0 x !0) + // + return this; + } + } + + readonly ParameterReference parameter; + + public HoistedParameter (AnonymousMethodStorey scope, ParameterReference par) + : base (scope, par.Name, par.Type) + { + this.parameter = par; + } + + public HoistedParameter (HoistedParameter hp, string name) + : base (hp.storey, name, hp.parameter.Type) + { + this.parameter = hp.parameter; + } + + #region Properties + + public bool IsAssigned { get; set; } + + public ParameterReference Parameter { + get { + return parameter; + } + } + + #endregion + + public void EmitHoistingAssignment (EmitContext ec) + { + // + // Remove hoisted redirection to emit assignment from original parameter + // + var temp = parameter.Parameter.HoistedVariant; + parameter.Parameter.HoistedVariant = null; + + var a = new HoistedFieldAssign (GetFieldExpression (ec), parameter); + a.EmitStatement (ec); + + parameter.Parameter.HoistedVariant = temp; + } + } + + class HoistedLocalVariable : HoistedVariable + { + public HoistedLocalVariable (AnonymousMethodStorey storey, LocalVariable local, string name) + : base (storey, name, local.Type) + { + } + } + + public class HoistedThis : HoistedVariable + { + public HoistedThis (AnonymousMethodStorey storey, Field field) + : base (storey, field) + { + } + } + + // + // Anonymous method expression as created by parser + // + public class AnonymousMethodExpression : Expression + { + // + // Special conversion for nested expression tree lambdas + // + class Quote : ShimExpression + { + public Quote (Expression expr) + : base (expr) + { + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + var args = new Arguments (1); + args.Add (new Argument (expr.CreateExpressionTree (ec))); + return CreateExpressionFactoryCall (ec, "Quote", args); + } + + protected override Expression DoResolve (ResolveContext rc) + { + expr = expr.Resolve (rc); + if (expr == null) + return null; + + eclass = expr.eclass; + type = expr.Type; + return this; + } + } + + readonly Dictionary compatibles; + + public ParametersBlock Block; + + public AnonymousMethodExpression (Location loc) + { + this.loc = loc; + this.compatibles = new Dictionary (); + } + + #region Properties + + public override string ExprClassName { + get { + return "anonymous method"; + } + } + + public virtual bool HasExplicitParameters { + get { + return Parameters != ParametersCompiled.Undefined; + } + } + + public override bool IsSideEffectFree { + get { + return true; + } + } + + public ParametersCompiled Parameters { + get { + return Block.Parameters; + } + } + + public bool IsAsync { + get; + internal set; + } + + public ReportPrinter TypeInferenceReportPrinter { + get; set; + } + + #endregion + + // + // Returns true if the body of lambda expression can be implicitly + // converted to the delegate of type `delegate_type' + // + public bool ImplicitStandardConversionExists (ResolveContext ec, TypeSpec delegate_type) + { + using (ec.With (ResolveContext.Options.InferReturnType, false)) { + using (ec.Set (ResolveContext.Options.ProbingMode)) { + var prev = ec.Report.SetPrinter (TypeInferenceReportPrinter ?? new NullReportPrinter ()); + + var res = Compatible (ec, delegate_type) != null; + + ec.Report.SetPrinter (prev); + + return res; + } + } + } + + TypeSpec CompatibleChecks (ResolveContext ec, TypeSpec delegate_type) + { + if (delegate_type.IsDelegate) + return delegate_type; + + if (delegate_type.IsExpressionTreeType) { + delegate_type = delegate_type.TypeArguments [0]; + if (delegate_type.IsDelegate) + return delegate_type; + + ec.Report.Error (835, loc, "Cannot convert `{0}' to an expression tree of non-delegate type `{1}'", + GetSignatureForError (), delegate_type.GetSignatureForError ()); + return null; + } + + ec.Report.Error (1660, loc, "Cannot convert `{0}' to non-delegate type `{1}'", + GetSignatureForError (), delegate_type.GetSignatureForError ()); + return null; + } + + protected bool VerifyExplicitParameters (ResolveContext ec, TypeInferenceContext tic, TypeSpec delegate_type, AParametersCollection parameters) + { + if (VerifyParameterCompatibility (ec, tic, delegate_type, parameters, ec.IsInProbingMode)) + return true; + + if (!ec.IsInProbingMode) + ec.Report.Error (1661, loc, + "Cannot convert `{0}' to delegate type `{1}' since there is a parameter mismatch", + GetSignatureForError (), delegate_type.GetSignatureForError ()); + + return false; + } + + protected bool VerifyParameterCompatibility (ResolveContext ec, TypeInferenceContext tic, TypeSpec delegate_type, AParametersCollection invoke_pd, bool ignore_errors) + { + if (Parameters.Count != invoke_pd.Count) { + if (ignore_errors) + return false; + + ec.Report.Error (1593, loc, "Delegate `{0}' does not take `{1}' arguments", + delegate_type.GetSignatureForError (), Parameters.Count.ToString ()); + return false; + } + + bool has_implicit_parameters = !HasExplicitParameters; + bool error = false; + + for (int i = 0; i < Parameters.Count; ++i) { + Parameter.Modifier p_mod = invoke_pd.FixedParameters [i].ModFlags; + if (Parameters.FixedParameters [i].ModFlags != p_mod && p_mod != Parameter.Modifier.PARAMS) { + if (ignore_errors) + return false; + + if (p_mod == Parameter.Modifier.NONE) + ec.Report.Error (1677, Parameters[i].Location, "Parameter `{0}' should not be declared with the `{1}' keyword", + (i + 1).ToString (), Parameter.GetModifierSignature (Parameters [i].ModFlags)); + else + ec.Report.Error (1676, Parameters[i].Location, "Parameter `{0}' must be declared with the `{1}' keyword", + (i+1).ToString (), Parameter.GetModifierSignature (p_mod)); + error = true; + } + + if (has_implicit_parameters) + continue; + + TypeSpec type = invoke_pd.Types [i]; + + if (tic != null) + type = tic.InflateGenericArgument (ec, type); + + if (!TypeSpecComparer.IsEqual (type, Parameters.Types [i])) { + if (ignore_errors) + return false; + + ec.Report.Error (1678, Parameters [i].Location, "Parameter `{0}' is declared as type `{1}' but should be `{2}'", + (i+1).ToString (), + Parameters.Types [i].GetSignatureForError (), + invoke_pd.Types [i].GetSignatureForError ()); + error = true; + } + } + + return !error; + } + + // + // Infers type arguments based on explicit arguments + // + public bool ExplicitTypeInference (TypeInferenceContext type_inference, TypeSpec delegate_type) + { + if (!HasExplicitParameters) + return false; + + if (!delegate_type.IsDelegate) { + if (!delegate_type.IsExpressionTreeType) + return false; + + delegate_type = TypeManager.GetTypeArguments (delegate_type) [0]; + if (!delegate_type.IsDelegate) + return false; + } + + AParametersCollection d_params = Delegate.GetParameters (delegate_type); + if (d_params.Count != Parameters.Count) + return false; + + var ptypes = Parameters.Types; + var dtypes = d_params.Types; + for (int i = 0; i < Parameters.Count; ++i) { + if (type_inference.ExactInference (ptypes[i], dtypes[i]) == 0) { + // + // Continue when 0 (quick path) does not mean inference failure. Checking for + // same type handles cases like int -> int + // + if (ptypes[i] == dtypes[i]) + continue; + + return false; + } + } + + return true; + } + + public TypeSpec InferReturnType (ResolveContext ec, TypeInferenceContext tic, TypeSpec delegate_type) + { + Expression expr; + AnonymousExpression am; + + if (compatibles.TryGetValue (delegate_type, out expr)) { + am = expr as AnonymousExpression; + return am == null ? null : am.ReturnType; + } + + using (ec.Set (ResolveContext.Options.ProbingMode | ResolveContext.Options.InferReturnType)) { + ReportPrinter prev; + if (TypeInferenceReportPrinter != null) { + prev = ec.Report.SetPrinter (TypeInferenceReportPrinter); + } else { + prev = null; + } + + var body = CompatibleMethodBody (ec, tic, null, delegate_type); + if (body != null) { + am = body.Compatible (ec, body); + } else { + am = null; + } + + if (TypeInferenceReportPrinter != null) { + ec.Report.SetPrinter (prev); + } + } + + if (am == null) + return null; + +// compatibles.Add (delegate_type, am); + return am.ReturnType; + } + + public override bool ContainsEmitWithAwait () + { + return false; + } + + // + // Returns AnonymousMethod container if this anonymous method + // expression can be implicitly converted to the delegate type `delegate_type' + // + public Expression Compatible (ResolveContext ec, TypeSpec type) + { + Expression am; + if (compatibles.TryGetValue (type, out am)) + return am; + + TypeSpec delegate_type = CompatibleChecks (ec, type); + if (delegate_type == null) + return null; + + // + // At this point its the first time we know the return type that is + // needed for the anonymous method. We create the method here. + // + + var invoke_mb = Delegate.GetInvokeMethod (delegate_type); + TypeSpec return_type = invoke_mb.ReturnType; + + // + // Second: the return type of the delegate must be compatible with + // the anonymous type. Instead of doing a pass to examine the block + // we satisfy the rule by setting the return type on the EmitContext + // to be the delegate type return type. + // + + var body = CompatibleMethodBody (ec, null, return_type, delegate_type); + if (body == null) + return null; + + bool etree_conversion = delegate_type != type; + + try { + if (etree_conversion) { + if (ec.HasSet (ResolveContext.Options.ExpressionTreeConversion)) { + // + // Nested expression tree lambda use same scope as parent + // lambda, this also means no variable capturing between this + // and parent scope + // + am = body.Compatible (ec, ec.CurrentAnonymousMethod); + + // + // Quote nested expression tree + // + if (am != null) + am = new Quote (am); + } else { + int errors = ec.Report.Errors; + + if (Block.IsAsync) { + ec.Report.Error (1989, loc, "Async lambda expressions cannot be converted to expression trees"); + } + + using (ec.Set (ResolveContext.Options.ExpressionTreeConversion)) { + am = body.Compatible (ec); + } + + // + // Rewrite expressions into expression tree when targeting Expression + // + if (am != null && errors == ec.Report.Errors) + am = CreateExpressionTree (ec, delegate_type); + } + } else { + am = body.Compatible (ec); + + if (body.DirectMethodGroupConversion != null) { + var errors_printer = new SessionReportPrinter (); + var old = ec.Report.SetPrinter (errors_printer); + var expr = new ImplicitDelegateCreation (delegate_type, body.DirectMethodGroupConversion, loc) { + AllowSpecialMethodsInvocation = true + }.Resolve (ec); + ec.Report.SetPrinter (old); + if (expr != null && errors_printer.ErrorsCount == 0) + am = expr; + } + } + } catch (CompletionResult) { + throw; + } catch (FatalException) { + throw; + } catch (Exception e) { + throw new InternalErrorException (e, loc); + } + + if (!ec.IsInProbingMode && !etree_conversion) { + compatibles.Add (type, am ?? EmptyExpression.Null); + } + + return am; + } + + protected virtual Expression CreateExpressionTree (ResolveContext ec, TypeSpec delegate_type) + { + return CreateExpressionTree (ec); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + ec.Report.Error (1946, loc, "An anonymous method cannot be converted to an expression tree"); + return null; + } + + protected virtual ParametersCompiled ResolveParameters (ResolveContext ec, TypeInferenceContext tic, TypeSpec delegate_type) + { + var delegate_parameters = Delegate.GetParameters (delegate_type); + + if (Parameters == ParametersCompiled.Undefined) { + // + // We provide a set of inaccessible parameters + // + Parameter[] fixedpars = new Parameter[delegate_parameters.Count]; + + for (int i = 0; i < delegate_parameters.Count; i++) { + Parameter.Modifier i_mod = delegate_parameters.FixedParameters [i].ModFlags; + if ((i_mod & Parameter.Modifier.OUT) != 0) { + if (!ec.IsInProbingMode) { + ec.Report.Error (1688, loc, + "Cannot convert anonymous method block without a parameter list to delegate type `{0}' because it has one or more `out' parameters", + delegate_type.GetSignatureForError ()); + } + + return null; + } + fixedpars[i] = new Parameter ( + new TypeExpression (delegate_parameters.Types [i], loc), null, + delegate_parameters.FixedParameters [i].ModFlags, null, loc); + } + + return ParametersCompiled.CreateFullyResolved (fixedpars, delegate_parameters.Types); + } + + if (!VerifyExplicitParameters (ec, tic, delegate_type, delegate_parameters)) { + return null; + } + + return Parameters; + } + + protected override Expression DoResolve (ResolveContext rc) + { + if (rc.HasSet (ResolveContext.Options.ConstantScope)) { + rc.Report.Error (1706, loc, "Anonymous methods and lambda expressions cannot be used in the current context"); + return null; + } + + // + // Update top-level block generated duting parsing with actual top-level block + // + if (rc.HasAny (ResolveContext.Options.FieldInitializerScope | ResolveContext.Options.BaseInitializer) && rc.CurrentMemberDefinition.Parent.PartialContainer.PrimaryConstructorParameters != null) { + var tb = rc.ConstructorBlock.ParametersBlock.TopBlock; + if (Block.TopBlock != tb) { + Block b = Block; + while (b.Parent != Block.TopBlock && b != Block.TopBlock) + b = b.Parent; + + b.Parent = tb; + tb.IncludeBlock (Block, Block.TopBlock); + b.ParametersBlock.TopBlock = tb; + } + } + + eclass = ExprClass.Value; + + // + // This hack means `The type is not accessible + // anywhere', we depend on special conversion + // rules. + // + type = InternalType.AnonymousMethod; + + if (!DoResolveParameters (rc)) + return null; + + return this; + } + + protected virtual bool DoResolveParameters (ResolveContext rc) + { + return Parameters.Resolve (rc); + } + + public override void Emit (EmitContext ec) + { + // nothing, as we only exist to not do anything. + } + + public static void Error_AddressOfCapturedVar (ResolveContext rc, IVariableReference var, Location loc) + { + if (rc.CurrentAnonymousMethod is AsyncInitializer) + return; + + rc.Report.Error (1686, loc, + "Local variable or parameter `{0}' cannot have their address taken and be used inside an anonymous method, lambda expression or query expression", + var.Name); + } + + public override string GetSignatureForError () + { + return ExprClassName; + } + + AnonymousMethodBody CompatibleMethodBody (ResolveContext ec, TypeInferenceContext tic, TypeSpec return_type, TypeSpec delegate_type) + { + ParametersCompiled p = ResolveParameters (ec, tic, delegate_type); + if (p == null) + return null; + + ParametersBlock b = ec.IsInProbingMode ? (ParametersBlock) Block.PerformClone () : Block; + + if (b.IsAsync) { + var rt = return_type; + if (rt != null && rt.Kind != MemberKind.Void && rt != ec.Module.PredefinedTypes.Task.TypeSpec && !rt.IsGenericTask) { + ec.Report.Error (4010, loc, "Cannot convert async {0} to delegate type `{1}'", + GetSignatureForError (), delegate_type.GetSignatureForError ()); + + return null; + } + + b = b.ConvertToAsyncTask (ec, ec.CurrentMemberDefinition.Parent.PartialContainer, p, return_type, delegate_type, loc); + } + + return CompatibleMethodFactory (return_type ?? InternalType.ErrorType, delegate_type, p, b); + } + + protected virtual AnonymousMethodBody CompatibleMethodFactory (TypeSpec return_type, TypeSpec delegate_type, ParametersCompiled p, ParametersBlock b) + { + return new AnonymousMethodBody (p, b, return_type, delegate_type, loc); + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + AnonymousMethodExpression target = (AnonymousMethodExpression) t; + + target.Block = (ParametersBlock) clonectx.LookupBlock (Block); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + // + // Abstract expression for any block which requires variables hoisting + // + public abstract class AnonymousExpression : ExpressionStatement + { + protected class AnonymousMethodMethod : Method + { + public readonly AnonymousExpression AnonymousMethod; + public readonly AnonymousMethodStorey Storey; + + public AnonymousMethodMethod (TypeDefinition parent, AnonymousExpression am, AnonymousMethodStorey storey, + TypeExpr return_type, + Modifiers mod, MemberName name, + ParametersCompiled parameters) + : base (parent, return_type, mod | Modifiers.COMPILER_GENERATED, + name, parameters, null) + { + this.AnonymousMethod = am; + this.Storey = storey; + + Parent.PartialContainer.Members.Add (this); + Block = new ToplevelBlock (am.block, parameters); + } + + public override EmitContext CreateEmitContext (ILGenerator ig, SourceMethodBuilder sourceMethod) + { + EmitContext ec = new EmitContext (this, ig, ReturnType, sourceMethod); + ec.CurrentAnonymousMethod = AnonymousMethod; + return ec; + } + + protected override void DefineTypeParameters () + { + // Type parameters were cloned + } + + protected override bool ResolveMemberType () + { + if (!base.ResolveMemberType ()) + return false; + + if (Storey != null && Storey.Mutator != null) { + if (!parameters.IsEmpty) { + var mutated = Storey.Mutator.Mutate (parameters.Types); + if (mutated != parameters.Types) + parameters = ParametersCompiled.CreateFullyResolved ((Parameter[]) parameters.FixedParameters, mutated); + } + + member_type = Storey.Mutator.Mutate (member_type); + } + + return true; + } + + public override void Emit () + { + if (MethodBuilder == null) { + Define (); + } + + base.Emit (); + } + } + + protected readonly ParametersBlock block; + + public TypeSpec ReturnType; + + protected AnonymousExpression (ParametersBlock block, TypeSpec return_type, Location loc) + { + this.ReturnType = return_type; + this.block = block; + this.loc = loc; + } + + public abstract string ContainerType { get; } + public abstract bool IsIterator { get; } + public abstract AnonymousMethodStorey Storey { get; } + + // + // The block that makes up the body for the anonymous method + // + public ParametersBlock Block { + get { + return block; + } + } + + public AnonymousExpression Compatible (ResolveContext ec) + { + return Compatible (ec, this); + } + + public AnonymousExpression Compatible (ResolveContext ec, AnonymousExpression ae) + { + if (block.Resolved) + return this; + + // TODO: Implement clone + BlockContext aec = new BlockContext (ec, block, ReturnType); + aec.CurrentAnonymousMethod = ae; + + var am = this as AnonymousMethodBody; + + if (ec.HasSet (ResolveContext.Options.InferReturnType) && am != null) { + am.ReturnTypeInference = new TypeInferenceContext (); + } + + var bc = ec as BlockContext; + + if (bc != null) { + aec.AssignmentInfoOffset = bc.AssignmentInfoOffset; + aec.EnclosingLoop = bc.EnclosingLoop; + aec.EnclosingLoopOrSwitch = bc.EnclosingLoopOrSwitch; + aec.Switch = bc.Switch; + } + + var errors = ec.Report.Errors; + + bool res = Block.Resolve (aec); + + if (res && errors == ec.Report.Errors) { + MarkReachable (new Reachability ()); + + if (!CheckReachableExit (ec.Report)) { + return null; + } + + if (bc != null) + bc.AssignmentInfoOffset = aec.AssignmentInfoOffset; + } + + if (am != null && am.ReturnTypeInference != null) { + am.ReturnTypeInference.FixAllTypes (ec); + ReturnType = am.ReturnTypeInference.InferredTypeArguments [0]; + am.ReturnTypeInference = null; + + // + // If e is synchronous the inferred return type is T + // If e is asynchronous and the body of F is either an expression classified as nothing + // or a statement block where no return statements have expressions, the inferred return type is Task + // If e is async and has an inferred result type T, the inferred return type is Task + // + if (block.IsAsync && ReturnType != null) { + ReturnType = ReturnType.Kind == MemberKind.Void ? + ec.Module.PredefinedTypes.Task.TypeSpec : + ec.Module.PredefinedTypes.TaskGeneric.TypeSpec.MakeGenericType (ec, new [] { ReturnType }); + } + } + + if (res && errors != ec.Report.Errors) + return null; + + return res ? this : null; + } + + public override bool ContainsEmitWithAwait () + { + return false; + } + + bool CheckReachableExit (Report report) + { + if (block.HasReachableClosingBrace && ReturnType.Kind != MemberKind.Void) { + // FIXME: Flow-analysis on MoveNext generated code + if (!IsIterator) { + report.Error (1643, StartLocation, + "Not all code paths return a value in anonymous method of type `{0}'", GetSignatureForError ()); + + return false; + } + } + + return true; + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + // We are reachable, mark block body reachable too + MarkReachable (new Reachability ()); + + CheckReachableExit (fc.Report); + + var das = fc.BranchDefiniteAssignment (); + var prev_pb = fc.ParametersBlock; + fc.ParametersBlock = Block; + var da_ontrue = fc.DefiniteAssignmentOnTrue; + var da_onfalse = fc.DefiniteAssignmentOnFalse; + + fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = null; + block.FlowAnalysis (fc); + + fc.ParametersBlock = prev_pb; + fc.DefiniteAssignment = das; + fc.DefiniteAssignmentOnTrue = da_ontrue; + fc.DefiniteAssignmentOnFalse = da_onfalse; + } + + public override void MarkReachable (Reachability rc) + { + block.MarkReachable (rc); + } + + public void SetHasThisAccess () + { + ExplicitBlock b = block; + do { + if (b.HasCapturedThis) + return; + + b.HasCapturedThis = true; + b = b.Parent == null ? null : b.Parent.Explicit; + } while (b != null); + } + } + + public class AnonymousMethodBody : AnonymousExpression + { + protected readonly ParametersCompiled parameters; + AnonymousMethodStorey storey; + + AnonymousMethodMethod method; + Field am_cache; + string block_name; + TypeInferenceContext return_inference; + + public AnonymousMethodBody (ParametersCompiled parameters, + ParametersBlock block, TypeSpec return_type, TypeSpec delegate_type, + Location loc) + : base (block, return_type, loc) + { + this.type = delegate_type; + this.parameters = parameters; + } + + #region Properties + + public override string ContainerType { + get { return "anonymous method"; } + } + + // + // Method-group instance for lambdas which can be replaced with + // simple method group call + // + public MethodGroupExpr DirectMethodGroupConversion { + get; set; + } + + public override bool IsIterator { + get { + return false; + } + } + + public ParametersCompiled Parameters { + get { + return parameters; + } + } + + public TypeInferenceContext ReturnTypeInference { + get { + return return_inference; + } + set { + return_inference = value; + } + } + + public override AnonymousMethodStorey Storey { + get { + return storey; + } + } + + #endregion + + public override Expression CreateExpressionTree (ResolveContext ec) + { + ec.Report.Error (1945, loc, "An expression tree cannot contain an anonymous method expression"); + return null; + } + + bool Define (ResolveContext ec) + { + if (!Block.Resolved && Compatible (ec) == null) + return false; + + if (block_name == null) { + MemberCore mc = (MemberCore) ec.MemberContext; + block_name = mc.MemberName.Basename; + } + + return true; + } + + // + // Creates a host for the anonymous method + // + AnonymousMethodMethod DoCreateMethodHost (EmitContext ec) + { + // + // Anonymous method body can be converted to + // + // 1, an instance method in current scope when only `this' is hoisted + // 2, a static method in current scope when neither `this' nor any variable is hoisted + // 3, an instance method in compiler generated storey when any hoisted variable exists + // + + Modifiers modifiers; + TypeDefinition parent = null; + TypeParameters hoisted_tparams = null; + ParametersCompiled method_parameters = parameters; + + var src_block = Block.Original.Explicit; + if (src_block.HasCapturedVariable || src_block.HasCapturedThis) { + parent = storey = FindBestMethodStorey (); + + if (storey == null) { + var top_block = src_block.ParametersBlock.TopBlock; + var sm = top_block.StateMachine; + + if (src_block.HasCapturedThis) { + // + // Remove hoisted 'this' request when simple instance method is + // enough. No hoisted variables only 'this' and don't need to + // propagate this to value type state machine. + // + StateMachine sm_parent; + var pb = src_block.ParametersBlock; + do { + sm_parent = pb.StateMachine; + pb = pb.Parent == null ? null : pb.Parent.ParametersBlock; + } while (sm_parent == null && pb != null); + + if (sm_parent == null) { + top_block.RemoveThisReferenceFromChildrenBlock (src_block); + } else if (sm_parent.Kind == MemberKind.Struct) { + // + // Special case where parent class is used to emit instance method + // because currect storey is of value type (async host) and we cannot + // use ldftn on non-boxed instances either to share mutated state + // + parent = sm_parent.Parent.PartialContainer; + hoisted_tparams = sm_parent.OriginalTypeParameters; + } else if (sm is IteratorStorey) { + // + // For iterators we can host everything in one class + // + parent = storey = sm; + } + } + } + + modifiers = storey != null ? Modifiers.INTERNAL : Modifiers.PRIVATE; + } else { + if (ec.CurrentAnonymousMethod != null) + parent = storey = ec.CurrentAnonymousMethod.Storey; + + modifiers = Modifiers.STATIC | Modifiers.PRIVATE; + + // + // Convert generated method to closed delegate method where unused + // this argument is generated during compilation which speeds up dispatch + // by about 25% + // + method_parameters = ParametersCompiled.Prefix (method_parameters, + new Parameter (null, null, 0, null, loc), ec.Module.Compiler.BuiltinTypes.Object); + } + + if (storey == null && hoisted_tparams == null) + hoisted_tparams = ec.CurrentTypeParameters; + + if (parent == null) + parent = ec.CurrentTypeDefinition.Parent.PartialContainer; + + string name = CompilerGeneratedContainer.MakeName (parent != storey ? block_name : null, + "m", null, parent.PartialContainer.CounterAnonymousMethods++); + + MemberName member_name; + if (hoisted_tparams != null) { + var type_params = new TypeParameters (hoisted_tparams.Count); + for (int i = 0; i < hoisted_tparams.Count; ++i) { + type_params.Add (hoisted_tparams[i].CreateHoistedCopy (null)); + } + + member_name = new MemberName (name, type_params, Location); + } else { + member_name = new MemberName (name, Location); + } + + return new AnonymousMethodMethod (parent, + this, storey, new TypeExpression (ReturnType, Location), modifiers, + member_name, method_parameters); + } + + protected override Expression DoResolve (ResolveContext ec) + { + if (!Define (ec)) + return null; + + eclass = ExprClass.Value; + return this; + } + + public override void Emit (EmitContext ec) + { + // + // Use same anonymous method implementation for scenarios where same + // code is used from multiple blocks, e.g. field initializers + // + if (method == null) { + // + // Delay an anonymous method definition to avoid emitting unused code + // for unreachable blocks or expression trees + // + method = DoCreateMethodHost (ec); + method.Define (); + method.PrepareEmit (); + } + + bool is_static = (method.ModFlags & Modifiers.STATIC) != 0; + if (is_static && am_cache == null && !ec.IsStaticConstructor) { + // + // Creates a field cache to store delegate instance if it's not generic + // + if (!method.MemberName.IsGeneric) { + var parent = method.Parent.PartialContainer; + int id = parent.AnonymousMethodsCounter++; + var cache_type = storey != null && storey.Mutator != null ? storey.Mutator.Mutate (type) : type; + + am_cache = new Field (parent, new TypeExpression (cache_type, loc), + Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED, + new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "am$cache", id), loc), null); + am_cache.Define (); + parent.AddField (am_cache); + } else { + // TODO: Implement caching of generated generic static methods + // + // Idea: + // + // Some extra class is needed to capture variable generic type + // arguments. Maybe we could re-use anonymous types, with a unique + // anonymous method id, but they are quite heavy. + // + // Consider : "() => typeof(T);" + // + // We need something like + // static class Wrap { + // public static DelegateType cache; + // } + // + // We then specialize local variable to capture all generic parameters + // and delegate type, e.g. "Wrap cache;" + // + } + } + + Label l_initialized = ec.DefineLabel (); + + if (am_cache != null) { + ec.Emit (OpCodes.Ldsfld, am_cache.Spec); + ec.Emit (OpCodes.Brtrue_S, l_initialized); + } + + // + // Load method delegate implementation + // + + if (is_static) { + ec.EmitNull (); + } else if (storey != null) { + Expression e = storey.GetStoreyInstanceExpression (ec).Resolve (new ResolveContext (ec.MemberContext)); + if (e != null) { + e.Emit (ec); + } + } else { + ec.EmitThis (); + + // + // Special case for value type storey where this is not lifted but + // droped off to parent class + // + if (ec.CurrentAnonymousMethod != null && ec.AsyncTaskStorey != null) + ec.Emit (OpCodes.Ldfld, ec.AsyncTaskStorey.HoistedThis.Field.Spec); + } + + var delegate_method = method.Spec; + if (storey != null && storey.MemberName.IsGeneric) { + TypeSpec t = storey.Instance.Type; + + // + // Mutate anonymous method instance type if we are in nested + // hoisted generic anonymous method storey + // + if (ec.IsAnonymousStoreyMutateRequired) { + t = storey.Mutator.Mutate (t); + } + + ec.Emit (OpCodes.Ldftn, TypeBuilder.GetMethod (t.GetMetaInfo (), (MethodInfo) delegate_method.GetMetaInfo ())); + } else { + if (delegate_method.IsGeneric) { + TypeParameterSpec[] tparams; + var sm = ec.CurrentAnonymousMethod == null ? null : ec.CurrentAnonymousMethod.Storey as StateMachine; + if (sm != null && sm.OriginalTypeParameters != null) { + tparams = sm.CurrentTypeParameters.Types; + } else { + tparams = method.TypeParameters; + } + + delegate_method = delegate_method.MakeGenericMethod (ec.MemberContext, tparams); + } + + ec.Emit (OpCodes.Ldftn, delegate_method); + } + + var constructor_method = Delegate.GetConstructor (type); + ec.Emit (OpCodes.Newobj, constructor_method); + + if (am_cache != null) { + ec.Emit (OpCodes.Stsfld, am_cache.Spec); + ec.MarkLabel (l_initialized); + ec.Emit (OpCodes.Ldsfld, am_cache.Spec); + } + } + + public override void EmitStatement (EmitContext ec) + { + throw new NotImplementedException (); + } + + // + // Look for the best storey for this anonymous method + // + AnonymousMethodStorey FindBestMethodStorey () + { + // + // Use the nearest parent block which has a storey + // + for (Block b = Block.Parent; b != null; b = b.Parent) { + AnonymousMethodStorey s = b.Explicit.AnonymousMethodStorey; + if (s != null) + return s; + } + + return null; + } + + public override string GetSignatureForError () + { + return type.GetSignatureForError (); + } + } + + // + // Anonymous type container + // + public class AnonymousTypeClass : CompilerGeneratedContainer + { + public const string ClassNamePrefix = "<>__AnonType"; + public const string SignatureForError = "anonymous type"; + + readonly IList parameters; + + private AnonymousTypeClass (ModuleContainer parent, MemberName name, IList parameters, Location loc) + : base (parent, name, parent.Evaluator != null ? Modifiers.PUBLIC : Modifiers.INTERNAL) + { + this.parameters = parameters; + } + + public static AnonymousTypeClass Create (TypeContainer parent, IList parameters, Location loc) + { + string name = ClassNamePrefix + parent.Module.CounterAnonymousTypes++; + + ParametersCompiled all_parameters; + TypeParameters tparams = null; + SimpleName[] t_args; + + if (parameters.Count == 0) { + all_parameters = ParametersCompiled.EmptyReadOnlyParameters; + t_args = null; + } else { + t_args = new SimpleName[parameters.Count]; + tparams = new TypeParameters (); + Parameter[] ctor_params = new Parameter[parameters.Count]; + for (int i = 0; i < parameters.Count; ++i) { + AnonymousTypeParameter p = parameters[i]; + for (int ii = 0; ii < i; ++ii) { + if (parameters[ii].Name == p.Name) { + parent.Compiler.Report.Error (833, parameters[ii].Location, + "`{0}': An anonymous type cannot have multiple properties with the same name", + p.Name); + + p = new AnonymousTypeParameter (null, "$" + i.ToString (), p.Location); + parameters[i] = p; + break; + } + } + + t_args[i] = new SimpleName ("<" + p.Name + ">__T", p.Location); + tparams.Add (new TypeParameter (i, new MemberName (t_args[i].Name, p.Location), null, null, Variance.None)); + ctor_params[i] = new Parameter (t_args[i], p.Name, Parameter.Modifier.NONE, null, p.Location); + } + + all_parameters = new ParametersCompiled (ctor_params); + } + + // + // Create generic anonymous type host with generic arguments + // named upon properties names + // + AnonymousTypeClass a_type = new AnonymousTypeClass (parent.Module, new MemberName (name, tparams, loc), parameters, loc); + + Constructor c = new Constructor (a_type, name, Modifiers.PUBLIC | Modifiers.DEBUGGER_HIDDEN, + null, all_parameters, loc); + c.Block = new ToplevelBlock (parent.Module.Compiler, c.ParameterInfo, loc); + + // + // Create fields and constructor body with field initialization + // + bool error = false; + for (int i = 0; i < parameters.Count; ++i) { + AnonymousTypeParameter p = parameters [i]; + + Field f = new Field (a_type, t_args [i], Modifiers.PRIVATE | Modifiers.READONLY | Modifiers.DEBUGGER_HIDDEN, + new MemberName ("<" + p.Name + ">", p.Location), null); + + if (!a_type.AddField (f)) { + error = true; + continue; + } + + c.Block.AddStatement (new StatementExpression ( + new SimpleAssign (new MemberAccess (new This (p.Location), f.Name), + c.Block.GetParameterReference (i, p.Location)))); + + ToplevelBlock get_block = new ToplevelBlock (parent.Module.Compiler, p.Location); + get_block.AddStatement (new Return ( + new MemberAccess (new This (p.Location), f.Name), p.Location)); + + Property prop = new Property (a_type, t_args [i], Modifiers.PUBLIC, + new MemberName (p.Name, p.Location), null); + prop.Get = new Property.GetMethod (prop, 0, null, p.Location); + prop.Get.Block = get_block; + a_type.AddMember (prop); + } + + if (error) + return null; + + a_type.AddConstructor (c); + return a_type; + } + + protected override bool DoDefineMembers () + { + if (!base.DoDefineMembers ()) + return false; + + Location loc = Location; + + var equals_parameters = ParametersCompiled.CreateFullyResolved ( + new Parameter (new TypeExpression (Compiler.BuiltinTypes.Object, loc), "obj", 0, null, loc), Compiler.BuiltinTypes.Object); + + Method equals = new Method (this, new TypeExpression (Compiler.BuiltinTypes.Bool, loc), + Modifiers.PUBLIC | Modifiers.OVERRIDE | Modifiers.DEBUGGER_HIDDEN, new MemberName ("Equals", loc), + equals_parameters, null); + + equals_parameters[0].Resolve (equals, 0); + + Method tostring = new Method (this, new TypeExpression (Compiler.BuiltinTypes.String, loc), + Modifiers.PUBLIC | Modifiers.OVERRIDE | Modifiers.DEBUGGER_HIDDEN, new MemberName ("ToString", loc), + ParametersCompiled.EmptyReadOnlyParameters, null); + + ToplevelBlock equals_block = new ToplevelBlock (Compiler, equals.ParameterInfo, loc); + + TypeExpr current_type; + if (CurrentTypeParameters != null) { + var targs = new TypeArguments (); + for (int i = 0; i < CurrentTypeParameters.Count; ++i) { + targs.Add (new TypeParameterExpr (CurrentTypeParameters[i], Location)); + } + + current_type = new GenericTypeExpr (Definition, targs, loc); + } else { + current_type = new TypeExpression (Definition, loc); + } + + var li_other = LocalVariable.CreateCompilerGenerated (CurrentType, equals_block, loc); + equals_block.AddStatement (new BlockVariable (new TypeExpression (li_other.Type, loc), li_other)); + var other_variable = new LocalVariableReference (li_other, loc); + + MemberAccess system_collections_generic = new MemberAccess (new MemberAccess ( + new QualifiedAliasMember ("global", "System", loc), "Collections", loc), "Generic", loc); + + Expression rs_equals = null; + Expression string_concat = new StringConstant (Compiler.BuiltinTypes, "{", loc); + Expression rs_hashcode = new IntConstant (Compiler.BuiltinTypes, -2128831035, loc); + for (int i = 0; i < parameters.Count; ++i) { + var p = parameters [i]; + var f = (Field) Members [i * 2]; + + MemberAccess equality_comparer = new MemberAccess (new MemberAccess ( + system_collections_generic, "EqualityComparer", + new TypeArguments (new SimpleName (CurrentTypeParameters [i].Name, loc)), loc), + "Default", loc); + + Arguments arguments_equal = new Arguments (2); + arguments_equal.Add (new Argument (new MemberAccess (new This (f.Location), f.Name))); + arguments_equal.Add (new Argument (new MemberAccess (other_variable, f.Name))); + + Expression field_equal = new Invocation (new MemberAccess (equality_comparer, + "Equals", loc), arguments_equal); + + Arguments arguments_hashcode = new Arguments (1); + arguments_hashcode.Add (new Argument (new MemberAccess (new This (f.Location), f.Name))); + Expression field_hashcode = new Invocation (new MemberAccess (equality_comparer, + "GetHashCode", loc), arguments_hashcode); + + IntConstant FNV_prime = new IntConstant (Compiler.BuiltinTypes, 16777619, loc); + rs_hashcode = new Binary (Binary.Operator.Multiply, + new Binary (Binary.Operator.ExclusiveOr, rs_hashcode, field_hashcode), + FNV_prime); + + Expression field_to_string = new Conditional (new BooleanExpression (new Binary (Binary.Operator.Inequality, + new MemberAccess (new This (f.Location), f.Name), new NullLiteral (loc))), + new Invocation (new MemberAccess ( + new MemberAccess (new This (f.Location), f.Name), "ToString"), null), + new StringConstant (Compiler.BuiltinTypes, string.Empty, loc), loc); + + if (rs_equals == null) { + rs_equals = field_equal; + string_concat = new Binary (Binary.Operator.Addition, + string_concat, + new Binary (Binary.Operator.Addition, + new StringConstant (Compiler.BuiltinTypes, " " + p.Name + " = ", loc), + field_to_string)); + continue; + } + + // + // Implementation of ToString () body using string concatenation + // + string_concat = new Binary (Binary.Operator.Addition, + new Binary (Binary.Operator.Addition, + string_concat, + new StringConstant (Compiler.BuiltinTypes, ", " + p.Name + " = ", loc)), + field_to_string); + + rs_equals = new Binary (Binary.Operator.LogicalAnd, rs_equals, field_equal); + } + + string_concat = new Binary (Binary.Operator.Addition, + string_concat, + new StringConstant (Compiler.BuiltinTypes, " }", loc)); + + // + // Equals (object obj) override + // + var other_variable_assign = new TemporaryVariableReference (li_other, loc); + equals_block.AddStatement (new StatementExpression ( + new SimpleAssign (other_variable_assign, + new As (equals_block.GetParameterReference (0, loc), + current_type, loc), loc))); + + Expression equals_test = new Binary (Binary.Operator.Inequality, other_variable, new NullLiteral (loc)); + if (rs_equals != null) + equals_test = new Binary (Binary.Operator.LogicalAnd, equals_test, rs_equals); + equals_block.AddStatement (new Return (equals_test, loc)); + + equals.Block = equals_block; + equals.Define (); + Members.Add (equals); + + // + // GetHashCode () override + // + Method hashcode = new Method (this, new TypeExpression (Compiler.BuiltinTypes.Int, loc), + Modifiers.PUBLIC | Modifiers.OVERRIDE | Modifiers.DEBUGGER_HIDDEN, + new MemberName ("GetHashCode", loc), + ParametersCompiled.EmptyReadOnlyParameters, null); + + // + // Modified FNV with good avalanche behavior and uniform + // distribution with larger hash sizes. + // + // const int FNV_prime = 16777619; + // int hash = (int) 2166136261; + // foreach (int d in data) + // hash = (hash ^ d) * FNV_prime; + // hash += hash << 13; + // hash ^= hash >> 7; + // hash += hash << 3; + // hash ^= hash >> 17; + // hash += hash << 5; + + ToplevelBlock hashcode_top = new ToplevelBlock (Compiler, loc); + Block hashcode_block = new Block (hashcode_top, loc, loc); + hashcode_top.AddStatement (new Unchecked (hashcode_block, loc)); + + var li_hash = LocalVariable.CreateCompilerGenerated (Compiler.BuiltinTypes.Int, hashcode_top, loc); + hashcode_block.AddStatement (new BlockVariable (new TypeExpression (li_hash.Type, loc), li_hash)); + LocalVariableReference hash_variable_assign = new LocalVariableReference (li_hash, loc); + hashcode_block.AddStatement (new StatementExpression ( + new SimpleAssign (hash_variable_assign, rs_hashcode))); + + var hash_variable = new LocalVariableReference (li_hash, loc); + hashcode_block.AddStatement (new StatementExpression ( + new CompoundAssign (Binary.Operator.Addition, hash_variable, + new Binary (Binary.Operator.LeftShift, hash_variable, new IntConstant (Compiler.BuiltinTypes, 13, loc))))); + hashcode_block.AddStatement (new StatementExpression ( + new CompoundAssign (Binary.Operator.ExclusiveOr, hash_variable, + new Binary (Binary.Operator.RightShift, hash_variable, new IntConstant (Compiler.BuiltinTypes, 7, loc))))); + hashcode_block.AddStatement (new StatementExpression ( + new CompoundAssign (Binary.Operator.Addition, hash_variable, + new Binary (Binary.Operator.LeftShift, hash_variable, new IntConstant (Compiler.BuiltinTypes, 3, loc))))); + hashcode_block.AddStatement (new StatementExpression ( + new CompoundAssign (Binary.Operator.ExclusiveOr, hash_variable, + new Binary (Binary.Operator.RightShift, hash_variable, new IntConstant (Compiler.BuiltinTypes, 17, loc))))); + hashcode_block.AddStatement (new StatementExpression ( + new CompoundAssign (Binary.Operator.Addition, hash_variable, + new Binary (Binary.Operator.LeftShift, hash_variable, new IntConstant (Compiler.BuiltinTypes, 5, loc))))); + + hashcode_block.AddStatement (new Return (hash_variable, loc)); + hashcode.Block = hashcode_top; + hashcode.Define (); + Members.Add (hashcode); + + // + // ToString () override + // + + ToplevelBlock tostring_block = new ToplevelBlock (Compiler, loc); + tostring_block.AddStatement (new Return (string_concat, loc)); + tostring.Block = tostring_block; + tostring.Define (); + Members.Add (tostring); + + return true; + } + + public override string GetSignatureForError () + { + return SignatureForError; + } + + public override CompilationSourceFile GetCompilationSourceFile () + { + return null; + } + + public IList Parameters { + get { + return parameters; + } + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/argument.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/argument.cs new file mode 100644 index 000000000..a5edeb53c --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/argument.cs @@ -0,0 +1,674 @@ +// +// argument.cs: Argument expressions +// +// Author: +// Miguel de Icaza (miguel@ximain.com) +// Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// Copyright 2003-2011 Novell, Inc. +// Copyright 2011 Xamarin Inc +// + +using System; +using System.Collections.Generic; + +#if STATIC +using IKVM.Reflection.Emit; +#else +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp +{ + // + // Argument expression used for invocation + // + public class Argument + { + public enum AType : byte + { + None = 0, + Ref = 1, // ref modifier used + Out = 2, // out modifier used + Default = 3, // argument created from default parameter value + DynamicTypeName = 4, // System.Type argument for dynamic binding + ExtensionType = 5, // Instance expression inserted as the first argument + + // Conditional instance expression inserted as the first argument + ExtensionTypeConditionalAccess = 5 | ConditionalAccessFlag, + + ConditionalAccessFlag = 1 << 7 + } + + public readonly AType ArgType; + public Expression Expr; + + public Argument (Expression expr, AType type) + { + this.Expr = expr; + this.ArgType = type; + } + + public Argument (Expression expr) + { + this.Expr = expr; + } + + #region Properties + + public bool IsByRef { + get { return ArgType == AType.Ref || ArgType == AType.Out; } + } + + public bool IsDefaultArgument { + get { return ArgType == AType.Default; } + } + + public bool IsExtensionType { + get { + return (ArgType & AType.ExtensionType) == AType.ExtensionType; + } + } + + public Parameter.Modifier Modifier { + get { + switch (ArgType) { + case AType.Out: + return Parameter.Modifier.OUT; + + case AType.Ref: + return Parameter.Modifier.REF; + + default: + return Parameter.Modifier.NONE; + } + } + } + + public TypeSpec Type { + get { return Expr.Type; } + } + + #endregion + + public Argument Clone (Expression expr) + { + Argument a = (Argument) MemberwiseClone (); + a.Expr = expr; + return a; + } + + public Argument Clone (CloneContext clonectx) + { + return Clone (Expr.Clone (clonectx)); + } + + public virtual Expression CreateExpressionTree (ResolveContext ec) + { + if (ArgType == AType.Default) + ec.Report.Error (854, Expr.Location, "An expression tree cannot contain an invocation which uses optional parameter"); + + return Expr.CreateExpressionTree (ec); + } + + + public virtual void Emit (EmitContext ec) + { + if (!IsByRef) { + if (ArgType == AType.ExtensionTypeConditionalAccess) { + var ie = new InstanceEmitter (Expr, false); + ie.Emit (ec, true); + } else { + Expr.Emit (ec); + } + + return; + } + + AddressOp mode = AddressOp.Store; + if (ArgType == AType.Ref) + mode |= AddressOp.Load; + + IMemoryLocation ml = (IMemoryLocation) Expr; + ml.AddressOf (ec, mode); + } + + public Argument EmitToField (EmitContext ec, bool cloneResult) + { + var res = Expr.EmitToField (ec); + if (cloneResult && res != Expr) + return new Argument (res, ArgType); + + Expr = res; + return this; + } + + public void FlowAnalysis (FlowAnalysisContext fc) + { + if (ArgType == AType.Out) { + var vr = Expr as VariableReference; + if (vr != null) { + if (vr.VariableInfo != null) + fc.SetVariableAssigned (vr.VariableInfo); + + return; + } + + var fe = Expr as FieldExpr; + if (fe != null) { + fe.SetFieldAssigned (fc); + return; + } + + return; + } + + Expr.FlowAnalysis (fc); + } + + public string GetSignatureForError () + { + if (Expr.eclass == ExprClass.MethodGroup) + return Expr.ExprClassName; + + return Expr.Type.GetSignatureForError (); + } + + public bool ResolveMethodGroup (ResolveContext ec) + { + SimpleName sn = Expr as SimpleName; + if (sn != null) + Expr = sn.GetMethodGroup (); + + // FIXME: csc doesn't report any error if you try to use `ref' or + // `out' in a delegate creation expression. + Expr = Expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup); + if (Expr == null) + return false; + + return true; + } + + public void Resolve (ResolveContext ec) + { + // Verify that the argument is readable + if (ArgType != AType.Out) + Expr = Expr.Resolve (ec); + + // Verify that the argument is writeable + if (Expr != null && IsByRef) + Expr = Expr.ResolveLValue (ec, EmptyExpression.OutAccess); + + if (Expr == null) + Expr = ErrorExpression.Instance; + } + } + + public class MovableArgument : Argument + { + LocalTemporary variable; + + public MovableArgument (Argument arg) + : this (arg.Expr, arg.ArgType) + { + } + + protected MovableArgument (Expression expr, AType modifier) + : base (expr, modifier) + { + } + + public override void Emit (EmitContext ec) + { + // TODO: Should guard against multiple emits + base.Emit (ec); + + // Release temporary variable when used + if (variable != null) + variable.Release (ec); + } + + public void EmitToVariable (EmitContext ec) + { + var type = Expr.Type; + if (IsByRef) { + var ml = (IMemoryLocation) Expr; + ml.AddressOf (ec, AddressOp.LoadStore); + type = ReferenceContainer.MakeType (ec.Module, type); + } else { + Expr.Emit (ec); + } + + variable = new LocalTemporary (type); + variable.Store (ec); + + Expr = variable; + } + } + + public class NamedArgument : MovableArgument + { + public readonly string Name; + readonly Location loc; + + public NamedArgument (string name, Location loc, Expression expr) + : this (name, loc, expr, AType.None) + { + } + + public NamedArgument (string name, Location loc, Expression expr, AType modifier) + : base (expr, modifier) + { + this.Name = name; + this.loc = loc; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + ec.Report.Error (853, loc, "An expression tree cannot contain named argument"); + return base.CreateExpressionTree (ec); + } + + public Location Location { + get { return loc; } + } + } + + public class Arguments + { + sealed class ArgumentsOrdered : Arguments + { + readonly List ordered; + + public ArgumentsOrdered (Arguments args) + : base (args.Count) + { + AddRange (args); + ordered = new List (); + } + + public void AddOrdered (MovableArgument arg) + { + ordered.Add (arg); + } + + public override void FlowAnalysis (FlowAnalysisContext fc, List movable = null) + { + foreach (var arg in ordered) { + if (arg.ArgType != Argument.AType.Out) + arg.FlowAnalysis (fc); + } + + base.FlowAnalysis (fc, ordered); + } + + public override Arguments Emit (EmitContext ec, bool dup_args, bool prepareAwait) + { + foreach (var a in ordered) { + if (prepareAwait) + a.EmitToField (ec, false); + else + a.EmitToVariable (ec); + } + + return base.Emit (ec, dup_args, prepareAwait); + } + } + + // Try not to add any more instances to this class, it's allocated a lot + List args; + + public Arguments (int capacity) + { + args = new List (capacity); + } + + private Arguments (List args) + { + this.args = args; + } + + public void Add (Argument arg) + { + args.Add (arg); + } + + public void AddRange (Arguments args) + { + this.args.AddRange (args.args); + } + + public bool ContainsEmitWithAwait () + { + foreach (var arg in args) { + if (arg.Expr.ContainsEmitWithAwait ()) + return true; + } + + return false; + } + + public ArrayInitializer CreateDynamicBinderArguments (ResolveContext rc) + { + Location loc = Location.Null; + var all = new ArrayInitializer (args.Count, loc); + + MemberAccess binder = DynamicExpressionStatement.GetBinderNamespace (loc); + + foreach (Argument a in args) { + Arguments dargs = new Arguments (2); + + // CSharpArgumentInfoFlags.None = 0 + const string info_flags_enum = "CSharpArgumentInfoFlags"; + Expression info_flags = new IntLiteral (rc.BuiltinTypes, 0, loc); + + if (a.Expr is Constant) { + info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags, + new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "Constant", loc)); + } else if (a.ArgType == Argument.AType.Ref) { + info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags, + new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "IsRef", loc)); + info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags, + new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "UseCompileTimeType", loc)); + } else if (a.ArgType == Argument.AType.Out) { + info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags, + new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "IsOut", loc)); + info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags, + new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "UseCompileTimeType", loc)); + } else if (a.ArgType == Argument.AType.DynamicTypeName) { + info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags, + new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "IsStaticType", loc)); + } + + var arg_type = a.Expr.Type; + + if (arg_type.BuiltinType != BuiltinTypeSpec.Type.Dynamic && arg_type != InternalType.NullLiteral) { + MethodGroupExpr mg = a.Expr as MethodGroupExpr; + if (mg != null) { + rc.Report.Error (1976, a.Expr.Location, + "The method group `{0}' cannot be used as an argument of dynamic operation. Consider using parentheses to invoke the method", + mg.Name); + } else if (arg_type == InternalType.AnonymousMethod) { + rc.Report.Error (1977, a.Expr.Location, + "An anonymous method or lambda expression cannot be used as an argument of dynamic operation. Consider using a cast"); + } else if (arg_type.Kind == MemberKind.Void || arg_type == InternalType.Arglist || arg_type.IsPointer) { + rc.Report.Error (1978, a.Expr.Location, + "An expression of type `{0}' cannot be used as an argument of dynamic operation", + arg_type.GetSignatureForError ()); + } + + info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags, + new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "UseCompileTimeType", loc)); + } + + string named_value; + NamedArgument na = a as NamedArgument; + if (na != null) { + info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags, + new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "NamedArgument", loc)); + + named_value = na.Name; + } else { + named_value = null; + } + + dargs.Add (new Argument (info_flags)); + dargs.Add (new Argument (new StringLiteral (rc.BuiltinTypes, named_value, loc))); + all.Add (new Invocation (new MemberAccess (new MemberAccess (binder, "CSharpArgumentInfo", loc), "Create", loc), dargs)); + } + + return all; + } + + public static Arguments CreateForExpressionTree (ResolveContext ec, Arguments args, params Expression[] e) + { + Arguments all = new Arguments ((args == null ? 0 : args.Count) + e.Length); + for (int i = 0; i < e.Length; ++i) { + if (e [i] != null) + all.Add (new Argument (e[i])); + } + + if (args != null) { + foreach (Argument a in args.args) { + Expression tree_arg = a.CreateExpressionTree (ec); + if (tree_arg != null) + all.Add (new Argument (tree_arg)); + } + } + + return all; + } + + public void CheckArrayAsAttribute (CompilerContext ctx) + { + foreach (Argument arg in args) { + // Type is undefined (was error 246) + if (arg.Type == null) + continue; + + if (arg.Type.IsArray) + ctx.Report.Warning (3016, 1, arg.Expr.Location, "Arrays as attribute arguments are not CLS-compliant"); + } + } + + public Arguments Clone (CloneContext ctx) + { + Arguments cloned = new Arguments (args.Count); + foreach (Argument a in args) + cloned.Add (a.Clone (ctx)); + + return cloned; + } + + public int Count { + get { return args.Count; } + } + + // + // Emits a list of resolved Arguments + // + public void Emit (EmitContext ec) + { + Emit (ec, false, false); + } + + // + // if `dup_args' is true or any of arguments contains await. + // A copy of all arguments will be returned to the caller + // + public virtual Arguments Emit (EmitContext ec, bool dup_args, bool prepareAwait) + { + List dups; + + if ((dup_args && Count != 0) || prepareAwait) + dups = new List (Count); + else + dups = null; + + LocalTemporary lt; + foreach (Argument a in args) { + if (prepareAwait) { + dups.Add (a.EmitToField (ec, true)); + continue; + } + + a.Emit (ec); + + if (!dup_args) { + continue; + } + + if (a.Expr.IsSideEffectFree) { + // + // No need to create a temporary variable for side effect free expressions. I assume + // all side-effect free expressions are cheap, this has to be tweaked when we become + // more aggressive on detection + // + dups.Add (a); + } else { + ec.Emit (OpCodes.Dup); + + // TODO: Release local temporary on next Emit + // Need to add a flag to argument to indicate this + lt = new LocalTemporary (a.Type); + lt.Store (ec); + + dups.Add (new Argument (lt, a.ArgType)); + } + } + + if (dups != null) + return new Arguments (dups); + + return null; + } + + public virtual void FlowAnalysis (FlowAnalysisContext fc, List movable = null) + { + bool has_out = false; + foreach (var arg in args) { + if (arg.ArgType == Argument.AType.Out) { + has_out = true; + continue; + } + + if (movable == null) { + arg.FlowAnalysis (fc); + continue; + } + + var ma = arg as MovableArgument; + if (ma != null && !movable.Contains (ma)) + arg.FlowAnalysis (fc); + } + + if (!has_out) + return; + + foreach (var arg in args) { + if (arg.ArgType != Argument.AType.Out) + continue; + + arg.FlowAnalysis (fc); + } + } + + public List.Enumerator GetEnumerator () + { + return args.GetEnumerator (); + } + + // + // At least one argument is of dynamic type + // + public bool HasDynamic { + get { + foreach (Argument a in args) { + if (a.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic && !a.IsByRef) + return true; + } + + return false; + } + } + + // + // At least one argument is named argument + // + public bool HasNamed { + get { + foreach (Argument a in args) { + if (a is NamedArgument) + return true; + } + + return false; + } + } + + + public void Insert (int index, Argument arg) + { + args.Insert (index, arg); + } + + public static System.Linq.Expressions.Expression[] MakeExpression (Arguments args, BuilderContext ctx) + { + if (args == null || args.Count == 0) + return null; + + var exprs = new System.Linq.Expressions.Expression [args.Count]; + for (int i = 0; i < exprs.Length; ++i) { + Argument a = args.args [i]; + exprs[i] = a.Expr.MakeExpression (ctx); + } + + return exprs; + } + + // + // For named arguments when the order of execution is different + // to order of invocation + // + public Arguments MarkOrderedArgument (NamedArgument a) + { + // + // An expression has no effect on left-to-right execution + // + if (a.Expr.IsSideEffectFree) + return this; + + ArgumentsOrdered ra = this as ArgumentsOrdered; + if (ra == null) { + ra = new ArgumentsOrdered (this); + + for (int i = 0; i < args.Count; ++i) { + var la = args [i]; + if (la == a) + break; + + // + // When the argument is filled later by default expression + // + if (la == null) + continue; + + var ma = la as MovableArgument; + if (ma == null) { + ma = new MovableArgument (la); + ra.args[i] = ma; + } + + ra.AddOrdered (ma); + } + } + + ra.AddOrdered (a); + return ra; + } + + // + // Returns dynamic when at least one argument is of dynamic type + // + public void Resolve (ResolveContext ec, out bool dynamic) + { + dynamic = false; + foreach (Argument a in args) { + a.Resolve (ec); + if (a.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic && !a.IsByRef) + dynamic = true; + } + } + + public void RemoveAt (int index) + { + args.RemoveAt (index); + } + + public Argument this [int index] { + get { return args [index]; } + set { args [index] = value; } + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/assembly.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/assembly.cs new file mode 100644 index 000000000..f93c03839 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/assembly.cs @@ -0,0 +1,1257 @@ +// +// assembly.cs: Assembly declaration and specifications +// +// Authors: +// Miguel de Icaza (miguel@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// Copyright 2001, 2002, 2003 Ximian, Inc. +// Copyright 2004-2011 Novell, Inc. +// Copyright 2011-2013 Xamarin Inc +// + + +using System; +using System.IO; +using System.Collections.Generic; +using System.Globalization; +using System.Security; +using System.Security.Cryptography; +using System.Security.Permissions; +using Mono.Security.Cryptography; +using Mono.CompilerServices.SymbolWriter; + +#if STATIC +using IKVM.Reflection; +using IKVM.Reflection.Emit; +using SecurityType = System.Collections.Generic.List; +#else +using SecurityType = System.Collections.Generic.Dictionary; +using System.Reflection; +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp +{ + public interface IAssemblyDefinition + { + string FullName { get; } + bool IsCLSCompliant { get; } + bool IsMissing { get; } + string Name { get; } + + byte[] GetPublicKeyToken (); + bool IsFriendAssemblyTo (IAssemblyDefinition assembly); + } + + public abstract class AssemblyDefinition : IAssemblyDefinition + { + // TODO: make it private and move all builder based methods here + public AssemblyBuilder Builder; + protected AssemblyBuilderExtension builder_extra; + MonoSymbolFile symbol_writer; + + bool is_cls_compliant; + bool wrap_non_exception_throws; + bool wrap_non_exception_throws_custom; + bool has_user_debuggable; + + protected ModuleContainer module; + readonly string name; + protected readonly string file_name; + + byte[] public_key, public_key_token; + bool delay_sign; + + // Holds private/public key pair when private key + // was available + StrongNameKeyPair private_key; + + Attribute cls_attribute; + Method entry_point; + + protected List added_modules; + SecurityType declarative_security; + Dictionary emitted_forwarders; + AssemblyAttributesPlaceholder module_target_attrs; + + // Win32 version info values + string vi_product, vi_product_version, vi_company, vi_copyright, vi_trademark; + + protected AssemblyDefinition (ModuleContainer module, string name) + { + this.module = module; + this.name = Path.GetFileNameWithoutExtension (name); + + wrap_non_exception_throws = true; + + delay_sign = Compiler.Settings.StrongNameDelaySign; + + // + // Load strong name key early enough for assembly importer to be able to + // use the keys for InternalsVisibleTo + // This should go somewhere close to ReferencesLoading but don't have the place yet + // + if (Compiler.Settings.HasKeyFileOrContainer) { + LoadPublicKey (Compiler.Settings.StrongNameKeyFile, Compiler.Settings.StrongNameKeyContainer); + } + } + + protected AssemblyDefinition (ModuleContainer module, string name, string fileName) + : this (module, name) + { + this.file_name = fileName; + } + + #region Properties + + public Attribute CLSCompliantAttribute { + get { + return cls_attribute; + } + } + + public CompilerContext Compiler { + get { + return module.Compiler; + } + } + + // + // Assembly entry point, aka Main method + // + public Method EntryPoint { + get { + return entry_point; + } + set { + entry_point = value; + } + } + + public string FullName { + get { + return Builder.FullName; + } + } + + public bool HasCLSCompliantAttribute { + get { + return cls_attribute != null; + } + } + + // TODO: This should not exist here but will require more changes + public MetadataImporter Importer { + get; set; + } + + public bool IsCLSCompliant { + get { + return is_cls_compliant; + } + } + + bool IAssemblyDefinition.IsMissing { + get { + return false; + } + } + + public bool IsSatelliteAssembly { get; private set; } + + public string Name { + get { + return name; + } + } + + public bool WrapNonExceptionThrows { + get { + return wrap_non_exception_throws; + } + } + + protected Report Report { + get { + return Compiler.Report; + } + } + + public MonoSymbolFile SymbolWriter { + get { + return symbol_writer; + } + } + + #endregion + + public void AddModule (ImportedModuleDefinition module) + { + if (added_modules == null) { + added_modules = new List (); + added_modules.Add (module); + } + } + + public void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.IsValidSecurityAttribute ()) { + a.ExtractSecurityPermissionSet (ctor, ref declarative_security); + return; + } + + if (a.Type == pa.AssemblyCulture) { + string value = a.GetString (); + if (value == null || value.Length == 0) + return; + + if (Compiler.Settings.Target == Target.Exe) { + Report.Error (7059, a.Location, "Executables cannot be satellite assemblies. Remove the attribute or keep it empty"); + return; + } + + if (value == "neutral") + value = ""; + + if (Compiler.Settings.Target == Target.Module) { + SetCustomAttribute (ctor, cdata); + } else { + builder_extra.SetCulture (value, a.Location); + } + + IsSatelliteAssembly = true; + return; + } + + if (a.Type == pa.AssemblyVersion) { + string value = a.GetString (); + if (value == null || value.Length == 0) + return; + + var vinfo = IsValidAssemblyVersion (value, true); + if (vinfo == null) { + Report.Error (7034, a.Location, "The specified version string `{0}' does not conform to the required format - major[.minor[.build[.revision]]]", + value); + return; + } + + if (Compiler.Settings.Target == Target.Module) { + SetCustomAttribute (ctor, cdata); + } else { + builder_extra.SetVersion (vinfo, a.Location); + } + + return; + } + + if (a.Type == pa.AssemblyAlgorithmId) { + const int pos = 2; // skip CA header + uint alg = (uint) cdata [pos]; + alg |= ((uint) cdata [pos + 1]) << 8; + alg |= ((uint) cdata [pos + 2]) << 16; + alg |= ((uint) cdata [pos + 3]) << 24; + + if (Compiler.Settings.Target == Target.Module) { + SetCustomAttribute (ctor, cdata); + } else { + builder_extra.SetAlgorithmId (alg, a.Location); + } + + return; + } + + if (a.Type == pa.AssemblyFlags) { + const int pos = 2; // skip CA header + uint flags = (uint) cdata[pos]; + flags |= ((uint) cdata [pos + 1]) << 8; + flags |= ((uint) cdata [pos + 2]) << 16; + flags |= ((uint) cdata [pos + 3]) << 24; + + // Ignore set PublicKey flag if assembly is not strongnamed + if ((flags & (uint) AssemblyNameFlags.PublicKey) != 0 && public_key == null) + flags &= ~(uint) AssemblyNameFlags.PublicKey; + + if (Compiler.Settings.Target == Target.Module) { + SetCustomAttribute (ctor, cdata); + } else { + builder_extra.SetFlags (flags, a.Location); + } + + return; + } + + if (a.Type == pa.TypeForwarder) { + TypeSpec t = a.GetArgumentType (); + if (t == null || TypeManager.HasElementType (t)) { + Report.Error (735, a.Location, "Invalid type specified as an argument for TypeForwardedTo attribute"); + return; + } + + if (emitted_forwarders == null) { + emitted_forwarders = new Dictionary (); + } else if (emitted_forwarders.ContainsKey (t.MemberDefinition)) { + Report.SymbolRelatedToPreviousError (emitted_forwarders[t.MemberDefinition].Location, null); + Report.Error (739, a.Location, "A duplicate type forward of type `{0}'", + t.GetSignatureForError ()); + return; + } + + emitted_forwarders.Add (t.MemberDefinition, a); + + if (t.MemberDefinition.DeclaringAssembly == this) { + Report.SymbolRelatedToPreviousError (t); + Report.Error (729, a.Location, "Cannot forward type `{0}' because it is defined in this assembly", + t.GetSignatureForError ()); + return; + } + + if (t.IsNested) { + Report.Error (730, a.Location, "Cannot forward type `{0}' because it is a nested type", + t.GetSignatureForError ()); + return; + } + + builder_extra.AddTypeForwarder (t.GetDefinition (), a.Location); + return; + } + + if (a.Type == pa.Extension) { + a.Error_MisusedExtensionAttribute (); + return; + } + + if (a.Type == pa.InternalsVisibleTo) { + string assembly_name = a.GetString (); + if (assembly_name == null) { + Report.Error (7030, a.Location, "Friend assembly reference cannot have `null' value"); + return; + } + + if (assembly_name.Length == 0) + return; +#if STATIC + ParsedAssemblyName aname; + ParseAssemblyResult r = Fusion.ParseAssemblyName (assembly_name, out aname); + if (r != ParseAssemblyResult.OK) { + Report.Warning (1700, 3, a.Location, "Friend assembly reference `{0}' is invalid and cannot be resolved", + assembly_name); + return; + } + + if (aname.Version != null || aname.Culture != null || aname.ProcessorArchitecture != ProcessorArchitecture.None) { + Report.Error (1725, a.Location, + "Friend assembly reference `{0}' is invalid. InternalsVisibleTo declarations cannot have a version, culture or processor architecture specified", + assembly_name); + + return; + } + + if (public_key != null && !aname.HasPublicKey) { + Report.Error (1726, a.Location, + "Friend assembly reference `{0}' is invalid. Strong named assemblies must specify a public key in their InternalsVisibleTo declarations", + assembly_name); + return; + } +#endif + } else if (a.Type == pa.RuntimeCompatibility) { + wrap_non_exception_throws_custom = true; + } else if (a.Type == pa.AssemblyFileVersion) { + vi_product_version = a.GetString (); + if (string.IsNullOrEmpty (vi_product_version) || IsValidAssemblyVersion (vi_product_version, false) == null) { + Report.Warning (7035, 1, a.Location, "The specified version string `{0}' does not conform to the recommended format major.minor.build.revision", + vi_product_version, a.Name); + return; + } + + // File version info decoding from blob is not supported + var cab = new CustomAttributeBuilder ((ConstructorInfo) ctor.GetMetaInfo (), new object[] { vi_product_version }); + Builder.SetCustomAttribute (cab); + return; + } else if (a.Type == pa.AssemblyProduct) { + vi_product = a.GetString (); + } else if (a.Type == pa.AssemblyCompany) { + vi_company = a.GetString (); + } else if (a.Type == pa.AssemblyDescription) { + // TODO: Needs extra api + } else if (a.Type == pa.AssemblyCopyright) { + vi_copyright = a.GetString (); + } else if (a.Type == pa.AssemblyTrademark) { + vi_trademark = a.GetString (); + } else if (a.Type == pa.Debuggable) { + has_user_debuggable = true; + } + + SetCustomAttribute (ctor, cdata); + } + + // + // When using assembly public key attributes InternalsVisibleTo key + // was not checked, we have to do it later when we actually know what + // our public key token is + // + void CheckReferencesPublicToken () + { + // TODO: It should check only references assemblies but there is + // no working SRE API + foreach (var entry in Importer.Assemblies) { + var a = entry as ImportedAssemblyDefinition; + if (a == null || a.IsMissing) + continue; + + if (public_key != null && !a.HasStrongName) { + Report.Error (1577, "Referenced assembly `{0}' does not have a strong name", + a.FullName); + } + + var ci = a.Assembly.GetName ().CultureInfo; + if (!ci.Equals (CultureInfo.InvariantCulture)) { + Report.Warning (8009, 1, "Referenced assembly `{0}' has different culture setting of `{1}'", + a.Name, ci.Name); + } + + if (!a.IsFriendAssemblyTo (this)) + continue; + + var attr = a.GetAssemblyVisibleToName (this); + var atoken = attr.GetPublicKeyToken (); + + if (ArrayComparer.IsEqual (GetPublicKeyToken (), atoken)) + continue; + + Report.SymbolRelatedToPreviousError (a.Location); + Report.Error (281, + "Friend access was granted to `{0}', but the output assembly is named `{1}'. Try adding a reference to `{0}' or change the output assembly name to match it", + attr.FullName, FullName); + } + } + + protected AssemblyName CreateAssemblyName () + { + var an = new AssemblyName (name); + + if (public_key != null && Compiler.Settings.Target != Target.Module) { + if (delay_sign) { + an.SetPublicKey (public_key); + } else { + if (public_key.Length == 16) { + Report.Error (1606, "Could not sign the assembly. ECMA key can only be used to delay-sign assemblies"); + } else if (private_key == null) { + Error_AssemblySigning ("The specified key file does not have a private key"); + } else { + an.KeyPair = private_key; + } + } + } + + return an; + } + + public virtual ModuleBuilder CreateModuleBuilder () + { + if (file_name == null) + throw new NotSupportedException ("transient module in static assembly"); + + var module_name = Path.GetFileName (file_name); + + // Always initialize module without symbolInfo. We could be framework dependent + // but returned ISymbolWriter does not have all what we need therefore some + // adaptor will be needed for now we alwayas emit MDB format when generating + // debug info + return Builder.DefineDynamicModule (module_name, module_name, false); + } + + public virtual void Emit () + { + if (Compiler.Settings.Target == Target.Module) { + module_target_attrs = new AssemblyAttributesPlaceholder (module, name); + module_target_attrs.CreateContainer (); + module_target_attrs.DefineContainer (); + module_target_attrs.Define (); + module.AddCompilerGeneratedClass (module_target_attrs); + } else if (added_modules != null) { + ReadModulesAssemblyAttributes (); + } + + if (Compiler.Settings.GenerateDebugInfo) { + symbol_writer = new MonoSymbolFile (); + } + + module.EmitContainer (); + + if (module.HasExtensionMethod) { + var pa = module.PredefinedAttributes.Extension; + if (pa.IsDefined) { + SetCustomAttribute (pa.Constructor, AttributeEncoder.Empty); + } + } + + if (!IsSatelliteAssembly) { + if (!has_user_debuggable && Compiler.Settings.GenerateDebugInfo) { + var pa = module.PredefinedAttributes.Debuggable; + if (pa.IsDefined) { + var modes = System.Diagnostics.DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints; + if (!Compiler.Settings.Optimize) + modes |= System.Diagnostics.DebuggableAttribute.DebuggingModes.DisableOptimizations; + + pa.EmitAttribute (Builder, modes); + } + } + + if (!wrap_non_exception_throws_custom) { + PredefinedAttribute pa = module.PredefinedAttributes.RuntimeCompatibility; + if (pa.IsDefined && pa.ResolveBuilder ()) { + var prop = module.PredefinedMembers.RuntimeCompatibilityWrapNonExceptionThrows.Get (); + if (prop != null) { + AttributeEncoder encoder = new AttributeEncoder (); + encoder.EncodeNamedPropertyArgument (prop, new BoolLiteral (Compiler.BuiltinTypes, true, Location.Null)); + SetCustomAttribute (pa.Constructor, encoder.ToArray ()); + } + } + } + + if (declarative_security != null) { +#if STATIC + foreach (var entry in declarative_security) { + Builder.__AddDeclarativeSecurity (entry); + } +#else + throw new NotSupportedException ("Assembly-level security"); +#endif + } + } + + CheckReferencesPublicToken (); + + SetEntryPoint (); + } + + public byte[] GetPublicKeyToken () + { + if (public_key == null || public_key_token != null) + return public_key_token; + + HashAlgorithm ha = SHA1.Create (); + byte[] hash = ha.ComputeHash (public_key); + // we need the last 8 bytes in reverse order + public_key_token = new byte[8]; + Buffer.BlockCopy (hash, hash.Length - 8, public_key_token, 0, 8); + Array.Reverse (public_key_token, 0, 8); + return public_key_token; + } + + // + // Either keyFile or keyContainer has to be non-null + // + void LoadPublicKey (string keyFile, string keyContainer) + { + if (keyContainer != null) { + try { + private_key = new StrongNameKeyPair (keyContainer); + public_key = private_key.PublicKey; + } catch { + Error_AssemblySigning ("The specified key container `" + keyContainer + "' does not exist"); + } + + return; + } + + bool key_file_exists = File.Exists (keyFile); + + // + // For attribute based KeyFile do additional lookup + // in output assembly path + // + if (!key_file_exists && Compiler.Settings.StrongNameKeyFile == null) { + // + // The key file can be relative to output assembly + // + string test_path = Path.Combine (Path.GetDirectoryName (file_name), keyFile); + key_file_exists = File.Exists (test_path); + if (key_file_exists) + keyFile = test_path; + } + + if (!key_file_exists) { + Error_AssemblySigning ("The specified key file `" + keyFile + "' does not exist"); + return; + } + + using (FileStream fs = new FileStream (keyFile, FileMode.Open, FileAccess.Read)) { + byte[] snkeypair = new byte[fs.Length]; + fs.Read (snkeypair, 0, snkeypair.Length); + + // check for ECMA key + if (snkeypair.Length == 16) { + public_key = snkeypair; + return; + } + + try { + // take it, with or without, a private key + RSA rsa = CryptoConvert.FromCapiKeyBlob (snkeypair); + // and make sure we only feed the public part to Sys.Ref + byte[] publickey = CryptoConvert.ToCapiPublicKeyBlob (rsa); + + // AssemblyName.SetPublicKey requires an additional header + byte[] publicKeyHeader = new byte[8] { 0x00, 0x24, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00 }; + + // Encode public key + public_key = new byte[12 + publickey.Length]; + Buffer.BlockCopy (publicKeyHeader, 0, public_key, 0, publicKeyHeader.Length); + + // Length of Public Key (in bytes) + int lastPart = public_key.Length - 12; + public_key[8] = (byte) (lastPart & 0xFF); + public_key[9] = (byte) ((lastPart >> 8) & 0xFF); + public_key[10] = (byte) ((lastPart >> 16) & 0xFF); + public_key[11] = (byte) ((lastPart >> 24) & 0xFF); + + Buffer.BlockCopy (publickey, 0, public_key, 12, publickey.Length); + } catch { + Error_AssemblySigning ("The specified key file `" + keyFile + "' has incorrect format"); + return; + } + + if (delay_sign) + return; + + try { + // TODO: Is there better way to test for a private key presence ? + CryptoConvert.FromCapiPrivateKeyBlob (snkeypair); + private_key = new StrongNameKeyPair (snkeypair); + } catch { } + } + } + + void ReadModulesAssemblyAttributes () + { + foreach (var m in added_modules) { + var cattrs = m.ReadAssemblyAttributes (); + if (cattrs == null) + continue; + + module.OptAttributes.AddAttributes (cattrs); + } + } + + public void Resolve () + { + if (Compiler.Settings.Unsafe && module.PredefinedTypes.SecurityAction.Define ()) { + // + // Emits [assembly: SecurityPermissionAttribute (SecurityAction.RequestMinimum, SkipVerification = true)] + // when -unsafe option was specified + // + Location loc = Location.Null; + + MemberAccess system_security_permissions = new MemberAccess (new MemberAccess ( + new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Security", loc), "Permissions", loc); + + var req_min = module.PredefinedMembers.SecurityActionRequestMinimum.Resolve (loc); + + Arguments pos = new Arguments (1); + pos.Add (new Argument (req_min.GetConstant (null))); + + Arguments named = new Arguments (1); + named.Add (new NamedArgument ("SkipVerification", loc, new BoolLiteral (Compiler.BuiltinTypes, true, loc))); + + Attribute g = new Attribute ("assembly", + new MemberAccess (system_security_permissions, "SecurityPermissionAttribute"), + new Arguments[] { pos, named }, loc, false); + g.AttachTo (module, module); + + // Disable no-location warnings (e.g. obsolete) for compiler generated attribute + Compiler.Report.DisableReporting (); + try { + var ctor = g.Resolve (); + if (ctor != null) { + g.ExtractSecurityPermissionSet (ctor, ref declarative_security); + } + } finally { + Compiler.Report.EnableReporting (); + } + } + + if (module.OptAttributes == null) + return; + + // Ensure that we only have GlobalAttributes, since the Search isn't safe with other types. + if (!module.OptAttributes.CheckTargets()) + return; + + cls_attribute = module.ResolveAssemblyAttribute (module.PredefinedAttributes.CLSCompliant); + + if (cls_attribute != null) { + is_cls_compliant = cls_attribute.GetClsCompliantAttributeValue (); + } + + if (added_modules != null && Compiler.Settings.VerifyClsCompliance && is_cls_compliant) { + foreach (var m in added_modules) { + if (!m.IsCLSCompliant) { + Report.Error (3013, + "Added modules must be marked with the CLSCompliant attribute to match the assembly", + m.Name); + } + } + } + + Attribute a = module.ResolveAssemblyAttribute (module.PredefinedAttributes.RuntimeCompatibility); + if (a != null) { + var val = a.GetNamedValue ("WrapNonExceptionThrows") as BoolConstant; + if (val != null) + wrap_non_exception_throws = val.Value; + } + } + + protected void ResolveAssemblySecurityAttributes () + { + string key_file = null; + string key_container = null; + + if (module.OptAttributes != null) { + foreach (Attribute a in module.OptAttributes.Attrs) { + // cannot rely on any resolve-based members before you call Resolve + if (a.ExplicitTarget != "assembly") + continue; + + // TODO: This code is buggy: comparing Attribute name without resolving is wrong. + // However, this is invoked by CodeGen.Init, when none of the namespaces + // are loaded yet. + // TODO: Does not handle quoted attributes properly + switch (a.Name) { + case "AssemblyKeyFile": + case "AssemblyKeyFileAttribute": + case "System.Reflection.AssemblyKeyFileAttribute": + if (Compiler.Settings.StrongNameKeyFile != null) { + Report.SymbolRelatedToPreviousError (a.Location, a.GetSignatureForError ()); + Report.Warning (1616, 1, "Option `{0}' overrides attribute `{1}' given in a source file or added module", + "keyfile", "System.Reflection.AssemblyKeyFileAttribute"); + } else { + string value = a.GetString (); + if (!string.IsNullOrEmpty (value)) { + Error_ObsoleteSecurityAttribute (a, "keyfile"); + key_file = value; + } + } + break; + case "AssemblyKeyName": + case "AssemblyKeyNameAttribute": + case "System.Reflection.AssemblyKeyNameAttribute": + if (Compiler.Settings.StrongNameKeyContainer != null) { + Report.SymbolRelatedToPreviousError (a.Location, a.GetSignatureForError ()); + Report.Warning (1616, 1, "Option `{0}' overrides attribute `{1}' given in a source file or added module", + "keycontainer", "System.Reflection.AssemblyKeyNameAttribute"); + } else { + string value = a.GetString (); + if (!string.IsNullOrEmpty (value)) { + Error_ObsoleteSecurityAttribute (a, "keycontainer"); + key_container = value; + } + } + break; + case "AssemblyDelaySign": + case "AssemblyDelaySignAttribute": + case "System.Reflection.AssemblyDelaySignAttribute": + bool b = a.GetBoolean (); + if (b) { + Error_ObsoleteSecurityAttribute (a, "delaysign"); + } + + delay_sign = b; + break; + } + } + } + + // We came here only to report assembly attributes warnings + if (public_key != null) + return; + + // + // Load the strong key file found in attributes when no + // command line key was given + // + if (key_file != null || key_container != null) { + LoadPublicKey (key_file, key_container); + } else if (delay_sign) { + Report.Warning (1607, 1, "Delay signing was requested but no key file was given"); + } + } + + public void EmbedResources () + { + // + // Add Win32 resources + // + if (Compiler.Settings.Win32ResourceFile != null) { + Builder.DefineUnmanagedResource (Compiler.Settings.Win32ResourceFile); + } else { + Builder.DefineVersionInfoResource (vi_product, vi_product_version, vi_company, vi_copyright, vi_trademark); + } + + if (Compiler.Settings.Win32IconFile != null) { + builder_extra.DefineWin32IconResource (Compiler.Settings.Win32IconFile); + } + + if (Compiler.Settings.Resources != null) { + if (Compiler.Settings.Target == Target.Module) { + Report.Error (1507, "Cannot link resource file when building a module"); + } else { + int counter = 0; + foreach (var res in Compiler.Settings.Resources) { + if (!File.Exists (res.FileName)) { + Report.Error (1566, "Error reading resource file `{0}'", res.FileName); + continue; + } + + if (res.IsEmbeded) { + Stream stream; + if (counter++ < 10) { + stream = File.OpenRead (res.FileName); + } else { + // TODO: SRE API requires resource stream to be available during AssemblyBuilder::Save + // we workaround it by reading everything into memory to compile projects with + // many embedded resource (over 3500) references + stream = new MemoryStream (File.ReadAllBytes (res.FileName)); + } + + module.Builder.DefineManifestResource (res.Name, stream, res.Attributes); + } else { + Builder.AddResourceFile (res.Name, Path.GetFileName (res.FileName), res.Attributes); + } + } + } + } + } + + public void Save () + { + PortableExecutableKinds pekind = PortableExecutableKinds.ILOnly; + ImageFileMachine machine; + + switch (Compiler.Settings.Platform) { + case Platform.X86: + pekind |= PortableExecutableKinds.Required32Bit; + machine = ImageFileMachine.I386; + break; + case Platform.X64: + pekind |= PortableExecutableKinds.PE32Plus; + machine = ImageFileMachine.AMD64; + break; + case Platform.IA64: + machine = ImageFileMachine.IA64; + break; + case Platform.AnyCPU32Preferred: +#if STATIC + pekind |= PortableExecutableKinds.Preferred32Bit; + machine = ImageFileMachine.I386; + break; +#else + throw new NotSupportedException (); +#endif + case Platform.Arm: +#if STATIC + machine = ImageFileMachine.ARM; + break; +#else + throw new NotSupportedException (); +#endif + case Platform.AnyCPU: + default: + machine = ImageFileMachine.I386; + break; + } + + Compiler.TimeReporter.Start (TimeReporter.TimerType.OutputSave); + try { + if (Compiler.Settings.Target == Target.Module) { + SaveModule (pekind, machine); + } else { + Builder.Save (module.Builder.ScopeName, pekind, machine); + } + } catch (Exception e) { + Report.Error (16, "Could not write to file `" + name + "', cause: " + e.Message); + } + Compiler.TimeReporter.Stop (TimeReporter.TimerType.OutputSave); + + // Save debug symbols file + if (symbol_writer != null && Compiler.Report.Errors == 0) { + // TODO: it should run in parallel + Compiler.TimeReporter.Start (TimeReporter.TimerType.DebugSave); + + var filename = file_name + ".mdb"; + try { + // We mmap the file, so unlink the previous version since it may be in use + File.Delete (filename); + } catch { + // We can safely ignore + } + + module.WriteDebugSymbol (symbol_writer); + + using (FileStream fs = new FileStream (filename, FileMode.Create, FileAccess.Write)) { + symbol_writer.CreateSymbolFile (module.Builder.ModuleVersionId, fs); + } + + Compiler.TimeReporter.Stop (TimeReporter.TimerType.DebugSave); + } + } + + protected virtual void SaveModule (PortableExecutableKinds pekind, ImageFileMachine machine) + { + Report.RuntimeMissingSupport (Location.Null, "-target:module"); + } + + void SetCustomAttribute (MethodSpec ctor, byte[] data) + { + if (module_target_attrs != null) + module_target_attrs.AddAssemblyAttribute (ctor, data); + else + Builder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), data); + } + + void SetEntryPoint () + { + if (!Compiler.Settings.NeedsEntryPoint) { + if (Compiler.Settings.MainClass != null) + Report.Error (2017, "Cannot specify -main if building a module or library"); + + return; + } + + PEFileKinds file_kind; + + switch (Compiler.Settings.Target) { + case Target.Library: + case Target.Module: + file_kind = PEFileKinds.Dll; + break; + case Target.WinExe: + file_kind = PEFileKinds.WindowApplication; + break; + default: + file_kind = PEFileKinds.ConsoleApplication; + break; + } + + if (entry_point == null) { + string main_class = Compiler.Settings.MainClass; + if (main_class != null) { + // TODO: Handle dotted names + var texpr = module.GlobalRootNamespace.LookupType (module, main_class, 0, LookupMode.Probing, Location.Null); + if (texpr == null) { + Report.Error (1555, "Could not find `{0}' specified for Main method", main_class); + return; + } + + var mtype = texpr.MemberDefinition as ClassOrStruct; + if (mtype == null) { + Report.Error (1556, "`{0}' specified for Main method must be a valid class or struct", main_class); + return; + } + + Report.Error (1558, mtype.Location, "`{0}' does not have a suitable static Main method", mtype.GetSignatureForError ()); + } else { + string pname = file_name == null ? name : Path.GetFileName (file_name); + Report.Error (5001, "Program `{0}' does not contain a static `Main' method suitable for an entry point", + pname); + } + + return; + } + + Builder.SetEntryPoint (entry_point.MethodBuilder, file_kind); + } + + void Error_ObsoleteSecurityAttribute (Attribute a, string option) + { + Report.Warning (1699, 1, a.Location, + "Use compiler option `{0}' or appropriate project settings instead of `{1}' attribute", + option, a.Name); + } + + void Error_AssemblySigning (string text) + { + Report.Error (1548, "Error during assembly signing. " + text); + } + + public bool IsFriendAssemblyTo (IAssemblyDefinition assembly) + { + return false; + } + + static Version IsValidAssemblyVersion (string version, bool allowGenerated) + { + string[] parts = version.Split ('.'); + if (parts.Length < 1 || parts.Length > 4) + return null; + + var values = new int[4]; + for (int i = 0; i < parts.Length; ++i) { + if (!int.TryParse (parts[i], out values[i])) { + if (parts[i].Length == 1 && parts[i][0] == '*' && allowGenerated) { + if (i == 2) { + // Nothing can follow * + if (parts.Length > 3) + return null; + + // Generate Build value based on days since 1/1/2000 + TimeSpan days = DateTime.Today - new DateTime (2000, 1, 1); + values[i] = System.Math.Max (days.Days, 0); + i = 3; + } + + if (i == 3) { + // Generate Revision value based on every other second today + var seconds = DateTime.Now - DateTime.Today; + values[i] = (int) seconds.TotalSeconds / 2; + continue; + } + } + + return null; + } + + if (values[i] > ushort.MaxValue) + return null; + } + + return new Version (values[0], values[1], values[2], values[3]); + } + } + + public class AssemblyResource : IEquatable + { + public AssemblyResource (string fileName, string name) + : this (fileName, name, false) + { + } + + public AssemblyResource (string fileName, string name, bool isPrivate) + { + FileName = fileName; + Name = name; + Attributes = isPrivate ? ResourceAttributes.Private : ResourceAttributes.Public; + } + + public ResourceAttributes Attributes { get; private set; } + public string Name { get; private set; } + public string FileName { get; private set; } + public bool IsEmbeded { get; set; } + + #region IEquatable Members + + public bool Equals (AssemblyResource other) + { + return Name == other.Name; + } + + #endregion + } + + // + // A placeholder class for assembly attributes when emitting module + // + class AssemblyAttributesPlaceholder : CompilerGeneratedContainer + { + static readonly string TypeNamePrefix = "<$AssemblyAttributes${0}>"; + public static readonly string AssemblyFieldName = "attributes"; + + Field assembly; + + public AssemblyAttributesPlaceholder (ModuleContainer parent, string outputName) + : base (parent, new MemberName (GetGeneratedName (outputName)), Modifiers.STATIC | Modifiers.INTERNAL) + { + assembly = new Field (this, new TypeExpression (parent.Compiler.BuiltinTypes.Object, Location), Modifiers.PUBLIC | Modifiers.STATIC, + new MemberName (AssemblyFieldName), null); + + AddField (assembly); + } + + public void AddAssemblyAttribute (MethodSpec ctor, byte[] data) + { + assembly.SetCustomAttribute (ctor, data); + } + + public static string GetGeneratedName (string outputName) + { + return string.Format (TypeNamePrefix, outputName); + } + } + + // + // Extension to System.Reflection.Emit.AssemblyBuilder to have fully compatible + // compiler. This is a default implementation for framework System.Reflection.Emit + // which does not implement any of the methods + // + public class AssemblyBuilderExtension + { + readonly CompilerContext ctx; + + public AssemblyBuilderExtension (CompilerContext ctx) + { + this.ctx = ctx; + } + + public virtual System.Reflection.Module AddModule (string module) + { + ctx.Report.RuntimeMissingSupport (Location.Null, "-addmodule"); + return null; + } + + public virtual void AddPermissionRequests (PermissionSet[] permissions) + { + ctx.Report.RuntimeMissingSupport (Location.Null, "assembly declarative security"); + } + + public virtual void AddTypeForwarder (TypeSpec type, Location loc) + { + ctx.Report.RuntimeMissingSupport (loc, "TypeForwardedToAttribute"); + } + + public virtual void DefineWin32IconResource (string fileName) + { + ctx.Report.RuntimeMissingSupport (Location.Null, "-win32icon"); + } + + public virtual void SetAlgorithmId (uint value, Location loc) + { + ctx.Report.RuntimeMissingSupport (loc, "AssemblyAlgorithmIdAttribute"); + } + + public virtual void SetCulture (string culture, Location loc) + { + ctx.Report.RuntimeMissingSupport (loc, "AssemblyCultureAttribute"); + } + + public virtual void SetFlags (uint flags, Location loc) + { + ctx.Report.RuntimeMissingSupport (loc, "AssemblyFlagsAttribute"); + } + + public virtual void SetVersion (Version version, Location loc) + { + ctx.Report.RuntimeMissingSupport (loc, "AssemblyVersionAttribute"); + } + } + + abstract class AssemblyReferencesLoader where T : class + { + protected readonly CompilerContext compiler; + + protected readonly List paths; + + protected AssemblyReferencesLoader (CompilerContext compiler) + { + this.compiler = compiler; + + paths = new List (); + paths.Add (Directory.GetCurrentDirectory ()); + paths.AddRange (compiler.Settings.ReferencesLookupPaths); + } + + public abstract bool HasObjectType (T assembly); + protected abstract string[] GetDefaultReferences (); + public abstract T LoadAssemblyFile (string fileName, bool isImplicitReference); + public abstract void LoadReferences (ModuleContainer module); + + protected void Error_FileNotFound (string fileName) + { + compiler.Report.Error (6, "Metadata file `{0}' could not be found", fileName); + } + + protected void Error_FileCorrupted (string fileName) + { + compiler.Report.Error (9, "Metadata file `{0}' does not contain valid metadata", fileName); + } + + protected void Error_AssemblyIsModule (string fileName) + { + compiler.Report.Error (1509, + "Referenced assembly file `{0}' is a module. Consider using `-addmodule' option to add the module", + fileName); + } + + protected void Error_ModuleIsAssembly (string fileName) + { + compiler.Report.Error (1542, + "Added module file `{0}' is an assembly. Consider using `-r' option to reference the file", + fileName); + } + + protected void LoadReferencesCore (ModuleContainer module, out T corlib_assembly, out List> loaded) + { + compiler.TimeReporter.Start (TimeReporter.TimerType.ReferencesLoading); + + loaded = new List> (); + + // + // Load mscorlib.dll as the first + // + if (module.Compiler.Settings.StdLib) { + corlib_assembly = LoadAssemblyFile ("mscorlib.dll", true); + } else { + corlib_assembly = default (T); + } + + T a; + foreach (string r in module.Compiler.Settings.AssemblyReferences) { + a = LoadAssemblyFile (r, false); + if (a == null || EqualityComparer.Default.Equals (a, corlib_assembly)) + continue; + + var key = Tuple.Create (module.GlobalRootNamespace, a); + if (loaded.Contains (key)) + continue; + + loaded.Add (key); + } + + if (corlib_assembly == null) { + // + // Requires second pass because HasObjectType can trigger assembly load event + // + for (int i = 0; i < loaded.Count; ++i) { + var assembly = loaded [i]; + + // + // corlib assembly is the first referenced assembly which contains System.Object + // + if (HasObjectType (assembly.Item2)) { + corlib_assembly = assembly.Item2; + loaded.RemoveAt (i); + break; + } + } + } + + foreach (var entry in module.Compiler.Settings.AssemblyReferencesAliases) { + a = LoadAssemblyFile (entry.Item2, false); + if (a == null) + continue; + + var key = Tuple.Create (module.CreateRootNamespace (entry.Item1), a); + if (loaded.Contains (key)) + continue; + + loaded.Add (key); + } + + if (compiler.Settings.LoadDefaultReferences) { + foreach (string r in GetDefaultReferences ()) { + a = LoadAssemblyFile (r, true); + if (a == null) + continue; + + var key = Tuple.Create (module.GlobalRootNamespace, a); + if (loaded.Contains (key)) + continue; + + loaded.Add (key); + } + } + + compiler.TimeReporter.Stop (TimeReporter.TimerType.ReferencesLoading); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/assign.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/assign.cs new file mode 100644 index 000000000..2bdc3331a --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/assign.cs @@ -0,0 +1,956 @@ +// +// assign.cs: Assignments. +// +// Author: +// Miguel de Icaza (miguel@ximian.com) +// Martin Baulig (martin@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2001, 2002, 2003 Ximian, Inc. +// Copyright 2004-2008 Novell, Inc +// Copyright 2011 Xamarin Inc +// +using System; + +#if STATIC +using IKVM.Reflection.Emit; +#else +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp { + + /// + /// This interface is implemented by expressions that can be assigned to. + /// + /// + /// This interface is implemented by Expressions whose values can not + /// store the result on the top of the stack. + /// + /// Expressions implementing this (Properties, Indexers and Arrays) would + /// perform an assignment of the Expression "source" into its final + /// location. + /// + /// No values on the top of the stack are expected to be left by + /// invoking this method. + /// + public interface IAssignMethod { + // + // This is an extra version of Emit. If leave_copy is `true' + // A copy of the expression will be left on the stack at the + // end of the code generated for EmitAssign + // + void Emit (EmitContext ec, bool leave_copy); + + // + // This method does the assignment + // `source' will be stored into the location specified by `this' + // if `leave_copy' is true, a copy of `source' will be left on the stack + // if `prepare_for_load' is true, when `source' is emitted, there will + // be data on the stack that it can use to compuatate its value. This is + // for expressions like a [f ()] ++, where you can't call `f ()' twice. + // + void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound); + + /* + For simple assignments, this interface is very simple, EmitAssign is called with source + as the source expression and leave_copy and prepare_for_load false. + + For compound assignments it gets complicated. + + EmitAssign will be called as before, however, prepare_for_load will be + true. The @source expression will contain an expression + which calls Emit. So, the calls look like: + + this.EmitAssign (ec, source, false, true) -> + source.Emit (ec); -> + [...] -> + this.Emit (ec, false); -> + end this.Emit (ec, false); -> + end [...] + end source.Emit (ec); + end this.EmitAssign (ec, source, false, true) + + + When prepare_for_load is true, EmitAssign emits a `token' on the stack that + Emit will use for its state. + + Let's take FieldExpr as an example. assume we are emitting f ().y += 1; + + Here is the call tree again. This time, each call is annotated with the IL + it produces: + + this.EmitAssign (ec, source, false, true) + call f + dup + + Binary.Emit () + this.Emit (ec, false); + ldfld y + end this.Emit (ec, false); + + IntConstant.Emit () + ldc.i4.1 + end IntConstant.Emit + + add + end Binary.Emit () + + stfld + end this.EmitAssign (ec, source, false, true) + + Observe two things: + 1) EmitAssign left a token on the stack. It was the result of f (). + 2) This token was used by Emit + + leave_copy (in both EmitAssign and Emit) tells the compiler to leave a copy + of the expression at that point in evaluation. This is used for pre/post inc/dec + and for a = x += y. Let's do the above example with leave_copy true in EmitAssign + + this.EmitAssign (ec, source, true, true) + call f + dup + + Binary.Emit () + this.Emit (ec, false); + ldfld y + end this.Emit (ec, false); + + IntConstant.Emit () + ldc.i4.1 + end IntConstant.Emit + + add + end Binary.Emit () + + dup + stloc temp + stfld + ldloc temp + end this.EmitAssign (ec, source, true, true) + + And with it true in Emit + + this.EmitAssign (ec, source, false, true) + call f + dup + + Binary.Emit () + this.Emit (ec, true); + ldfld y + dup + stloc temp + end this.Emit (ec, true); + + IntConstant.Emit () + ldc.i4.1 + end IntConstant.Emit + + add + end Binary.Emit () + + stfld + ldloc temp + end this.EmitAssign (ec, source, false, true) + + Note that these two examples are what happens for ++x and x++, respectively. + */ + } + + /// + /// An Expression to hold a temporary value. + /// + /// + /// The LocalTemporary class is used to hold temporary values of a given + /// type to "simulate" the expression semantics. The local variable is + /// never captured. + /// + /// The local temporary is used to alter the normal flow of code generation + /// basically it creates a local variable, and its emit instruction generates + /// code to access this value, return its address or save its value. + /// + /// If `is_address' is true, then the value that we store is the address to the + /// real value, and not the value itself. + /// + /// This is needed for a value type, because otherwise you just end up making a + /// copy of the value on the stack and modifying it. You really need a pointer + /// to the origional value so that you can modify it in that location. This + /// Does not happen with a class because a class is a pointer -- so you always + /// get the indirection. + /// + /// + public class LocalTemporary : Expression, IMemoryLocation, IAssignMethod { + LocalBuilder builder; + + public LocalTemporary (TypeSpec t) + { + type = t; + eclass = ExprClass.Value; + } + + public LocalTemporary (LocalBuilder b, TypeSpec t) + : this (t) + { + builder = b; + } + + public void Release (EmitContext ec) + { + ec.FreeTemporaryLocal (builder, type); + builder = null; + } + + public override bool ContainsEmitWithAwait () + { + return false; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Arguments args = new Arguments (1); + args.Add (new Argument (this)); + return CreateExpressionFactoryCall (ec, "Constant", args); + } + + protected override Expression DoResolve (ResolveContext ec) + { + return this; + } + + public override Expression DoResolveLValue (ResolveContext ec, Expression right_side) + { + return this; + } + + public override void Emit (EmitContext ec) + { + if (builder == null) + throw new InternalErrorException ("Emit without Store, or after Release"); + + ec.Emit (OpCodes.Ldloc, builder); + } + + #region IAssignMethod Members + + public void Emit (EmitContext ec, bool leave_copy) + { + Emit (ec); + + if (leave_copy) + Emit (ec); + } + + public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound) + { + if (isCompound) + throw new NotImplementedException (); + + source.Emit (ec); + + Store (ec); + + if (leave_copy) + Emit (ec); + } + + #endregion + + public LocalBuilder Builder { + get { return builder; } + } + + public void Store (EmitContext ec) + { + if (builder == null) + builder = ec.GetTemporaryLocal (type); + + ec.Emit (OpCodes.Stloc, builder); + } + + public void AddressOf (EmitContext ec, AddressOp mode) + { + if (builder == null) + builder = ec.GetTemporaryLocal (type); + + if (builder.LocalType.IsByRef) { + // + // if is_address, than this is just the address anyways, + // so we just return this. + // + ec.Emit (OpCodes.Ldloc, builder); + } else { + ec.Emit (OpCodes.Ldloca, builder); + } + } + } + + /// + /// The Assign node takes care of assigning the value of source into + /// the expression represented by target. + /// + public abstract class Assign : ExpressionStatement { + protected Expression target, source; + + protected Assign (Expression target, Expression source, Location loc) + { + this.target = target; + this.source = source; + this.loc = loc; + } + + public Expression Target { + get { return target; } + } + + public Expression Source { + get { + return source; + } + } + + public override Location StartLocation { + get { + return target.StartLocation; + } + } + + public override bool ContainsEmitWithAwait () + { + return target.ContainsEmitWithAwait () || source.ContainsEmitWithAwait (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + ec.Report.Error (832, loc, "An expression tree cannot contain an assignment operator"); + return null; + } + + protected override Expression DoResolve (ResolveContext ec) + { + bool ok = true; + source = source.Resolve (ec); + + if (source == null) { + ok = false; + source = ErrorExpression.Instance; + } + + target = target.ResolveLValue (ec, source); + + if (target == null || !ok) + return null; + + TypeSpec target_type = target.Type; + TypeSpec source_type = source.Type; + + eclass = ExprClass.Value; + type = target_type; + + if (!(target is IAssignMethod)) { + target.Error_ValueAssignment (ec, source); + return null; + } + + if (target_type != source_type) { + Expression resolved = ResolveConversions (ec); + + if (resolved != this) + return resolved; + } + + return this; + } + +#if NET_4_0 || MOBILE_DYNAMIC + public override System.Linq.Expressions.Expression MakeExpression (BuilderContext ctx) + { + var tassign = target as IDynamicAssign; + if (tassign == null) + throw new InternalErrorException (target.GetType () + " does not support dynamic assignment"); + + var target_object = tassign.MakeAssignExpression (ctx, source); + + // + // Some hacking is needed as DLR does not support void type and requires + // always have object convertible return type to support caching and chaining + // + // We do this by introducing an explicit block which returns RHS value when + // available or null + // + if (target_object.NodeType == System.Linq.Expressions.ExpressionType.Block) + return target_object; + + System.Linq.Expressions.UnaryExpression source_object; + if (ctx.HasSet (BuilderContext.Options.CheckedScope)) { + source_object = System.Linq.Expressions.Expression.ConvertChecked (source.MakeExpression (ctx), target_object.Type); + } else { + source_object = System.Linq.Expressions.Expression.Convert (source.MakeExpression (ctx), target_object.Type); + } + + return System.Linq.Expressions.Expression.Assign (target_object, source_object); + } +#endif + protected virtual Expression ResolveConversions (ResolveContext ec) + { + source = Convert.ImplicitConversionRequired (ec, source, target.Type, source.Location); + if (source == null) + return null; + + return this; + } + + void Emit (EmitContext ec, bool is_statement) + { + IAssignMethod t = (IAssignMethod) target; + t.EmitAssign (ec, source, !is_statement, this is CompoundAssign); + } + + public override void Emit (EmitContext ec) + { + Emit (ec, false); + } + + public override void EmitStatement (EmitContext ec) + { + Emit (ec, true); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + source.FlowAnalysis (fc); + + if (target is ArrayAccess || target is IndexerExpr || target is PropertyExpr) + target.FlowAnalysis (fc); + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + Assign _target = (Assign) t; + + _target.target = target.Clone (clonectx); + _target.source = source.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class SimpleAssign : Assign + { + public SimpleAssign (Expression target, Expression source) + : this (target, source, target.Location) + { + } + + public SimpleAssign (Expression target, Expression source, Location loc) + : base (target, source, loc) + { + } + + bool CheckEqualAssign (Expression t) + { + if (source is Assign) { + Assign a = (Assign) source; + if (t.Equals (a.Target)) + return true; + return a is SimpleAssign && ((SimpleAssign) a).CheckEqualAssign (t); + } + return t.Equals (source); + } + + protected override Expression DoResolve (ResolveContext ec) + { + Expression e = base.DoResolve (ec); + if (e == null || e != this) + return e; + + if (CheckEqualAssign (target)) + ec.Report.Warning (1717, 3, loc, "Assignment made to same variable; did you mean to assign something else?"); + + return this; + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + base.FlowAnalysis (fc); + + var vr = target as VariableReference; + if (vr != null) { + if (vr.VariableInfo != null) + fc.SetVariableAssigned (vr.VariableInfo); + + return; + } + + var fe = target as FieldExpr; + if (fe != null) { + fe.SetFieldAssigned (fc); + return; + } + } + + public override void MarkReachable (Reachability rc) + { + var es = source as ExpressionStatement; + if (es != null) + es.MarkReachable (rc); + } + } + + public class RuntimeExplicitAssign : Assign + { + public RuntimeExplicitAssign (Expression target, Expression source) + : base (target, source, target.Location) + { + } + + protected override Expression ResolveConversions (ResolveContext ec) + { + source = EmptyCast.Create (source, target.Type); + return this; + } + } + + // + // Compiler generated assign + // + class CompilerAssign : Assign + { + public CompilerAssign (Expression target, Expression source, Location loc) + : base (target, source, loc) + { + if (target.Type != null) { + type = target.Type; + eclass = ExprClass.Value; + } + } + + protected override Expression DoResolve (ResolveContext ec) + { + var expr = base.DoResolve (ec); + var vr = target as VariableReference; + if (vr != null && vr.VariableInfo != null) + vr.VariableInfo.IsEverAssigned = false; + + return expr; + } + + public void UpdateSource (Expression source) + { + base.source = source; + } + } + + // + // Implements fields and events class initializers + // + public class FieldInitializer : Assign + { + // + // Field initializers are tricky for partial classes. They have to + // share same constructor (block) for expression trees resolve but + // they have they own resolve scope + // + sealed class FieldInitializerContext : BlockContext + { + readonly ExplicitBlock ctor_block; + + public FieldInitializerContext (IMemberContext mc, BlockContext constructorContext) + : base (mc, null, constructorContext.ReturnType) + { + flags |= Options.FieldInitializerScope | Options.ConstructorScope; + this.ctor_block = constructorContext.CurrentBlock.Explicit; + + if (ctor_block.IsCompilerGenerated) + CurrentBlock = ctor_block; + } + + public override ExplicitBlock ConstructorBlock { + get { + return ctor_block; + } + } + } + + // + // Keep resolved value because field initializers have their own rules + // + ExpressionStatement resolved; + FieldBase mc; + + public FieldInitializer (FieldBase mc, Expression expression, Location loc) + : base (new FieldExpr (mc.Spec, expression.Location), expression, loc) + { + this.mc = mc; + if (!mc.IsStatic) + ((FieldExpr)target).InstanceExpression = new CompilerGeneratedThis (mc.CurrentType, expression.Location); + } + + public int AssignmentOffset { get; private set; } + + public FieldBase Field { + get { + return mc; + } + } + + public override Location StartLocation { + get { + return loc; + } + } + + protected override Expression DoResolve (ResolveContext rc) + { + // Field initializer can be resolved (fail) many times + if (source == null) + return null; + + if (resolved == null) { + var bc = (BlockContext) rc; + var ctx = new FieldInitializerContext (mc, bc); + resolved = base.DoResolve (ctx) as ExpressionStatement; + AssignmentOffset = ctx.AssignmentInfoOffset - bc.AssignmentInfoOffset; + } + + return resolved; + } + + public override void EmitStatement (EmitContext ec) + { + if (resolved == null) + return; + + // + // Emit sequence symbol info even if we are in compiler generated + // block to allow debugging field initializers when constructor is + // compiler generated + // + if (ec.HasSet (BuilderContext.Options.OmitDebugInfo) && ec.HasMethodSymbolBuilder) { + using (ec.With (BuilderContext.Options.OmitDebugInfo, false)) { + ec.Mark (loc); + } + } + + if (resolved != this) + resolved.EmitStatement (ec); + else + base.EmitStatement (ec); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + source.FlowAnalysis (fc); + ((FieldExpr) target).SetFieldAssigned (fc); + } + + public bool IsDefaultInitializer { + get { + Constant c = source as Constant; + if (c == null) + return false; + + FieldExpr fe = (FieldExpr)target; + return c.IsDefaultInitializer (fe.Type); + } + } + + public override bool IsSideEffectFree { + get { + return source.IsSideEffectFree; + } + } + } + + class PrimaryConstructorAssign : SimpleAssign + { + readonly Field field; + readonly Parameter parameter; + + public PrimaryConstructorAssign (Field field, Parameter parameter) + : base (null, null, parameter.Location) + { + this.field = field; + this.parameter = parameter; + } + + protected override Expression DoResolve (ResolveContext rc) + { + target = new FieldExpr (field, loc); + source = rc.CurrentBlock.ParametersBlock.GetParameterInfo (parameter).CreateReferenceExpression (rc, loc); + return base.DoResolve (rc); + } + + public override void EmitStatement (EmitContext ec) + { + using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) { + base.EmitStatement (ec); + } + } + } + + // + // This class is used for compound assignments. + // + public class CompoundAssign : Assign + { + // This is just a hack implemented for arrays only + public sealed class TargetExpression : Expression + { + readonly Expression child; + + public TargetExpression (Expression child) + { + this.child = child; + this.loc = child.Location; + } + + public override bool ContainsEmitWithAwait () + { + return child.ContainsEmitWithAwait (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + throw new NotSupportedException ("ET"); + } + + protected override Expression DoResolve (ResolveContext ec) + { + type = child.Type; + eclass = ExprClass.Value; + return this; + } + + public override void Emit (EmitContext ec) + { + child.Emit (ec); + } + + public override Expression EmitToField (EmitContext ec) + { + return child.EmitToField (ec); + } + } + + // Used for underlying binary operator + readonly Binary.Operator op; + Expression right; + Expression left; + + public Binary.Operator Op { + get { + return op; + } + } + + public CompoundAssign (Binary.Operator op, Expression target, Expression source) + : base (target, source, target.Location) + { + right = source; + this.op = op; + } + + public CompoundAssign (Binary.Operator op, Expression target, Expression source, Expression left) + : this (op, target, source) + { + this.left = left; + } + + public Binary.Operator Operator { + get { + return op; + } + } + + protected override Expression DoResolve (ResolveContext ec) + { + right = right.Resolve (ec); + if (right == null) + return null; + + MemberAccess ma = target as MemberAccess; + using (ec.Set (ResolveContext.Options.CompoundAssignmentScope)) { + target = target.Resolve (ec); + } + + if (target == null) + return null; + + if (target is MethodGroupExpr){ + ec.Report.Error (1656, loc, + "Cannot assign to `{0}' because it is a `{1}'", + ((MethodGroupExpr)target).Name, target.ExprClassName); + return null; + } + + var event_expr = target as EventExpr; + if (event_expr != null) { + source = Convert.ImplicitConversionRequired (ec, right, target.Type, loc); + if (source == null) + return null; + + Expression rside; + if (op == Binary.Operator.Addition) + rside = EmptyExpression.EventAddition; + else if (op == Binary.Operator.Subtraction) + rside = EmptyExpression.EventSubtraction; + else + rside = null; + + target = target.ResolveLValue (ec, rside); + if (target == null) + return null; + + eclass = ExprClass.Value; + type = event_expr.Operator.ReturnType; + return this; + } + + // + // Only now we can decouple the original source/target + // into a tree, to guarantee that we do not have side + // effects. + // + if (left == null) + left = new TargetExpression (target); + + source = new Binary (op, left, right, true); + + if (target is DynamicMemberAssignable) { + Arguments targs = ((DynamicMemberAssignable) target).Arguments; + source = source.Resolve (ec); + + Arguments args = new Arguments (targs.Count + 1); + args.AddRange (targs); + args.Add (new Argument (source)); + + var binder_flags = CSharpBinderFlags.ValueFromCompoundAssignment; + + // + // Compound assignment does target conversion using additional method + // call, set checked context as the binary operation can overflow + // + if (ec.HasSet (ResolveContext.Options.CheckedScope)) + binder_flags |= CSharpBinderFlags.CheckedContext; + + if (target is DynamicMemberBinder) { + source = new DynamicMemberBinder (ma.Name, binder_flags, args, loc).Resolve (ec); + + // Handles possible event addition/subtraction + if (op == Binary.Operator.Addition || op == Binary.Operator.Subtraction) { + args = new Arguments (targs.Count + 1); + args.AddRange (targs); + args.Add (new Argument (right)); + string method_prefix = op == Binary.Operator.Addition ? + Event.AEventAccessor.AddPrefix : Event.AEventAccessor.RemovePrefix; + + var invoke = DynamicInvocation.CreateSpecialNameInvoke ( + new MemberAccess (right, method_prefix + ma.Name, loc), args, loc).Resolve (ec); + + args = new Arguments (targs.Count); + args.AddRange (targs); + source = new DynamicEventCompoundAssign (ma.Name, args, + (ExpressionStatement) source, (ExpressionStatement) invoke, loc).Resolve (ec); + } + } else { + source = new DynamicIndexBinder (binder_flags, args, loc).Resolve (ec); + } + + return source; + } + + return base.DoResolve (ec); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + target.FlowAnalysis (fc); + source.FlowAnalysis (fc); + } + + protected override Expression ResolveConversions (ResolveContext ec) + { + // + // LAMESPEC: Under dynamic context no target conversion is happening + // This allows more natual dynamic behaviour but breaks compatibility + // with static binding + // + if (target is RuntimeValueExpression) + return this; + + TypeSpec target_type = target.Type; + + // + // 1. the return type is implicitly convertible to the type of target + // + if (Convert.ImplicitConversionExists (ec, source, target_type)) { + source = Convert.ImplicitConversion (ec, source, target_type, loc); + return this; + } + + // + // Otherwise, if the selected operator is a predefined operator + // + Binary b = source as Binary; + if (b == null) { + if (source is ReducedExpression) + b = ((ReducedExpression) source).OriginalExpression as Binary; + else if (source is ReducedExpression.ReducedConstantExpression) { + b = ((ReducedExpression.ReducedConstantExpression) source).OriginalExpression as Binary; + } else if (source is Nullable.LiftedBinaryOperator) { + var po = ((Nullable.LiftedBinaryOperator) source); + if (po.UserOperator == null) + b = po.Binary; + } else if (source is TypeCast) { + b = ((TypeCast) source).Child as Binary; + } + } + + if (b != null) { + // + // 2a. the operator is a shift operator + // + // 2b. the return type is explicitly convertible to the type of x, and + // y is implicitly convertible to the type of x + // + if ((b.Oper & Binary.Operator.ShiftMask) != 0 || + Convert.ImplicitConversionExists (ec, right, target_type)) { + source = Convert.ExplicitConversion (ec, source, target_type, loc); + return this; + } + } + + if (source.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + Arguments arg = new Arguments (1); + arg.Add (new Argument (source)); + return new SimpleAssign (target, new DynamicConversion (target_type, CSharpBinderFlags.ConvertExplicit, arg, loc), loc).Resolve (ec); + } + + right.Error_ValueCannotBeConverted (ec, target_type, false); + return null; + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + CompoundAssign ctarget = (CompoundAssign) t; + + ctarget.right = ctarget.source = source.Clone (clonectx); + ctarget.target = target.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/async.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/async.cs new file mode 100644 index 000000000..68bbd3e18 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/async.cs @@ -0,0 +1,1005 @@ +// +// async.cs: Asynchronous functions +// +// Author: +// Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2011 Novell, Inc. +// Copyright 2011-2012 Xamarin Inc. +// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Collections; + +#if STATIC +using IKVM.Reflection; +using IKVM.Reflection.Emit; +#else +using System.Reflection; +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp +{ + public class Await : ExpressionStatement + { + Expression expr; + AwaitStatement stmt; + + public Expression Expression { + get { + return expr; + } + } + + public Await (Expression expr, Location loc) + { + this.expr = expr; + this.loc = loc; + } + + public Expression Expr { + get { + return expr; + } + } + + public AwaitStatement Statement { + get { + return stmt; + } + } + + protected override void CloneTo (CloneContext clonectx, Expression target) + { + var t = (Await) target; + + t.expr = expr.Clone (clonectx); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + throw new NotImplementedException ("ET"); + } + + public override bool ContainsEmitWithAwait () + { + return true; + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + stmt.Expr.FlowAnalysis (fc); + + stmt.RegisterResumePoint (); + } + + protected override Expression DoResolve (ResolveContext rc) + { + if (rc.HasSet (ResolveContext.Options.LockScope)) { + rc.Report.Error (1996, loc, + "The `await' operator cannot be used in the body of a lock statement"); + } + + if (rc.IsUnsafe) { + rc.Report.Error (4004, loc, + "The `await' operator cannot be used in an unsafe context"); + } + + var bc = (BlockContext) rc; + + stmt = new AwaitStatement (expr, loc); + if (!stmt.Resolve (bc)) + return null; + + type = stmt.ResultType; + eclass = ExprClass.Variable; + return this; + } + + public override void Emit (EmitContext ec) + { + stmt.EmitPrologue (ec); + + using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) { + stmt.Emit (ec); + } + } + + public override Expression EmitToField (EmitContext ec) + { + stmt.EmitPrologue (ec); + return stmt.GetResultExpression (ec); + } + + public void EmitAssign (EmitContext ec, FieldExpr field) + { + stmt.EmitPrologue (ec); + field.InstanceExpression.Emit (ec); + stmt.Emit (ec); + } + + public override void EmitStatement (EmitContext ec) + { + stmt.EmitStatement (ec); + } + + public override void MarkReachable (Reachability rc) + { + base.MarkReachable (rc); + stmt.MarkReachable (rc); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class AwaitStatement : YieldStatement + { + public sealed class AwaitableMemberAccess : MemberAccess + { + public AwaitableMemberAccess (Expression expr) + : base (expr, "GetAwaiter") + { + } + + public bool ProbingMode { get; set; } + + protected override void Error_TypeDoesNotContainDefinition (ResolveContext rc, TypeSpec type, string name) + { + Error_OperatorCannotBeApplied (rc, type); + } + + protected override void Error_OperatorCannotBeApplied (ResolveContext rc, TypeSpec type) + { + if (ProbingMode) + return; + + var invocation = LeftExpression as Invocation; + if (invocation != null && invocation.MethodGroup != null && (invocation.MethodGroup.BestCandidate.Modifiers & Modifiers.ASYNC) != 0) { + rc.Report.Error (4008, loc, "Cannot await void method `{0}'. Consider changing method return type to `Task'", + invocation.GetSignatureForError ()); + } else { + rc.Report.Error (4001, loc, "Cannot await `{0}' expression", type.GetSignatureForError ()); + } + } + } + + sealed class GetResultInvocation : Invocation + { + public GetResultInvocation (MethodGroupExpr mge, Arguments arguments) + : base (null, arguments) + { + mg = mge; + type = mg.BestCandidateReturnType; + } + + public override Expression EmitToField (EmitContext ec) + { + return this; + } + } + + Field awaiter; + AwaiterDefinition awaiter_definition; + TypeSpec type; + TypeSpec result_type; + + public AwaitStatement (Expression expr, Location loc) + : base (expr, loc) + { + unwind_protect = true; + } + + #region Properties + + bool IsDynamic { + get { + return awaiter_definition == null; + } + } + + public TypeSpec ResultType { + get { + return result_type; + } + } + + #endregion + + protected override void DoEmit (EmitContext ec) + { + using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) { + GetResultExpression (ec).Emit (ec); + } + } + + public Expression GetResultExpression (EmitContext ec) + { + var fe_awaiter = new FieldExpr (awaiter, loc); + fe_awaiter.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc); + + // + // result = awaiter.GetResult (); + // + if (IsDynamic) { + var rc = new ResolveContext (ec.MemberContext); + return new Invocation (new MemberAccess (fe_awaiter, "GetResult"), new Arguments (0)).Resolve (rc); + } + + var mg_result = MethodGroupExpr.CreatePredefined (awaiter_definition.GetResult, fe_awaiter.Type, loc); + mg_result.InstanceExpression = fe_awaiter; + + return new GetResultInvocation (mg_result, new Arguments (0)); + } + + public void EmitPrologue (EmitContext ec) + { + awaiter = ((AsyncTaskStorey) machine_initializer.Storey).AddAwaiter (expr.Type); + + var fe_awaiter = new FieldExpr (awaiter, loc); + fe_awaiter.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc); + + Label skip_continuation = ec.DefineLabel (); + + using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) { + // + // awaiter = expr.GetAwaiter (); + // + fe_awaiter.EmitAssign (ec, expr, false, false); + + Expression completed_expr; + if (IsDynamic) { + var rc = new ResolveContext (ec.MemberContext); + + Arguments dargs = new Arguments (1); + dargs.Add (new Argument (fe_awaiter)); + completed_expr = new DynamicMemberBinder ("IsCompleted", dargs, loc).Resolve (rc); + + dargs = new Arguments (1); + dargs.Add (new Argument (completed_expr)); + completed_expr = new DynamicConversion (ec.Module.Compiler.BuiltinTypes.Bool, 0, dargs, loc).Resolve (rc); + } else { + var pe = PropertyExpr.CreatePredefined (awaiter_definition.IsCompleted, loc); + pe.InstanceExpression = fe_awaiter; + completed_expr = pe; + } + + completed_expr.EmitBranchable (ec, skip_continuation, true); + } + + base.DoEmit (ec); + + // + // The stack has to be empty before calling await continuation. We handle this + // by lifting values which would be left on stack into class fields. The process + // is quite complicated and quite hard to test because any expression can possibly + // leave a value on the stack. + // + // Following assert fails when some of expression called before is missing EmitToField + // or parent expression fails to find await in children expressions + // + ec.AssertEmptyStack (); + + var storey = (AsyncTaskStorey) machine_initializer.Storey; + if (IsDynamic) { + storey.EmitAwaitOnCompletedDynamic (ec, fe_awaiter); + } else { + storey.EmitAwaitOnCompleted (ec, fe_awaiter); + } + + // Return ok + machine_initializer.EmitLeave (ec, unwind_protect); + + ec.MarkLabel (resume_point); + ec.MarkLabel (skip_continuation); + } + + public void EmitStatement (EmitContext ec) + { + EmitPrologue (ec); + DoEmit (ec); + + awaiter.IsAvailableForReuse = true; + + if (ResultType.Kind != MemberKind.Void) + ec.Emit (OpCodes.Pop); + } + + void Error_WrongAwaiterPattern (ResolveContext rc, TypeSpec awaiter) + { + rc.Report.Error (4011, loc, "The awaiter type `{0}' must have suitable IsCompleted and GetResult members", + awaiter.GetSignatureForError ()); + } + + public override bool Resolve (BlockContext bc) + { + if (bc.CurrentBlock is Linq.QueryBlock) { + bc.Report.Error (1995, loc, + "The `await' operator may only be used in a query expression within the first collection expression of the initial `from' clause or within the collection expression of a `join' clause"); + return false; + } + + if (!base.Resolve (bc)) + return false; + + type = expr.Type; + Arguments args = new Arguments (0); + + // + // The await expression is of dynamic type + // + if (type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + result_type = type; + expr = new Invocation (new MemberAccess (expr, "GetAwaiter"), args).Resolve (bc); + return true; + } + + // + // Check whether the expression is awaitable + // + Expression ama = new AwaitableMemberAccess (expr).Resolve (bc); + if (ama == null) + return false; + + var errors_printer = new SessionReportPrinter (); + var old = bc.Report.SetPrinter (errors_printer); + ama = new Invocation (ama, args).Resolve (bc); + bc.Report.SetPrinter (old); + + if (errors_printer.ErrorsCount > 0 || !MemberAccess.IsValidDotExpression (ama.Type)) { + bc.Report.Error (1986, expr.Location, + "The `await' operand type `{0}' must have suitable GetAwaiter method", + expr.Type.GetSignatureForError ()); + + return false; + } + + var awaiter_type = ama.Type; + + awaiter_definition = bc.Module.GetAwaiter (awaiter_type); + + if (!awaiter_definition.IsValidPattern) { + Error_WrongAwaiterPattern (bc, awaiter_type); + return false; + } + + if (!awaiter_definition.INotifyCompletion) { + bc.Report.Error (4027, loc, "The awaiter type `{0}' must implement interface `{1}'", + awaiter_type.GetSignatureForError (), bc.Module.PredefinedTypes.INotifyCompletion.GetSignatureForError ()); + return false; + } + + expr = ama; + result_type = awaiter_definition.GetResult.ReturnType; + + return true; + } + } + + class AsyncInitializerStatement : StatementExpression + { + public AsyncInitializerStatement (AsyncInitializer expr) + : base (expr) + { + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + base.DoFlowAnalysis (fc); + + var init = (AsyncInitializer) Expr; + var res = !init.Block.HasReachableClosingBrace; + var storey = (AsyncTaskStorey) init.Storey; + + if (storey.ReturnType.IsGenericTask) + return res; + + return true; + } + + public override Reachability MarkReachable (Reachability rc) + { + if (!rc.IsUnreachable) + reachable = true; + + var init = (AsyncInitializer) Expr; + rc = init.Block.MarkReachable (rc); + + var storey = (AsyncTaskStorey) init.Storey; + + // + // Explicit return is required for Task state machine + // + if (storey.ReturnType != null && storey.ReturnType.IsGenericTask) + return rc; + + return Reachability.CreateUnreachable (); + } + } + + public class AsyncInitializer : StateMachineInitializer + { + TypeInferenceContext return_inference; + + public AsyncInitializer (ParametersBlock block, TypeDefinition host, TypeSpec returnType) + : base (block, host, returnType) + { + } + + #region Properties + + public override string ContainerType { + get { + return "async state machine block"; + } + } + + public TypeSpec DelegateType { + get; set; + } + + public StackFieldExpr HoistedReturnState { + get; set; + } + + public override bool IsIterator { + get { + return false; + } + } + + public TypeInferenceContext ReturnTypeInference { + get { + return return_inference; + } + } + + #endregion + + protected override BlockContext CreateBlockContext (BlockContext bc) + { + var ctx = base.CreateBlockContext (bc); + var am = bc.CurrentAnonymousMethod as AnonymousMethodBody; + if (am != null) + return_inference = am.ReturnTypeInference; + + ctx.Set (ResolveContext.Options.TryScope); + + return ctx; + } + + public override void Emit (EmitContext ec) + { + throw new NotImplementedException (); + } + + public void EmitCatchBlock (EmitContext ec) + { + var catch_value = LocalVariable.CreateCompilerGenerated (ec.Module.Compiler.BuiltinTypes.Exception, block, Location); + + ec.BeginCatchBlock (catch_value.Type); + catch_value.EmitAssign (ec); + + ec.EmitThis (); + ec.EmitInt ((int) IteratorStorey.State.After); + ec.Emit (OpCodes.Stfld, storey.PC.Spec); + + ((AsyncTaskStorey) Storey).EmitSetException (ec, new LocalVariableReference (catch_value, Location)); + + ec.Emit (OpCodes.Leave, move_next_ok); + ec.EndExceptionBlock (); + + } + + protected override void EmitMoveNextEpilogue (EmitContext ec) + { + var storey = (AsyncTaskStorey) Storey; + storey.EmitSetResult (ec); + } + + public override void EmitStatement (EmitContext ec) + { + var storey = (AsyncTaskStorey) Storey; + storey.EmitInitializer (ec); + ec.Emit (OpCodes.Ret); + } + + public override void MarkReachable (Reachability rc) + { + // + // Reachability has been done in AsyncInitializerStatement + // + } + } + + class AsyncTaskStorey : StateMachine + { + int awaiters; + Field builder; + readonly TypeSpec return_type; + MethodSpec set_result; + MethodSpec set_exception; + MethodSpec builder_factory; + MethodSpec builder_start; + PropertySpec task; + int locals_captured; + Dictionary> stack_fields; + Dictionary> awaiter_fields; + + public AsyncTaskStorey (ParametersBlock block, IMemberContext context, AsyncInitializer initializer, TypeSpec type) + : base (block, initializer.Host, context.CurrentMemberDefinition as MemberBase, context.CurrentTypeParameters, "async", MemberKind.Struct) + { + return_type = type; + awaiter_fields = new Dictionary> (); + } + + #region Properties + + public Expression HoistedReturnValue { get; set; } + + public TypeSpec ReturnType { + get { + return return_type; + } + } + + public PropertySpec Task { + get { + return task; + } + } + + protected override TypeAttributes TypeAttr { + get { + return base.TypeAttr & ~TypeAttributes.SequentialLayout; + } + } + + #endregion + + public Field AddAwaiter (TypeSpec type) + { + if (mutator != null) + type = mutator.Mutate (type); + + List existing_fields; + if (awaiter_fields.TryGetValue (type, out existing_fields)) { + foreach (var f in existing_fields) { + if (f.IsAvailableForReuse) { + f.IsAvailableForReuse = false; + return f; + } + } + } + + var field = AddCompilerGeneratedField ("$awaiter" + awaiters++.ToString ("X"), new TypeExpression (type, Location), true); + field.Define (); + + if (existing_fields == null) { + existing_fields = new List (); + awaiter_fields.Add (type, existing_fields); + } + + existing_fields.Add (field); + return field; + } + + public Field AddCapturedLocalVariable (TypeSpec type, bool requiresUninitialized = false) + { + if (mutator != null) + type = mutator.Mutate (type); + + List existing_fields = null; + if (stack_fields == null) { + stack_fields = new Dictionary> (); + } else if (stack_fields.TryGetValue (type, out existing_fields) && !requiresUninitialized) { + foreach (var f in existing_fields) { + if (f.IsAvailableForReuse) { + f.IsAvailableForReuse = false; + return f; + } + } + } + + var field = AddCompilerGeneratedField ("$stack" + locals_captured++.ToString ("X"), new TypeExpression (type, Location), true); + field.Define (); + + if (existing_fields == null) { + existing_fields = new List (); + stack_fields.Add (type, existing_fields); + } + + existing_fields.Add (field); + + return field; + } + + protected override bool DoDefineMembers () + { + PredefinedType builder_type; + PredefinedMember bf; + PredefinedMember bs; + PredefinedMember sr; + PredefinedMember se; + PredefinedMember sm; + bool has_task_return_type = false; + var pred_members = Module.PredefinedMembers; + + if (return_type.Kind == MemberKind.Void) { + builder_type = Module.PredefinedTypes.AsyncVoidMethodBuilder; + bf = pred_members.AsyncVoidMethodBuilderCreate; + bs = pred_members.AsyncVoidMethodBuilderStart; + sr = pred_members.AsyncVoidMethodBuilderSetResult; + se = pred_members.AsyncVoidMethodBuilderSetException; + sm = pred_members.AsyncVoidMethodBuilderSetStateMachine; + } else if (return_type == Module.PredefinedTypes.Task.TypeSpec) { + builder_type = Module.PredefinedTypes.AsyncTaskMethodBuilder; + bf = pred_members.AsyncTaskMethodBuilderCreate; + bs = pred_members.AsyncTaskMethodBuilderStart; + sr = pred_members.AsyncTaskMethodBuilderSetResult; + se = pred_members.AsyncTaskMethodBuilderSetException; + sm = pred_members.AsyncTaskMethodBuilderSetStateMachine; + task = pred_members.AsyncTaskMethodBuilderTask.Get (); + } else { + builder_type = Module.PredefinedTypes.AsyncTaskMethodBuilderGeneric; + bf = pred_members.AsyncTaskMethodBuilderGenericCreate; + bs = pred_members.AsyncTaskMethodBuilderGenericStart; + sr = pred_members.AsyncTaskMethodBuilderGenericSetResult; + se = pred_members.AsyncTaskMethodBuilderGenericSetException; + sm = pred_members.AsyncTaskMethodBuilderGenericSetStateMachine; + task = pred_members.AsyncTaskMethodBuilderGenericTask.Get (); + has_task_return_type = true; + } + + set_result = sr.Get (); + set_exception = se.Get (); + builder_factory = bf.Get (); + builder_start = bs.Get (); + + var istate_machine = Module.PredefinedTypes.IAsyncStateMachine; + var set_statemachine = sm.Get (); + + if (!builder_type.Define () || !istate_machine.Define () || set_result == null || builder_factory == null || + set_exception == null || set_statemachine == null || builder_start == null || + !Module.PredefinedTypes.INotifyCompletion.Define ()) { + Report.Error (1993, Location, + "Cannot find compiler required types for asynchronous functions support. Are you targeting the wrong framework version?"); + return base.DoDefineMembers (); + } + + var bt = builder_type.TypeSpec; + + // + // Inflate generic Task types + // + if (has_task_return_type) { + var task_return_type = return_type.TypeArguments; + if (mutator != null) + task_return_type = mutator.Mutate (task_return_type); + + bt = bt.MakeGenericType (Module, task_return_type); + set_result = MemberCache.GetMember (bt, set_result); + set_exception = MemberCache.GetMember (bt, set_exception); + set_statemachine = MemberCache.GetMember (bt, set_statemachine); + + if (task != null) + task = MemberCache.GetMember (bt, task); + } + + builder = AddCompilerGeneratedField ("$builder", new TypeExpression (bt, Location)); + + var set_state_machine = new Method (this, new TypeExpression (Compiler.BuiltinTypes.Void, Location), + Modifiers.COMPILER_GENERATED | Modifiers.DEBUGGER_HIDDEN | Modifiers.PUBLIC, + new MemberName ("SetStateMachine"), + ParametersCompiled.CreateFullyResolved ( + new Parameter (new TypeExpression (istate_machine.TypeSpec, Location), "stateMachine", Parameter.Modifier.NONE, null, Location), + istate_machine.TypeSpec), + null); + + ToplevelBlock block = new ToplevelBlock (Compiler, set_state_machine.ParameterInfo, Location); + block.IsCompilerGenerated = true; + set_state_machine.Block = block; + + Members.Add (set_state_machine); + + if (!base.DoDefineMembers ()) + return false; + + // + // Fabricates SetStateMachine method + // + // public void SetStateMachine (IAsyncStateMachine stateMachine) + // { + // $builder.SetStateMachine (stateMachine); + // } + // + var mg = MethodGroupExpr.CreatePredefined (set_statemachine, bt, Location); + mg.InstanceExpression = new FieldExpr (builder, Location); + + var param_reference = block.GetParameterReference (0, Location); + param_reference.Type = istate_machine.TypeSpec; + param_reference.eclass = ExprClass.Variable; + + var args = new Arguments (1); + args.Add (new Argument (param_reference)); + set_state_machine.Block.AddStatement (new StatementExpression (new Invocation (mg, args))); + + if (has_task_return_type) { + HoistedReturnValue = TemporaryVariableReference.Create (bt.TypeArguments [0], StateMachineMethod.Block, Location); + } + + return true; + } + + public void EmitAwaitOnCompletedDynamic (EmitContext ec, FieldExpr awaiter) + { + var critical = Module.PredefinedTypes.ICriticalNotifyCompletion; + if (!critical.Define ()) { + throw new NotImplementedException (); + } + + var temp_critical = new LocalTemporary (critical.TypeSpec); + var label_critical = ec.DefineLabel (); + var label_end = ec.DefineLabel (); + + // + // Special path for dynamic awaiters + // + // var awaiter = this.$awaiter as ICriticalNotifyCompletion; + // if (awaiter == null) { + // var completion = (INotifyCompletion) this.$awaiter; + // this.$builder.AwaitOnCompleted (ref completion, ref this); + // } else { + // this.$builder.AwaitUnsafeOnCompleted (ref awaiter, ref this); + // } + // + awaiter.Emit (ec); + ec.Emit (OpCodes.Isinst, critical.TypeSpec); + temp_critical.Store (ec); + temp_critical.Emit (ec); + ec.Emit (OpCodes.Brtrue_S, label_critical); + + var temp = new LocalTemporary (Module.PredefinedTypes.INotifyCompletion.TypeSpec); + awaiter.Emit (ec); + ec.Emit (OpCodes.Castclass, temp.Type); + temp.Store (ec); + EmitOnCompleted (ec, temp, false); + temp.Release (ec); + ec.Emit (OpCodes.Br_S, label_end); + + ec.MarkLabel (label_critical); + + EmitOnCompleted (ec, temp_critical, true); + + ec.MarkLabel (label_end); + + temp_critical.Release (ec); + } + + public void EmitAwaitOnCompleted (EmitContext ec, FieldExpr awaiter) + { + bool unsafe_version = false; + if (Module.PredefinedTypes.ICriticalNotifyCompletion.Define ()) { + unsafe_version = awaiter.Type.ImplementsInterface (Module.PredefinedTypes.ICriticalNotifyCompletion.TypeSpec, false); + } + + EmitOnCompleted (ec, awaiter, unsafe_version); + } + + void EmitOnCompleted (EmitContext ec, Expression awaiter, bool unsafeVersion) + { + var pm = Module.PredefinedMembers; + PredefinedMember predefined; + bool has_task_return_type = false; + if (return_type.Kind == MemberKind.Void) { + predefined = unsafeVersion ? pm.AsyncVoidMethodBuilderOnCompletedUnsafe : pm.AsyncVoidMethodBuilderOnCompleted; + } else if (return_type == Module.PredefinedTypes.Task.TypeSpec) { + predefined = unsafeVersion ? pm.AsyncTaskMethodBuilderOnCompletedUnsafe : pm.AsyncTaskMethodBuilderOnCompleted; + } else { + predefined = unsafeVersion ? pm.AsyncTaskMethodBuilderGenericOnCompletedUnsafe : pm.AsyncTaskMethodBuilderGenericOnCompleted; + has_task_return_type = true; + } + + var on_completed = predefined.Resolve (Location); + if (on_completed == null) + return; + + if (has_task_return_type) + on_completed = MemberCache.GetMember (set_result.DeclaringType, on_completed); + + on_completed = on_completed.MakeGenericMethod (this, awaiter.Type, ec.CurrentType); + + var mg = MethodGroupExpr.CreatePredefined (on_completed, on_completed.DeclaringType, Location); + mg.InstanceExpression = new FieldExpr (builder, Location) { + InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, Location) + }; + + var args = new Arguments (2); + args.Add (new Argument (awaiter, Argument.AType.Ref)); + args.Add (new Argument (new CompilerGeneratedThis (CurrentType, Location), Argument.AType.Ref)); + using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) { + mg.EmitCall (ec, args, true); + } + } + + public void EmitInitializer (EmitContext ec) + { + // + // Some predefined types are missing + // + if (builder == null) + return; + + var instance = (TemporaryVariableReference) Instance; + var builder_field = builder.Spec; + if (MemberName.Arity > 0) { + builder_field = MemberCache.GetMember (instance.Type, builder_field); + } + + // + // Inflated factory method when task is of generic type + // + if (builder_factory.DeclaringType.IsGeneric) { + var task_return_type = return_type.TypeArguments; + var bt = builder_factory.DeclaringType.MakeGenericType (Module, task_return_type); + builder_factory = MemberCache.GetMember (bt, builder_factory); + builder_start = MemberCache.GetMember (bt, builder_start); + } + + // + // stateMachine.$builder = AsyncTaskMethodBuilder<{task-type}>.Create(); + // + instance.AddressOf (ec, AddressOp.Store); + ec.Emit (OpCodes.Call, builder_factory); + ec.Emit (OpCodes.Stfld, builder_field); + + // + // stateMachine.$builder.Start<{storey-type}>(ref stateMachine); + // + instance.AddressOf (ec, AddressOp.Store); + ec.Emit (OpCodes.Ldflda, builder_field); + if (Task != null) + ec.Emit (OpCodes.Dup); + instance.AddressOf (ec, AddressOp.Store); + ec.Emit (OpCodes.Call, builder_start.MakeGenericMethod (Module, instance.Type)); + + // + // Emits return stateMachine.$builder.Task; + // + if (Task != null) { + var task_get = Task.Get; + + if (MemberName.Arity > 0) { + task_get = MemberCache.GetMember (builder_field.MemberType, task_get); + } + + var pe_task = new PropertyExpr (Task, Location) { + InstanceExpression = EmptyExpression.Null, // Comes from the dup above + Getter = task_get + }; + + pe_task.Emit (ec); + } + } + + public void EmitSetException (EmitContext ec, LocalVariableReference exceptionVariable) + { + // + // $builder.SetException (Exception) + // + var mg = MethodGroupExpr.CreatePredefined (set_exception, set_exception.DeclaringType, Location); + mg.InstanceExpression = new FieldExpr (builder, Location) { + InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, Location) + }; + + Arguments args = new Arguments (1); + args.Add (new Argument (exceptionVariable)); + + using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) { + mg.EmitCall (ec, args, true); + } + } + + public void EmitSetResult (EmitContext ec) + { + // + // $builder.SetResult (); + // $builder.SetResult (value); + // + var mg = MethodGroupExpr.CreatePredefined (set_result, set_result.DeclaringType, Location); + mg.InstanceExpression = new FieldExpr (builder, Location) { + InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, Location) + }; + + Arguments args; + if (HoistedReturnValue == null) { + args = new Arguments (0); + } else { + args = new Arguments (1); + args.Add (new Argument (HoistedReturnValue)); + } + + using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) { + mg.EmitCall (ec, args, true); + } + } + + protected override TypeSpec[] ResolveBaseTypes (out FullNamedExpression base_class) + { + base_type = Compiler.BuiltinTypes.ValueType; + base_class = null; + + var istate_machine = Module.PredefinedTypes.IAsyncStateMachine; + if (istate_machine.Define ()) { + return new[] { istate_machine.TypeSpec }; + } + + return null; + } + } + + public class StackFieldExpr : FieldExpr, IExpressionCleanup + { + public StackFieldExpr (Field field) + : base (field, Location.Null) + { + } + + public bool IsAvailableForReuse { + get { + var field = (Field) spec.MemberDefinition; + return field.IsAvailableForReuse; + } + set { + var field = (Field) spec.MemberDefinition; + field.IsAvailableForReuse = value; + } + } + + public override void AddressOf (EmitContext ec, AddressOp mode) + { + base.AddressOf (ec, mode); + + if (mode == AddressOp.Load) { + IsAvailableForReuse = true; + } + } + + public override void Emit (EmitContext ec) + { + base.Emit (ec); + + PrepareCleanup (ec); + } + + public void EmitLoad (EmitContext ec) + { + base.Emit (ec); + } + + public void PrepareCleanup (EmitContext ec) + { + IsAvailableForReuse = true; + + // + // Release any captured reference type stack variables + // to imitate real stack behavour and help GC stuff early + // + if (TypeSpec.IsReferenceType (type)) { + ec.AddStatementEpilog (this); + } + } + + void IExpressionCleanup.EmitCleanup (EmitContext ec) + { + EmitAssign (ec, new NullConstant (type, loc), false, false); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/attribute.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/attribute.cs new file mode 100644 index 000000000..9e0437492 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/attribute.cs @@ -0,0 +1,2140 @@ +// +// attribute.cs: Attributes handling +// +// Author: Ravi Pratap (ravi@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) +// Copyright 2003-2008 Novell, Inc. +// Copyright 2011-2013 Xamarin Inc +// + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using System.Security; +using System.Security.Permissions; +using System.Text; +using System.IO; + +#if STATIC +using SecurityType = System.Collections.Generic.List; +using BadImageFormat = IKVM.Reflection.BadImageFormatException; +using IKVM.Reflection; +using IKVM.Reflection.Emit; +#else +using SecurityType = System.Collections.Generic.Dictionary; +using BadImageFormat = System.BadImageFormatException; +using System.Reflection; +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp { + + /// + /// Base class for objects that can have Attributes applied to them. + /// + public abstract class Attributable { + // + // Holds all attributes attached to this element + // + protected Attributes attributes; + + public void AddAttributes (Attributes attrs, IMemberContext context) + { + if (attrs == null) + return; + + if (attributes == null) + attributes = attrs; + else + attributes.AddAttributes (attrs.Attrs); + attrs.AttachTo (this, context); + } + + public Attributes OptAttributes { + get { + return attributes; + } + set { + attributes = value; + } + } + + /// + /// Use member-specific procedure to apply attribute @a in @cb to the entity being built in @builder + /// + public abstract void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa); + + /// + /// Returns one AttributeTarget for this element. + /// + public abstract AttributeTargets AttributeTargets { get; } + + public abstract bool IsClsComplianceRequired (); + + /// + /// Gets list of valid attribute targets for explicit target declaration. + /// The first array item is default target. Don't break this rule. + /// + public abstract string[] ValidAttributeTargets { get; } + }; + + public class Attribute + { + public readonly string ExplicitTarget; + public AttributeTargets Target; + readonly ATypeNameExpression expression; + + Arguments pos_args, named_args; + + bool resolve_error; + bool arg_resolved; + readonly bool nameEscaped; + readonly Location loc; + public TypeSpec Type; + + // + // An attribute can be attached to multiple targets (e.g. multiple fields) + // + Attributable[] targets; + + // + // A member context for the attribute, it's much easier to hold it here + // than trying to pull it during resolve + // + IMemberContext context; + + public static readonly AttributeUsageAttribute DefaultUsageAttribute = new AttributeUsageAttribute (AttributeTargets.All); + public static readonly object[] EmptyObject = new object [0]; + + List> named_values; + + public Attribute (string target, ATypeNameExpression expr, Arguments[] args, Location loc, bool nameEscaped) + { + this.expression = expr; + if (args != null) { + pos_args = args[0]; + named_args = args[1]; + } + this.loc = loc; + ExplicitTarget = target; + this.nameEscaped = nameEscaped; + } + + public Location Location { + get { + return loc; + } + } + + public Arguments NamedArguments { + get { + return named_args; + } + } + + public Arguments PositionalArguments { + get { + return pos_args; + } + } + + public bool ResolveError { + get { + return resolve_error; + } + } + + public ATypeNameExpression TypeExpression { + get { + return expression; + } + } + + void AddModuleCharSet (ResolveContext rc) + { + const string dll_import_char_set = "CharSet"; + + // + // Only when not customized by user + // + if (HasField (dll_import_char_set)) + return; + + if (!rc.Module.PredefinedTypes.CharSet.Define ()) { + return; + } + + if (NamedArguments == null) + named_args = new Arguments (1); + + var value = Constant.CreateConstantFromValue (rc.Module.PredefinedTypes.CharSet.TypeSpec, rc.Module.DefaultCharSet, Location); + NamedArguments.Add (new NamedArgument (dll_import_char_set, loc, value)); + } + + public Attribute Clone () + { + Attribute a = new Attribute (ExplicitTarget, expression, null, loc, nameEscaped); + a.pos_args = pos_args; + a.named_args = NamedArguments; + return a; + } + + // + // When the same attribute is attached to multiple fiels + // we use @target field as a list of targets. The attribute + // has to be resolved only once but emitted for each target. + // + public void AttachTo (Attributable target, IMemberContext context) + { + if (this.targets == null) { + this.targets = new Attributable[] { target }; + this.context = context; + return; + } + + // When re-attaching global attributes + if (context is NamespaceContainer) { + this.targets[0] = target; + this.context = context; + return; + } + + // Resize target array + Attributable[] new_array = new Attributable [this.targets.Length + 1]; + targets.CopyTo (new_array, 0); + new_array [targets.Length] = target; + this.targets = new_array; + + // No need to update context, different targets cannot have + // different contexts, it's enough to remove same attributes + // from secondary members. + + target.OptAttributes = null; + } + + public ResolveContext CreateResolveContext () + { + return new ResolveContext (context, ResolveContext.Options.ConstantScope); + } + + static void Error_InvalidNamedArgument (ResolveContext rc, NamedArgument name) + { + rc.Report.Error (617, name.Location, "`{0}' is not a valid named attribute argument. Named attribute arguments " + + "must be fields which are not readonly, static, const or read-write properties which are " + + "public and not static", + name.Name); + } + + static void Error_InvalidNamedArgumentType (ResolveContext rc, NamedArgument name) + { + rc.Report.Error (655, name.Location, + "`{0}' is not a valid named attribute argument because it is not a valid attribute parameter type", + name.Name); + } + + public static void Error_AttributeArgumentIsDynamic (IMemberContext context, Location loc) + { + context.Module.Compiler.Report.Error (1982, loc, "An attribute argument cannot be dynamic expression"); + } + + public void Error_MissingGuidAttribute () + { + Report.Error (596, Location, "The Guid attribute must be specified with the ComImport attribute"); + } + + public void Error_MisusedExtensionAttribute () + { + Report.Error (1112, Location, "Do not use `{0}' directly. Use parameter modifier `this' instead", GetSignatureForError ()); + } + + public void Error_MisusedDynamicAttribute () + { + Report.Error (1970, loc, "Do not use `{0}' directly. Use `dynamic' keyword instead", GetSignatureForError ()); + } + + void Error_AttributeEmitError (string inner) + { + Report.Error (647, Location, "Error during emitting `{0}' attribute. The reason is `{1}'", + Type.GetSignatureForError (), inner); + } + + public void Error_InvalidArgumentValue (TypeSpec attributeType) + { + Report.Error (591, Location, "Invalid value for argument to `{0}' attribute", attributeType.GetSignatureForError ()); + } + + public void Error_InvalidSecurityParent () + { + Report.Error (7070, Location, + "Security attribute `{0}' is not valid on this declaration type. Security attributes are only valid on assembly, type and method declarations", + Type.GetSignatureForError ()); + } + + Attributable Owner { + get { + return targets [0]; + } + } + + /// + /// Tries to resolve the type of the attribute. Flags an error if it can't, and complain is true. + /// + void ResolveAttributeType (bool comparisonOnly) + { + var resolve_printer = new SessionReportPrinter (); + SessionReportPrinter secondary_printer = null; + ReportPrinter prev_recorder = Report.SetPrinter (resolve_printer); + + bool t1_is_attr = false; + bool t2_is_attr = false; + TypeSpec t1, t2; + ATypeNameExpression expanded = null; + + // TODO: Additional warnings such as CS0436 are swallowed because we don't + // print on success + + try { + t1 = expression.ResolveAsType (context); + resolve_printer.EndSession (); + + if (t1 != null && resolve_printer.ErrorsCount == 0) + t1_is_attr = t1.IsAttribute; + + if (nameEscaped) { + t2 = null; + } else { + expanded = (ATypeNameExpression) expression.Clone (null); + expanded.Name += "Attribute"; + + secondary_printer = new SessionReportPrinter (); + Report.SetPrinter (secondary_printer); + t2 = expanded.ResolveAsType (context); + secondary_printer.EndSession (); + if (t2 != null && secondary_printer.ErrorsCount == 0) + t2_is_attr = t2.IsAttribute; + + secondary_printer.EndSession (); + } + } finally { + context.Module.Compiler.Report.SetPrinter (prev_recorder); + } + + if (t1_is_attr && t2_is_attr && t1 != t2) { + if (!comparisonOnly) { + Report.Error (1614, Location, "`{0}' is ambiguous between `{1}' and `{2}'. Use either `@{0}' or `{0}Attribute'", + GetSignatureForError (), expression.GetSignatureForError (), expanded.GetSignatureForError ()); + resolve_error = true; + } + + return; + } + + if (t1_is_attr) { + Type = t1; + return; + } + + if (t2_is_attr) { + Type = t2; + return; + } + + if (comparisonOnly) + return; + + resolve_error = true; + + if (t1 != null) { + if (resolve_printer.IsEmpty) { + Report.SymbolRelatedToPreviousError (t1); + Report.Error (616, Location, "`{0}': is not an attribute class", t1.GetSignatureForError ()); + } else { + resolve_printer.Merge (prev_recorder); + } + + return; + } + + if (t2 != null) { + if (secondary_printer.IsEmpty) { + Report.SymbolRelatedToPreviousError (t2); + Report.Error (616, Location, "`{0}': is not an attribute class", t2.GetSignatureForError ()); + } else { + secondary_printer.Merge (prev_recorder); + } + + return; + } + + resolve_printer.Merge (prev_recorder); + } + + public TypeSpec ResolveTypeForComparison () + { + if (Type == null && !resolve_error) + ResolveAttributeType (true); + return Type; + } + + public string GetSignatureForError () + { + if (Type != null) + return Type.GetSignatureForError (); + + return expression.GetSignatureForError (); + } + + public bool HasSecurityAttribute { + get { + PredefinedAttribute pa = context.Module.PredefinedAttributes.Security; + return pa.IsDefined && TypeSpec.IsBaseClass (Type, pa.TypeSpec, false); + } + } + + public bool IsValidSecurityAttribute () + { + return HasSecurityAttribute && IsSecurityActionValid (); + } + + static bool IsValidMethodImplOption (int value) + { + // + // Allow to use AggressiveInlining on any runtime/corlib + // + MethodImplOptions all = (MethodImplOptions) 256; + foreach (MethodImplOptions v in System.Enum.GetValues (typeof (MethodImplOptions))) { + all |= v; + } + + return ((MethodImplOptions) value | all) == all; + } + + public static bool IsValidArgumentType (TypeSpec t) + { + if (t.IsArray) { + var ac = (ArrayContainer) t; + if (ac.Rank > 1) + return false; + + t = ac.Element; + } + + switch (t.BuiltinType) { + case BuiltinTypeSpec.Type.Int: + case BuiltinTypeSpec.Type.UInt: + case BuiltinTypeSpec.Type.Long: + case BuiltinTypeSpec.Type.ULong: + case BuiltinTypeSpec.Type.Float: + case BuiltinTypeSpec.Type.Double: + case BuiltinTypeSpec.Type.Char: + case BuiltinTypeSpec.Type.Short: + case BuiltinTypeSpec.Type.Bool: + case BuiltinTypeSpec.Type.SByte: + case BuiltinTypeSpec.Type.Byte: + case BuiltinTypeSpec.Type.UShort: + + case BuiltinTypeSpec.Type.String: + case BuiltinTypeSpec.Type.Object: + case BuiltinTypeSpec.Type.Dynamic: + case BuiltinTypeSpec.Type.Type: + return true; + } + + return t.IsEnum; + } + + // TODO: Don't use this ambiguous value + public string Name { + get { return expression.Name; } + } + + public ATypeNameExpression TypeNameExpression { + get { + return expression; + } + } + + public Report Report { + get { return context.Module.Compiler.Report; } + } + + public MethodSpec Resolve () + { + if (resolve_error) + return null; + + resolve_error = true; + arg_resolved = true; + + if (Type == null) { + ResolveAttributeType (false); + if (Type == null) + return null; + } + + if (Type.IsAbstract) { + Report.Error (653, Location, "Cannot apply attribute class `{0}' because it is abstract", GetSignatureForError ()); + return null; + } + + ObsoleteAttribute obsolete_attr = Type.GetAttributeObsolete (); + if (obsolete_attr != null) { + AttributeTester.Report_ObsoleteMessage (obsolete_attr, Type.GetSignatureForError (), Location, Report); + } + + ResolveContext rc = null; + + MethodSpec ctor; + // Try if the attribute is simple and has been resolved before + if (pos_args != null || !context.Module.AttributeConstructorCache.TryGetValue (Type, out ctor)) { + rc = CreateResolveContext (); + ctor = ResolveConstructor (rc); + if (ctor == null) { + return null; + } + + if (pos_args == null && ctor.Parameters.IsEmpty) + context.Module.AttributeConstructorCache.Add (Type, ctor); + } + + // + // Add [module: DefaultCharSet] to all DllImport import attributes + // + var module = context.Module; + if ((Type == module.PredefinedAttributes.DllImport || Type == module.PredefinedAttributes.UnmanagedFunctionPointer) && module.HasDefaultCharSet) { + if (rc == null) + rc = CreateResolveContext (); + + AddModuleCharSet (rc); + } + + if (NamedArguments != null) { + if (rc == null) + rc = CreateResolveContext (); + + if (!ResolveNamedArguments (rc)) + return null; + } + + resolve_error = false; + return ctor; + } + + MethodSpec ResolveConstructor (ResolveContext ec) + { + if (pos_args != null) { + bool dynamic; + pos_args.Resolve (ec, out dynamic); + if (dynamic) { + Error_AttributeArgumentIsDynamic (ec.MemberContext, loc); + return null; + } + } + + return Expression.ConstructorLookup (ec, Type, ref pos_args, loc); + } + + bool ResolveNamedArguments (ResolveContext ec) + { + int named_arg_count = NamedArguments.Count; + var seen_names = new List (named_arg_count); + + named_values = new List> (named_arg_count); + + foreach (NamedArgument a in NamedArguments) { + string name = a.Name; + if (seen_names.Contains (name)) { + ec.Report.Error (643, a.Location, "Duplicate named attribute `{0}' argument", name); + continue; + } + + seen_names.Add (name); + + a.Resolve (ec); + + Expression member = Expression.MemberLookup (ec, false, Type, name, 0, Expression.MemberLookupRestrictions.ExactArity, loc); + + if (member == null) { + member = Expression.MemberLookup (ec, true, Type, name, 0, Expression.MemberLookupRestrictions.ExactArity, loc); + + if (member != null) { + // TODO: ec.Report.SymbolRelatedToPreviousError (member); + Expression.ErrorIsInaccesible (ec, member.GetSignatureForError (), loc); + return false; + } + } + + if (member == null){ + Expression.Error_TypeDoesNotContainDefinition (ec, Location, Type, name); + return false; + } + + if (!(member is PropertyExpr || member is FieldExpr)) { + Error_InvalidNamedArgument (ec, a); + return false; + } + + ObsoleteAttribute obsolete_attr; + + if (member is PropertyExpr) { + var pi = ((PropertyExpr) member).PropertyInfo; + + if (!pi.HasSet || !pi.HasGet || pi.IsStatic || !pi.Get.IsPublic || !pi.Set.IsPublic) { + ec.Report.SymbolRelatedToPreviousError (pi); + Error_InvalidNamedArgument (ec, a); + return false; + } + + if (!IsValidArgumentType (member.Type)) { + ec.Report.SymbolRelatedToPreviousError (pi); + Error_InvalidNamedArgumentType (ec, a); + return false; + } + + obsolete_attr = pi.GetAttributeObsolete (); + pi.MemberDefinition.SetIsAssigned (); + } else { + var fi = ((FieldExpr) member).Spec; + + if (fi.IsReadOnly || fi.IsStatic || !fi.IsPublic) { + Error_InvalidNamedArgument (ec, a); + return false; + } + + if (!IsValidArgumentType (member.Type)) { + ec.Report.SymbolRelatedToPreviousError (fi); + Error_InvalidNamedArgumentType (ec, a); + return false; + } + + obsolete_attr = fi.GetAttributeObsolete (); + fi.MemberDefinition.SetIsAssigned (); + } + + if (obsolete_attr != null && !context.IsObsolete) + AttributeTester.Report_ObsoleteMessage (obsolete_attr, member.GetSignatureForError (), member.Location, Report); + + if (a.Type != member.Type) { + a.Expr = Convert.ImplicitConversionRequired (ec, a.Expr, member.Type, a.Expr.Location); + } + + if (a.Expr != null) + named_values.Add (new KeyValuePair ((MemberExpr) member, a)); + } + + return true; + } + + /// + /// Get a string containing a list of valid targets for the attribute 'attr' + /// + public string GetValidTargets () + { + StringBuilder sb = new StringBuilder (); + AttributeTargets targets = Type.GetAttributeUsage (context.Module.PredefinedAttributes.AttributeUsage).ValidOn; + + if ((targets & AttributeTargets.Assembly) != 0) + sb.Append ("assembly, "); + + if ((targets & AttributeTargets.Module) != 0) + sb.Append ("module, "); + + if ((targets & AttributeTargets.Class) != 0) + sb.Append ("class, "); + + if ((targets & AttributeTargets.Struct) != 0) + sb.Append ("struct, "); + + if ((targets & AttributeTargets.Enum) != 0) + sb.Append ("enum, "); + + if ((targets & AttributeTargets.Constructor) != 0) + sb.Append ("constructor, "); + + if ((targets & AttributeTargets.Method) != 0) + sb.Append ("method, "); + + if ((targets & AttributeTargets.Property) != 0) + sb.Append ("property, indexer, "); + + if ((targets & AttributeTargets.Field) != 0) + sb.Append ("field, "); + + if ((targets & AttributeTargets.Event) != 0) + sb.Append ("event, "); + + if ((targets & AttributeTargets.Interface) != 0) + sb.Append ("interface, "); + + if ((targets & AttributeTargets.Parameter) != 0) + sb.Append ("parameter, "); + + if ((targets & AttributeTargets.Delegate) != 0) + sb.Append ("delegate, "); + + if ((targets & AttributeTargets.ReturnValue) != 0) + sb.Append ("return, "); + + if ((targets & AttributeTargets.GenericParameter) != 0) + sb.Append ("type parameter, "); + + return sb.Remove (sb.Length - 2, 2).ToString (); + } + + public AttributeUsageAttribute GetAttributeUsageAttribute () + { + if (!arg_resolved) + // TODO: It is not neccessary to call whole Resolve (ApplyAttribute does it now) we need only ctor args. + // But because a lot of attribute class code must be rewritten will be better to wait... + Resolve (); + + if (resolve_error) + return DefaultUsageAttribute; + + AttributeUsageAttribute usage_attribute = new AttributeUsageAttribute ((AttributeTargets) ((Constant) pos_args[0].Expr).GetValue ()); + + var field = GetNamedValue ("AllowMultiple") as BoolConstant; + if (field != null) + usage_attribute.AllowMultiple = field.Value; + + field = GetNamedValue ("Inherited") as BoolConstant; + if (field != null) + usage_attribute.Inherited = field.Value; + + return usage_attribute; + } + + /// + /// Returns custom name of indexer + /// + public string GetIndexerAttributeValue () + { + if (!arg_resolved) + // TODO: It is not neccessary to call whole Resolve (ApplyAttribute does it now) we need only ctor args. + // But because a lot of attribute class code must be rewritten will be better to wait... + Resolve (); + + if (resolve_error || pos_args.Count != 1 || !(pos_args[0].Expr is Constant)) + return null; + + return ((Constant) pos_args[0].Expr).GetValue () as string; + } + + /// + /// Returns condition of ConditionalAttribute + /// + public string GetConditionalAttributeValue () + { + if (!arg_resolved) + // TODO: It is not neccessary to call whole Resolve (ApplyAttribute does it now) we need only ctor args. + // But because a lot of attribute class code must be rewritten will be better to wait... + Resolve (); + + if (resolve_error) + return null; + + return ((Constant) pos_args[0].Expr).GetValue () as string; + } + + /// + /// Creates the instance of ObsoleteAttribute from this attribute instance + /// + public ObsoleteAttribute GetObsoleteAttribute () + { + if (!arg_resolved) { + // corlib only case when obsolete is used before is resolved + var c = Type.MemberDefinition as Class; + if (c != null && !c.HasMembersDefined) + c.Define (); + + // TODO: It is not neccessary to call whole Resolve (ApplyAttribute does it now) we need only ctor args. + // But because a lot of attribute class code must be rewritten will be better to wait... + Resolve (); + } + + if (resolve_error) + return null; + + if (pos_args == null) + return new ObsoleteAttribute (); + + string msg = ((Constant) pos_args[0].Expr).GetValue () as string; + if (pos_args.Count == 1) + return new ObsoleteAttribute (msg); + + return new ObsoleteAttribute (msg, ((BoolConstant) pos_args[1].Expr).Value); + } + + /// + /// Returns value of CLSCompliantAttribute contructor parameter but because the method can be called + /// before ApplyAttribute. We need to resolve the arguments. + /// This situation occurs when class deps is differs from Emit order. + /// + public bool GetClsCompliantAttributeValue () + { + if (!arg_resolved) + // TODO: It is not neccessary to call whole Resolve (ApplyAttribute does it now) we need only ctor args. + // But because a lot of attribute class code must be rewritten will be better to wait... + Resolve (); + + if (resolve_error) + return false; + + return ((BoolConstant) pos_args[0].Expr).Value; + } + + public TypeSpec GetCoClassAttributeValue () + { + if (!arg_resolved) + Resolve (); + + if (resolve_error) + return null; + + return GetArgumentType (); + } + + public bool CheckTarget () + { + string[] valid_targets = Owner.ValidAttributeTargets; + if (ExplicitTarget == null || ExplicitTarget == valid_targets [0]) { + Target = Owner.AttributeTargets; + return true; + } + + // TODO: we can skip the first item + if (Array.Exists (valid_targets, i => i == ExplicitTarget)) { + switch (ExplicitTarget) { + case "return": Target = AttributeTargets.ReturnValue; return true; + case "param": Target = AttributeTargets.Parameter; return true; + case "field": Target = AttributeTargets.Field; return true; + case "method": Target = AttributeTargets.Method; return true; + case "property": Target = AttributeTargets.Property; return true; + case "module": Target = AttributeTargets.Module; return true; + } + throw new InternalErrorException ("Unknown explicit target: " + ExplicitTarget); + } + + StringBuilder sb = new StringBuilder (); + foreach (string s in valid_targets) { + sb.Append (s); + sb.Append (", "); + } + sb.Remove (sb.Length - 2, 2); + Report.Warning (657, 1, Location, + "`{0}' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are `{1}'. All attributes in this section will be ignored", + ExplicitTarget, sb.ToString ()); + return false; + } + + /// + /// Tests permitted SecurityAction for assembly or other types + /// + bool IsSecurityActionValid () + { + SecurityAction action = GetSecurityActionValue (); + bool for_assembly = Target == AttributeTargets.Assembly || Target == AttributeTargets.Module; + var c = (Constant)pos_args [0].Expr; + + switch (action) { +#pragma warning disable 618 + case SecurityAction.Demand: + case SecurityAction.Assert: + case SecurityAction.Deny: + case SecurityAction.PermitOnly: + case SecurityAction.LinkDemand: + case SecurityAction.InheritanceDemand: + if (!for_assembly) + return true; + break; + + case SecurityAction.RequestMinimum: + case SecurityAction.RequestOptional: + case SecurityAction.RequestRefuse: + if (for_assembly) + return true; + break; +#pragma warning restore 618 + + default: + Report.Error (7049, c.Location, "Security attribute `{0}' has an invalid SecurityAction value `{1}'", + Type.GetSignatureForError (), c.GetValueAsLiteral()); + return false; + } + + switch (Target) { + case AttributeTargets.Assembly: + Report.Error (7050, c.Location, "SecurityAction value `{0}' is invalid for security attributes applied to an assembly", + c.GetSignatureForError ()); + break; + default: + Report.Error (7051, c.Location, "SecurityAction value `{0}' is invalid for security attributes applied to a type or a method", + c.GetSignatureForError ()); + break; + } + + return false; + } + + System.Security.Permissions.SecurityAction GetSecurityActionValue () + { + return (SecurityAction) ((Constant) pos_args[0].Expr).GetValue (); + } + + /// + /// Creates instance of SecurityAttribute class and add result of CreatePermission method to permission table. + /// + /// + public void ExtractSecurityPermissionSet (MethodSpec ctor, ref SecurityType permissions) + { +#if STATIC + object[] values = new object[pos_args.Count]; + for (int i = 0; i < values.Length; ++i) + values[i] = ((Constant) pos_args[i].Expr).GetValue (); + + PropertyInfo[] prop; + object[] prop_values; + if (named_values == null) { + prop = null; + prop_values = null; + } else { + prop = new PropertyInfo[named_values.Count]; + prop_values = new object [named_values.Count]; + for (int i = 0; i < prop.Length; ++i) { + prop [i] = ((PropertyExpr) named_values [i].Key).PropertyInfo.MetaInfo; + prop_values [i] = ((Constant) named_values [i].Value.Expr).GetValue (); + } + } + + if (permissions == null) + permissions = new SecurityType (); + + var cab = new CustomAttributeBuilder ((ConstructorInfo) ctor.GetMetaInfo (), values, prop, prop_values); + permissions.Add (cab); +#else + throw new NotSupportedException (); +#endif + } + + public Constant GetNamedValue (string name) + { + if (named_values == null) + return null; + + for (int i = 0; i < named_values.Count; ++i) { + if (named_values [i].Value.Name == name) + return named_values [i].Value.Expr as Constant; + } + + return null; + } + + public CharSet GetCharSetValue () + { + return (CharSet) System.Enum.Parse (typeof (CharSet), ((Constant) pos_args[0].Expr).GetValue ().ToString ()); + } + + public bool HasField (string fieldName) + { + if (named_values == null) + return false; + + foreach (var na in named_values) { + if (na.Value.Name == fieldName) + return true; + } + + return false; + } + + // + // Returns true for MethodImplAttribute with MethodImplOptions.InternalCall value + // + public bool IsInternalCall () + { + return (GetMethodImplOptions () & MethodImplOptions.InternalCall) != 0; + } + + public MethodImplOptions GetMethodImplOptions () + { + MethodImplOptions options = 0; + if (pos_args.Count == 1) { + options = (MethodImplOptions) System.Enum.Parse (typeof (MethodImplOptions), ((Constant) pos_args[0].Expr).GetValue ().ToString ()); + } else if (HasField ("Value")) { + var named = GetNamedValue ("Value"); + options = (MethodImplOptions) System.Enum.Parse (typeof (MethodImplOptions), named.GetValue ().ToString ()); + } + + return options; + } + + // + // Returns true for StructLayoutAttribute with LayoutKind.Explicit value + // + public bool IsExplicitLayoutKind () + { + if (pos_args == null || pos_args.Count != 1) + return false; + + var value = (LayoutKind) System.Enum.Parse (typeof (LayoutKind), ((Constant) pos_args[0].Expr).GetValue ().ToString ()); + return value == LayoutKind.Explicit; + } + + public Expression GetParameterDefaultValue () + { + if (pos_args == null) + return null; + + return pos_args[0].Expr; + } + + public override bool Equals (object obj) + { + Attribute a = obj as Attribute; + if (a == null) + return false; + + return Type == a.Type && Target == a.Target; + } + + public override int GetHashCode () + { + return Type.GetHashCode () ^ Target.GetHashCode (); + } + + /// + /// Emit attribute for Attributable symbol + /// + public void Emit (Dictionary> allEmitted) + { + var ctor = Resolve (); + if (ctor == null) + return; + + var predefined = context.Module.PredefinedAttributes; + + AttributeUsageAttribute usage_attr = Type.GetAttributeUsage (predefined.AttributeUsage); + if ((usage_attr.ValidOn & Target) == 0) { + Report.Error (592, Location, "The attribute `{0}' is not valid on this declaration type. " + + "It is valid on `{1}' declarations only", + GetSignatureForError (), GetValidTargets ()); + return; + } + + byte[] cdata; + if (pos_args == null && named_values == null) { + cdata = AttributeEncoder.Empty; + } else { + AttributeEncoder encoder = new AttributeEncoder (); + + if (pos_args != null) { + var param_types = ctor.Parameters.Types; + for (int j = 0; j < pos_args.Count; ++j) { + var pt = param_types[j]; + var arg_expr = pos_args[j].Expr; + if (j == 0) { + if ((Type == predefined.IndexerName || Type == predefined.Conditional) && arg_expr is Constant) { + string v = ((Constant) arg_expr).GetValue () as string; + if (!Tokenizer.IsValidIdentifier (v) || (Type == predefined.IndexerName && Tokenizer.IsKeyword (v))) { + context.Module.Compiler.Report.Error (633, arg_expr.Location, + "The argument to the `{0}' attribute must be a valid identifier", GetSignatureForError ()); + return; + } + } else if (Type == predefined.Guid) { + string v = ((StringConstant) arg_expr).Value; + try { + new Guid (v); + } catch { + Error_InvalidArgumentValue (Type); + return; + } + } else if (Type == predefined.AttributeUsage) { + int v = ((IntConstant) ((EnumConstant) arg_expr).Child).Value; + if (v == 0) + Error_InvalidArgumentValue (Type); + } else if (Type == predefined.MarshalAs) { + if (pos_args.Count == 1) { + var u_type = (UnmanagedType) System.Enum.Parse (typeof (UnmanagedType), ((Constant) pos_args[0].Expr).GetValue ().ToString ()); + if (u_type == UnmanagedType.ByValArray && !(Owner is FieldBase)) { + Report.Error (7055, pos_args [0].Expr.Location, "Unmanaged type `ByValArray' is only valid for fields"); + } + } + } else if (Type == predefined.DllImport) { + if (pos_args.Count == 1 && pos_args[0].Expr is Constant) { + var value = ((Constant) pos_args[0].Expr).GetValue () as string; + if (string.IsNullOrEmpty (value)) + Error_InvalidArgumentValue (Type); + } + } else if (Type == predefined.MethodImpl) { + if (pos_args.Count == 1) { + var value = (int) ((Constant) arg_expr).GetValueAsLong (); + + if (!IsValidMethodImplOption (value)) { + Error_InvalidArgumentValue (Type); + } + } + } + } + + arg_expr.EncodeAttributeValue (context, encoder, pt, pt); + } + } + + if (named_values != null) { + encoder.Encode ((ushort) named_values.Count); + foreach (var na in named_values) { + if (na.Key is FieldExpr) + encoder.Encode ((byte) 0x53); + else + encoder.Encode ((byte) 0x54); + + encoder.Encode (na.Key.Type); + encoder.Encode (na.Value.Name); + na.Value.Expr.EncodeAttributeValue (context, encoder, na.Key.Type, na.Key.Type); + } + } else { + encoder.EncodeEmptyNamedArguments (); + } + + cdata = encoder.ToArray (); + } + + if (!ctor.DeclaringType.IsConditionallyExcluded (context)) { + try { + foreach (Attributable target in targets) + target.ApplyAttributeBuilder (this, ctor, cdata, predefined); + } catch (Exception e) { + if (e is BadImageFormat && Report.Errors > 0) + return; + + Error_AttributeEmitError (e.Message); + return; + } + } + + if (!usage_attr.AllowMultiple && allEmitted != null) { + if (allEmitted.ContainsKey (this)) { + var a = allEmitted [this]; + if (a == null) { + a = new List (2); + allEmitted [this] = a; + } + a.Add (this); + } else { + allEmitted.Add (this, null); + } + } + + if (!context.Module.Compiler.Settings.VerifyClsCompliance) + return; + + // Here we are testing attribute arguments for array usage (error 3016) + if (Owner.IsClsComplianceRequired ()) { + if (pos_args != null) + pos_args.CheckArrayAsAttribute (context.Module.Compiler); + + if (NamedArguments == null) + return; + + NamedArguments.CheckArrayAsAttribute (context.Module.Compiler); + } + } + + private Expression GetValue () + { + if (pos_args == null || pos_args.Count < 1) + return null; + + return pos_args[0].Expr; + } + + public string GetString () + { + Expression e = GetValue (); + if (e is StringConstant) + return ((StringConstant)e).Value; + return null; + } + + public bool GetBoolean () + { + Expression e = GetValue (); + if (e is BoolConstant) + return ((BoolConstant)e).Value; + return false; + } + + public TypeSpec GetArgumentType () + { + TypeOf e = GetValue () as TypeOf; + if (e == null) + return null; + return e.TypeArgument; + } + } + + public class Attributes + { + public readonly List Attrs; +#if FULL_AST + public readonly List> Sections = new List> (); +#endif + + public Attributes (Attribute a) + { + Attrs = new List (); + Attrs.Add (a); + +#if FULL_AST + Sections.Add (Attrs); +#endif + } + + public Attributes (List attrs) + { + Attrs = attrs ?? new List (); +#if FULL_AST + Sections.Add (attrs); +#endif + } + + public void AddAttribute (Attribute attr) + { + Attrs.Add (attr); + } + + public void AddAttributes (List attrs) + { +#if FULL_AST + Sections.Add (attrs); +#else + Attrs.AddRange (attrs); +#endif + } + + public void AttachTo (Attributable attributable, IMemberContext context) + { + foreach (Attribute a in Attrs) + a.AttachTo (attributable, context); + } + + public Attributes Clone () + { + var al = new List (Attrs.Count); + foreach (Attribute a in Attrs) + al.Add (a.Clone ()); + + return new Attributes (al); + } + + /// + /// Checks whether attribute target is valid for the current element + /// + public bool CheckTargets () + { + for (int i = 0; i < Attrs.Count; ++i) { + if (!Attrs [i].CheckTarget ()) + Attrs.RemoveAt (i--); + } + + return true; + } + + public void ConvertGlobalAttributes (TypeContainer member, NamespaceContainer currentNamespace, bool isGlobal) + { + var member_explicit_targets = member.ValidAttributeTargets; + for (int i = 0; i < Attrs.Count; ++i) { + var attr = Attrs[0]; + if (attr.ExplicitTarget == null) + continue; + + int ii; + for (ii = 0; ii < member_explicit_targets.Length; ++ii) { + if (attr.ExplicitTarget == member_explicit_targets[ii]) { + ii = -1; + break; + } + } + + if (ii < 0 || !isGlobal) + continue; + + member.Module.AddAttribute (attr, currentNamespace); + Attrs.RemoveAt (i); + --i; + } + } + + public bool HasResolveError() + { + foreach (var a in Attrs) { + if (a.ResolveError) + return true; + } + + return false; + } + + public Attribute Search (PredefinedAttribute t) + { + return Search (null, t); + } + + public Attribute Search (string explicitTarget, PredefinedAttribute t) + { + foreach (Attribute a in Attrs) { + if (explicitTarget != null && a.ExplicitTarget != explicitTarget) + continue; + + if (a.ResolveTypeForComparison () == t) + return a; + } + return null; + } + + /// + /// Returns all attributes of type 't'. Use it when attribute is AllowMultiple = true + /// + public Attribute[] SearchMulti (PredefinedAttribute t) + { + List ar = null; + + foreach (Attribute a in Attrs) { + if (a.ResolveTypeForComparison () == t) { + if (ar == null) + ar = new List (Attrs.Count); + ar.Add (a); + } + } + + return ar == null ? null : ar.ToArray (); + } + + public void Emit () + { + CheckTargets (); + + Dictionary> ld = Attrs.Count > 1 ? new Dictionary> () : null; + + foreach (Attribute a in Attrs) + a.Emit (ld); + + if (ld == null || ld.Count == 0) + return; + + foreach (var d in ld) { + if (d.Value == null) + continue; + + Attribute a = d.Key; + + foreach (Attribute collision in d.Value) + a.Report.SymbolRelatedToPreviousError (collision.Location, ""); + + a.Report.Error (579, a.Location, "The attribute `{0}' cannot be applied multiple times", + a.GetSignatureForError ()); + } + } + + public bool Contains (PredefinedAttribute t) + { + return Search (t) != null; + } + } + + public sealed class AttributeEncoder + { + [Flags] + public enum EncodedTypeProperties + { + None = 0, + DynamicType = 1, + TypeParameter = 1 << 1 + } + + public static readonly byte[] Empty; + + byte[] buffer; + int pos; + const ushort Version = 1; + + static AttributeEncoder () + { + Empty = new byte[4]; + Empty[0] = (byte) Version; + } + + public AttributeEncoder () + { + buffer = new byte[32]; + Encode (Version); + } + + public void Encode (bool value) + { + Encode (value ? (byte) 1 : (byte) 0); + } + + public void Encode (byte value) + { + if (pos == buffer.Length) + Grow (1); + + buffer [pos++] = value; + } + + public void Encode (sbyte value) + { + Encode ((byte) value); + } + + public void Encode (short value) + { + if (pos + 2 > buffer.Length) + Grow (2); + + buffer[pos++] = (byte) value; + buffer[pos++] = (byte) (value >> 8); + } + + public void Encode (ushort value) + { + Encode ((short) value); + } + + public void Encode (int value) + { + if (pos + 4 > buffer.Length) + Grow (4); + + buffer[pos++] = (byte) value; + buffer[pos++] = (byte) (value >> 8); + buffer[pos++] = (byte) (value >> 16); + buffer[pos++] = (byte) (value >> 24); + } + + public void Encode (uint value) + { + Encode ((int) value); + } + + public void Encode (long value) + { + if (pos + 8 > buffer.Length) + Grow (8); + + buffer[pos++] = (byte) value; + buffer[pos++] = (byte) (value >> 8); + buffer[pos++] = (byte) (value >> 16); + buffer[pos++] = (byte) (value >> 24); + buffer[pos++] = (byte) (value >> 32); + buffer[pos++] = (byte) (value >> 40); + buffer[pos++] = (byte) (value >> 48); + buffer[pos++] = (byte) (value >> 56); + } + + public void Encode (ulong value) + { + Encode ((long) value); + } + + public void Encode (float value) + { + Encode (SingleConverter.SingleToInt32Bits (value)); + } + + public void Encode (double value) + { + Encode (BitConverter.DoubleToInt64Bits (value)); + } + + public void Encode (string value) + { + if (value == null) { + Encode ((byte) 0xFF); + return; + } + + var buf = Encoding.UTF8.GetBytes(value); + WriteCompressedValue (buf.Length); + + if (pos + buf.Length > buffer.Length) + Grow (buf.Length); + + Buffer.BlockCopy (buf, 0, buffer, pos, buf.Length); + pos += buf.Length; + } + + public EncodedTypeProperties Encode (TypeSpec type) + { + switch (type.BuiltinType) { + case BuiltinTypeSpec.Type.Bool: + Encode ((byte) 0x02); + break; + case BuiltinTypeSpec.Type.Char: + Encode ((byte) 0x03); + break; + case BuiltinTypeSpec.Type.SByte: + Encode ((byte) 0x04); + break; + case BuiltinTypeSpec.Type.Byte: + Encode ((byte) 0x05); + break; + case BuiltinTypeSpec.Type.Short: + Encode ((byte) 0x06); + break; + case BuiltinTypeSpec.Type.UShort: + Encode ((byte) 0x07); + break; + case BuiltinTypeSpec.Type.Int: + Encode ((byte) 0x08); + break; + case BuiltinTypeSpec.Type.UInt: + Encode ((byte) 0x09); + break; + case BuiltinTypeSpec.Type.Long: + Encode ((byte) 0x0A); + break; + case BuiltinTypeSpec.Type.ULong: + Encode ((byte) 0x0B); + break; + case BuiltinTypeSpec.Type.Float: + Encode ((byte) 0x0C); + break; + case BuiltinTypeSpec.Type.Double: + Encode ((byte) 0x0D); + break; + case BuiltinTypeSpec.Type.String: + Encode ((byte) 0x0E); + break; + case BuiltinTypeSpec.Type.Type: + Encode ((byte) 0x50); + break; + case BuiltinTypeSpec.Type.Object: + Encode ((byte) 0x51); + break; + case BuiltinTypeSpec.Type.Dynamic: + Encode ((byte) 0x51); + return EncodedTypeProperties.DynamicType; + default: + if (type.IsArray) { + Encode ((byte) 0x1D); + return Encode (TypeManager.GetElementType (type)); + } + + if (type.Kind == MemberKind.Enum) { + Encode ((byte) 0x55); + EncodeTypeName (type); + } + + break; + } + + return EncodedTypeProperties.None; + } + + public void EncodeTypeName (TypeSpec type) + { + var old_type = type.GetMetaInfo (); + Encode (type.MemberDefinition.IsImported ? old_type.AssemblyQualifiedName : old_type.FullName); + } + + public void EncodeTypeName (TypeContainer type) + { + Encode (type.GetSignatureForMetadata ()); + } + + + // + // Encodes single property named argument per call + // + public void EncodeNamedPropertyArgument (PropertySpec property, Constant value) + { + Encode ((ushort) 1); // length + Encode ((byte) 0x54); // property + Encode (property.MemberType); + Encode (property.Name); + value.EncodeAttributeValue (null, this, property.MemberType, property.MemberType); + } + + // + // Encodes single field named argument per call + // + public void EncodeNamedFieldArgument (FieldSpec field, Constant value) + { + Encode ((ushort) 1); // length + Encode ((byte) 0x53); // field + Encode (field.MemberType); + Encode (field.Name); + value.EncodeAttributeValue (null, this, field.MemberType, field.MemberType); + } + + public void EncodeNamedArguments (T[] members, Constant[] values) where T : MemberSpec, IInterfaceMemberSpec + { + Encode ((ushort) members.Length); + + for (int i = 0; i < members.Length; ++i) + { + var member = members[i]; + + if (member.Kind == MemberKind.Field) + Encode ((byte) 0x53); + else if (member.Kind == MemberKind.Property) + Encode ((byte) 0x54); + else + throw new NotImplementedException (member.Kind.ToString ()); + + Encode (member.MemberType); + Encode (member.Name); + values [i].EncodeAttributeValue (null, this, member.MemberType, member.MemberType); + } + } + + public void EncodeEmptyNamedArguments () + { + Encode ((ushort) 0); + } + + void Grow (int inc) + { + int size = System.Math.Max (pos * 4, pos + inc + 2); + Array.Resize (ref buffer, size); + } + + void WriteCompressedValue (int value) + { + if (value < 0x80) { + Encode ((byte) value); + return; + } + + if (value < 0x4000) { + Encode ((byte) (0x80 | (value >> 8))); + Encode ((byte) value); + return; + } + + Encode (value); + } + + public byte[] ToArray () + { + byte[] buf = new byte[pos]; + Array.Copy (buffer, buf, pos); + return buf; + } + } + + + /// + /// Helper class for attribute verification routine. + /// + static class AttributeTester + { + /// + /// Common method for Obsolete error/warning reporting. + /// + public static void Report_ObsoleteMessage (ObsoleteAttribute oa, string member, Location loc, Report Report) + { + if (oa.IsError) { + Report.Error (619, loc, "`{0}' is obsolete: `{1}'", member, oa.Message); + return; + } + + if (oa.Message == null || oa.Message.Length == 0) { + Report.Warning (612, 1, loc, "`{0}' is obsolete", member); + return; + } + Report.Warning (618, 2, loc, "`{0}' is obsolete: `{1}'", member, oa.Message); + } + } + + // + // Predefined attribute types + // + public class PredefinedAttributes + { + // Build-in attributes + public readonly PredefinedAttribute ParamArray; + public readonly PredefinedAttribute Out; + + // Optional attributes + public readonly PredefinedAttribute Obsolete; + public readonly PredefinedAttribute DllImport; + public readonly PredefinedAttribute MethodImpl; + public readonly PredefinedAttribute MarshalAs; + public readonly PredefinedAttribute In; + public readonly PredefinedAttribute IndexerName; + public readonly PredefinedAttribute Conditional; + public readonly PredefinedAttribute CLSCompliant; + public readonly PredefinedAttribute Security; + public readonly PredefinedAttribute Required; + public readonly PredefinedAttribute Guid; + public readonly PredefinedAttribute AssemblyCulture; + public readonly PredefinedAttribute AssemblyVersion; + public readonly PredefinedAttribute AssemblyAlgorithmId; + public readonly PredefinedAttribute AssemblyFlags; + public readonly PredefinedAttribute AssemblyFileVersion; + public readonly PredefinedAttribute ComImport; + public readonly PredefinedAttribute CoClass; + public readonly PredefinedAttribute AttributeUsage; + public readonly PredefinedAttribute DefaultParameterValue; + public readonly PredefinedAttribute OptionalParameter; + public readonly PredefinedAttribute UnverifiableCode; + public readonly PredefinedAttribute DefaultCharset; + public readonly PredefinedAttribute TypeForwarder; + public readonly PredefinedAttribute FixedBuffer; + public readonly PredefinedAttribute CompilerGenerated; + public readonly PredefinedAttribute InternalsVisibleTo; + public readonly PredefinedAttribute RuntimeCompatibility; + public readonly PredefinedAttribute DebuggerHidden; + public readonly PredefinedAttribute UnsafeValueType; + public readonly PredefinedAttribute UnmanagedFunctionPointer; + public readonly PredefinedDebuggerBrowsableAttribute DebuggerBrowsable; + public readonly PredefinedAttribute DebuggerStepThrough; + public readonly PredefinedDebuggableAttribute Debuggable; + + // New in .NET 3.5 + public readonly PredefinedAttribute Extension; + + // New in .NET 4.0 + public readonly PredefinedDynamicAttribute Dynamic; + + // New in .NET 4.5 + public readonly PredefinedStateMachineAttribute AsyncStateMachine; + + // + // Optional types which are used as types and for member lookup + // + public readonly PredefinedAttribute DefaultMember; + public readonly PredefinedDecimalAttribute DecimalConstant; + public readonly PredefinedAttribute StructLayout; + public readonly PredefinedAttribute FieldOffset; + public readonly PredefinedAttribute AssemblyProduct; + public readonly PredefinedAttribute AssemblyCompany; + public readonly PredefinedAttribute AssemblyDescription; + public readonly PredefinedAttribute AssemblyCopyright; + public readonly PredefinedAttribute AssemblyTrademark; + public readonly PredefinedAttribute CallerMemberNameAttribute; + public readonly PredefinedAttribute CallerLineNumberAttribute; + public readonly PredefinedAttribute CallerFilePathAttribute; + + public PredefinedAttributes (ModuleContainer module) + { + ParamArray = new PredefinedAttribute (module, "System", "ParamArrayAttribute"); + Out = new PredefinedAttribute (module, "System.Runtime.InteropServices", "OutAttribute"); + ParamArray.Resolve (); + Out.Resolve (); + + Obsolete = new PredefinedAttribute (module, "System", "ObsoleteAttribute"); + DllImport = new PredefinedAttribute (module, "System.Runtime.InteropServices", "DllImportAttribute"); + MethodImpl = new PredefinedAttribute (module, "System.Runtime.CompilerServices", "MethodImplAttribute"); + MarshalAs = new PredefinedAttribute (module, "System.Runtime.InteropServices", "MarshalAsAttribute"); + In = new PredefinedAttribute (module, "System.Runtime.InteropServices", "InAttribute"); + IndexerName = new PredefinedAttribute (module, "System.Runtime.CompilerServices", "IndexerNameAttribute"); + Conditional = new PredefinedAttribute (module, "System.Diagnostics", "ConditionalAttribute"); + CLSCompliant = new PredefinedAttribute (module, "System", "CLSCompliantAttribute"); + Security = new PredefinedAttribute (module, "System.Security.Permissions", "SecurityAttribute"); + Required = new PredefinedAttribute (module, "System.Runtime.CompilerServices", "RequiredAttributeAttribute"); + Guid = new PredefinedAttribute (module, "System.Runtime.InteropServices", "GuidAttribute"); + AssemblyCulture = new PredefinedAttribute (module, "System.Reflection", "AssemblyCultureAttribute"); + AssemblyVersion = new PredefinedAttribute (module, "System.Reflection", "AssemblyVersionAttribute"); + AssemblyAlgorithmId = new PredefinedAttribute (module, "System.Reflection", "AssemblyAlgorithmIdAttribute"); + AssemblyFlags = new PredefinedAttribute (module, "System.Reflection", "AssemblyFlagsAttribute"); + AssemblyFileVersion = new PredefinedAttribute (module, "System.Reflection", "AssemblyFileVersionAttribute"); + ComImport = new PredefinedAttribute (module, "System.Runtime.InteropServices", "ComImportAttribute"); + CoClass = new PredefinedAttribute (module, "System.Runtime.InteropServices", "CoClassAttribute"); + AttributeUsage = new PredefinedAttribute (module, "System", "AttributeUsageAttribute"); + DefaultParameterValue = new PredefinedAttribute (module, "System.Runtime.InteropServices", "DefaultParameterValueAttribute"); + OptionalParameter = new PredefinedAttribute (module, "System.Runtime.InteropServices", "OptionalAttribute"); + UnverifiableCode = new PredefinedAttribute (module, "System.Security", "UnverifiableCodeAttribute"); + + DefaultCharset = new PredefinedAttribute (module, "System.Runtime.InteropServices", "DefaultCharSetAttribute"); + TypeForwarder = new PredefinedAttribute (module, "System.Runtime.CompilerServices", "TypeForwardedToAttribute"); + FixedBuffer = new PredefinedAttribute (module, "System.Runtime.CompilerServices", "FixedBufferAttribute"); + CompilerGenerated = new PredefinedAttribute (module, "System.Runtime.CompilerServices", "CompilerGeneratedAttribute"); + InternalsVisibleTo = new PredefinedAttribute (module, "System.Runtime.CompilerServices", "InternalsVisibleToAttribute"); + RuntimeCompatibility = new PredefinedAttribute (module, "System.Runtime.CompilerServices", "RuntimeCompatibilityAttribute"); + DebuggerHidden = new PredefinedAttribute (module, "System.Diagnostics", "DebuggerHiddenAttribute"); + UnsafeValueType = new PredefinedAttribute (module, "System.Runtime.CompilerServices", "UnsafeValueTypeAttribute"); + UnmanagedFunctionPointer = new PredefinedAttribute (module, "System.Runtime.InteropServices", "UnmanagedFunctionPointerAttribute"); + DebuggerBrowsable = new PredefinedDebuggerBrowsableAttribute (module, "System.Diagnostics", "DebuggerBrowsableAttribute"); + DebuggerStepThrough = new PredefinedAttribute (module, "System.Diagnostics", "DebuggerStepThroughAttribute"); + Debuggable = new PredefinedDebuggableAttribute (module, "System.Diagnostics", "DebuggableAttribute"); + + Extension = new PredefinedAttribute (module, "System.Runtime.CompilerServices", "ExtensionAttribute"); + + Dynamic = new PredefinedDynamicAttribute (module, "System.Runtime.CompilerServices", "DynamicAttribute"); + + DefaultMember = new PredefinedAttribute (module, "System.Reflection", "DefaultMemberAttribute"); + DecimalConstant = new PredefinedDecimalAttribute (module, "System.Runtime.CompilerServices", "DecimalConstantAttribute"); + StructLayout = new PredefinedAttribute (module, "System.Runtime.InteropServices", "StructLayoutAttribute"); + FieldOffset = new PredefinedAttribute (module, "System.Runtime.InteropServices", "FieldOffsetAttribute"); + AssemblyProduct = new PredefinedAttribute (module, "System.Reflection", "AssemblyProductAttribute"); + AssemblyCompany = new PredefinedAttribute (module, "System.Reflection", "AssemblyCompanyAttribute"); + AssemblyDescription = new PredefinedAttribute (module, "System.Reflection", "AssemblyDescriptionAttribute"); + AssemblyCopyright = new PredefinedAttribute (module, "System.Reflection", "AssemblyCopyrightAttribute"); + AssemblyTrademark = new PredefinedAttribute (module, "System.Reflection", "AssemblyTrademarkAttribute"); + + AsyncStateMachine = new PredefinedStateMachineAttribute (module, "System.Runtime.CompilerServices", "AsyncStateMachineAttribute"); + + CallerMemberNameAttribute = new PredefinedAttribute (module, "System.Runtime.CompilerServices", "CallerMemberNameAttribute"); + CallerLineNumberAttribute = new PredefinedAttribute (module, "System.Runtime.CompilerServices", "CallerLineNumberAttribute"); + CallerFilePathAttribute = new PredefinedAttribute (module, "System.Runtime.CompilerServices", "CallerFilePathAttribute"); + + // TODO: Should define only attributes which are used for comparison + const System.Reflection.BindingFlags all_fields = System.Reflection.BindingFlags.Public | + System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.DeclaredOnly; + + foreach (var fi in GetType ().GetFields (all_fields)) { + ((PredefinedAttribute) fi.GetValue (this)).Define (); + } + } + } + + public class PredefinedAttribute : PredefinedType + { + protected MethodSpec ctor; + + public PredefinedAttribute (ModuleContainer module, string ns, string name) + : base (module, MemberKind.Class, ns, name) + { + } + + #region Properties + + public MethodSpec Constructor { + get { + return ctor; + } + } + + #endregion + + public static bool operator == (TypeSpec type, PredefinedAttribute pa) + { + return type == pa.type && pa.type != null; + } + + public static bool operator != (TypeSpec type, PredefinedAttribute pa) + { + return type != pa.type; + } + + public override int GetHashCode () + { + return base.GetHashCode (); + } + + public override bool Equals (object obj) + { + throw new NotSupportedException (); + } + + public void EmitAttribute (ConstructorBuilder builder) + { + if (ResolveBuilder ()) + builder.SetCustomAttribute (GetCtorMetaInfo (), AttributeEncoder.Empty); + } + + public void EmitAttribute (MethodBuilder builder) + { + if (ResolveBuilder ()) + builder.SetCustomAttribute (GetCtorMetaInfo (), AttributeEncoder.Empty); + } + + public void EmitAttribute (PropertyBuilder builder) + { + if (ResolveBuilder ()) + builder.SetCustomAttribute (GetCtorMetaInfo (), AttributeEncoder.Empty); + } + + public void EmitAttribute (FieldBuilder builder) + { + if (ResolveBuilder ()) + builder.SetCustomAttribute (GetCtorMetaInfo (), AttributeEncoder.Empty); + } + + public void EmitAttribute (TypeBuilder builder) + { + if (ResolveBuilder ()) + builder.SetCustomAttribute (GetCtorMetaInfo (), AttributeEncoder.Empty); + } + + public void EmitAttribute (AssemblyBuilder builder) + { + if (ResolveBuilder ()) + builder.SetCustomAttribute (GetCtorMetaInfo (), AttributeEncoder.Empty); + } + + public void EmitAttribute (ModuleBuilder builder) + { + if (ResolveBuilder ()) + builder.SetCustomAttribute (GetCtorMetaInfo (), AttributeEncoder.Empty); + } + + public void EmitAttribute (ParameterBuilder builder) + { + if (ResolveBuilder ()) + builder.SetCustomAttribute (GetCtorMetaInfo (), AttributeEncoder.Empty); + } + + ConstructorInfo GetCtorMetaInfo () + { + return (ConstructorInfo) ctor.GetMetaInfo (); + } + + public bool ResolveBuilder () + { + if (ctor != null) + return true; + + // + // Handle all parameter-less attributes as optional + // + if (!Define ()) + return false; + + ctor = (MethodSpec) MemberCache.FindMember (type, MemberFilter.Constructor (ParametersCompiled.EmptyReadOnlyParameters), BindingRestriction.DeclaredOnly); + return ctor != null; + } + } + + public class PredefinedDebuggerBrowsableAttribute : PredefinedAttribute + { + public PredefinedDebuggerBrowsableAttribute (ModuleContainer module, string ns, string name) + : base (module, ns, name) + { + } + + public void EmitAttribute (FieldBuilder builder, System.Diagnostics.DebuggerBrowsableState state) + { + var ctor = module.PredefinedMembers.DebuggerBrowsableAttributeCtor.Get (); + if (ctor == null) + return; + + AttributeEncoder encoder = new AttributeEncoder (); + encoder.Encode ((int) state); + encoder.EncodeEmptyNamedArguments (); + + builder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), encoder.ToArray ()); + } + } + + public class PredefinedDebuggableAttribute : PredefinedAttribute + { + public PredefinedDebuggableAttribute (ModuleContainer module, string ns, string name) + : base (module, ns, name) + { + } + + public void EmitAttribute (AssemblyBuilder builder, System.Diagnostics.DebuggableAttribute.DebuggingModes modes) + { + var atype = module.PredefinedAttributes.Debuggable; + if (!atype.Define ()) + return; + + MethodSpec ctor = null; + foreach (MethodSpec m in MemberCache.FindMembers (atype.TypeSpec, CSharp.Constructor.ConstructorName, true)) { + if (m.Parameters.Count != 1) + continue; + + if (m.Parameters.Types[0].Kind == MemberKind.Enum) { + ctor = m; + } + } + + if (ctor == null) + return; + + AttributeEncoder encoder = new AttributeEncoder (); + encoder.Encode ((int) modes); + encoder.EncodeEmptyNamedArguments (); + + builder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), encoder.ToArray ()); + } + } + + public class PredefinedDecimalAttribute : PredefinedAttribute + { + public PredefinedDecimalAttribute (ModuleContainer module, string ns, string name) + : base (module, ns, name) + { + } + + public void EmitAttribute (ParameterBuilder builder, decimal value, Location loc) + { + var ctor = module.PredefinedMembers.DecimalConstantAttributeCtor.Resolve (loc); + if (ctor == null) + return; + + int[] bits = decimal.GetBits (value); + AttributeEncoder encoder = new AttributeEncoder (); + encoder.Encode ((byte) (bits[3] >> 16)); + encoder.Encode ((byte) (bits[3] >> 31)); + encoder.Encode ((uint) bits[2]); + encoder.Encode ((uint) bits[1]); + encoder.Encode ((uint) bits[0]); + encoder.EncodeEmptyNamedArguments (); + + builder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), encoder.ToArray ()); + } + + public void EmitAttribute (FieldBuilder builder, decimal value, Location loc) + { + var ctor = module.PredefinedMembers.DecimalConstantAttributeCtor.Resolve (loc); + if (ctor == null) + return; + + int[] bits = decimal.GetBits (value); + AttributeEncoder encoder = new AttributeEncoder (); + encoder.Encode ((byte) (bits[3] >> 16)); + encoder.Encode ((byte) (bits[3] >> 31)); + encoder.Encode ((uint) bits[2]); + encoder.Encode ((uint) bits[1]); + encoder.Encode ((uint) bits[0]); + encoder.EncodeEmptyNamedArguments (); + + builder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), encoder.ToArray ()); + } + } + + public class PredefinedStateMachineAttribute : PredefinedAttribute + { + public PredefinedStateMachineAttribute (ModuleContainer module, string ns, string name) + : base (module, ns, name) + { + } + + public void EmitAttribute (MethodBuilder builder, StateMachine type) + { + var predefined_ctor = module.PredefinedMembers.AsyncStateMachineAttributeCtor; + + var ctor = predefined_ctor.Get (); + + if (ctor == null) + return; + + AttributeEncoder encoder = new AttributeEncoder (); + encoder.EncodeTypeName (type); + encoder.EncodeEmptyNamedArguments (); + + builder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), encoder.ToArray ()); + } + } + + public class PredefinedDynamicAttribute : PredefinedAttribute + { + MethodSpec tctor; + + public PredefinedDynamicAttribute (ModuleContainer module, string ns, string name) + : base (module, ns, name) + { + } + + public void EmitAttribute (FieldBuilder builder, TypeSpec type, Location loc) + { + if (ResolveTransformationCtor (loc)) { + var cab = new CustomAttributeBuilder ((ConstructorInfo) tctor.GetMetaInfo (), new object[] { GetTransformationFlags (type) }); + builder.SetCustomAttribute (cab); + } + } + + public void EmitAttribute (ParameterBuilder builder, TypeSpec type, Location loc) + { + if (ResolveTransformationCtor (loc)) { + var cab = new CustomAttributeBuilder ((ConstructorInfo) tctor.GetMetaInfo (), new object[] { GetTransformationFlags (type) }); + builder.SetCustomAttribute (cab); + } + } + + public void EmitAttribute (PropertyBuilder builder, TypeSpec type, Location loc) + { + if (ResolveTransformationCtor (loc)) { + var cab = new CustomAttributeBuilder ((ConstructorInfo) tctor.GetMetaInfo (), new object[] { GetTransformationFlags (type) }); + builder.SetCustomAttribute (cab); + } + } + + public void EmitAttribute (TypeBuilder builder, TypeSpec type, Location loc) + { + if (ResolveTransformationCtor (loc)) { + var cab = new CustomAttributeBuilder ((ConstructorInfo) tctor.GetMetaInfo (), new object[] { GetTransformationFlags (type) }); + builder.SetCustomAttribute (cab); + } + } + + // + // When any element of the type is a dynamic type + // + // This method builds a transformation array for dynamic types + // used in places where DynamicAttribute cannot be applied to. + // It uses bool flag when type is of dynamic type and each + // section always starts with "false" for some reason. + // + // LAMESPEC: This should be part of C# specification + // + // Example: Func + // Transformation: { false, true, false, false, true } + // + static bool[] GetTransformationFlags (TypeSpec t) + { + bool[] element; + var ac = t as ArrayContainer; + if (ac != null) { + element = GetTransformationFlags (ac.Element); + if (element == null) + return new bool[] { false, false }; + + bool[] res = new bool[element.Length + 1]; + res[0] = false; + Array.Copy (element, 0, res, 1, element.Length); + return res; + } + + if (t == null) + return null; + + if (t.IsGeneric) { + List transform = null; + var targs = t.TypeArguments; + for (int i = 0; i < targs.Length; ++i) { + element = GetTransformationFlags (targs[i]); + if (element != null) { + if (transform == null) { + transform = new List (); + for (int ii = 0; ii <= i; ++ii) + transform.Add (false); + } + + transform.AddRange (element); + } else if (transform != null) { + transform.Add (false); + } + } + + if (transform != null) + return transform.ToArray (); + } + + if (t.BuiltinType == BuiltinTypeSpec.Type.Dynamic) + return new bool[] { true }; + + return null; + } + + bool ResolveTransformationCtor (Location loc) + { + if (tctor != null) + return true; + + tctor = module.PredefinedMembers.DynamicAttributeCtor.Resolve (loc); + return tctor != null; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/cfold.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/cfold.cs new file mode 100644 index 000000000..da85bf386 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/cfold.cs @@ -0,0 +1,1192 @@ +// +// cfold.cs: Constant Folding +// +// Author: +// Miguel de Icaza (miguel@ximian.com) +// Marek Safar (marek.safar@seznam.cz) +// +// Copyright 2002, 2003 Ximian, Inc. +// Copyright 2003-2011, Novell, Inc. +// +using System; + +namespace Mono.CSharp { + + public static class ConstantFold + { + public static TypeSpec[] CreateBinaryPromotionsTypes (BuiltinTypes types) + { + return new TypeSpec[] { + types.Decimal, types.Double, types.Float, + types.ULong, types.Long, types.UInt + }; + } + + // + // Performs the numeric promotions on the left and right expresions + // and deposits the results on `lc' and `rc'. + // + // On success, the types of `lc' and `rc' on output will always match, + // and the pair will be one of: + // + // TODO: BinaryFold should be called as an optimization step only, + // error checking here is weak + // + static bool DoBinaryNumericPromotions (ResolveContext rc, ref Constant left, ref Constant right) + { + TypeSpec ltype = left.Type; + TypeSpec rtype = right.Type; + + foreach (TypeSpec t in rc.BuiltinTypes.BinaryPromotionsTypes) { + if (t == ltype) + return t == rtype || ConvertPromotion (rc, ref right, ref left, t); + + if (t == rtype) + return t == ltype || ConvertPromotion (rc, ref left, ref right, t); + } + + left = left.ConvertImplicitly (rc.BuiltinTypes.Int); + right = right.ConvertImplicitly (rc.BuiltinTypes.Int); + return left != null && right != null; + } + + static bool ConvertPromotion (ResolveContext rc, ref Constant prim, ref Constant second, TypeSpec type) + { + Constant c = prim.ConvertImplicitly (type); + if (c != null) { + prim = c; + return true; + } + + if (type.BuiltinType == BuiltinTypeSpec.Type.UInt) { + type = rc.BuiltinTypes.Long; + prim = prim.ConvertImplicitly (type); + second = second.ConvertImplicitly (type); + return prim != null && second != null; + } + + return false; + } + + internal static void Error_CompileTimeOverflow (ResolveContext rc, Location loc) + { + rc.Report.Error (220, loc, "The operation overflows at compile time in checked mode"); + } + + /// + /// Constant expression folder for binary operations. + /// + /// Returns null if the expression can not be folded. + /// + static public Constant BinaryFold (ResolveContext ec, Binary.Operator oper, + Constant left, Constant right, Location loc) + { + Constant result = null; + + if (left is EmptyConstantCast) + return BinaryFold (ec, oper, ((EmptyConstantCast)left).child, right, loc); + + if (left is SideEffectConstant) { + result = BinaryFold (ec, oper, ((SideEffectConstant) left).value, right, loc); + if (result == null) + return null; + return new SideEffectConstant (result, left, loc); + } + + if (right is EmptyConstantCast) + return BinaryFold (ec, oper, left, ((EmptyConstantCast)right).child, loc); + + if (right is SideEffectConstant) { + result = BinaryFold (ec, oper, left, ((SideEffectConstant) right).value, loc); + if (result == null) + return null; + return new SideEffectConstant (result, right, loc); + } + + TypeSpec lt = left.Type; + TypeSpec rt = right.Type; + bool bool_res; + + if (lt.BuiltinType == BuiltinTypeSpec.Type.Bool && lt == rt) { + bool lv = (bool) left.GetValue (); + bool rv = (bool) right.GetValue (); + switch (oper) { + case Binary.Operator.BitwiseAnd: + case Binary.Operator.LogicalAnd: + return new BoolConstant (ec.BuiltinTypes, lv && rv, left.Location); + case Binary.Operator.BitwiseOr: + case Binary.Operator.LogicalOr: + return new BoolConstant (ec.BuiltinTypes, lv || rv, left.Location); + case Binary.Operator.ExclusiveOr: + return new BoolConstant (ec.BuiltinTypes, lv ^ rv, left.Location); + case Binary.Operator.Equality: + return new BoolConstant (ec.BuiltinTypes, lv == rv, left.Location); + case Binary.Operator.Inequality: + return new BoolConstant (ec.BuiltinTypes, lv != rv, left.Location); + } + return null; + } + + // + // During an enum evaluation, none of the rules are valid + // Not sure whether it is bug in csc or in documentation + // + if (ec.HasSet (ResolveContext.Options.EnumScope)){ + if (left is EnumConstant) + left = ((EnumConstant) left).Child; + + if (right is EnumConstant) + right = ((EnumConstant) right).Child; + } else if (left is EnumConstant && rt == lt) { + switch (oper){ + /// + /// E operator |(E x, E y); + /// E operator &(E x, E y); + /// E operator ^(E x, E y); + /// + case Binary.Operator.BitwiseOr: + case Binary.Operator.BitwiseAnd: + case Binary.Operator.ExclusiveOr: + result = BinaryFold (ec, oper, ((EnumConstant)left).Child, ((EnumConstant)right).Child, loc); + if (result != null) + result = result.Reduce (ec, lt); + return result; + + /// + /// U operator -(E x, E y); + /// + case Binary.Operator.Subtraction: + result = BinaryFold (ec, oper, ((EnumConstant)left).Child, ((EnumConstant)right).Child, loc); + if (result != null) + result = result.Reduce (ec, EnumSpec.GetUnderlyingType (lt)); + return result; + + /// + /// bool operator ==(E x, E y); + /// bool operator !=(E x, E y); + /// bool operator <(E x, E y); + /// bool operator >(E x, E y); + /// bool operator <=(E x, E y); + /// bool operator >=(E x, E y); + /// + case Binary.Operator.Equality: + case Binary.Operator.Inequality: + case Binary.Operator.LessThan: + case Binary.Operator.GreaterThan: + case Binary.Operator.LessThanOrEqual: + case Binary.Operator.GreaterThanOrEqual: + return BinaryFold(ec, oper, ((EnumConstant)left).Child, ((EnumConstant)right).Child, loc); + } + return null; + } + + switch (oper){ + case Binary.Operator.BitwiseOr: + // + // bool? operator |(bool? x, bool? y); + // + if ((lt.BuiltinType == BuiltinTypeSpec.Type.Bool && right is NullLiteral) || + (rt.BuiltinType == BuiltinTypeSpec.Type.Bool && left is NullLiteral)) { + var b = new Binary (oper, left, right).ResolveOperator (ec); + + // false | null => null + // null | false => null + if ((right is NullLiteral && left.IsDefaultValue) || (left is NullLiteral && right.IsDefaultValue)) + return Nullable.LiftedNull.CreateFromExpression (ec, b); + + // true | null => true + // null | true => true + return ReducedExpression.Create (new BoolConstant (ec.BuiltinTypes, true, loc), b); + } + + if (!DoBinaryNumericPromotions (ec, ref left, ref right)) + return null; + + if (left is IntConstant){ + int res = ((IntConstant) left).Value | ((IntConstant) right).Value; + + return new IntConstant (ec.BuiltinTypes, res, left.Location); + } + if (left is UIntConstant){ + uint res = ((UIntConstant)left).Value | ((UIntConstant)right).Value; + + return new UIntConstant (ec.BuiltinTypes, res, left.Location); + } + if (left is LongConstant){ + long res = ((LongConstant)left).Value | ((LongConstant)right).Value; + + return new LongConstant (ec.BuiltinTypes, res, left.Location); + } + if (left is ULongConstant){ + ulong res = ((ULongConstant)left).Value | + ((ULongConstant)right).Value; + + return new ULongConstant (ec.BuiltinTypes, res, left.Location); + } + break; + + case Binary.Operator.BitwiseAnd: + // + // bool? operator &(bool? x, bool? y); + // + if ((lt.BuiltinType == BuiltinTypeSpec.Type.Bool && right is NullLiteral) || + (rt.BuiltinType == BuiltinTypeSpec.Type.Bool && left is NullLiteral)) { + var b = new Binary (oper, left, right).ResolveOperator (ec); + + // false & null => false + // null & false => false + if ((right is NullLiteral && left.IsDefaultValue) || (left is NullLiteral && right.IsDefaultValue)) + return ReducedExpression.Create (new BoolConstant (ec.BuiltinTypes, false, loc), b); + + // true & null => null + // null & true => null + return Nullable.LiftedNull.CreateFromExpression (ec, b); + } + + if (!DoBinaryNumericPromotions (ec, ref left, ref right)) + return null; + + /// + /// int operator &(int x, int y); + /// uint operator &(uint x, uint y); + /// long operator &(long x, long y); + /// ulong operator &(ulong x, ulong y); + /// + if (left is IntConstant){ + int res = ((IntConstant) left).Value & ((IntConstant) right).Value; + return new IntConstant (ec.BuiltinTypes, res, left.Location); + } + if (left is UIntConstant){ + uint res = ((UIntConstant)left).Value & ((UIntConstant)right).Value; + return new UIntConstant (ec.BuiltinTypes, res, left.Location); + } + if (left is LongConstant){ + long res = ((LongConstant)left).Value & ((LongConstant)right).Value; + return new LongConstant (ec.BuiltinTypes, res, left.Location); + } + if (left is ULongConstant){ + ulong res = ((ULongConstant)left).Value & + ((ULongConstant)right).Value; + + return new ULongConstant (ec.BuiltinTypes, res, left.Location); + } + break; + + case Binary.Operator.ExclusiveOr: + if (!DoBinaryNumericPromotions (ec, ref left, ref right)) + return null; + + if (left is IntConstant){ + int res = ((IntConstant) left).Value ^ ((IntConstant) right).Value; + return new IntConstant (ec.BuiltinTypes, res, left.Location); + } + if (left is UIntConstant){ + uint res = ((UIntConstant)left).Value ^ ((UIntConstant)right).Value; + + return new UIntConstant (ec.BuiltinTypes, res, left.Location); + } + if (left is LongConstant){ + long res = ((LongConstant)left).Value ^ ((LongConstant)right).Value; + + return new LongConstant (ec.BuiltinTypes, res, left.Location); + } + if (left is ULongConstant){ + ulong res = ((ULongConstant)left).Value ^ + ((ULongConstant)right).Value; + + return new ULongConstant (ec.BuiltinTypes, res, left.Location); + } + break; + + case Binary.Operator.Addition: + // + // If both sides are strings, then concatenate + // + // string operator + (string x, string y) + // + if (lt.BuiltinType == BuiltinTypeSpec.Type.String || rt.BuiltinType == BuiltinTypeSpec.Type.String){ + if (lt == rt) + return new StringConstant (ec.BuiltinTypes, (string)left.GetValue () + (string)right.GetValue (), + left.Location); + + if (lt == InternalType.NullLiteral || left.IsNull) + return new StringConstant (ec.BuiltinTypes, "" + right.GetValue (), left.Location); + + if (rt == InternalType.NullLiteral || right.IsNull) + return new StringConstant (ec.BuiltinTypes, left.GetValue () + "", left.Location); + + return null; + } + + // + // string operator + (string x, object y) + // + if (lt == InternalType.NullLiteral) { + if (rt.BuiltinType == BuiltinTypeSpec.Type.Object) + return new StringConstant (ec.BuiltinTypes, "" + right.GetValue (), left.Location); + + if (lt == rt) { + ec.Report.Error (34, loc, "Operator `{0}' is ambiguous on operands of type `{1}' and `{2}'", + "+", lt.GetSignatureForError (), rt.GetSignatureForError ()); + return null; + } + + return right; + } + + // + // string operator + (object x, string y) + // + if (rt == InternalType.NullLiteral) { + if (lt.BuiltinType == BuiltinTypeSpec.Type.Object) + return new StringConstant (ec.BuiltinTypes, right.GetValue () + "", left.Location); + + return left; + } + + // + // handle "E operator + (E x, U y)" + // handle "E operator + (Y y, E x)" + // + EnumConstant lc = left as EnumConstant; + EnumConstant rc = right as EnumConstant; + if (lc != null || rc != null){ + if (lc == null) { + lc = rc; + lt = lc.Type; + right = left; + } + + // U has to be implicitly convetible to E.base + right = right.ConvertImplicitly (lc.Child.Type); + if (right == null) + return null; + + result = BinaryFold (ec, oper, lc.Child, right, loc); + if (result == null) + return null; + + result = result.Reduce (ec, lt); + if (result == null || lt.IsEnum) + return result; + + return new EnumConstant (result, lt); + } + + if (!DoBinaryNumericPromotions (ec, ref left, ref right)) + return null; + + try { + if (left is DoubleConstant){ + double res; + + if (ec.ConstantCheckState) + res = checked (((DoubleConstant) left).Value + + ((DoubleConstant) right).Value); + else + res = unchecked (((DoubleConstant) left).Value + + ((DoubleConstant) right).Value); + + return new DoubleConstant (ec.BuiltinTypes, res, left.Location); + } + if (left is FloatConstant){ + double a, b, res; + a = ((FloatConstant) left).DoubleValue; + b = ((FloatConstant) right).DoubleValue; + + if (ec.ConstantCheckState) + res = checked (a + b); + else + res = unchecked (a + b); + + result = new FloatConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is ULongConstant){ + ulong res; + + if (ec.ConstantCheckState) + res = checked (((ULongConstant) left).Value + + ((ULongConstant) right).Value); + else + res = unchecked (((ULongConstant) left).Value + + ((ULongConstant) right).Value); + + result = new ULongConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is LongConstant){ + long res; + + if (ec.ConstantCheckState) + res = checked (((LongConstant) left).Value + + ((LongConstant) right).Value); + else + res = unchecked (((LongConstant) left).Value + + ((LongConstant) right).Value); + + result = new LongConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is UIntConstant){ + uint res; + + if (ec.ConstantCheckState) + res = checked (((UIntConstant) left).Value + + ((UIntConstant) right).Value); + else + res = unchecked (((UIntConstant) left).Value + + ((UIntConstant) right).Value); + + result = new UIntConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is IntConstant){ + int res; + + if (ec.ConstantCheckState) + res = checked (((IntConstant) left).Value + + ((IntConstant) right).Value); + else + res = unchecked (((IntConstant) left).Value + + ((IntConstant) right).Value); + + result = new IntConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is DecimalConstant) { + decimal res; + + if (ec.ConstantCheckState) + res = checked (((DecimalConstant) left).Value + + ((DecimalConstant) right).Value); + else + res = unchecked (((DecimalConstant) left).Value + + ((DecimalConstant) right).Value); + + result = new DecimalConstant (ec.BuiltinTypes, res, left.Location); + } + } catch (OverflowException){ + Error_CompileTimeOverflow (ec, loc); + } + + return result; + + case Binary.Operator.Subtraction: + // + // handle "E operator - (E x, U y)" + // handle "E operator - (Y y, E x)" + // + lc = left as EnumConstant; + rc = right as EnumConstant; + if (lc != null || rc != null){ + if (lc == null) { + lc = rc; + lt = lc.Type; + right = left; + } + + // U has to be implicitly convetible to E.base + right = right.ConvertImplicitly (lc.Child.Type); + if (right == null) + return null; + + result = BinaryFold (ec, oper, lc.Child, right, loc); + if (result == null) + return null; + + result = result.Reduce (ec, lt); + if (result == null) + return null; + + return new EnumConstant (result, lt); + } + + if (left is NullLiteral && right is NullLiteral) { + var lifted_int = new Nullable.NullableType (ec.BuiltinTypes.Int, loc); + lifted_int.ResolveAsType (ec); + return (Constant) new Binary (oper, lifted_int, right).ResolveOperator (ec); + } + + if (!DoBinaryNumericPromotions (ec, ref left, ref right)) + return null; + + try { + if (left is DoubleConstant){ + double res; + + if (ec.ConstantCheckState) + res = checked (((DoubleConstant) left).Value - + ((DoubleConstant) right).Value); + else + res = unchecked (((DoubleConstant) left).Value - + ((DoubleConstant) right).Value); + + result = new DoubleConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is FloatConstant){ + double a, b, res; + a = ((FloatConstant) left).DoubleValue; + b = ((FloatConstant) right).DoubleValue; + + if (ec.ConstantCheckState) + res = checked (a - b); + else + res = unchecked (a - b); + + result = new FloatConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is ULongConstant){ + ulong res; + + if (ec.ConstantCheckState) + res = checked (((ULongConstant) left).Value - + ((ULongConstant) right).Value); + else + res = unchecked (((ULongConstant) left).Value - + ((ULongConstant) right).Value); + + result = new ULongConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is LongConstant){ + long res; + + if (ec.ConstantCheckState) + res = checked (((LongConstant) left).Value - + ((LongConstant) right).Value); + else + res = unchecked (((LongConstant) left).Value - + ((LongConstant) right).Value); + + result = new LongConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is UIntConstant){ + uint res; + + if (ec.ConstantCheckState) + res = checked (((UIntConstant) left).Value - + ((UIntConstant) right).Value); + else + res = unchecked (((UIntConstant) left).Value - + ((UIntConstant) right).Value); + + result = new UIntConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is IntConstant){ + int res; + + if (ec.ConstantCheckState) + res = checked (((IntConstant) left).Value - + ((IntConstant) right).Value); + else + res = unchecked (((IntConstant) left).Value - + ((IntConstant) right).Value); + + result = new IntConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is DecimalConstant) { + decimal res; + + if (ec.ConstantCheckState) + res = checked (((DecimalConstant) left).Value - + ((DecimalConstant) right).Value); + else + res = unchecked (((DecimalConstant) left).Value - + ((DecimalConstant) right).Value); + + return new DecimalConstant (ec.BuiltinTypes, res, left.Location); + } else { + throw new Exception ( "Unexepected subtraction input: " + left); + } + } catch (OverflowException){ + Error_CompileTimeOverflow (ec, loc); + } + + return result; + + case Binary.Operator.Multiply: + if (left is NullLiteral && right is NullLiteral) { + var lifted_int = new Nullable.NullableType (ec.BuiltinTypes.Int, loc); + lifted_int.ResolveAsType (ec); + return (Constant) new Binary (oper, lifted_int, right).ResolveOperator (ec); + } + + if (!DoBinaryNumericPromotions (ec, ref left, ref right)) + return null; + + try { + if (left is DoubleConstant){ + double res; + + if (ec.ConstantCheckState) + res = checked (((DoubleConstant) left).Value * + ((DoubleConstant) right).Value); + else + res = unchecked (((DoubleConstant) left).Value * + ((DoubleConstant) right).Value); + + return new DoubleConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is FloatConstant){ + double a, b, res; + a = ((FloatConstant) left).DoubleValue; + b = ((FloatConstant) right).DoubleValue; + + if (ec.ConstantCheckState) + res = checked (a * b); + else + res = unchecked (a * b); + + return new FloatConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is ULongConstant){ + ulong res; + + if (ec.ConstantCheckState) + res = checked (((ULongConstant) left).Value * + ((ULongConstant) right).Value); + else + res = unchecked (((ULongConstant) left).Value * + ((ULongConstant) right).Value); + + return new ULongConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is LongConstant){ + long res; + + if (ec.ConstantCheckState) + res = checked (((LongConstant) left).Value * + ((LongConstant) right).Value); + else + res = unchecked (((LongConstant) left).Value * + ((LongConstant) right).Value); + + return new LongConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is UIntConstant){ + uint res; + + if (ec.ConstantCheckState) + res = checked (((UIntConstant) left).Value * + ((UIntConstant) right).Value); + else + res = unchecked (((UIntConstant) left).Value * + ((UIntConstant) right).Value); + + return new UIntConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is IntConstant){ + int res; + + if (ec.ConstantCheckState) + res = checked (((IntConstant) left).Value * + ((IntConstant) right).Value); + else + res = unchecked (((IntConstant) left).Value * + ((IntConstant) right).Value); + + return new IntConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is DecimalConstant) { + decimal res; + + if (ec.ConstantCheckState) + res = checked (((DecimalConstant) left).Value * + ((DecimalConstant) right).Value); + else + res = unchecked (((DecimalConstant) left).Value * + ((DecimalConstant) right).Value); + + return new DecimalConstant (ec.BuiltinTypes, res, left.Location); + } else { + throw new Exception ( "Unexepected multiply input: " + left); + } + } catch (OverflowException){ + Error_CompileTimeOverflow (ec, loc); + } + break; + + case Binary.Operator.Division: + if (left is NullLiteral && right is NullLiteral) { + var lifted_int = new Nullable.NullableType (ec.BuiltinTypes.Int, loc); + lifted_int.ResolveAsType (ec); + return (Constant) new Binary (oper, lifted_int, right).ResolveOperator (ec); + } + + if (!DoBinaryNumericPromotions (ec, ref left, ref right)) + return null; + + try { + if (left is DoubleConstant){ + double res; + + if (ec.ConstantCheckState) + res = checked (((DoubleConstant) left).Value / + ((DoubleConstant) right).Value); + else + res = unchecked (((DoubleConstant) left).Value / + ((DoubleConstant) right).Value); + + return new DoubleConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is FloatConstant){ + double a, b, res; + a = ((FloatConstant) left).DoubleValue; + b = ((FloatConstant) right).DoubleValue; + + if (ec.ConstantCheckState) + res = checked (a / b); + else + res = unchecked (a / b); + + return new FloatConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is ULongConstant){ + ulong res; + + if (ec.ConstantCheckState) + res = checked (((ULongConstant) left).Value / + ((ULongConstant) right).Value); + else + res = unchecked (((ULongConstant) left).Value / + ((ULongConstant) right).Value); + + return new ULongConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is LongConstant){ + long res; + + if (ec.ConstantCheckState) + res = checked (((LongConstant) left).Value / + ((LongConstant) right).Value); + else + res = unchecked (((LongConstant) left).Value / + ((LongConstant) right).Value); + + return new LongConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is UIntConstant){ + uint res; + + if (ec.ConstantCheckState) + res = checked (((UIntConstant) left).Value / + ((UIntConstant) right).Value); + else + res = unchecked (((UIntConstant) left).Value / + ((UIntConstant) right).Value); + + return new UIntConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is IntConstant){ + int res; + + if (ec.ConstantCheckState) + res = checked (((IntConstant) left).Value / + ((IntConstant) right).Value); + else + res = unchecked (((IntConstant) left).Value / + ((IntConstant) right).Value); + + return new IntConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is DecimalConstant) { + decimal res; + + if (ec.ConstantCheckState) + res = checked (((DecimalConstant) left).Value / + ((DecimalConstant) right).Value); + else + res = unchecked (((DecimalConstant) left).Value / + ((DecimalConstant) right).Value); + + return new DecimalConstant (ec.BuiltinTypes, res, left.Location); + } else { + throw new Exception ( "Unexepected division input: " + left); + } + } catch (OverflowException){ + Error_CompileTimeOverflow (ec, loc); + + } catch (DivideByZeroException) { + ec.Report.Error (20, loc, "Division by constant zero"); + } + + break; + + case Binary.Operator.Modulus: + if (left is NullLiteral && right is NullLiteral) { + var lifted_int = new Nullable.NullableType (ec.BuiltinTypes.Int, loc); + lifted_int.ResolveAsType (ec); + return (Constant) new Binary (oper, lifted_int, right).ResolveOperator (ec); + } + + if (!DoBinaryNumericPromotions (ec, ref left, ref right)) + return null; + + try { + if (left is DoubleConstant){ + double res; + + if (ec.ConstantCheckState) + res = checked (((DoubleConstant) left).Value % + ((DoubleConstant) right).Value); + else + res = unchecked (((DoubleConstant) left).Value % + ((DoubleConstant) right).Value); + + return new DoubleConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is FloatConstant){ + double a, b, res; + a = ((FloatConstant) left).DoubleValue; + b = ((FloatConstant) right).DoubleValue; + + if (ec.ConstantCheckState) + res = checked (a % b); + else + res = unchecked (a % b); + + return new FloatConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is ULongConstant){ + ulong res; + + if (ec.ConstantCheckState) + res = checked (((ULongConstant) left).Value % + ((ULongConstant) right).Value); + else + res = unchecked (((ULongConstant) left).Value % + ((ULongConstant) right).Value); + + return new ULongConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is LongConstant){ + long res; + + if (ec.ConstantCheckState) + res = checked (((LongConstant) left).Value % + ((LongConstant) right).Value); + else + res = unchecked (((LongConstant) left).Value % + ((LongConstant) right).Value); + + return new LongConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is UIntConstant){ + uint res; + + if (ec.ConstantCheckState) + res = checked (((UIntConstant) left).Value % + ((UIntConstant) right).Value); + else + res = unchecked (((UIntConstant) left).Value % + ((UIntConstant) right).Value); + + return new UIntConstant (ec.BuiltinTypes, res, left.Location); + } else if (left is IntConstant){ + int res; + + if (ec.ConstantCheckState) + res = checked (((IntConstant) left).Value % + ((IntConstant) right).Value); + else + res = unchecked (((IntConstant) left).Value % + ((IntConstant) right).Value); + + return new IntConstant (ec.BuiltinTypes, res, left.Location); + } + + if (left is DecimalConstant) { + decimal res; + + if (ec.ConstantCheckState) + res = checked (((DecimalConstant) left).Value % + ((DecimalConstant) right).Value); + else + res = unchecked (((DecimalConstant) left).Value % + ((DecimalConstant) right).Value); + + return new DecimalConstant (ec.BuiltinTypes, res, left.Location); + } + + throw new Exception ( "Unexepected modulus input: " + left); + } catch (DivideByZeroException){ + ec.Report.Error (20, loc, "Division by constant zero"); + } catch (OverflowException){ + Error_CompileTimeOverflow (ec, loc); + } + break; + + // + // There is no overflow checking on left shift + // + case Binary.Operator.LeftShift: + if (left is NullLiteral && right is NullLiteral) { + var lifted_int = new Nullable.NullableType (ec.BuiltinTypes.Int, loc); + lifted_int.ResolveAsType (ec); + return (Constant) new Binary (oper, lifted_int, right).ResolveOperator (ec); + } + + IntConstant ic = right.ConvertImplicitly (ec.BuiltinTypes.Int) as IntConstant; + if (ic == null){ + return null; + } + + int lshift_val = ic.Value; + switch (left.Type.BuiltinType) { + case BuiltinTypeSpec.Type.ULong: + return new ULongConstant (ec.BuiltinTypes, ((ULongConstant) left).Value << lshift_val, left.Location); + case BuiltinTypeSpec.Type.Long: + return new LongConstant (ec.BuiltinTypes, ((LongConstant) left).Value << lshift_val, left.Location); + case BuiltinTypeSpec.Type.UInt: + return new UIntConstant (ec.BuiltinTypes, ((UIntConstant) left).Value << lshift_val, left.Location); + } + + // null << value => null + if (left is NullLiteral) + return (Constant) new Binary (oper, left, right).ResolveOperator (ec); + + left = left.ConvertImplicitly (ec.BuiltinTypes.Int); + if (left.Type.BuiltinType == BuiltinTypeSpec.Type.Int) + return new IntConstant (ec.BuiltinTypes, ((IntConstant) left).Value << lshift_val, left.Location); + + return null; + + // + // There is no overflow checking on right shift + // + case Binary.Operator.RightShift: + if (left is NullLiteral && right is NullLiteral) { + var lifted_int = new Nullable.NullableType (ec.BuiltinTypes.Int, loc); + lifted_int.ResolveAsType (ec); + return (Constant) new Binary (oper, lifted_int, right).ResolveOperator (ec); + } + + IntConstant sic = right.ConvertImplicitly (ec.BuiltinTypes.Int) as IntConstant; + if (sic == null){ + return null; + } + int rshift_val = sic.Value; + switch (left.Type.BuiltinType) { + case BuiltinTypeSpec.Type.ULong: + return new ULongConstant (ec.BuiltinTypes, ((ULongConstant) left).Value >> rshift_val, left.Location); + case BuiltinTypeSpec.Type.Long: + return new LongConstant (ec.BuiltinTypes, ((LongConstant) left).Value >> rshift_val, left.Location); + case BuiltinTypeSpec.Type.UInt: + return new UIntConstant (ec.BuiltinTypes, ((UIntConstant) left).Value >> rshift_val, left.Location); + } + + // null >> value => null + if (left is NullLiteral) + return (Constant) new Binary (oper, left, right).ResolveOperator (ec); + + left = left.ConvertImplicitly (ec.BuiltinTypes.Int); + if (left.Type.BuiltinType == BuiltinTypeSpec.Type.Int) + return new IntConstant (ec.BuiltinTypes, ((IntConstant) left).Value >> rshift_val, left.Location); + + return null; + + case Binary.Operator.Equality: + if (TypeSpec.IsReferenceType (lt) && TypeSpec.IsReferenceType (rt) || + (left is Nullable.LiftedNull && right.IsNull) || + (right is Nullable.LiftedNull && left.IsNull)) { + if (left.IsNull || right.IsNull) { + return ReducedExpression.Create ( + new BoolConstant (ec.BuiltinTypes, left.IsNull == right.IsNull, left.Location), + new Binary (oper, left, right)); + } + + if (left is StringConstant && right is StringConstant) + return new BoolConstant (ec.BuiltinTypes, + ((StringConstant) left).Value == ((StringConstant) right).Value, left.Location); + + return null; + } + + if (!DoBinaryNumericPromotions (ec, ref left, ref right)) + return null; + + bool_res = false; + if (left is DoubleConstant) + bool_res = ((DoubleConstant) left).Value == + ((DoubleConstant) right).Value; + else if (left is FloatConstant) + bool_res = ((FloatConstant) left).DoubleValue == + ((FloatConstant) right).DoubleValue; + else if (left is ULongConstant) + bool_res = ((ULongConstant) left).Value == + ((ULongConstant) right).Value; + else if (left is LongConstant) + bool_res = ((LongConstant) left).Value == + ((LongConstant) right).Value; + else if (left is UIntConstant) + bool_res = ((UIntConstant) left).Value == + ((UIntConstant) right).Value; + else if (left is IntConstant) + bool_res = ((IntConstant) left).Value == + ((IntConstant) right).Value; + else + return null; + + return new BoolConstant (ec.BuiltinTypes, bool_res, left.Location); + + case Binary.Operator.Inequality: + if (TypeSpec.IsReferenceType (lt) && TypeSpec.IsReferenceType (rt) || + (left is Nullable.LiftedNull && right.IsNull) || + (right is Nullable.LiftedNull && left.IsNull)) { + if (left.IsNull || right.IsNull) { + return ReducedExpression.Create ( + new BoolConstant (ec.BuiltinTypes, left.IsNull != right.IsNull, left.Location), + new Binary (oper, left, right)); + } + + if (left is StringConstant && right is StringConstant) + return new BoolConstant (ec.BuiltinTypes, + ((StringConstant) left).Value != ((StringConstant) right).Value, left.Location); + + return null; + } + + if (!DoBinaryNumericPromotions (ec, ref left, ref right)) + return null; + + bool_res = false; + if (left is DoubleConstant) + bool_res = ((DoubleConstant) left).Value != + ((DoubleConstant) right).Value; + else if (left is FloatConstant) + bool_res = ((FloatConstant) left).DoubleValue != + ((FloatConstant) right).DoubleValue; + else if (left is ULongConstant) + bool_res = ((ULongConstant) left).Value != + ((ULongConstant) right).Value; + else if (left is LongConstant) + bool_res = ((LongConstant) left).Value != + ((LongConstant) right).Value; + else if (left is UIntConstant) + bool_res = ((UIntConstant) left).Value != + ((UIntConstant) right).Value; + else if (left is IntConstant) + bool_res = ((IntConstant) left).Value != + ((IntConstant) right).Value; + else + return null; + + return new BoolConstant (ec.BuiltinTypes, bool_res, left.Location); + + case Binary.Operator.LessThan: + if (right is NullLiteral) { + if (left is NullLiteral) { + var lifted_int = new Nullable.NullableType (ec.BuiltinTypes.Int, loc); + lifted_int.ResolveAsType (ec); + return (Constant) new Binary (oper, lifted_int, right).ResolveOperator (ec); + } + } + + if (!DoBinaryNumericPromotions (ec, ref left, ref right)) + return null; + + bool_res = false; + if (left is DoubleConstant) + bool_res = ((DoubleConstant) left).Value < + ((DoubleConstant) right).Value; + else if (left is FloatConstant) + bool_res = ((FloatConstant) left).DoubleValue < + ((FloatConstant) right).DoubleValue; + else if (left is ULongConstant) + bool_res = ((ULongConstant) left).Value < + ((ULongConstant) right).Value; + else if (left is LongConstant) + bool_res = ((LongConstant) left).Value < + ((LongConstant) right).Value; + else if (left is UIntConstant) + bool_res = ((UIntConstant) left).Value < + ((UIntConstant) right).Value; + else if (left is IntConstant) + bool_res = ((IntConstant) left).Value < + ((IntConstant) right).Value; + else + return null; + + return new BoolConstant (ec.BuiltinTypes, bool_res, left.Location); + + case Binary.Operator.GreaterThan: + if (right is NullLiteral) { + if (left is NullLiteral) { + var lifted_int = new Nullable.NullableType (ec.BuiltinTypes.Int, loc); + lifted_int.ResolveAsType (ec); + return (Constant) new Binary (oper, lifted_int, right).ResolveOperator (ec); + } + } + + if (!DoBinaryNumericPromotions (ec, ref left, ref right)) + return null; + + bool_res = false; + if (left is DoubleConstant) + bool_res = ((DoubleConstant) left).Value > + ((DoubleConstant) right).Value; + else if (left is FloatConstant) + bool_res = ((FloatConstant) left).DoubleValue > + ((FloatConstant) right).DoubleValue; + else if (left is ULongConstant) + bool_res = ((ULongConstant) left).Value > + ((ULongConstant) right).Value; + else if (left is LongConstant) + bool_res = ((LongConstant) left).Value > + ((LongConstant) right).Value; + else if (left is UIntConstant) + bool_res = ((UIntConstant) left).Value > + ((UIntConstant) right).Value; + else if (left is IntConstant) + bool_res = ((IntConstant) left).Value > + ((IntConstant) right).Value; + else + return null; + + return new BoolConstant (ec.BuiltinTypes, bool_res, left.Location); + + case Binary.Operator.GreaterThanOrEqual: + if (right is NullLiteral) { + if (left is NullLiteral) { + var lifted_int = new Nullable.NullableType (ec.BuiltinTypes.Int, loc); + lifted_int.ResolveAsType (ec); + return (Constant) new Binary (oper, lifted_int, right).ResolveOperator (ec); + } + } + + if (!DoBinaryNumericPromotions (ec, ref left, ref right)) + return null; + + bool_res = false; + if (left is DoubleConstant) + bool_res = ((DoubleConstant) left).Value >= + ((DoubleConstant) right).Value; + else if (left is FloatConstant) + bool_res = ((FloatConstant) left).DoubleValue >= + ((FloatConstant) right).DoubleValue; + else if (left is ULongConstant) + bool_res = ((ULongConstant) left).Value >= + ((ULongConstant) right).Value; + else if (left is LongConstant) + bool_res = ((LongConstant) left).Value >= + ((LongConstant) right).Value; + else if (left is UIntConstant) + bool_res = ((UIntConstant) left).Value >= + ((UIntConstant) right).Value; + else if (left is IntConstant) + bool_res = ((IntConstant) left).Value >= + ((IntConstant) right).Value; + else + return null; + + return new BoolConstant (ec.BuiltinTypes, bool_res, left.Location); + + case Binary.Operator.LessThanOrEqual: + if (right is NullLiteral) { + if (left is NullLiteral) { + var lifted_int = new Nullable.NullableType (ec.BuiltinTypes.Int, loc); + lifted_int.ResolveAsType (ec); + return (Constant) new Binary (oper, lifted_int, right).ResolveOperator (ec); + } + } + + if (!DoBinaryNumericPromotions (ec, ref left, ref right)) + return null; + + bool_res = false; + if (left is DoubleConstant) + bool_res = ((DoubleConstant) left).Value <= + ((DoubleConstant) right).Value; + else if (left is FloatConstant) + bool_res = ((FloatConstant) left).DoubleValue <= + ((FloatConstant) right).DoubleValue; + else if (left is ULongConstant) + bool_res = ((ULongConstant) left).Value <= + ((ULongConstant) right).Value; + else if (left is LongConstant) + bool_res = ((LongConstant) left).Value <= + ((LongConstant) right).Value; + else if (left is UIntConstant) + bool_res = ((UIntConstant) left).Value <= + ((UIntConstant) right).Value; + else if (left is IntConstant) + bool_res = ((IntConstant) left).Value <= + ((IntConstant) right).Value; + else + return null; + + return new BoolConstant (ec.BuiltinTypes, bool_res, left.Location); + } + + return null; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/class.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/class.cs new file mode 100644 index 000000000..ae4c02766 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/class.cs @@ -0,0 +1,3895 @@ +// +// class.cs: Class and Struct handlers +// +// Authors: Miguel de Icaza (miguel@gnu.org) +// Martin Baulig (martin@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com) +// Copyright 2004-2011 Novell, Inc +// Copyright 2011 Xamarin, Inc (http://www.xamarin.com) +// + +using System; +using System.Linq; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Security; +using System.Security.Permissions; +using System.Text; +using System.Diagnostics; +using Mono.CompilerServices.SymbolWriter; + +#if NET_2_1 +using XmlElement = System.Object; +#endif + +#if STATIC +using SecurityType = System.Collections.Generic.List; +using IKVM.Reflection; +using IKVM.Reflection.Emit; +#else +using SecurityType = System.Collections.Generic.Dictionary; +using System.Reflection; +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp +{ + // + // General types container, used as a base class for all constructs which can hold types + // + public abstract class TypeContainer : MemberCore + { + public readonly MemberKind Kind; + public readonly string Basename; + + protected List containers; + + TypeDefinition main_container; + + protected Dictionary defined_names; + + protected bool is_defined; + + public int CounterAnonymousMethods { get; set; } + public int CounterAnonymousContainers { get; set; } + public int CounterSwitchTypes { get; set; } + + protected TypeContainer (TypeContainer parent, MemberName name, Attributes attrs, MemberKind kind) + : base (parent, name, attrs) + { + this.Kind = kind; + if (name != null) + this.Basename = name.Basename; + + defined_names = new Dictionary (); + } + + public override TypeSpec CurrentType { + get { + return null; + } + } + + public Dictionary DefinedNames { + get { + return defined_names; + } + } + + public TypeDefinition PartialContainer { + get { + return main_container; + } + protected set { + main_container = value; + } + } + + public IList Containers { + get { + return containers; + } + } + + // + // Any unattached attributes during parsing get added here. User + // by FULL_AST mode + // + public Attributes UnattachedAttributes { + get; set; + } + + public void AddCompilerGeneratedClass (CompilerGeneratedContainer c) + { + AddTypeContainerMember (c); + } + + public virtual void AddPartial (TypeDefinition next_part) + { + MemberCore mc; + (PartialContainer ?? this).defined_names.TryGetValue (next_part.Basename, out mc); + + AddPartial (next_part, mc as TypeDefinition); + } + + protected void AddPartial (TypeDefinition next_part, TypeDefinition existing) + { + next_part.ModFlags |= Modifiers.PARTIAL; + + if (existing == null) { + AddTypeContainer (next_part); + return; + } + + if ((existing.ModFlags & Modifiers.PARTIAL) == 0) { + if (existing.Kind != next_part.Kind) { + AddTypeContainer (next_part); + } else { + Report.SymbolRelatedToPreviousError (next_part); + Error_MissingPartialModifier (existing); + } + + return; + } + + if (existing.Kind != next_part.Kind) { + Report.SymbolRelatedToPreviousError (existing); + Report.Error (261, next_part.Location, + "Partial declarations of `{0}' must be all classes, all structs or all interfaces", + next_part.GetSignatureForError ()); + } + + if ((existing.ModFlags & Modifiers.AccessibilityMask) != (next_part.ModFlags & Modifiers.AccessibilityMask) && + ((existing.ModFlags & Modifiers.DEFAULT_ACCESS_MODIFIER) == 0 && + (next_part.ModFlags & Modifiers.DEFAULT_ACCESS_MODIFIER) == 0)) { + Report.SymbolRelatedToPreviousError (existing); + Report.Error (262, next_part.Location, + "Partial declarations of `{0}' have conflicting accessibility modifiers", + next_part.GetSignatureForError ()); + } + + var tc_names = existing.CurrentTypeParameters; + if (tc_names != null) { + for (int i = 0; i < tc_names.Count; ++i) { + var tp = next_part.MemberName.TypeParameters[i]; + if (tc_names[i].MemberName.Name != tp.MemberName.Name) { + Report.SymbolRelatedToPreviousError (existing.Location, ""); + Report.Error (264, next_part.Location, "Partial declarations of `{0}' must have the same type parameter names in the same order", + next_part.GetSignatureForError ()); + break; + } + + if (tc_names[i].Variance != tp.Variance) { + Report.SymbolRelatedToPreviousError (existing.Location, ""); + Report.Error (1067, next_part.Location, "Partial declarations of `{0}' must have the same type parameter variance modifiers", + next_part.GetSignatureForError ()); + break; + } + } + } + + if ((next_part.ModFlags & Modifiers.DEFAULT_ACCESS_MODIFIER) != 0) { + existing.ModFlags |= next_part.ModFlags & ~(Modifiers.DEFAULT_ACCESS_MODIFIER | Modifiers.AccessibilityMask); + } else if ((existing.ModFlags & Modifiers.DEFAULT_ACCESS_MODIFIER) != 0) { + existing.ModFlags &= ~(Modifiers.DEFAULT_ACCESS_MODIFIER | Modifiers.AccessibilityMask); + existing.ModFlags |= next_part.ModFlags; + } else { + existing.ModFlags |= next_part.ModFlags; + } + + existing.Definition.Modifiers = existing.ModFlags; + + if (next_part.attributes != null) { + if (existing.attributes == null) + existing.attributes = next_part.attributes; + else + existing.attributes.AddAttributes (next_part.attributes.Attrs); + } + + next_part.PartialContainer = existing; + + existing.AddPartialPart (next_part); + + AddTypeContainerMember (next_part); + } + + public virtual void AddTypeContainer (TypeContainer tc) + { + AddTypeContainerMember (tc); + + var tparams = tc.MemberName.TypeParameters; + if (tparams != null && tc.PartialContainer != null) { + var td = (TypeDefinition) tc; + for (int i = 0; i < tparams.Count; ++i) { + var tp = tparams[i]; + if (tp.MemberName == null) + continue; + + td.AddNameToContainer (tp, tp.Name); + } + } + } + + protected virtual void AddTypeContainerMember (TypeContainer tc) + { + containers.Add (tc); + } + + public virtual void CloseContainer () + { + if (containers != null) { + foreach (TypeContainer tc in containers) { + tc.CloseContainer (); + } + } + } + + public virtual void CreateMetadataName (StringBuilder sb) + { + if (Parent != null && Parent.MemberName != null) + Parent.CreateMetadataName (sb); + + MemberName.CreateMetadataName (sb); + } + + public virtual bool CreateContainer () + { + if (containers != null) { + foreach (TypeContainer tc in containers) { + tc.CreateContainer (); + } + } + + return true; + } + + public override bool Define () + { + if (containers != null) { + foreach (TypeContainer tc in containers) { + tc.Define (); + } + } + + // Release cache used by parser only + if (Module.Evaluator == null) { + defined_names = null; + } else { + defined_names.Clear (); + } + + return true; + } + + public virtual void PrepareEmit () + { + if (containers != null) { + foreach (var t in containers) { + try { + t.PrepareEmit (); + } catch (Exception e) { + if (MemberName == MemberName.Null) + throw; + + throw new InternalErrorException (t, e); + } + } + } + } + + public virtual bool DefineContainer () + { + if (is_defined) + return true; + + is_defined = true; + + DoDefineContainer (); + + if (containers != null) { + foreach (TypeContainer tc in containers) { + try { + tc.DefineContainer (); + } catch (Exception e) { + if (MemberName == MemberName.Null) + throw; + + throw new InternalErrorException (tc, e); + } + } + } + + return true; + } + + public virtual void ExpandBaseInterfaces () + { + if (containers != null) { + foreach (TypeContainer tc in containers) { + tc.ExpandBaseInterfaces (); + } + } + } + + protected virtual void DefineNamespace () + { + if (containers != null) { + foreach (var tc in containers) { + try { + tc.DefineNamespace (); + } catch (Exception e) { + throw new InternalErrorException (tc, e); + } + } + } + } + + protected virtual void DoDefineContainer () + { + } + + public virtual void EmitContainer () + { + if (containers != null) { + for (int i = 0; i < containers.Count; ++i) + containers[i].EmitContainer (); + } + } + + protected void Error_MissingPartialModifier (MemberCore type) + { + Report.Error (260, type.Location, + "Missing partial modifier on declaration of type `{0}'. Another partial declaration of this type exists", + type.GetSignatureForError ()); + } + + public override string GetSignatureForDocumentation () + { + if (Parent != null && Parent.MemberName != null) + return Parent.GetSignatureForDocumentation () + "." + MemberName.GetSignatureForDocumentation (); + + return MemberName.GetSignatureForDocumentation (); + } + + public override string GetSignatureForError () + { + if (Parent != null && Parent.MemberName != null) + return Parent.GetSignatureForError () + "." + MemberName.GetSignatureForError (); + + return MemberName.GetSignatureForError (); + } + + public string GetSignatureForMetadata () + { + if (Parent is TypeDefinition) { + return Parent.GetSignatureForMetadata () + "+" + TypeNameParser.Escape (MemberName.Basename); + } + + var sb = new StringBuilder (); + CreateMetadataName (sb); + return sb.ToString (); + } + + public virtual void RemoveContainer (TypeContainer cont) + { + if (containers != null) + containers.Remove (cont); + + var tc = Parent == Module ? Module : this; + tc.defined_names.Remove (cont.Basename); + } + + public virtual void VerifyMembers () + { + if (containers != null) { + foreach (TypeContainer tc in containers) + tc.VerifyMembers (); + } + } + + public override void WriteDebugSymbol (MonoSymbolFile file) + { + if (containers != null) { + foreach (TypeContainer tc in containers) { + tc.WriteDebugSymbol (file); + } + } + } + } + + public abstract class TypeDefinition : TypeContainer, ITypeDefinition + { + // + // Different context is needed when resolving type container base + // types. Type names come from the parent scope but type parameter + // names from the container scope. + // + public struct BaseContext : IMemberContext + { + TypeContainer tc; + + public BaseContext (TypeContainer tc) + { + this.tc = tc; + } + + #region IMemberContext Members + + public CompilerContext Compiler { + get { return tc.Compiler; } + } + + public TypeSpec CurrentType { + get { return tc.PartialContainer.CurrentType; } + } + + public TypeParameters CurrentTypeParameters { + get { return tc.PartialContainer.CurrentTypeParameters; } + } + + public MemberCore CurrentMemberDefinition { + get { return tc; } + } + + public bool IsObsolete { + get { return tc.IsObsolete; } + } + + public bool IsUnsafe { + get { return tc.IsUnsafe; } + } + + public bool IsStatic { + get { return tc.IsStatic; } + } + + public ModuleContainer Module { + get { return tc.Module; } + } + + public string GetSignatureForError () + { + return tc.GetSignatureForError (); + } + + public ExtensionMethodCandidates LookupExtensionMethod (TypeSpec extensionType, string name, int arity) + { + return null; + } + + public FullNamedExpression LookupNamespaceAlias (string name) + { + return tc.Parent.LookupNamespaceAlias (name); + } + + public FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc) + { + if (arity == 0) { + var tp = CurrentTypeParameters; + if (tp != null) { + TypeParameter t = tp.Find (name); + if (t != null) + return new TypeParameterExpr (t, loc); + } + } + + return tc.Parent.LookupNamespaceOrType (name, arity, mode, loc); + } + + #endregion + } + + [Flags] + enum CachedMethods + { + Equals = 1, + GetHashCode = 1 << 1, + HasStaticFieldInitializer = 1 << 2 + } + + readonly List members; + + // Holds a list of fields that have initializers + protected List initialized_fields; + + // Holds a list of static fields that have initializers + protected List initialized_static_fields; + + Dictionary hoisted_base_call_proxies; + + Dictionary Cache = new Dictionary (); + + // + // Points to the first non-static field added to the container. + // + // This is an arbitrary choice. We are interested in looking at _some_ non-static field, + // and the first one's as good as any. + // + protected FieldBase first_nonstatic_field; + + // + // This one is computed after we can distinguish interfaces + // from classes from the arraylist `type_bases' + // + protected TypeSpec base_type; + FullNamedExpression base_type_expr; // TODO: It's temporary variable + protected TypeSpec[] iface_exprs; + + protected List type_bases; + + // Partial parts for classes only + List class_partial_parts; + + TypeDefinition InTransit; + + public TypeBuilder TypeBuilder; + GenericTypeParameterBuilder[] all_tp_builders; + // + // All recursive type parameters put together sharing same + // TypeParameter instances + // + TypeParameters all_type_parameters; + + public const string DefaultIndexerName = "Item"; + + bool has_normal_indexers; + string indexer_name; + protected bool requires_delayed_unmanagedtype_check; + bool error; + bool members_defined; + bool members_defined_ok; + protected bool has_static_constructor; + + private CachedMethods cached_method; + + protected TypeSpec spec; + TypeSpec current_type; + + public int DynamicSitesCounter; + public int AnonymousMethodsCounter; + public int MethodGroupsCounter; + + static readonly string[] attribute_targets = new [] { "type" }; + static readonly string[] attribute_targets_primary = new [] { "type", "method" }; + + /// + /// The pending methods that need to be implemented + // (interfaces or abstract methods) + /// + PendingImplementation pending; + + protected TypeDefinition (TypeContainer parent, MemberName name, Attributes attrs, MemberKind kind) + : base (parent, name, attrs, kind) + { + PartialContainer = this; + members = new List (); + } + + #region Properties + + public List BaseTypeExpressions { + get { + return type_bases; + } + } + + public override TypeSpec CurrentType { + get { + if (current_type == null) { + if (IsGenericOrParentIsGeneric) { + // + // Switch to inflated version as it's used by all expressions + // + var targs = CurrentTypeParameters == null ? TypeSpec.EmptyTypes : CurrentTypeParameters.Types; + current_type = spec.MakeGenericType (this, targs); + } else { + current_type = spec; + } + } + + return current_type; + } + } + + public override TypeParameters CurrentTypeParameters { + get { + return PartialContainer.MemberName.TypeParameters; + } + } + + int CurrentTypeParametersStartIndex { + get { + int total = all_tp_builders.Length; + if (CurrentTypeParameters != null) { + return total - CurrentTypeParameters.Count; + } + return total; + } + } + + public virtual AssemblyDefinition DeclaringAssembly { + get { + return Module.DeclaringAssembly; + } + } + + IAssemblyDefinition ITypeDefinition.DeclaringAssembly { + get { + return Module.DeclaringAssembly; + } + } + + public TypeSpec Definition { + get { + return spec; + } + } + + public bool HasMembersDefined { + get { + return members_defined; + } + } + + public List TypeBaseExpressions { + get { + return type_bases; + } + } + + public bool HasInstanceConstructor { + get { + return (caching_flags & Flags.HasInstanceConstructor) != 0; + } + set { + caching_flags |= Flags.HasInstanceConstructor; + } + } + + // Indicated whether container has StructLayout attribute set Explicit + public bool HasExplicitLayout { + get { return (caching_flags & Flags.HasExplicitLayout) != 0; } + set { caching_flags |= Flags.HasExplicitLayout; } + } + + public bool HasOperators { + get { + return (caching_flags & Flags.HasUserOperators) != 0; + } + set { + caching_flags |= Flags.HasUserOperators; + } + } + + public bool HasStructLayout { + get { return (caching_flags & Flags.HasStructLayout) != 0; } + set { caching_flags |= Flags.HasStructLayout; } + } + + public TypeSpec[] Interfaces { + get { + return iface_exprs; + } + } + + public bool IsGenericOrParentIsGeneric { + get { + return all_type_parameters != null; + } + } + + public bool IsTopLevel { + get { + return !(Parent is TypeDefinition); + } + } + + public bool IsPartial { + get { + return (ModFlags & Modifiers.PARTIAL) != 0; + } + } + + bool ITypeDefinition.IsTypeForwarder { + get { + return false; + } + } + + bool ITypeDefinition.IsCyclicTypeForwarder { + get { + return false; + } + } + + // + // Returns true for secondary partial containers + // + bool IsPartialPart { + get { + return PartialContainer != this; + } + } + + public MemberCache MemberCache { + get { + return spec.MemberCache; + } + } + + public List Members { + get { + return members; + } + } + + string ITypeDefinition.Namespace { + get { + var p = Parent; + while (p.Kind != MemberKind.Namespace) + p = p.Parent; + + return p.MemberName == null ? null : p.GetSignatureForError (); + } + } + + public ParametersCompiled PrimaryConstructorParameters { get; set; } + + public Arguments PrimaryConstructorBaseArguments { get; set; } + + public Location PrimaryConstructorBaseArgumentsStart { get; set; } + + public TypeParameters TypeParametersAll { + get { + return all_type_parameters; + } + } + + public override string[] ValidAttributeTargets { + get { + return PrimaryConstructorParameters != null ? attribute_targets_primary : attribute_targets; + } + } + +#if FULL_AST + public bool HasOptionalSemicolon { + get; + private set; + } + Location optionalSemicolon; + public Location OptionalSemicolon { + get { + return optionalSemicolon; + } + set { + optionalSemicolon = value; + HasOptionalSemicolon = true; + } + } +#endif + + #endregion + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + + public void AddMember (MemberCore symbol) + { + if (symbol.MemberName.ExplicitInterface != null) { + if (!(Kind == MemberKind.Class || Kind == MemberKind.Struct)) { + Report.Error (541, symbol.Location, + "`{0}': explicit interface declaration can only be declared in a class or struct", + symbol.GetSignatureForError ()); + } + } + + AddNameToContainer (symbol, symbol.MemberName.Name); + members.Add (symbol); + } + + public override void AddTypeContainer (TypeContainer tc) + { + AddNameToContainer (tc, tc.Basename); + + base.AddTypeContainer (tc); + } + + protected override void AddTypeContainerMember (TypeContainer tc) + { + members.Add (tc); + + if (containers == null) + containers = new List (); + + base.AddTypeContainerMember (tc); + } + + // + // Adds the member to defined_names table. It tests for duplications and enclosing name conflicts + // + public virtual void AddNameToContainer (MemberCore symbol, string name) + { + if (((ModFlags | symbol.ModFlags) & Modifiers.COMPILER_GENERATED) != 0) + return; + + MemberCore mc; + if (!PartialContainer.defined_names.TryGetValue (name, out mc)) { + PartialContainer.defined_names.Add (name, symbol); + return; + } + + if (symbol.EnableOverloadChecks (mc)) + return; + + InterfaceMemberBase im = mc as InterfaceMemberBase; + if (im != null && im.IsExplicitImpl) + return; + + Report.SymbolRelatedToPreviousError (mc); + if ((mc.ModFlags & Modifiers.PARTIAL) != 0 && (symbol is ClassOrStruct || symbol is Interface)) { + Error_MissingPartialModifier (symbol); + return; + } + + if (symbol is TypeParameter) { + Report.Error (692, symbol.Location, + "Duplicate type parameter `{0}'", symbol.GetSignatureForError ()); + } else { + Report.Error (102, symbol.Location, + "The type `{0}' already contains a definition for `{1}'", + GetSignatureForError (), name); + } + + return; + } + + public void AddConstructor (Constructor c) + { + AddConstructor (c, false); + } + + public void AddConstructor (Constructor c, bool isDefault) + { + bool is_static = (c.ModFlags & Modifiers.STATIC) != 0; + if (!isDefault) + AddNameToContainer (c, is_static ? Constructor.TypeConstructorName : Constructor.ConstructorName); + + if (is_static && c.ParameterInfo.IsEmpty) { + PartialContainer.has_static_constructor = true; + } else { + PartialContainer.HasInstanceConstructor = true; + } + + members.Add (c); + } + + public bool AddField (FieldBase field) + { + AddMember (field); + + if ((field.ModFlags & Modifiers.STATIC) != 0) + return true; + + var first_field = PartialContainer.first_nonstatic_field; + if (first_field == null) { + PartialContainer.first_nonstatic_field = field; + return true; + } + + if (Kind == MemberKind.Struct && first_field.Parent != field.Parent) { + Report.SymbolRelatedToPreviousError (first_field.Parent); + Report.Warning (282, 3, field.Location, + "struct instance field `{0}' found in different declaration from instance field `{1}'", + field.GetSignatureForError (), first_field.GetSignatureForError ()); + } + return true; + } + + /// + /// Indexer has special handling in constrast to other AddXXX because the name can be driven by IndexerNameAttribute + /// + public void AddIndexer (Indexer i) + { + members.Add (i); + } + + public void AddOperator (Operator op) + { + PartialContainer.HasOperators = true; + AddMember (op); + } + + public void AddPartialPart (TypeDefinition part) + { + if (Kind != MemberKind.Class) + return; + + if (class_partial_parts == null) + class_partial_parts = new List (); + + class_partial_parts.Add (part); + } + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.Target == AttributeTargets.Method) { + foreach (var m in members) { + var c = m as Constructor; + if (c == null) + continue; + + if (c.IsPrimaryConstructor) { + c.ApplyAttributeBuilder (a, ctor, cdata, pa); + return; + } + } + + throw new InternalErrorException (); + } + + if (has_normal_indexers && a.Type == pa.DefaultMember) { + Report.Error (646, a.Location, "Cannot specify the `DefaultMember' attribute on type containing an indexer"); + return; + } + + if (a.Type == pa.Required) { + Report.Error (1608, a.Location, "The RequiredAttribute attribute is not permitted on C# types"); + return; + } + + TypeBuilder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), cdata); + } + + public override AttributeTargets AttributeTargets { + get { + throw new NotSupportedException (); + } + } + + public TypeSpec BaseType { + get { + return spec.BaseType; + } + } + + protected virtual TypeAttributes TypeAttr { + get { + return ModifiersExtensions.TypeAttr (ModFlags, IsTopLevel); + } + } + + public int TypeParametersCount { + get { + return MemberName.Arity; + } + } + + TypeParameterSpec[] ITypeDefinition.TypeParameters { + get { + return PartialContainer.CurrentTypeParameters.Types; + } + } + + public string GetAttributeDefaultMember () + { + return indexer_name ?? DefaultIndexerName; + } + + public bool IsComImport { + get { + if (OptAttributes == null) + return false; + + return OptAttributes.Contains (Module.PredefinedAttributes.ComImport); + } + } + + public virtual void RegisterFieldForInitialization (MemberCore field, FieldInitializer expression) + { + if (IsPartialPart) + PartialContainer.RegisterFieldForInitialization (field, expression); + + if ((field.ModFlags & Modifiers.STATIC) != 0){ + if (initialized_static_fields == null) { + HasStaticFieldInitializer = true; + initialized_static_fields = new List (4); + } + + initialized_static_fields.Add (expression); + } else { + if (initialized_fields == null) + initialized_fields = new List (4); + + initialized_fields.Add (expression); + } + } + + public void ResolveFieldInitializers (BlockContext ec) + { + Debug.Assert (!IsPartialPart); + + if (ec.IsStatic) { + if (initialized_static_fields == null) + return; + + bool has_complex_initializer = !ec.Module.Compiler.Settings.Optimize; + int i; + ExpressionStatement [] init = new ExpressionStatement [initialized_static_fields.Count]; + for (i = 0; i < initialized_static_fields.Count; ++i) { + FieldInitializer fi = initialized_static_fields [i]; + ExpressionStatement s = fi.ResolveStatement (ec); + if (s == null) { + s = EmptyExpressionStatement.Instance; + } else if (!fi.IsSideEffectFree) { + has_complex_initializer = true; + } + + init [i] = s; + } + + for (i = 0; i < initialized_static_fields.Count; ++i) { + FieldInitializer fi = initialized_static_fields [i]; + // + // Need special check to not optimize code like this + // static int a = b = 5; + // static int b = 0; + // + if (!has_complex_initializer && fi.IsDefaultInitializer) + continue; + + ec.AssignmentInfoOffset += fi.AssignmentOffset; + ec.CurrentBlock.AddScopeStatement (new StatementExpression (init [i])); + } + + return; + } + + if (initialized_fields == null) + return; + + for (int i = 0; i < initialized_fields.Count; ++i) { + FieldInitializer fi = initialized_fields [i]; + + // + // Clone before resolving otherwise when field initializer is needed + // in more than 1 constructor any resolve after the initial one would + // only took the resolved expression which is problem for expressions + // that generate extra expressions or code during Resolve phase + // + var cloned = fi.Clone (new CloneContext ()); + + ExpressionStatement s = fi.ResolveStatement (ec); + if (s == null) { + initialized_fields [i] = new FieldInitializer (fi.Field, ErrorExpression.Instance, Location.Null); + continue; + } + + // + // Field is re-initialized to its default value => removed + // + if (fi.IsDefaultInitializer && ec.Module.Compiler.Settings.Optimize) + continue; + + ec.AssignmentInfoOffset += fi.AssignmentOffset; + ec.CurrentBlock.AddScopeStatement (new StatementExpression (s)); + initialized_fields [i] = (FieldInitializer) cloned; + } + } + + public override string DocComment { + get { + return comment; + } + set { + if (value == null) + return; + + comment += value; + } + } + + public PendingImplementation PendingImplementations { + get { return pending; } + } + + internal override void GenerateDocComment (DocumentationBuilder builder) + { + if (IsPartialPart) + return; + + base.GenerateDocComment (builder); + + foreach (var member in members) + member.GenerateDocComment (builder); + } + + public TypeSpec GetAttributeCoClass () + { + if (OptAttributes == null) + return null; + + Attribute a = OptAttributes.Search (Module.PredefinedAttributes.CoClass); + if (a == null) + return null; + + return a.GetCoClassAttributeValue (); + } + + public AttributeUsageAttribute GetAttributeUsage (PredefinedAttribute pa) + { + Attribute a = null; + if (OptAttributes != null) { + a = OptAttributes.Search (pa); + } + + if (a == null) + return null; + + return a.GetAttributeUsageAttribute (); + } + + public virtual CompilationSourceFile GetCompilationSourceFile () + { + TypeContainer ns = Parent; + while (true) { + var sf = ns as CompilationSourceFile; + if (sf != null) + return sf; + + ns = ns.Parent; + } + } + + public virtual void SetBaseTypes (List baseTypes) + { + type_bases = baseTypes; + } + + /// + /// This function computes the Base class and also the + /// list of interfaces that the class or struct @c implements. + /// + /// The return value is an array (might be null) of + /// interfaces implemented (as Types). + /// + /// The @base_class argument is set to the base object or null + /// if this is `System.Object'. + /// + protected virtual TypeSpec[] ResolveBaseTypes (out FullNamedExpression base_class) + { + base_class = null; + if (type_bases == null) + return null; + + int count = type_bases.Count; + TypeSpec[] ifaces = null; + var base_context = new BaseContext (this); + for (int i = 0, j = 0; i < count; i++){ + FullNamedExpression fne = type_bases [i]; + + var fne_resolved = fne.ResolveAsType (base_context); + if (fne_resolved == null) + continue; + + if (i == 0 && Kind == MemberKind.Class && !fne_resolved.IsInterface) { + if (fne_resolved.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + Report.Error (1965, Location, "Class `{0}' cannot derive from the dynamic type", + GetSignatureForError ()); + + continue; + } + + base_type = fne_resolved; + base_class = fne; + continue; + } + + if (ifaces == null) + ifaces = new TypeSpec [count - i]; + + if (fne_resolved.IsInterface) { + for (int ii = 0; ii < j; ++ii) { + if (fne_resolved == ifaces [ii]) { + Report.Error (528, Location, "`{0}' is already listed in interface list", + fne_resolved.GetSignatureForError ()); + break; + } + } + + if (Kind == MemberKind.Interface && !IsAccessibleAs (fne_resolved)) { + Report.Error (61, fne.Location, + "Inconsistent accessibility: base interface `{0}' is less accessible than interface `{1}'", + fne_resolved.GetSignatureForError (), GetSignatureForError ()); + } + } else { + Report.SymbolRelatedToPreviousError (fne_resolved); + if (Kind != MemberKind.Class) { + Report.Error (527, fne.Location, "Type `{0}' in interface list is not an interface", fne_resolved.GetSignatureForError ()); + } else if (base_class != null) + Report.Error (1721, fne.Location, "`{0}': Classes cannot have multiple base classes (`{1}' and `{2}')", + GetSignatureForError (), base_class.GetSignatureForError (), fne_resolved.GetSignatureForError ()); + else { + Report.Error (1722, fne.Location, "`{0}': Base class `{1}' must be specified as first", + GetSignatureForError (), fne_resolved.GetSignatureForError ()); + } + } + + ifaces [j++] = fne_resolved; + } + + return ifaces; + } + + // + // Checks that some operators come in pairs: + // == and != + // > and < + // >= and <= + // true and false + // + // They are matched based on the return type and the argument types + // + void CheckPairedOperators () + { + bool has_equality_or_inequality = false; + List found_matched = new List (); + + for (int i = 0; i < members.Count; ++i) { + var o_a = members[i] as Operator; + if (o_a == null) + continue; + + var o_type = o_a.OperatorType; + if (o_type == Operator.OpType.Equality || o_type == Operator.OpType.Inequality) + has_equality_or_inequality = true; + + if (found_matched.Contains (o_type)) + continue; + + var matching_type = o_a.GetMatchingOperator (); + if (matching_type == Operator.OpType.TOP) { + continue; + } + + bool pair_found = false; + for (int ii = 0; ii < members.Count; ++ii) { + var o_b = members[ii] as Operator; + if (o_b == null || o_b.OperatorType != matching_type) + continue; + + if (!TypeSpecComparer.IsEqual (o_a.ReturnType, o_b.ReturnType)) + continue; + + if (!TypeSpecComparer.Equals (o_a.ParameterTypes, o_b.ParameterTypes)) + continue; + + found_matched.Add (matching_type); + pair_found = true; + break; + } + + if (!pair_found) { + Report.Error (216, o_a.Location, + "The operator `{0}' requires a matching operator `{1}' to also be defined", + o_a.GetSignatureForError (), Operator.GetName (matching_type)); + } + } + + if (has_equality_or_inequality) { + if (!HasEquals) + Report.Warning (660, 2, Location, "`{0}' defines operator == or operator != but does not override Object.Equals(object o)", + GetSignatureForError ()); + + if (!HasGetHashCode) + Report.Warning (661, 2, Location, "`{0}' defines operator == or operator != but does not override Object.GetHashCode()", + GetSignatureForError ()); + } + } + + public override void CreateMetadataName (StringBuilder sb) + { + if (Parent.MemberName != null) { + Parent.CreateMetadataName (sb); + + if (sb.Length != 0) { + sb.Append ("."); + } + } + + sb.Append (MemberName.Basename); + } + + bool CreateTypeBuilder () + { + // + // Sets .size to 1 for structs with no instance fields + // + int type_size = Kind == MemberKind.Struct && first_nonstatic_field == null && !(this is StateMachine) ? 1 : 0; + + var parent_def = Parent as TypeDefinition; + if (parent_def == null) { + var sb = new StringBuilder (); + CreateMetadataName (sb); + TypeBuilder = Module.CreateBuilder (sb.ToString (), TypeAttr, type_size); + } else { + TypeBuilder = parent_def.TypeBuilder.DefineNestedType (Basename, TypeAttr, null, type_size); + } + + if (DeclaringAssembly.Importer != null) + DeclaringAssembly.Importer.AddCompiledType (TypeBuilder, spec); + + spec.SetMetaInfo (TypeBuilder); + spec.MemberCache = new MemberCache (this); + + TypeParameters parentAllTypeParameters = null; + if (parent_def != null) { + spec.DeclaringType = Parent.CurrentType; + parent_def.MemberCache.AddMember (spec); + parentAllTypeParameters = parent_def.all_type_parameters; + } + + if (MemberName.TypeParameters != null || parentAllTypeParameters != null) { + var tparam_names = CreateTypeParameters (parentAllTypeParameters); + + all_tp_builders = TypeBuilder.DefineGenericParameters (tparam_names); + + if (CurrentTypeParameters != null) { + CurrentTypeParameters.Create (spec, CurrentTypeParametersStartIndex, this); + CurrentTypeParameters.Define (all_tp_builders); + } + } + + return true; + } + + string[] CreateTypeParameters (TypeParameters parentAllTypeParameters) + { + string[] names; + int parent_offset = 0; + if (parentAllTypeParameters != null) { + if (CurrentTypeParameters == null) { + all_type_parameters = parentAllTypeParameters; + return parentAllTypeParameters.GetAllNames (); + } + + names = new string[parentAllTypeParameters.Count + CurrentTypeParameters.Count]; + all_type_parameters = new TypeParameters (names.Length); + all_type_parameters.Add (parentAllTypeParameters); + + parent_offset = all_type_parameters.Count; + for (int i = 0; i < parent_offset; ++i) + names[i] = all_type_parameters[i].MemberName.Name; + + } else { + names = new string[CurrentTypeParameters.Count]; + } + + for (int i = 0; i < CurrentTypeParameters.Count; ++i) { + if (all_type_parameters != null) + all_type_parameters.Add (MemberName.TypeParameters[i]); + + var name = CurrentTypeParameters[i].MemberName.Name; + names[parent_offset + i] = name; + for (int ii = 0; ii < parent_offset + i; ++ii) { + if (names[ii] != name) + continue; + + var tp = CurrentTypeParameters[i]; + var conflict = all_type_parameters[ii]; + + tp.WarningParentNameConflict (conflict); + } + } + + if (all_type_parameters == null) + all_type_parameters = CurrentTypeParameters; + + return names; + } + + + public SourceMethodBuilder CreateMethodSymbolEntry () + { + if (Module.DeclaringAssembly.SymbolWriter == null || (ModFlags & Modifiers.DEBUGGER_HIDDEN) != 0) + return null; + + var source_file = GetCompilationSourceFile (); + if (source_file == null) + return null; + + return new SourceMethodBuilder (source_file.SymbolUnitEntry); + } + + // + // Creates a proxy base method call inside this container for hoisted base member calls + // + public MethodSpec CreateHoistedBaseCallProxy (ResolveContext rc, MethodSpec method) + { + Method proxy_method; + + // + // One proxy per base method is enough + // + if (hoisted_base_call_proxies == null) { + hoisted_base_call_proxies = new Dictionary (); + proxy_method = null; + } else { + hoisted_base_call_proxies.TryGetValue (method, out proxy_method); + } + + if (proxy_method == null) { + string name = CompilerGeneratedContainer.MakeName (method.Name, null, "BaseCallProxy", hoisted_base_call_proxies.Count); + + MemberName member_name; + TypeArguments targs = null; + TypeSpec return_type = method.ReturnType; + var local_param_types = method.Parameters.Types; + + if (method.IsGeneric) { + // + // Copy all base generic method type parameters info + // + var hoisted_tparams = method.GenericDefinition.TypeParameters; + var tparams = new TypeParameters (); + + targs = new TypeArguments (); + targs.Arguments = new TypeSpec[hoisted_tparams.Length]; + for (int i = 0; i < hoisted_tparams.Length; ++i) { + var tp = hoisted_tparams[i]; + var local_tp = new TypeParameter (tp, null, new MemberName (tp.Name, Location), null); + tparams.Add (local_tp); + + targs.Add (new SimpleName (tp.Name, Location)); + targs.Arguments[i] = local_tp.Type; + } + + member_name = new MemberName (name, tparams, Location); + + // + // Mutate any method type parameters from original + // to newly created hoisted version + // + var mutator = new TypeParameterMutator (hoisted_tparams, tparams); + return_type = mutator.Mutate (return_type); + local_param_types = mutator.Mutate (local_param_types); + } else { + member_name = new MemberName (name); + } + + var base_parameters = new Parameter[method.Parameters.Count]; + for (int i = 0; i < base_parameters.Length; ++i) { + var base_param = method.Parameters.FixedParameters[i]; + base_parameters[i] = new Parameter (new TypeExpression (local_param_types [i], Location), + base_param.Name, base_param.ModFlags, null, Location); + base_parameters[i].Resolve (this, i); + } + + var cloned_params = ParametersCompiled.CreateFullyResolved (base_parameters, method.Parameters.Types); + if (method.Parameters.HasArglist) { + cloned_params.FixedParameters[0] = new Parameter (null, "__arglist", Parameter.Modifier.NONE, null, Location); + cloned_params.Types[0] = Module.PredefinedTypes.RuntimeArgumentHandle.Resolve (); + } + + // Compiler generated proxy + proxy_method = new Method (this, new TypeExpression (return_type, Location), + Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED | Modifiers.DEBUGGER_HIDDEN, + member_name, cloned_params, null); + + var block = new ToplevelBlock (Compiler, proxy_method.ParameterInfo, Location) { + IsCompilerGenerated = true + }; + + var mg = MethodGroupExpr.CreatePredefined (method, method.DeclaringType, Location); + mg.InstanceExpression = new BaseThis (method.DeclaringType, Location); + if (targs != null) + mg.SetTypeArguments (rc, targs); + + // Get all the method parameters and pass them as arguments + var real_base_call = new Invocation (mg, block.GetAllParametersArguments ()); + Statement statement; + if (method.ReturnType.Kind == MemberKind.Void) + statement = new StatementExpression (real_base_call); + else + statement = new Return (real_base_call, Location); + + block.AddStatement (statement); + proxy_method.Block = block; + + members.Add (proxy_method); + proxy_method.Define (); + proxy_method.PrepareEmit (); + + hoisted_base_call_proxies.Add (method, proxy_method); + } + + return proxy_method.Spec; + } + + protected bool DefineBaseTypes () + { + if (IsPartialPart && Kind == MemberKind.Class) + return true; + + return DoDefineBaseType (); + } + + bool DoDefineBaseType () + { + iface_exprs = ResolveBaseTypes (out base_type_expr); + bool set_base_type; + + if (IsPartialPart) { + set_base_type = false; + + if (base_type_expr != null) { + if (PartialContainer.base_type_expr != null && PartialContainer.base_type != base_type) { + Report.SymbolRelatedToPreviousError (base_type_expr.Location, ""); + Report.Error (263, Location, + "Partial declarations of `{0}' must not specify different base classes", + GetSignatureForError ()); + } else { + PartialContainer.base_type_expr = base_type_expr; + PartialContainer.base_type = base_type; + set_base_type = true; + } + } + + if (iface_exprs != null) { + if (PartialContainer.iface_exprs == null) + PartialContainer.iface_exprs = iface_exprs; + else { + var ifaces = new List (PartialContainer.iface_exprs); + foreach (var iface_partial in iface_exprs) { + if (ifaces.Contains (iface_partial)) + continue; + + ifaces.Add (iface_partial); + } + + PartialContainer.iface_exprs = ifaces.ToArray (); + } + } + + PartialContainer.members.AddRange (members); + if (containers != null) { + if (PartialContainer.containers == null) + PartialContainer.containers = new List (); + + PartialContainer.containers.AddRange (containers); + } + + if (PrimaryConstructorParameters != null) { + if (PartialContainer.PrimaryConstructorParameters != null) { + Report.Error (8036, Location, "Only one part of a partial type can declare primary constructor parameters"); + } else { + PartialContainer.PrimaryConstructorParameters = PrimaryConstructorParameters; + } + } + + members_defined = members_defined_ok = true; + caching_flags |= Flags.CloseTypeCreated; + } else { + set_base_type = true; + } + + var cycle = CheckRecursiveDefinition (this); + if (cycle != null) { + Report.SymbolRelatedToPreviousError (cycle); + if (this is Interface) { + Report.Error (529, Location, + "Inherited interface `{0}' causes a cycle in the interface hierarchy of `{1}'", + GetSignatureForError (), cycle.GetSignatureForError ()); + + iface_exprs = null; + PartialContainer.iface_exprs = null; + } else { + Report.Error (146, Location, + "Circular base class dependency involving `{0}' and `{1}'", + GetSignatureForError (), cycle.GetSignatureForError ()); + + base_type = null; + PartialContainer.base_type = null; + } + } + + if (iface_exprs != null) { + if (!PrimaryConstructorBaseArgumentsStart.IsNull) { + Report.Error (8049, PrimaryConstructorBaseArgumentsStart, "Implemented interfaces cannot have arguments"); + } + + foreach (var iface_type in iface_exprs) { + // Prevents a crash, the interface might not have been resolved: 442144 + if (iface_type == null) + continue; + + if (!spec.AddInterfaceDefined (iface_type)) + continue; + + TypeBuilder.AddInterfaceImplementation (iface_type.GetMetaInfo ()); + } + } + + if (Kind == MemberKind.Interface) { + spec.BaseType = Compiler.BuiltinTypes.Object; + return true; + } + + if (set_base_type) { + SetBaseType (); + } + + // + // Base type of partial container has to be resolved before we + // resolve any nested types of the container. We need to know + // partial parts because the base type can be specified in file + // defined after current container + // + if (class_partial_parts != null) { + foreach (var pp in class_partial_parts) { + if (pp.PrimaryConstructorBaseArguments != null) + PrimaryConstructorBaseArguments = pp.PrimaryConstructorBaseArguments; + + pp.DoDefineBaseType (); + } + + } + + return true; + } + + void SetBaseType () + { + if (base_type == null) { + TypeBuilder.SetParent (null); + return; + } + + if (spec.BaseType == base_type) + return; + + spec.BaseType = base_type; + + if (IsPartialPart) + spec.UpdateInflatedInstancesBaseType (); + + // Set base type after type creation + TypeBuilder.SetParent (base_type.GetMetaInfo ()); + } + + public override void ExpandBaseInterfaces () + { + if (!IsPartialPart) + DoExpandBaseInterfaces (); + + base.ExpandBaseInterfaces (); + } + + public void DoExpandBaseInterfaces () + { + if ((caching_flags & Flags.InterfacesExpanded) != 0) + return; + + caching_flags |= Flags.InterfacesExpanded; + + // + // Expand base interfaces. It cannot be done earlier because all partial + // interface parts need to be defined before the type they are used from + // + if (iface_exprs != null) { + foreach (var iface in iface_exprs) { + if (iface == null) + continue; + + var td = iface.MemberDefinition as TypeDefinition; + if (td != null) + td.DoExpandBaseInterfaces (); + + if (iface.Interfaces == null) + continue; + + foreach (var biface in iface.Interfaces) { + if (spec.AddInterfaceDefined (biface)) { + TypeBuilder.AddInterfaceImplementation (biface.GetMetaInfo ()); + } + } + } + } + + // + // Include all base type interfaces too, see ImportTypeBase for details + // + if (base_type != null) { + var td = base_type.MemberDefinition as TypeDefinition; + if (td != null) + td.DoExpandBaseInterfaces (); + + // + // Simply use base interfaces only, they are all expanded which makes + // it easy to handle generic type argument propagation with single + // inflator only. + // + // interface IA : IB + // interface IB : IC + // interface IC + // + if (base_type.Interfaces != null) { + foreach (var iface in base_type.Interfaces) { + spec.AddInterfaceDefined (iface); + } + } + } + } + + public override void PrepareEmit () + { + if ((caching_flags & Flags.CloseTypeCreated) != 0) + return; + + foreach (var member in members) { + var pbm = member as PropertyBasedMember; + if (pbm != null) + pbm.PrepareEmit (); + + var pm = member as IParametersMember; + if (pm != null) { + var mc = member as MethodOrOperator; + if (mc != null) { + mc.PrepareEmit (); + } + + var p = pm.Parameters; + if (p.IsEmpty) + continue; + + ((ParametersCompiled) p).ResolveDefaultValues (member); + continue; + } + + var c = member as Const; + if (c != null) + c.DefineValue (); + } + + base.PrepareEmit (); + } + + // + // Defines the type in the appropriate ModuleBuilder or TypeBuilder. + // + public override bool CreateContainer () + { + if (TypeBuilder != null) + return !error; + + if (error) + return false; + + if (IsPartialPart) { + spec = PartialContainer.spec; + TypeBuilder = PartialContainer.TypeBuilder; + all_tp_builders = PartialContainer.all_tp_builders; + all_type_parameters = PartialContainer.all_type_parameters; + } else { + if (!CreateTypeBuilder ()) { + error = true; + return false; + } + } + + return base.CreateContainer (); + } + + protected override void DoDefineContainer () + { + DefineBaseTypes (); + + DoResolveTypeParameters (); + } + + // + // Replaces normal spec with predefined one when compiling corlib + // and this type container defines predefined type + // + public void SetPredefinedSpec (BuiltinTypeSpec spec) + { + // When compiling build-in types we start with two + // version of same type. One is of BuiltinTypeSpec and + // second one is ordinary TypeSpec. The unification + // happens at later stage when we know which type + // really matches the builtin type signature. However + // that means TypeSpec create during CreateType of this + // type has to be replaced with builtin one + // + spec.SetMetaInfo (TypeBuilder); + spec.MemberCache = this.spec.MemberCache; + spec.DeclaringType = this.spec.DeclaringType; + + this.spec = spec; + current_type = null; + } + + public override void RemoveContainer (TypeContainer cont) + { + base.RemoveContainer (cont); + Members.Remove (cont); + Cache.Remove (cont.Basename); + } + + protected virtual bool DoResolveTypeParameters () + { + var tparams = MemberName.TypeParameters; + if (tparams == null) + return true; + + var base_context = new BaseContext (this); + for (int i = 0; i < tparams.Count; ++i) { + var tp = tparams[i]; + + if (!tp.ResolveConstraints (base_context)) { + error = true; + return false; + } + + if (IsPartialPart) { + var pc_tp = PartialContainer.CurrentTypeParameters [i]; + + tp.Create (spec, this); + tp.Define (pc_tp); + + if (tp.OptAttributes != null) { + if (pc_tp.OptAttributes == null) + pc_tp.OptAttributes = tp.OptAttributes; + else + pc_tp.OptAttributes.Attrs.AddRange (tp.OptAttributes.Attrs); + } + } + } + + if (IsPartialPart) { + PartialContainer.CurrentTypeParameters.UpdateConstraints (this); + } + + return true; + } + + TypeSpec CheckRecursiveDefinition (TypeDefinition tc) + { + if (InTransit != null) + return spec; + + InTransit = tc; + + if (base_type != null) { + var ptc = base_type.MemberDefinition as TypeDefinition; + if (ptc != null && ptc.CheckRecursiveDefinition (this) != null) + return base_type; + } + + if (iface_exprs != null) { + foreach (var iface in iface_exprs) { + // the interface might not have been resolved, prevents a crash, see #442144 + if (iface == null) + continue; + var ptc = iface.MemberDefinition as Interface; + if (ptc != null && ptc.CheckRecursiveDefinition (this) != null) + return iface; + } + } + + if (!IsTopLevel && Parent.PartialContainer.CheckRecursiveDefinition (this) != null) + return spec; + + InTransit = null; + return null; + } + + /// + /// Populates our TypeBuilder with fields and methods + /// + public sealed override bool Define () + { + if (members_defined) + return members_defined_ok; + + members_defined_ok = DoDefineMembers (); + members_defined = true; + + base.Define (); + + return members_defined_ok; + } + + protected virtual bool DoDefineMembers () + { + Debug.Assert (!IsPartialPart); + + if (iface_exprs != null) { + foreach (var iface_type in iface_exprs) { + if (iface_type == null) + continue; + + // Ensure the base is always setup + var compiled_iface = iface_type.MemberDefinition as Interface; + if (compiled_iface != null) + compiled_iface.Define (); + + ObsoleteAttribute oa = iface_type.GetAttributeObsolete (); + if (oa != null && !IsObsolete) + AttributeTester.Report_ObsoleteMessage (oa, iface_type.GetSignatureForError (), Location, Report); + + if (iface_type.Arity > 0) { + // TODO: passing `this' is wrong, should be base type iface instead + VarianceDecl.CheckTypeVariance (iface_type, Variance.Covariant, this); + + if (((InflatedTypeSpec) iface_type).HasDynamicArgument () && !IsCompilerGenerated) { + Report.Error (1966, Location, + "`{0}': cannot implement a dynamic interface `{1}'", + GetSignatureForError (), iface_type.GetSignatureForError ()); + return false; + } + } + + if (iface_type.IsGenericOrParentIsGeneric) { + foreach (var prev_iface in iface_exprs) { + if (prev_iface == iface_type || prev_iface == null) + break; + + if (!TypeSpecComparer.Unify.IsEqual (iface_type, prev_iface)) + continue; + + Report.Error (695, Location, + "`{0}' cannot implement both `{1}' and `{2}' because they may unify for some type parameter substitutions", + GetSignatureForError (), prev_iface.GetSignatureForError (), iface_type.GetSignatureForError ()); + } + } + } + + if (Kind == MemberKind.Interface) { + foreach (var iface in spec.Interfaces) { + MemberCache.AddInterface (iface); + } + } + } + + if (base_type != null) { + // + // Run checks skipped during DefineType (e.g FullNamedExpression::ResolveAsType) + // + if (base_type_expr != null) { + ObsoleteAttribute obsolete_attr = base_type.GetAttributeObsolete (); + if (obsolete_attr != null && !IsObsolete) + AttributeTester.Report_ObsoleteMessage (obsolete_attr, base_type.GetSignatureForError (), base_type_expr.Location, Report); + + if (IsGenericOrParentIsGeneric && base_type.IsAttribute) { + Report.Error (698, base_type_expr.Location, + "A generic type cannot derive from `{0}' because it is an attribute class", + base_type.GetSignatureForError ()); + } + } + + var baseContainer = base_type.MemberDefinition as ClassOrStruct; + if (baseContainer != null) { + baseContainer.Define (); + + // + // It can trigger define of this type (for generic types only) + // + if (HasMembersDefined) + return true; + } + } + + if (Kind == MemberKind.Struct || Kind == MemberKind.Class) { + pending = PendingImplementation.GetPendingImplementations (this); + } + + var count = members.Count; + for (int i = 0; i < count; ++i) { + var mc = members[i] as InterfaceMemberBase; + if (mc == null || !mc.IsExplicitImpl) + continue; + + try { + mc.Define (); + } catch (Exception e) { + throw new InternalErrorException (mc, e); + } + } + + for (int i = 0; i < count; ++i) { + var mc = members[i] as InterfaceMemberBase; + if (mc != null && mc.IsExplicitImpl) + continue; + + if (members[i] is TypeContainer) + continue; + + try { + members[i].Define (); + } catch (Exception e) { + throw new InternalErrorException (members[i], e); + } + } + + if (HasOperators) { + CheckPairedOperators (); + } + + if (requires_delayed_unmanagedtype_check) { + requires_delayed_unmanagedtype_check = false; + foreach (var member in members) { + var f = member as Field; + if (f != null && f.MemberType != null && f.MemberType.IsPointer) + TypeManager.VerifyUnmanaged (Module, f.MemberType, f.Location); + } + } + + ComputeIndexerName(); + + if (HasEquals && !HasGetHashCode) { + Report.Warning (659, 3, Location, + "`{0}' overrides Object.Equals(object) but does not override Object.GetHashCode()", GetSignatureForError ()); + } + + if (Kind == MemberKind.Interface && iface_exprs != null) { + MemberCache.RemoveHiddenMembers (spec); + } + + return true; + } + + void ComputeIndexerName () + { + var indexers = MemberCache.FindMembers (spec, MemberCache.IndexerNameAlias, true); + if (indexers == null) + return; + + string class_indexer_name = null; + + // + // Check normal indexers for consistent name, explicit interface implementation + // indexers are ignored + // + foreach (var indexer in indexers) { + // + // FindMembers can return unfiltered full hierarchy names + // + if (indexer.DeclaringType != spec) + continue; + + has_normal_indexers = true; + + if (class_indexer_name == null) { + indexer_name = class_indexer_name = indexer.Name; + continue; + } + + if (indexer.Name != class_indexer_name) + Report.Error (668, ((Indexer)indexer.MemberDefinition).Location, + "Two indexers have different names; the IndexerName attribute must be used with the same name on every indexer within a type"); + } + } + + void EmitIndexerName () + { + if (!has_normal_indexers) + return; + + var ctor = Module.PredefinedMembers.DefaultMemberAttributeCtor.Get (); + if (ctor == null) + return; + + var encoder = new AttributeEncoder (); + encoder.Encode (GetAttributeDefaultMember ()); + encoder.EncodeEmptyNamedArguments (); + + TypeBuilder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), encoder.ToArray ()); + } + + public override void VerifyMembers () + { + // + // Check for internal or private fields that were never assigned + // + if (!IsCompilerGenerated && Compiler.Settings.WarningLevel >= 3 && this == PartialContainer) { + bool is_type_exposed = Kind == MemberKind.Struct || IsExposedFromAssembly (); + foreach (var member in members) { + if (member is Event) { + // + // An event can be assigned from same class only, report + // this warning for all accessibility modes + // + if (!member.IsUsed && !PartialContainer.HasStructLayout) + Report.Warning (67, 3, member.Location, "The event `{0}' is never used", member.GetSignatureForError ()); + + continue; + } + + if ((member.ModFlags & Modifiers.AccessibilityMask) != Modifiers.PRIVATE) { + if (is_type_exposed) + continue; + + member.SetIsUsed (); + } + + var f = member as Field; + if (f == null) + continue; + + if (!member.IsUsed) { + if (!PartialContainer.HasStructLayout) { + if ((member.caching_flags & Flags.IsAssigned) == 0) { + Report.Warning (169, 3, member.Location, "The private field `{0}' is never used", member.GetSignatureForError ()); + } else { + Report.Warning (414, 3, member.Location, "The private field `{0}' is assigned but its value is never used", + member.GetSignatureForError ()); + } + } + + continue; + } + + if ((f.caching_flags & Flags.IsAssigned) != 0) + continue; + + // + // Only report 649 on level 4 + // + if (Compiler.Settings.WarningLevel < 4) + continue; + + // + // Don't be pedantic when type requires specific layout + // + if (f.OptAttributes != null || PartialContainer.HasStructLayout) + continue; + + Constant c = New.Constantify (f.MemberType, f.Location); + string value; + if (c != null) { + value = c.GetValueAsLiteral (); + } else if (TypeSpec.IsReferenceType (f.MemberType)) { + value = "null"; + } else { + value = null; + } + + if (value != null) + value = " `" + value + "'"; + + Report.Warning (649, 4, f.Location, "Field `{0}' is never assigned to, and will always have its default value{1}", + f.GetSignatureForError (), value); + } + } + + base.VerifyMembers (); + } + + public override void Emit () + { + if (OptAttributes != null) + OptAttributes.Emit (); + + if (!IsCompilerGenerated) { + if (!IsTopLevel) { + MemberSpec candidate; + bool overrides = false; + var conflict_symbol = MemberCache.FindBaseMember (this, out candidate, ref overrides); + if (conflict_symbol == null && candidate == null) { + if ((ModFlags & Modifiers.NEW) != 0) + Report.Warning (109, 4, Location, "The member `{0}' does not hide an inherited member. The new keyword is not required", + GetSignatureForError ()); + } else { + if ((ModFlags & Modifiers.NEW) == 0) { + if (candidate == null) + candidate = conflict_symbol; + + Report.SymbolRelatedToPreviousError (candidate); + Report.Warning (108, 2, Location, "`{0}' hides inherited member `{1}'. Use the new keyword if hiding was intended", + GetSignatureForError (), candidate.GetSignatureForError ()); + } + } + } + + // Run constraints check on all possible generic types + if (base_type != null && base_type_expr != null) { + ConstraintChecker.Check (this, base_type, base_type_expr.Location); + } + + if (iface_exprs != null) { + foreach (var iface_type in iface_exprs) { + if (iface_type == null) + continue; + + ConstraintChecker.Check (this, iface_type, Location); // TODO: Location is wrong + } + } + } + + if (all_tp_builders != null) { + int current_starts_index = CurrentTypeParametersStartIndex; + for (int i = 0; i < all_tp_builders.Length; i++) { + if (i < current_starts_index) { + all_type_parameters[i].EmitConstraints (all_tp_builders [i]); + } else { + var tp = CurrentTypeParameters [i - current_starts_index]; + tp.CheckGenericConstraints (!IsObsolete); + tp.Emit (); + } + } + } + + if ((ModFlags & Modifiers.COMPILER_GENERATED) != 0 && !Parent.IsCompilerGenerated) + Module.PredefinedAttributes.CompilerGenerated.EmitAttribute (TypeBuilder); + +#if STATIC + if ((TypeBuilder.Attributes & TypeAttributes.StringFormatMask) == 0 && Module.HasDefaultCharSet) + TypeBuilder.__SetAttributes (TypeBuilder.Attributes | Module.DefaultCharSetType); +#endif + + base.Emit (); + + for (int i = 0; i < members.Count; i++) { + var m = members[i]; + if ((m.caching_flags & Flags.CloseTypeCreated) != 0) + continue; + + m.Emit (); + } + + EmitIndexerName (); + CheckAttributeClsCompliance (); + + if (pending != null) + pending.VerifyPendingMethods (); + } + + + void CheckAttributeClsCompliance () + { + if (!spec.IsAttribute || !IsExposedFromAssembly () || !Compiler.Settings.VerifyClsCompliance || !IsClsComplianceRequired ()) + return; + + foreach (var m in members) { + var c = m as Constructor; + if (c == null) + continue; + + if (c.HasCompliantArgs) + return; + } + + Report.Warning (3015, 1, Location, "`{0}' has no accessible constructors which use only CLS-compliant types", GetSignatureForError ()); + } + + public sealed override void EmitContainer () + { + if ((caching_flags & Flags.CloseTypeCreated) != 0) + return; + + Emit (); + } + + public override void CloseContainer () + { + if ((caching_flags & Flags.CloseTypeCreated) != 0) + return; + + // Close base type container first to avoid TypeLoadException + if (spec.BaseType != null) { + var btype = spec.BaseType.MemberDefinition as TypeContainer; + if (btype != null) { + btype.CloseContainer (); + + if ((caching_flags & Flags.CloseTypeCreated) != 0) + return; + } + } + + try { + caching_flags |= Flags.CloseTypeCreated; + TypeBuilder.CreateType (); + } catch (TypeLoadException) { + // + // This is fine, the code still created the type + // + } catch (Exception e) { + throw new InternalErrorException (this, e); + } + + base.CloseContainer (); + + containers = null; + initialized_fields = null; + initialized_static_fields = null; + type_bases = null; + OptAttributes = null; + } + + // + // Performs the validation on a Method's modifiers (properties have + // the same properties). + // + // TODO: Why is it not done at parse stage, move to Modifiers::Check + // + public bool MethodModifiersValid (MemberCore mc) + { + const Modifiers vao = (Modifiers.VIRTUAL | Modifiers.ABSTRACT | Modifiers.OVERRIDE); + const Modifiers nv = (Modifiers.NEW | Modifiers.VIRTUAL); + bool ok = true; + var flags = mc.ModFlags; + + // + // At most one of static, virtual or override + // + if ((flags & Modifiers.STATIC) != 0){ + if ((flags & vao) != 0){ + Report.Error (112, mc.Location, "A static member `{0}' cannot be marked as override, virtual or abstract", + mc.GetSignatureForError ()); + ok = false; + } + } + + if ((flags & Modifiers.OVERRIDE) != 0 && (flags & nv) != 0){ + Report.Error (113, mc.Location, "A member `{0}' marked as override cannot be marked as new or virtual", + mc.GetSignatureForError ()); + ok = false; + } + + // + // If the declaration includes the abstract modifier, then the + // declaration does not include static, virtual or extern + // + if ((flags & Modifiers.ABSTRACT) != 0){ + if ((flags & Modifiers.EXTERN) != 0){ + Report.Error ( + 180, mc.Location, "`{0}' cannot be both extern and abstract", mc.GetSignatureForError ()); + ok = false; + } + + if ((flags & Modifiers.SEALED) != 0) { + Report.Error (502, mc.Location, "`{0}' cannot be both abstract and sealed", mc.GetSignatureForError ()); + ok = false; + } + + if ((flags & Modifiers.VIRTUAL) != 0){ + Report.Error (503, mc.Location, "The abstract method `{0}' cannot be marked virtual", mc.GetSignatureForError ()); + ok = false; + } + + if ((ModFlags & Modifiers.ABSTRACT) == 0){ + Report.SymbolRelatedToPreviousError (this); + Report.Error (513, mc.Location, "`{0}' is abstract but it is declared in the non-abstract class `{1}'", + mc.GetSignatureForError (), GetSignatureForError ()); + ok = false; + } + } + + if ((flags & Modifiers.PRIVATE) != 0){ + if ((flags & vao) != 0){ + Report.Error (621, mc.Location, "`{0}': virtual or abstract members cannot be private", mc.GetSignatureForError ()); + ok = false; + } + } + + if ((flags & Modifiers.SEALED) != 0){ + if ((flags & Modifiers.OVERRIDE) == 0){ + Report.Error (238, mc.Location, "`{0}' cannot be sealed because it is not an override", mc.GetSignatureForError ()); + ok = false; + } + } + + return ok; + } + + protected override bool VerifyClsCompliance () + { + if (!base.VerifyClsCompliance ()) + return false; + + // Check all container names for user classes + if (Kind != MemberKind.Delegate) + MemberCache.VerifyClsCompliance (Definition, Report); + + if (BaseType != null && !BaseType.IsCLSCompliant ()) { + Report.Warning (3009, 1, Location, "`{0}': base type `{1}' is not CLS-compliant", + GetSignatureForError (), BaseType.GetSignatureForError ()); + } + return true; + } + + /// + /// Performs checks for an explicit interface implementation. First it + /// checks whether the `interface_type' is a base inteface implementation. + /// Then it checks whether `name' exists in the interface type. + /// + public bool VerifyImplements (InterfaceMemberBase mb) + { + var ifaces = PartialContainer.Interfaces; + if (ifaces != null) { + foreach (TypeSpec t in ifaces){ + if (t == mb.InterfaceType) + return true; + + var expanded_base = t.Interfaces; + if (expanded_base == null) + continue; + + foreach (var bt in expanded_base) { + if (bt == mb.InterfaceType) + return true; + } + } + } + + Report.SymbolRelatedToPreviousError (mb.InterfaceType); + Report.Error (540, mb.Location, "`{0}': containing type does not implement interface `{1}'", + mb.GetSignatureForError (), mb.InterfaceType.GetSignatureForError ()); + return false; + } + + // + // Used for visiblity checks to tests whether this definition shares + // base type baseType, it does member-definition search + // + public bool IsBaseTypeDefinition (TypeSpec baseType) + { + // RootContext check + if (TypeBuilder == null) + return false; + + var type = spec; + do { + if (type.MemberDefinition == baseType.MemberDefinition) + return true; + + type = type.BaseType; + } while (type != null); + + return false; + } + + public override bool IsClsComplianceRequired () + { + if (IsPartialPart) + return PartialContainer.IsClsComplianceRequired (); + + return base.IsClsComplianceRequired (); + } + + bool ITypeDefinition.IsInternalAsPublic (IAssemblyDefinition assembly) + { + return Module.DeclaringAssembly == assembly; + } + + public virtual bool IsUnmanagedType () + { + return false; + } + + public void LoadMembers (TypeSpec declaringType, bool onlyTypes, ref MemberCache cache) + { + throw new NotSupportedException ("Not supported for compiled definition " + GetSignatureForError ()); + } + + // + // Public function used to locate types. + // + // Returns: Type or null if they type can not be found. + // + public override FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc) + { + FullNamedExpression e; + if (arity == 0 && Cache.TryGetValue (name, out e) && mode != LookupMode.IgnoreAccessibility) + return e; + + e = null; + + if (arity == 0) { + var tp = CurrentTypeParameters; + if (tp != null) { + TypeParameter tparam = tp.Find (name); + if (tparam != null) + e = new TypeParameterExpr (tparam, Location.Null); + } + } + + if (e == null) { + TypeSpec t = LookupNestedTypeInHierarchy (name, arity); + + if (t != null && (t.IsAccessible (this) || mode == LookupMode.IgnoreAccessibility)) + e = new TypeExpression (t, Location.Null); + else { + var errors = Compiler.Report.Errors; + e = Parent.LookupNamespaceOrType (name, arity, mode, loc); + + // TODO: LookupNamespaceOrType does more than just lookup. The result + // cannot be cached or the error reporting won't happen + if (errors != Compiler.Report.Errors) + return e; + } + } + + // TODO MemberCache: How to cache arity stuff ? + if (arity == 0 && mode == LookupMode.Normal) + Cache[name] = e; + + return e; + } + + TypeSpec LookupNestedTypeInHierarchy (string name, int arity) + { + // Has any nested type + // Does not work, because base type can have + //if (PartialContainer.Types == null) + // return null; + + var container = PartialContainer.CurrentType; + return MemberCache.FindNestedType (container, name, arity); + } + + public void Mark_HasEquals () + { + cached_method |= CachedMethods.Equals; + } + + public void Mark_HasGetHashCode () + { + cached_method |= CachedMethods.GetHashCode; + } + + public override void WriteDebugSymbol (MonoSymbolFile file) + { + if (IsPartialPart) + return; + + foreach (var m in members) { + m.WriteDebugSymbol (file); + } + } + + /// + /// Method container contains Equals method + /// + public bool HasEquals { + get { + return (cached_method & CachedMethods.Equals) != 0; + } + } + + /// + /// Method container contains GetHashCode method + /// + public bool HasGetHashCode { + get { + return (cached_method & CachedMethods.GetHashCode) != 0; + } + } + + public bool HasStaticFieldInitializer { + get { + return (cached_method & CachedMethods.HasStaticFieldInitializer) != 0; + } + set { + if (value) + cached_method |= CachedMethods.HasStaticFieldInitializer; + else + cached_method &= ~CachedMethods.HasStaticFieldInitializer; + } + } + + public override string DocCommentHeader { + get { return "T:"; } + } + } + + public abstract class ClassOrStruct : TypeDefinition + { + public const TypeAttributes StaticClassAttribute = TypeAttributes.Abstract | TypeAttributes.Sealed; + + SecurityType declarative_security; + protected Constructor generated_primary_constructor; + + protected ClassOrStruct (TypeContainer parent, MemberName name, Attributes attrs, MemberKind kind) + : base (parent, name, attrs, kind) + { + } + + public ToplevelBlock PrimaryConstructorBlock { get; set; } + + protected override TypeAttributes TypeAttr { + get { + TypeAttributes ta = base.TypeAttr; + if (!has_static_constructor) + ta |= TypeAttributes.BeforeFieldInit; + + if (Kind == MemberKind.Class) { + ta |= TypeAttributes.AutoLayout | TypeAttributes.Class; + if (IsStatic) + ta |= StaticClassAttribute; + } else { + ta |= TypeAttributes.SequentialLayout; + } + + return ta; + } + } + + public override void AddNameToContainer (MemberCore symbol, string name) + { + if (!(symbol is Constructor) && symbol.MemberName.Name == MemberName.Name) { + if (symbol is TypeParameter) { + Report.Error (694, symbol.Location, + "Type parameter `{0}' has same name as containing type, or method", + symbol.GetSignatureForError ()); + return; + } + + InterfaceMemberBase imb = symbol as InterfaceMemberBase; + if (imb == null || !imb.IsExplicitImpl) { + Report.SymbolRelatedToPreviousError (this); + Report.Error (542, symbol.Location, "`{0}': member names cannot be the same as their enclosing type", + symbol.GetSignatureForError ()); + return; + } + } + + base.AddNameToContainer (symbol, name); + } + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.IsValidSecurityAttribute ()) { + a.ExtractSecurityPermissionSet (ctor, ref declarative_security); + return; + } + + if (a.Type == pa.StructLayout) { + PartialContainer.HasStructLayout = true; + if (a.IsExplicitLayoutKind ()) + PartialContainer.HasExplicitLayout = true; + } + + if (a.Type == pa.Dynamic) { + a.Error_MisusedDynamicAttribute (); + return; + } + + base.ApplyAttributeBuilder (a, ctor, cdata, pa); + } + + /// + /// Defines the default constructors + /// + protected virtual Constructor DefineDefaultConstructor (bool is_static) + { + // The default instance constructor is public + // If the class is abstract, the default constructor is protected + // The default static constructor is private + + Modifiers mods; + ParametersCompiled parameters = null; + if (is_static) { + mods = Modifiers.STATIC | Modifiers.PRIVATE; + parameters = ParametersCompiled.EmptyReadOnlyParameters; + } else { + mods = ((ModFlags & Modifiers.ABSTRACT) != 0) ? Modifiers.PROTECTED : Modifiers.PUBLIC; + parameters = PrimaryConstructorParameters ?? ParametersCompiled.EmptyReadOnlyParameters; + } + + var c = new Constructor (this, MemberName.Name, mods, null, parameters, Location); + if (Kind == MemberKind.Class) + c.Initializer = new GeneratedBaseInitializer (Location, PrimaryConstructorBaseArguments); + + if (PrimaryConstructorParameters != null && !is_static) + c.IsPrimaryConstructor = true; + + AddConstructor (c, true); + if (PrimaryConstructorBlock == null) { + c.Block = new ToplevelBlock (Compiler, parameters, Location) { + IsCompilerGenerated = true + }; + } else { + c.Block = PrimaryConstructorBlock; + } + + return c; + } + + protected override bool DoDefineMembers () + { + CheckProtectedModifier (); + + if (PrimaryConstructorParameters != null) { + foreach (Parameter p in PrimaryConstructorParameters.FixedParameters) { + if (p.Name == MemberName.Name) { + Report.Error (8039, p.Location, "Primary constructor of type `{0}' has parameter of same name as containing type", + GetSignatureForError ()); + } + + if (CurrentTypeParameters != null) { + for (int i = 0; i < CurrentTypeParameters.Count; ++i) { + var tp = CurrentTypeParameters [i]; + if (p.Name == tp.Name) { + Report.Error (8038, p.Location, "Primary constructor of type `{0}' has parameter of same name as type parameter `{1}'", + GetSignatureForError (), p.GetSignatureForError ()); + } + } + } + } + } + + base.DoDefineMembers (); + + return true; + } + + public override void Emit () + { + if (!has_static_constructor && HasStaticFieldInitializer) { + var c = DefineDefaultConstructor (true); + c.Define (); + } + + base.Emit (); + + if (declarative_security != null) { + foreach (var de in declarative_security) { +#if STATIC + TypeBuilder.__AddDeclarativeSecurity (de); +#else + TypeBuilder.AddDeclarativeSecurity (de.Key, de.Value); +#endif + } + } + } + } + + + public sealed class Class : ClassOrStruct + { + const Modifiers AllowedModifiers = + Modifiers.NEW | + Modifiers.PUBLIC | + Modifiers.PROTECTED | + Modifiers.INTERNAL | + Modifiers.PRIVATE | + Modifiers.ABSTRACT | + Modifiers.SEALED | + Modifiers.STATIC | + Modifiers.UNSAFE; + + public Class (TypeContainer parent, MemberName name, Modifiers mod, Attributes attrs) + : base (parent, name, attrs, MemberKind.Class) + { + var accmods = IsTopLevel ? Modifiers.INTERNAL : Modifiers.PRIVATE; + this.ModFlags = ModifiersExtensions.Check (AllowedModifiers, mod, accmods, Location, Report); + spec = new TypeSpec (Kind, null, this, null, ModFlags); + } + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + + public override void SetBaseTypes (List baseTypes) + { + var pmn = MemberName; + if (pmn.Name == "Object" && !pmn.IsGeneric && Parent.MemberName.Name == "System" && Parent.MemberName.Left == null) + Report.Error (537, Location, + "The class System.Object cannot have a base class or implement an interface."); + + base.SetBaseTypes (baseTypes); + } + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.Type == pa.AttributeUsage) { + if (!BaseType.IsAttribute && spec.BuiltinType != BuiltinTypeSpec.Type.Attribute) { + Report.Error (641, a.Location, "Attribute `{0}' is only valid on classes derived from System.Attribute", a.GetSignatureForError ()); + } + } + + if (a.Type == pa.Conditional && !BaseType.IsAttribute) { + Report.Error (1689, a.Location, "Attribute `System.Diagnostics.ConditionalAttribute' is only valid on methods or attribute classes"); + return; + } + + if (a.Type == pa.ComImport && !attributes.Contains (pa.Guid)) { + a.Error_MissingGuidAttribute (); + return; + } + + if (a.Type == pa.Extension) { + a.Error_MisusedExtensionAttribute (); + return; + } + + if (a.Type.IsConditionallyExcluded (this)) + return; + + base.ApplyAttributeBuilder (a, ctor, cdata, pa); + } + + public override AttributeTargets AttributeTargets { + get { + return AttributeTargets.Class; + } + } + + protected override bool DoDefineMembers () + { + if ((ModFlags & Modifiers.ABSTRACT) == Modifiers.ABSTRACT && (ModFlags & (Modifiers.SEALED | Modifiers.STATIC)) != 0) { + Report.Error (418, Location, "`{0}': an abstract class cannot be sealed or static", GetSignatureForError ()); + } + + if ((ModFlags & (Modifiers.SEALED | Modifiers.STATIC)) == (Modifiers.SEALED | Modifiers.STATIC)) { + Report.Error (441, Location, "`{0}': a class cannot be both static and sealed", GetSignatureForError ()); + } + + if (IsStatic) { + if (PrimaryConstructorParameters != null) { + Report.Error (-800, Location, "`{0}': Static classes cannot have primary constructor", GetSignatureForError ()); + PrimaryConstructorParameters = null; + } + + foreach (var m in Members) { + if (m is Operator) { + Report.Error (715, m.Location, "`{0}': Static classes cannot contain user-defined operators", m.GetSignatureForError ()); + continue; + } + + if (m is Destructor) { + Report.Error (711, m.Location, "`{0}': Static classes cannot contain destructor", GetSignatureForError ()); + continue; + } + + if (m is Indexer) { + Report.Error (720, m.Location, "`{0}': cannot declare indexers in a static class", m.GetSignatureForError ()); + continue; + } + + if ((m.ModFlags & Modifiers.STATIC) != 0 || m is TypeContainer) + continue; + + if (m is Constructor) { + Report.Error (710, m.Location, "`{0}': Static classes cannot have instance constructors", GetSignatureForError ()); + continue; + } + + Report.Error (708, m.Location, "`{0}': cannot declare instance members in a static class", m.GetSignatureForError ()); + } + } else { + if (!PartialContainer.HasInstanceConstructor || PrimaryConstructorParameters != null) + generated_primary_constructor = DefineDefaultConstructor (false); + } + + return base.DoDefineMembers (); + } + + public override void Emit () + { + base.Emit (); + + if ((ModFlags & Modifiers.METHOD_EXTENSION) != 0) + Module.PredefinedAttributes.Extension.EmitAttribute (TypeBuilder); + + if (base_type != null && base_type.HasDynamicElement) { + Module.PredefinedAttributes.Dynamic.EmitAttribute (TypeBuilder, base_type, Location); + } + } + + public override void GetCompletionStartingWith (string prefix, List results) + { + base.GetCompletionStartingWith (prefix, results); + + var bt = base_type; + while (bt != null) { + results.AddRange (MemberCache.GetCompletitionMembers (this, bt, prefix).Where (l => l.IsStatic).Select (l => l.Name)); + bt = bt.BaseType; + } + } + + protected override TypeSpec[] ResolveBaseTypes (out FullNamedExpression base_class) + { + var ifaces = base.ResolveBaseTypes (out base_class); + + if (base_class == null) { + if (spec.BuiltinType != BuiltinTypeSpec.Type.Object) + base_type = Compiler.BuiltinTypes.Object; + } else { + if (base_type.IsGenericParameter){ + Report.Error (689, base_class.Location, "`{0}': Cannot derive from type parameter `{1}'", + GetSignatureForError (), base_type.GetSignatureForError ()); + } else if (base_type.IsStatic) { + Report.SymbolRelatedToPreviousError (base_type); + Report.Error (709, Location, "`{0}': Cannot derive from static class `{1}'", + GetSignatureForError (), base_type.GetSignatureForError ()); + } else if (base_type.IsSealed) { + Report.SymbolRelatedToPreviousError (base_type); + Report.Error (509, Location, "`{0}': cannot derive from sealed type `{1}'", + GetSignatureForError (), base_type.GetSignatureForError ()); + } else if (PartialContainer.IsStatic && base_type.BuiltinType != BuiltinTypeSpec.Type.Object) { + Report.Error (713, Location, "Static class `{0}' cannot derive from type `{1}'. Static classes must derive from object", + GetSignatureForError (), base_type.GetSignatureForError ()); + } + + switch (base_type.BuiltinType) { + case BuiltinTypeSpec.Type.Enum: + case BuiltinTypeSpec.Type.ValueType: + case BuiltinTypeSpec.Type.MulticastDelegate: + case BuiltinTypeSpec.Type.Delegate: + case BuiltinTypeSpec.Type.Array: + if (!(spec is BuiltinTypeSpec)) { + Report.Error (644, Location, "`{0}' cannot derive from special class `{1}'", + GetSignatureForError (), base_type.GetSignatureForError ()); + + base_type = Compiler.BuiltinTypes.Object; + } + break; + } + + if (!IsAccessibleAs (base_type)) { + Report.SymbolRelatedToPreviousError (base_type); + Report.Error (60, Location, "Inconsistent accessibility: base class `{0}' is less accessible than class `{1}'", + base_type.GetSignatureForError (), GetSignatureForError ()); + } + } + + if (PartialContainer.IsStatic && ifaces != null) { + foreach (var t in ifaces) + Report.SymbolRelatedToPreviousError (t); + Report.Error (714, Location, "Static class `{0}' cannot implement interfaces", GetSignatureForError ()); + } + + return ifaces; + } + + /// Search for at least one defined condition in ConditionalAttribute of attribute class + /// Valid only for attribute classes. + public override string[] ConditionalConditions () + { + if ((caching_flags & (Flags.Excluded_Undetected | Flags.Excluded)) == 0) + return null; + + caching_flags &= ~Flags.Excluded_Undetected; + + if (OptAttributes == null) + return null; + + Attribute[] attrs = OptAttributes.SearchMulti (Module.PredefinedAttributes.Conditional); + if (attrs == null) + return null; + + string[] conditions = new string[attrs.Length]; + for (int i = 0; i < conditions.Length; ++i) + conditions[i] = attrs[i].GetConditionalAttributeValue (); + + caching_flags |= Flags.Excluded; + return conditions; + } + } + + public sealed class Struct : ClassOrStruct + { + bool is_unmanaged, has_unmanaged_check_done; + bool InTransit; + + // + // Modifiers allowed in a struct declaration + // + const Modifiers AllowedModifiers = + Modifiers.NEW | + Modifiers.PUBLIC | + Modifiers.PROTECTED | + Modifiers.INTERNAL | + Modifiers.UNSAFE | + Modifiers.PRIVATE; + + public Struct (TypeContainer parent, MemberName name, Modifiers mod, Attributes attrs) + : base (parent, name, attrs, MemberKind.Struct) + { + var accmods = IsTopLevel ? Modifiers.INTERNAL : Modifiers.PRIVATE; + this.ModFlags = ModifiersExtensions.Check (AllowedModifiers, mod, accmods, Location, Report) | Modifiers.SEALED ; + spec = new TypeSpec (Kind, null, this, null, ModFlags); + } + + public override AttributeTargets AttributeTargets { + get { + return AttributeTargets.Struct; + } + } + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + base.ApplyAttributeBuilder (a, ctor, cdata, pa); + + // + // When struct constains fixed fixed and struct layout has explicitly + // set CharSet, its value has to be propagated to compiler generated + // fixed types + // + if (a.Type == pa.StructLayout) { + var value = a.GetNamedValue ("CharSet"); + if (value == null) + return; + + for (int i = 0; i < Members.Count; ++i) { + FixedField ff = Members [i] as FixedField; + if (ff == null) + continue; + + ff.CharSet = (CharSet) System.Enum.Parse (typeof (CharSet), value.GetValue ().ToString ()); + } + } + } + + bool CheckStructCycles () + { + if (InTransit) + return false; + + InTransit = true; + foreach (var member in Members) { + var field = member as Field; + if (field == null) + continue; + + TypeSpec ftype = field.Spec.MemberType; + if (!ftype.IsStruct) + continue; + + if (ftype is BuiltinTypeSpec) + continue; + + foreach (var targ in ftype.TypeArguments) { + if (!CheckFieldTypeCycle (targ)) { + Report.Error (523, field.Location, + "Struct member `{0}' of type `{1}' causes a cycle in the struct layout", + field.GetSignatureForError (), ftype.GetSignatureForError ()); + break; + } + } + + // + // Static fields of exactly same type are allowed + // + if (field.IsStatic && ftype == CurrentType) + continue; + + if (!CheckFieldTypeCycle (ftype)) { + Report.Error (523, field.Location, + "Struct member `{0}' of type `{1}' causes a cycle in the struct layout", + field.GetSignatureForError (), ftype.GetSignatureForError ()); + break; + } + } + + InTransit = false; + return true; + } + + static bool CheckFieldTypeCycle (TypeSpec ts) + { + var fts = ts.MemberDefinition as Struct; + if (fts == null) + return true; + + return fts.CheckStructCycles (); + } + + protected override bool DoDefineMembers () + { + if (PrimaryConstructorParameters != null) + generated_primary_constructor = DefineDefaultConstructor (false); + + return base.DoDefineMembers (); + } + + public override void Emit () + { + CheckStructCycles (); + + base.Emit (); + } + + bool HasExplicitConstructor () + { + foreach (var m in Members) { + var c = m as Constructor; + if (c == null) + continue; + + if (!c.ParameterInfo.IsEmpty) + return true; + } + + return false; + } + + public override bool IsUnmanagedType () + { + if (has_unmanaged_check_done) + return is_unmanaged; + + if (requires_delayed_unmanagedtype_check) + return true; + + var parent_def = Parent.PartialContainer; + if (parent_def != null && parent_def.IsGenericOrParentIsGeneric) { + has_unmanaged_check_done = true; + return false; + } + + if (first_nonstatic_field != null) { + requires_delayed_unmanagedtype_check = true; + + foreach (var member in Members) { + var f = member as Field; + if (f == null) + continue; + + if (f.IsStatic) + continue; + + // It can happen when recursive unmanaged types are defined + // struct S { S* s; } + TypeSpec mt = f.MemberType; + if (mt == null) { + return true; + } + + if (mt.IsUnmanaged) + continue; + + has_unmanaged_check_done = true; + return false; + } + + has_unmanaged_check_done = true; + } + + is_unmanaged = true; + return true; + } + + protected override TypeSpec[] ResolveBaseTypes (out FullNamedExpression base_class) + { + var ifaces = base.ResolveBaseTypes (out base_class); + base_type = Compiler.BuiltinTypes.ValueType; + return ifaces; + } + + public override void RegisterFieldForInitialization (MemberCore field, FieldInitializer expression) + { + if ((field.ModFlags & Modifiers.STATIC) == 0 && !HasExplicitConstructor ()) { + Report.Error (8054, field.Location, "`{0}': Structs without explicit constructors cannot contain members with initializers", + field.GetSignatureForError ()); + + return; + } + + base.RegisterFieldForInitialization (field, expression); + } + } + + /// + /// Interfaces + /// + public sealed class Interface : TypeDefinition { + + /// + /// Modifiers allowed in a class declaration + /// + const Modifiers AllowedModifiers = + Modifiers.NEW | + Modifiers.PUBLIC | + Modifiers.PROTECTED | + Modifiers.INTERNAL | + Modifiers.UNSAFE | + Modifiers.PRIVATE; + + public Interface (TypeContainer parent, MemberName name, Modifiers mod, Attributes attrs) + : base (parent, name, attrs, MemberKind.Interface) + { + var accmods = IsTopLevel ? Modifiers.INTERNAL : Modifiers.PRIVATE; + + this.ModFlags = ModifiersExtensions.Check (AllowedModifiers, mod, accmods, name.Location, Report); + spec = new TypeSpec (Kind, null, this, null, ModFlags); + } + + #region Properties + + public override AttributeTargets AttributeTargets { + get { + return AttributeTargets.Interface; + } + } + + protected override TypeAttributes TypeAttr { + get { + const TypeAttributes DefaultTypeAttributes = + TypeAttributes.AutoLayout | + TypeAttributes.Abstract | + TypeAttributes.Interface; + + return base.TypeAttr | DefaultTypeAttributes; + } + } + + #endregion + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.Type == pa.ComImport && !attributes.Contains (pa.Guid)) { + a.Error_MissingGuidAttribute (); + return; + } + + base.ApplyAttributeBuilder (a, ctor, cdata, pa); + } + + protected override bool VerifyClsCompliance () + { + if (!base.VerifyClsCompliance ()) + return false; + + if (iface_exprs != null) { + foreach (var iface in iface_exprs) { + if (iface.IsCLSCompliant ()) + continue; + + Report.SymbolRelatedToPreviousError (iface); + Report.Warning (3027, 1, Location, "`{0}' is not CLS-compliant because base interface `{1}' is not CLS-compliant", + GetSignatureForError (), iface.GetSignatureForError ()); + } + } + + return true; + } + } + + public abstract class InterfaceMemberBase : MemberBase + { + // + // Common modifiers allowed in a class declaration + // + protected const Modifiers AllowedModifiersClass = + Modifiers.NEW | + Modifiers.PUBLIC | + Modifiers.PROTECTED | + Modifiers.INTERNAL | + Modifiers.PRIVATE | + Modifiers.STATIC | + Modifiers.VIRTUAL | + Modifiers.SEALED | + Modifiers.OVERRIDE | + Modifiers.ABSTRACT | + Modifiers.UNSAFE | + Modifiers.EXTERN; + + // + // Common modifiers allowed in a struct declaration + // + protected const Modifiers AllowedModifiersStruct = + Modifiers.NEW | + Modifiers.PUBLIC | + Modifiers.PROTECTED | + Modifiers.INTERNAL | + Modifiers.PRIVATE | + Modifiers.STATIC | + Modifiers.OVERRIDE | + Modifiers.UNSAFE | + Modifiers.EXTERN; + + // + // Common modifiers allowed in a interface declaration + // + protected const Modifiers AllowedModifiersInterface = + Modifiers.NEW | + Modifiers.UNSAFE; + + // + // Whether this is an interface member. + // + public bool IsInterface; + + // + // If true, this is an explicit interface implementation + // + public readonly bool IsExplicitImpl; + + protected bool is_external_implementation; + + // + // The interface type we are explicitly implementing + // + public TypeSpec InterfaceType; + + // + // The method we're overriding if this is an override method. + // + protected MethodSpec base_method; + + readonly Modifiers explicit_mod_flags; + public MethodAttributes flags; + + protected InterfaceMemberBase (TypeDefinition parent, FullNamedExpression type, Modifiers mod, Modifiers allowed_mod, MemberName name, Attributes attrs) + : base (parent, type, mod, allowed_mod, Modifiers.PRIVATE, name, attrs) + { + IsInterface = parent.Kind == MemberKind.Interface; + IsExplicitImpl = (MemberName.ExplicitInterface != null); + explicit_mod_flags = mod; + } + + public abstract Variance ExpectedMemberTypeVariance { get; } + + protected override bool CheckBase () + { + if (!base.CheckBase ()) + return false; + + if ((caching_flags & Flags.MethodOverloadsExist) != 0) + CheckForDuplications (); + + if (IsExplicitImpl) + return true; + + // For System.Object only + if (Parent.BaseType == null) + return true; + + MemberSpec candidate; + bool overrides = false; + var base_member = FindBaseMember (out candidate, ref overrides); + + if ((ModFlags & Modifiers.OVERRIDE) != 0) { + if (base_member == null) { + if (candidate == null) { + if (this is Method && ((Method)this).ParameterInfo.IsEmpty && MemberName.Name == Destructor.MetadataName && MemberName.Arity == 0) { + Report.Error (249, Location, "Do not override `{0}'. Use destructor syntax instead", + "object.Finalize()"); + } else { + Report.Error (115, Location, "`{0}' is marked as an override but no suitable {1} found to override", + GetSignatureForError (), SimpleName.GetMemberType (this)); + } + } else { + Report.SymbolRelatedToPreviousError (candidate); + if (this is Event) + Report.Error (72, Location, "`{0}': cannot override because `{1}' is not an event", + GetSignatureForError (), TypeManager.GetFullNameSignature (candidate)); + else if (this is PropertyBase) + Report.Error (544, Location, "`{0}': cannot override because `{1}' is not a property", + GetSignatureForError (), TypeManager.GetFullNameSignature (candidate)); + else + Report.Error (505, Location, "`{0}': cannot override because `{1}' is not a method", + GetSignatureForError (), TypeManager.GetFullNameSignature (candidate)); + } + + return false; + } + + // + // Handles ambiguous overrides + // + if (candidate != null) { + Report.SymbolRelatedToPreviousError (candidate); + Report.SymbolRelatedToPreviousError (base_member); + + // Get member definition for error reporting + var m1 = MemberCache.GetMember (base_member.DeclaringType.GetDefinition (), base_member); + var m2 = MemberCache.GetMember (candidate.DeclaringType.GetDefinition (), candidate); + + Report.Error (462, Location, + "`{0}' cannot override inherited members `{1}' and `{2}' because they have the same signature when used in type `{3}'", + GetSignatureForError (), m1.GetSignatureForError (), m2.GetSignatureForError (), Parent.GetSignatureForError ()); + } + + if (!CheckOverrideAgainstBase (base_member)) + return false; + + ObsoleteAttribute oa = base_member.GetAttributeObsolete (); + if (oa != null) { + if (OptAttributes == null || !OptAttributes.Contains (Module.PredefinedAttributes.Obsolete)) { + Report.SymbolRelatedToPreviousError (base_member); + Report.Warning (672, 1, Location, "Member `{0}' overrides obsolete member `{1}'. Add the Obsolete attribute to `{0}'", + GetSignatureForError (), base_member.GetSignatureForError ()); + } + } else { + if (OptAttributes != null && OptAttributes.Contains (Module.PredefinedAttributes.Obsolete)) { + Report.SymbolRelatedToPreviousError (base_member); + Report.Warning (809, 1, Location, "Obsolete member `{0}' overrides non-obsolete member `{1}'", + GetSignatureForError (), base_member.GetSignatureForError ()); + } + } + + base_method = base_member as MethodSpec; + return true; + } + + if (base_member == null && candidate != null && (!(candidate is IParametersMember) || !(this is IParametersMember))) + base_member = candidate; + + if (base_member == null) { + if ((ModFlags & Modifiers.NEW) != 0) { + if (base_member == null) { + Report.Warning (109, 4, Location, "The member `{0}' does not hide an inherited member. The new keyword is not required", + GetSignatureForError ()); + } + } + } else { + if ((ModFlags & Modifiers.NEW) == 0) { + ModFlags |= Modifiers.NEW; + if (!IsCompilerGenerated) { + Report.SymbolRelatedToPreviousError (base_member); + if (!IsInterface && (base_member.Modifiers & (Modifiers.ABSTRACT | Modifiers.VIRTUAL | Modifiers.OVERRIDE)) != 0) { + Report.Warning (114, 2, Location, "`{0}' hides inherited member `{1}'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword", + GetSignatureForError (), base_member.GetSignatureForError ()); + } else { + Report.Warning (108, 2, Location, "`{0}' hides inherited member `{1}'. Use the new keyword if hiding was intended", + GetSignatureForError (), base_member.GetSignatureForError ()); + } + } + } + + if (!IsInterface && base_member.IsAbstract && !overrides && !IsStatic) { + Report.SymbolRelatedToPreviousError (base_member); + Report.Error (533, Location, "`{0}' hides inherited abstract member `{1}'", + GetSignatureForError (), base_member.GetSignatureForError ()); + } + } + + return true; + } + + protected virtual bool CheckForDuplications () + { + return Parent.MemberCache.CheckExistingMembersOverloads (this, ParametersCompiled.EmptyReadOnlyParameters); + } + + // + // Performs various checks on the MethodInfo `mb' regarding the modifier flags + // that have been defined. + // + protected virtual bool CheckOverrideAgainstBase (MemberSpec base_member) + { + bool ok = true; + + if ((base_member.Modifiers & (Modifiers.ABSTRACT | Modifiers.VIRTUAL | Modifiers.OVERRIDE)) == 0) { + Report.SymbolRelatedToPreviousError (base_member); + Report.Error (506, Location, + "`{0}': cannot override inherited member `{1}' because it is not marked virtual, abstract or override", + GetSignatureForError (), TypeManager.CSharpSignature (base_member)); + ok = false; + } + + // Now we check that the overriden method is not final + if ((base_member.Modifiers & Modifiers.SEALED) != 0) { + Report.SymbolRelatedToPreviousError (base_member); + Report.Error (239, Location, "`{0}': cannot override inherited member `{1}' because it is sealed", + GetSignatureForError (), TypeManager.CSharpSignature (base_member)); + ok = false; + } + + var base_member_type = ((IInterfaceMemberSpec) base_member).MemberType; + if (!TypeSpecComparer.Override.IsEqual (MemberType, base_member_type)) { + Report.SymbolRelatedToPreviousError (base_member); + if (this is PropertyBasedMember) { + Report.Error (1715, Location, "`{0}': type must be `{1}' to match overridden member `{2}'", + GetSignatureForError (), base_member_type.GetSignatureForError (), base_member.GetSignatureForError ()); + } else { + Report.Error (508, Location, "`{0}': return type must be `{1}' to match overridden member `{2}'", + GetSignatureForError (), base_member_type.GetSignatureForError (), base_member.GetSignatureForError ()); + } + ok = false; + } + + return ok; + } + + protected static bool CheckAccessModifiers (MemberCore this_member, MemberSpec base_member) + { + var thisp = this_member.ModFlags & Modifiers.AccessibilityMask; + var base_classp = base_member.Modifiers & Modifiers.AccessibilityMask; + + if ((base_classp & (Modifiers.PROTECTED | Modifiers.INTERNAL)) == (Modifiers.PROTECTED | Modifiers.INTERNAL)) { + // + // It must be at least "protected" + // + if ((thisp & Modifiers.PROTECTED) == 0) { + return false; + } + + // + // when overriding protected internal, the method can be declared + // protected internal only within the same assembly or assembly + // which has InternalsVisibleTo + // + if ((thisp & Modifiers.INTERNAL) != 0) { + return base_member.DeclaringType.MemberDefinition.IsInternalAsPublic (this_member.Module.DeclaringAssembly); + } + + // + // protected overriding protected internal inside same assembly + // requires internal modifier as well + // + if (base_member.DeclaringType.MemberDefinition.IsInternalAsPublic (this_member.Module.DeclaringAssembly)) { + return false; + } + + return true; + } + + return thisp == base_classp; + } + + public override bool Define () + { + if (IsInterface) { + ModFlags = Modifiers.PUBLIC | Modifiers.ABSTRACT | + Modifiers.VIRTUAL | (ModFlags & (Modifiers.UNSAFE | Modifiers.NEW)); + + flags = MethodAttributes.Public | + MethodAttributes.Abstract | + MethodAttributes.HideBySig | + MethodAttributes.NewSlot | + MethodAttributes.Virtual; + } else { + Parent.PartialContainer.MethodModifiersValid (this); + + flags = ModifiersExtensions.MethodAttr (ModFlags); + } + + if (IsExplicitImpl) { + InterfaceType = MemberName.ExplicitInterface.ResolveAsType (Parent); + if (InterfaceType == null) + return false; + + if ((ModFlags & Modifiers.PARTIAL) != 0) { + Report.Error (754, Location, "A partial method `{0}' cannot explicitly implement an interface", + GetSignatureForError ()); + } + + if (!InterfaceType.IsInterface) { + Report.SymbolRelatedToPreviousError (InterfaceType); + Report.Error (538, Location, "The type `{0}' in explicit interface declaration is not an interface", + InterfaceType.GetSignatureForError ()); + } else { + Parent.PartialContainer.VerifyImplements (this); + } + + Modifiers allowed_explicit = Modifiers.AllowedExplicitImplFlags; + if (this is Method) + allowed_explicit |= Modifiers.ASYNC; + + ModifiersExtensions.Check (allowed_explicit, explicit_mod_flags, 0, Location, Report); + } + + return base.Define (); + } + + protected bool DefineParameters (ParametersCompiled parameters) + { + if (!parameters.Resolve (this)) + return false; + + bool error = false; + for (int i = 0; i < parameters.Count; ++i) { + Parameter p = parameters [i]; + + if (p.HasDefaultValue && (IsExplicitImpl || this is Operator || (this is Indexer && parameters.Count == 1))) + p.Warning_UselessOptionalParameter (Report); + + if (p.CheckAccessibility (this)) + continue; + + TypeSpec t = parameters.Types [i]; + Report.SymbolRelatedToPreviousError (t); + if (this is Indexer) + Report.Error (55, Location, + "Inconsistent accessibility: parameter type `{0}' is less accessible than indexer `{1}'", + t.GetSignatureForError (), GetSignatureForError ()); + else if (this is Operator) + Report.Error (57, Location, + "Inconsistent accessibility: parameter type `{0}' is less accessible than operator `{1}'", + t.GetSignatureForError (), GetSignatureForError ()); + else + Report.Error (51, Location, + "Inconsistent accessibility: parameter type `{0}' is less accessible than method `{1}'", + t.GetSignatureForError (), GetSignatureForError ()); + error = true; + } + return !error; + } + + protected override void DoMemberTypeDependentChecks () + { + base.DoMemberTypeDependentChecks (); + + VarianceDecl.CheckTypeVariance (MemberType, ExpectedMemberTypeVariance, this); + } + + public override void Emit() + { + // for extern static method must be specified either DllImport attribute or MethodImplAttribute. + // We are more strict than csc and report this as an error because SRE does not allow emit that + if ((ModFlags & Modifiers.EXTERN) != 0 && !is_external_implementation && (OptAttributes == null || !OptAttributes.HasResolveError ())) { + if (this is Constructor) { + Report.Warning (824, 1, Location, + "Constructor `{0}' is marked `external' but has no external implementation specified", GetSignatureForError ()); + } else { + Report.Warning (626, 1, Location, + "`{0}' is marked as an external but has no DllImport attribute. Consider adding a DllImport attribute to specify the external implementation", + GetSignatureForError ()); + } + } + + base.Emit (); + } + + public override bool EnableOverloadChecks (MemberCore overload) + { + // + // Two members can differ in their explicit interface + // type parameter only + // + InterfaceMemberBase imb = overload as InterfaceMemberBase; + if (imb != null && imb.IsExplicitImpl) { + if (IsExplicitImpl) { + caching_flags |= Flags.MethodOverloadsExist; + } + return true; + } + + return IsExplicitImpl; + } + + protected void Error_CannotChangeAccessModifiers (MemberCore member, MemberSpec base_member) + { + var base_modifiers = base_member.Modifiers; + + // Remove internal modifier from types which are not internally accessible + if ((base_modifiers & Modifiers.AccessibilityMask) == (Modifiers.PROTECTED | Modifiers.INTERNAL) && + !base_member.DeclaringType.MemberDefinition.IsInternalAsPublic (member.Module.DeclaringAssembly)) + base_modifiers = Modifiers.PROTECTED; + + Report.SymbolRelatedToPreviousError (base_member); + Report.Error (507, member.Location, + "`{0}': cannot change access modifiers when overriding `{1}' inherited member `{2}'", + member.GetSignatureForError (), + ModifiersExtensions.AccessibilityName (base_modifiers), + base_member.GetSignatureForError ()); + } + + protected void Error_StaticReturnType () + { + Report.Error (722, Location, + "`{0}': static types cannot be used as return types", + MemberType.GetSignatureForError ()); + } + + /// + /// Gets base method and its return type + /// + protected virtual MemberSpec FindBaseMember (out MemberSpec bestCandidate, ref bool overrides) + { + return MemberCache.FindBaseMember (this, out bestCandidate, ref overrides); + } + + // + // The "short" name of this property / indexer / event. This is the + // name without the explicit interface. + // + public string ShortName { + get { return MemberName.Name; } + } + + // + // Returns full metadata method name + // + public string GetFullName (MemberName name) + { + return GetFullName (name.Name); + } + + public string GetFullName (string name) + { + if (!IsExplicitImpl) + return name; + + // + // When dealing with explicit members a full interface type + // name is added to member name to avoid possible name conflicts + // + // We use CSharpName which gets us full name with benefit of + // replacing predefined names which saves some space and name + // is still unique + // + return InterfaceType.GetSignatureForError () + "." + name; + } + + public override string GetSignatureForDocumentation () + { + if (IsExplicitImpl) + return Parent.GetSignatureForDocumentation () + "." + InterfaceType.GetExplicitNameSignatureForDocumentation () + "#" + ShortName; + + return Parent.GetSignatureForDocumentation () + "." + ShortName; + } + + public override bool IsUsed + { + get { return IsExplicitImpl || base.IsUsed; } + } + + public override void SetConstraints (List constraints_list) + { + if (((ModFlags & Modifiers.OVERRIDE) != 0 || IsExplicitImpl)) { + Report.Error (460, Location, + "`{0}': Cannot specify constraints for overrides and explicit interface implementation methods", + GetSignatureForError ()); + } + + base.SetConstraints (constraints_list); + } + } + + public abstract class MemberBase : MemberCore + { + protected FullNamedExpression type_expr; + protected TypeSpec member_type; + public new TypeDefinition Parent; + + protected MemberBase (TypeDefinition parent, FullNamedExpression type, Modifiers mod, Modifiers allowed_mod, Modifiers def_mod, MemberName name, Attributes attrs) + : base (parent, name, attrs) + { + this.Parent = parent; + this.type_expr = type; + + if (name != MemberName.Null) + ModFlags = ModifiersExtensions.Check (allowed_mod, mod, def_mod, Location, Report); + } + + #region Properties + + public TypeSpec MemberType { + get { + return member_type; + } + } + + public FullNamedExpression TypeExpression { + get { + return type_expr; + } + } + + #endregion + + // + // Main member define entry + // + public override bool Define () + { + DoMemberTypeIndependentChecks (); + + // + // Returns false only when type resolution failed + // + if (!ResolveMemberType ()) + return false; + + DoMemberTypeDependentChecks (); + return true; + } + + // + // Any type_name independent checks + // + protected virtual void DoMemberTypeIndependentChecks () + { + if ((Parent.ModFlags & Modifiers.SEALED) != 0 && + (ModFlags & (Modifiers.VIRTUAL | Modifiers.ABSTRACT)) != 0) { + Report.Error (549, Location, "New virtual member `{0}' is declared in a sealed class `{1}'", + GetSignatureForError (), Parent.GetSignatureForError ()); + } + } + + // + // Any type_name dependent checks + // + protected virtual void DoMemberTypeDependentChecks () + { + // verify accessibility + if (!IsAccessibleAs (MemberType)) { + Report.SymbolRelatedToPreviousError (MemberType); + if (this is Property) + Report.Error (53, Location, + "Inconsistent accessibility: property type `" + + MemberType.GetSignatureForError () + "' is less " + + "accessible than property `" + GetSignatureForError () + "'"); + else if (this is Indexer) + Report.Error (54, Location, + "Inconsistent accessibility: indexer return type `" + + MemberType.GetSignatureForError () + "' is less " + + "accessible than indexer `" + GetSignatureForError () + "'"); + else if (this is MethodCore) { + if (this is Operator) + Report.Error (56, Location, + "Inconsistent accessibility: return type `" + + MemberType.GetSignatureForError () + "' is less " + + "accessible than operator `" + GetSignatureForError () + "'"); + else + Report.Error (50, Location, + "Inconsistent accessibility: return type `" + + MemberType.GetSignatureForError () + "' is less " + + "accessible than method `" + GetSignatureForError () + "'"); + } else if (this is Event) { + Report.Error (7025, Location, + "Inconsistent accessibility: event type `{0}' is less accessible than event `{1}'", + MemberType.GetSignatureForError (), GetSignatureForError ()); + } else { + Report.Error (52, Location, + "Inconsistent accessibility: field type `" + + MemberType.GetSignatureForError () + "' is less " + + "accessible than field `" + GetSignatureForError () + "'"); + } + } + } + + protected void IsTypePermitted () + { + if (MemberType.IsSpecialRuntimeType) { + if (Parent is StateMachine) { + Report.Error (4012, Location, + "Parameters or local variables of type `{0}' cannot be declared in async methods or iterators", + MemberType.GetSignatureForError ()); + } else if (Parent is HoistedStoreyClass) { + Report.Error (4013, Location, + "Local variables of type `{0}' cannot be used inside anonymous methods, lambda expressions or query expressions", + MemberType.GetSignatureForError ()); + } else { + Report.Error (610, Location, + "Field or property cannot be of type `{0}'", MemberType.GetSignatureForError ()); + } + } + } + + protected virtual bool CheckBase () + { + CheckProtectedModifier (); + + return true; + } + + public override string GetSignatureForDocumentation () + { + return Parent.GetSignatureForDocumentation () + "." + MemberName.Basename; + } + + protected virtual bool ResolveMemberType () + { + if (member_type != null) + throw new InternalErrorException ("Multi-resolve"); + + member_type = type_expr.ResolveAsType (this); + return member_type != null; + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/codegen.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/codegen.cs new file mode 100644 index 000000000..55a061cf0 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/codegen.cs @@ -0,0 +1,1365 @@ +// +// codegen.cs: The code generator +// +// Authors: +// Miguel de Icaza (miguel@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// Copyright 2001, 2002, 2003 Ximian, Inc. +// Copyright 2004 Novell, Inc. +// Copyright 2011 Xamarin Inc +// + +using System; +using System.Collections.Generic; +using Mono.CompilerServices.SymbolWriter; + +#if STATIC +using MetaType = IKVM.Reflection.Type; +using IKVM.Reflection; +using IKVM.Reflection.Emit; +#else +using MetaType = System.Type; +using System.Reflection; +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp +{ + /// + /// An Emit Context is created for each body of code (from methods, + /// properties bodies, indexer bodies or constructor bodies) + /// + public class EmitContext : BuilderContext + { + // TODO: Has to be private + public readonly ILGenerator ig; + + /// + /// The value that is allowed to be returned or NULL if there is no + /// return type. + /// + readonly TypeSpec return_type; + + /// + /// Keeps track of the Type to LocalBuilder temporary storage created + /// to store structures (used to compute the address of the structure + /// value on structure method invocations) + /// + Dictionary temporary_storage; + + /// + /// The location where we store the return value. + /// + public LocalBuilder return_value; + + + /// + /// Current loop begin and end labels. + /// + public Label LoopBegin, LoopEnd; + + /// + /// Default target in a switch statement. Only valid if + /// InSwitch is true + /// + public Label DefaultTarget; + + /// + /// If this is non-null, points to the current switch statement + /// + public Switch Switch; + + /// + /// Whether we are inside an anonymous method. + /// + public AnonymousExpression CurrentAnonymousMethod; + + readonly IMemberContext member_context; + + readonly SourceMethodBuilder methodSymbols; + + DynamicSiteClass dynamic_site_container; + + Label? return_label; + + List epilogue_expressions; + + public EmitContext (IMemberContext rc, ILGenerator ig, TypeSpec return_type, SourceMethodBuilder methodSymbols) + { + this.member_context = rc; + this.ig = ig; + this.return_type = return_type; + + if (rc.Module.Compiler.Settings.Checked) + flags |= Options.CheckedScope; + + if (methodSymbols != null) { + this.methodSymbols = methodSymbols; + if (!rc.Module.Compiler.Settings.Optimize) + flags |= Options.AccurateDebugInfo; + } else { + flags |= Options.OmitDebugInfo; + } + +#if STATIC + ig.__CleverExceptionBlockAssistance (); +#endif + } + + #region Properties + + internal AsyncTaskStorey AsyncTaskStorey { + get { + return CurrentAnonymousMethod.Storey as AsyncTaskStorey; + } + } + + public BuiltinTypes BuiltinTypes { + get { + return MemberContext.Module.Compiler.BuiltinTypes; + } + } + + public ConditionalAccessContext ConditionalAccess { get; set; } + + public TypeSpec CurrentType { + get { return member_context.CurrentType; } + } + + public TypeParameters CurrentTypeParameters { + get { return member_context.CurrentTypeParameters; } + } + + public MemberCore CurrentTypeDefinition { + get { return member_context.CurrentMemberDefinition; } + } + + public bool EmitAccurateDebugInfo { + get { + return (flags & Options.AccurateDebugInfo) != 0; + } + } + + public bool HasMethodSymbolBuilder { + get { + return methodSymbols != null; + } + } + + public bool HasReturnLabel { + get { + return return_label.HasValue; + } + } + + public bool IsStatic { + get { return member_context.IsStatic; } + } + + public bool IsStaticConstructor { + get { + return member_context.IsStatic && (flags & Options.ConstructorScope) != 0; + } + } + + public bool IsAnonymousStoreyMutateRequired { + get { + return CurrentAnonymousMethod != null && + CurrentAnonymousMethod.Storey != null && + CurrentAnonymousMethod.Storey.Mutator != null; + } + } + + public IMemberContext MemberContext { + get { + return member_context; + } + } + + public ModuleContainer Module { + get { + return member_context.Module; + } + } + + public bool NotifyEvaluatorOnStore { + get { + return Module.Evaluator != null && Module.Evaluator.ModificationListener != null; + } + } + + // Has to be used for specific emitter errors only any + // possible resolver errors have to be reported during Resolve + public Report Report { + get { + return member_context.Module.Compiler.Report; + } + } + + public TypeSpec ReturnType { + get { + return return_type; + } + } + + // + // The label where we have to jump before leaving the context + // + public Label ReturnLabel { + get { + return return_label.Value; + } + } + + public List StatementEpilogue { + get { + return epilogue_expressions; + } + } + + public LocalVariable AsyncThrowVariable { get; set; } + + public List TryFinallyUnwind { get; set; } + + #endregion + + public void AddStatementEpilog (IExpressionCleanup cleanupExpression) + { + if (epilogue_expressions == null) { + epilogue_expressions = new List (); + } else if (epilogue_expressions.Contains (cleanupExpression)) { + return; + } + + epilogue_expressions.Add (cleanupExpression); + } + + public void AssertEmptyStack () + { +#if STATIC + if (ig.__StackHeight != 0) + throw new InternalErrorException ("Await yields with non-empty stack in `{0}", + member_context.GetSignatureForError ()); +#endif + } + + /// + /// This is called immediately before emitting an IL opcode to tell the symbol + /// writer to which source line this opcode belongs. + /// + public bool Mark (Location loc) + { + if ((flags & Options.OmitDebugInfo) != 0) + return false; + + if (loc.IsNull || methodSymbols == null) + return false; + + var sf = loc.SourceFile; + if (sf.IsHiddenLocation (loc)) + return false; + +#if NET_4_0 + methodSymbols.MarkSequencePoint (ig.ILOffset, sf.SourceFileEntry, loc.Row, loc.Column, false); +#endif + return true; + } + + public void MarkCallEntry (Location loc) + { + if (!EmitAccurateDebugInfo) + return; + + // + // TODO: This should emit different kind of sequence point to make + // step-over work for statement over multiple lines + // + // Debugging experience for Foo (A () + B ()) where A and B are + // on separate lines is not great + // + Mark (loc); + } + + public void DefineLocalVariable (string name, LocalBuilder builder) + { + if ((flags & Options.OmitDebugInfo) != 0) + return; + + methodSymbols.AddLocal (builder.LocalIndex, name); + } + + public void BeginCatchBlock (TypeSpec type) + { + if (IsAnonymousStoreyMutateRequired) + type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type); + + ig.BeginCatchBlock (type.GetMetaInfo ()); + } + + public void BeginFilterHandler () + { + ig.BeginCatchBlock (null); + } + + public void BeginExceptionBlock () + { + ig.BeginExceptionBlock (); + } + + public void BeginExceptionFilterBlock () + { + ig.BeginExceptFilterBlock (); + } + + public void BeginFinallyBlock () + { + ig.BeginFinallyBlock (); + } + + public void BeginScope () + { + if ((flags & Options.OmitDebugInfo) != 0) + return; + +#if NET_4_0 + methodSymbols.StartBlock (CodeBlockEntry.Type.Lexical, ig.ILOffset); +#endif + } + + public void BeginCompilerScope () + { + if ((flags & Options.OmitDebugInfo) != 0) + return; + +#if NET_4_0 + methodSymbols.StartBlock (CodeBlockEntry.Type.CompilerGenerated, ig.ILOffset); +#endif + } + + public void EndExceptionBlock () + { + ig.EndExceptionBlock (); + } + + public void EndScope () + { + if ((flags & Options.OmitDebugInfo) != 0) + return; + +#if NET_4_0 + methodSymbols.EndBlock (ig.ILOffset); +#endif + } + + public void CloseConditionalAccess (TypeSpec type) + { + if (type != null) + Emit (OpCodes.Newobj, Nullable.NullableInfo.GetConstructor (type)); + + MarkLabel (ConditionalAccess.EndLabel); + ConditionalAccess = null; + } + + // + // Creates a nested container in this context for all dynamic compiler generated stuff + // + internal DynamicSiteClass CreateDynamicSite () + { + if (dynamic_site_container == null) { + var mc = member_context.CurrentMemberDefinition as MemberBase; + dynamic_site_container = new DynamicSiteClass (CurrentTypeDefinition.Parent.PartialContainer, mc, member_context.CurrentTypeParameters); + + CurrentTypeDefinition.Module.AddCompilerGeneratedClass (dynamic_site_container); + dynamic_site_container.CreateContainer (); + dynamic_site_container.DefineContainer (); + dynamic_site_container.Define (); + + var inflator = new TypeParameterInflator (Module, CurrentType, TypeParameterSpec.EmptyTypes, TypeSpec.EmptyTypes); + var inflated = dynamic_site_container.CurrentType.InflateMember (inflator); + CurrentType.MemberCache.AddMember (inflated); + } + + return dynamic_site_container; + } + + public Label CreateReturnLabel () + { + if (!return_label.HasValue) + return_label = DefineLabel (); + + return return_label.Value; + } + + public LocalBuilder DeclareLocal (TypeSpec type, bool pinned) + { + if (IsAnonymousStoreyMutateRequired) + type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type); + + return ig.DeclareLocal (type.GetMetaInfo (), pinned); + } + + public Label DefineLabel () + { + return ig.DefineLabel (); + } + + // + // Creates temporary field in current async storey + // + public StackFieldExpr GetTemporaryField (TypeSpec type, bool initializedFieldRequired = false) + { + var f = AsyncTaskStorey.AddCapturedLocalVariable (type, initializedFieldRequired); + var fexpr = new StackFieldExpr (f); + fexpr.InstanceExpression = new CompilerGeneratedThis (CurrentType, Location.Null); + return fexpr; + } + + public void MarkLabel (Label label) + { + ig.MarkLabel (label); + } + + public void Emit (OpCode opcode) + { + ig.Emit (opcode); + } + + public void Emit (OpCode opcode, LocalBuilder local) + { + ig.Emit (opcode, local); + } + + public void Emit (OpCode opcode, string arg) + { + ig.Emit (opcode, arg); + } + + public void Emit (OpCode opcode, double arg) + { + ig.Emit (opcode, arg); + } + + public void Emit (OpCode opcode, float arg) + { + ig.Emit (opcode, arg); + } + + public void Emit (OpCode opcode, Label label) + { + ig.Emit (opcode, label); + } + + public void Emit (OpCode opcode, Label[] labels) + { + ig.Emit (opcode, labels); + } + + public void Emit (OpCode opcode, TypeSpec type) + { + if (IsAnonymousStoreyMutateRequired) + type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type); + + ig.Emit (opcode, type.GetMetaInfo ()); + } + + public void Emit (OpCode opcode, FieldSpec field) + { + if (IsAnonymousStoreyMutateRequired) + field = field.Mutate (CurrentAnonymousMethod.Storey.Mutator); + + ig.Emit (opcode, field.GetMetaInfo ()); + } + + public void Emit (OpCode opcode, MethodSpec method) + { + if (IsAnonymousStoreyMutateRequired) + method = method.Mutate (CurrentAnonymousMethod.Storey.Mutator); + + if (method.IsConstructor) + ig.Emit (opcode, (ConstructorInfo) method.GetMetaInfo ()); + else + ig.Emit (opcode, (MethodInfo) method.GetMetaInfo ()); + } + + // TODO: REMOVE breaks mutator + public void Emit (OpCode opcode, MethodInfo method) + { + ig.Emit (opcode, method); + } + + public void Emit (OpCode opcode, MethodSpec method, MetaType[] vargs) + { + // TODO MemberCache: This should mutate too + ig.EmitCall (opcode, (MethodInfo) method.GetMetaInfo (), vargs); + } + + public void EmitArrayNew (ArrayContainer ac) + { + if (ac.Rank == 1) { + var type = IsAnonymousStoreyMutateRequired ? + CurrentAnonymousMethod.Storey.Mutator.Mutate (ac.Element) : + ac.Element; + + ig.Emit (OpCodes.Newarr, type.GetMetaInfo ()); + } else { + if (IsAnonymousStoreyMutateRequired) + ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator); + + ig.Emit (OpCodes.Newobj, ac.GetConstructor ()); + } + } + + public void EmitArrayAddress (ArrayContainer ac) + { + if (ac.Rank > 1) { + if (IsAnonymousStoreyMutateRequired) + ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator); + + ig.Emit (OpCodes.Call, ac.GetAddressMethod ()); + } else { + var type = IsAnonymousStoreyMutateRequired ? + CurrentAnonymousMethod.Storey.Mutator.Mutate (ac.Element) : + ac.Element; + + ig.Emit (OpCodes.Ldelema, type.GetMetaInfo ()); + } + } + + // + // Emits the right opcode to load from an array + // + public void EmitArrayLoad (ArrayContainer ac) + { + if (ac.Rank > 1) { + if (IsAnonymousStoreyMutateRequired) + ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator); + + ig.Emit (OpCodes.Call, ac.GetGetMethod ()); + return; + } + + + var type = ac.Element; + if (type.Kind == MemberKind.Enum) + type = EnumSpec.GetUnderlyingType (type); + + switch (type.BuiltinType) { + case BuiltinTypeSpec.Type.Bool: + // + // Workaround MSIL limitation. Load bool element as single bit, + // bool array can actually store any byte value + // + ig.Emit (OpCodes.Ldelem_U1); + ig.Emit (OpCodes.Ldc_I4_0); + ig.Emit (OpCodes.Cgt_Un); + break; + case BuiltinTypeSpec.Type.Byte: + ig.Emit (OpCodes.Ldelem_U1); + break; + case BuiltinTypeSpec.Type.SByte: + ig.Emit (OpCodes.Ldelem_I1); + break; + case BuiltinTypeSpec.Type.Short: + ig.Emit (OpCodes.Ldelem_I2); + break; + case BuiltinTypeSpec.Type.UShort: + case BuiltinTypeSpec.Type.Char: + ig.Emit (OpCodes.Ldelem_U2); + break; + case BuiltinTypeSpec.Type.Int: + ig.Emit (OpCodes.Ldelem_I4); + break; + case BuiltinTypeSpec.Type.UInt: + ig.Emit (OpCodes.Ldelem_U4); + break; + case BuiltinTypeSpec.Type.ULong: + case BuiltinTypeSpec.Type.Long: + ig.Emit (OpCodes.Ldelem_I8); + break; + case BuiltinTypeSpec.Type.Float: + ig.Emit (OpCodes.Ldelem_R4); + break; + case BuiltinTypeSpec.Type.Double: + ig.Emit (OpCodes.Ldelem_R8); + break; + case BuiltinTypeSpec.Type.IntPtr: + ig.Emit (OpCodes.Ldelem_I); + break; + default: + switch (type.Kind) { + case MemberKind.Struct: + if (IsAnonymousStoreyMutateRequired) + type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type); + + ig.Emit (OpCodes.Ldelema, type.GetMetaInfo ()); + ig.Emit (OpCodes.Ldobj, type.GetMetaInfo ()); + break; + case MemberKind.TypeParameter: + if (IsAnonymousStoreyMutateRequired) + type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type); + + ig.Emit (OpCodes.Ldelem, type.GetMetaInfo ()); + break; + case MemberKind.PointerType: + ig.Emit (OpCodes.Ldelem_I); + break; + default: + ig.Emit (OpCodes.Ldelem_Ref); + break; + } + break; + } + } + + // + // Emits the right opcode to store to an array + // + public void EmitArrayStore (ArrayContainer ac) + { + if (ac.Rank > 1) { + if (IsAnonymousStoreyMutateRequired) + ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator); + + ig.Emit (OpCodes.Call, ac.GetSetMethod ()); + return; + } + + var type = ac.Element; + + if (type.Kind == MemberKind.Enum) + type = EnumSpec.GetUnderlyingType (type); + + switch (type.BuiltinType) { + case BuiltinTypeSpec.Type.Byte: + case BuiltinTypeSpec.Type.SByte: + case BuiltinTypeSpec.Type.Bool: + Emit (OpCodes.Stelem_I1); + return; + case BuiltinTypeSpec.Type.Short: + case BuiltinTypeSpec.Type.UShort: + case BuiltinTypeSpec.Type.Char: + Emit (OpCodes.Stelem_I2); + return; + case BuiltinTypeSpec.Type.Int: + case BuiltinTypeSpec.Type.UInt: + Emit (OpCodes.Stelem_I4); + return; + case BuiltinTypeSpec.Type.Long: + case BuiltinTypeSpec.Type.ULong: + Emit (OpCodes.Stelem_I8); + return; + case BuiltinTypeSpec.Type.Float: + Emit (OpCodes.Stelem_R4); + return; + case BuiltinTypeSpec.Type.Double: + Emit (OpCodes.Stelem_R8); + return; + } + + switch (type.Kind) { + case MemberKind.Struct: + Emit (OpCodes.Stobj, type); + break; + case MemberKind.TypeParameter: + Emit (OpCodes.Stelem, type); + break; + case MemberKind.PointerType: + Emit (OpCodes.Stelem_I); + break; + default: + Emit (OpCodes.Stelem_Ref); + break; + } + } + + public void EmitInt (int i) + { + EmitIntConstant (i); + } + + void EmitIntConstant (int i) + { + switch (i) { + case -1: + ig.Emit (OpCodes.Ldc_I4_M1); + break; + + case 0: + ig.Emit (OpCodes.Ldc_I4_0); + break; + + case 1: + ig.Emit (OpCodes.Ldc_I4_1); + break; + + case 2: + ig.Emit (OpCodes.Ldc_I4_2); + break; + + case 3: + ig.Emit (OpCodes.Ldc_I4_3); + break; + + case 4: + ig.Emit (OpCodes.Ldc_I4_4); + break; + + case 5: + ig.Emit (OpCodes.Ldc_I4_5); + break; + + case 6: + ig.Emit (OpCodes.Ldc_I4_6); + break; + + case 7: + ig.Emit (OpCodes.Ldc_I4_7); + break; + + case 8: + ig.Emit (OpCodes.Ldc_I4_8); + break; + + default: + if (i >= -128 && i <= 127) { + ig.Emit (OpCodes.Ldc_I4_S, (sbyte) i); + } else + ig.Emit (OpCodes.Ldc_I4, i); + break; + } + } + + public void EmitLong (long l) + { + if (l >= int.MinValue && l <= int.MaxValue) { + EmitIntConstant (unchecked ((int) l)); + ig.Emit (OpCodes.Conv_I8); + } else if (l >= 0 && l <= uint.MaxValue) { + EmitIntConstant (unchecked ((int) l)); + ig.Emit (OpCodes.Conv_U8); + } else { + ig.Emit (OpCodes.Ldc_I8, l); + } + } + + // + // Load the object from the pointer. + // + public void EmitLoadFromPtr (TypeSpec type) + { + if (type.Kind == MemberKind.Enum) + type = EnumSpec.GetUnderlyingType (type); + + switch (type.BuiltinType) { + case BuiltinTypeSpec.Type.Int: + ig.Emit (OpCodes.Ldind_I4); + break; + case BuiltinTypeSpec.Type.UInt: + ig.Emit (OpCodes.Ldind_U4); + break; + case BuiltinTypeSpec.Type.Short: + ig.Emit (OpCodes.Ldind_I2); + break; + case BuiltinTypeSpec.Type.UShort: + case BuiltinTypeSpec.Type.Char: + ig.Emit (OpCodes.Ldind_U2); + break; + case BuiltinTypeSpec.Type.Byte: + ig.Emit (OpCodes.Ldind_U1); + break; + case BuiltinTypeSpec.Type.SByte: + ig.Emit (OpCodes.Ldind_I1); + break; + case BuiltinTypeSpec.Type.Bool: + ig.Emit (OpCodes.Ldind_I1); + ig.Emit (OpCodes.Ldc_I4_0); + ig.Emit (OpCodes.Cgt_Un); + break; + case BuiltinTypeSpec.Type.ULong: + case BuiltinTypeSpec.Type.Long: + ig.Emit (OpCodes.Ldind_I8); + break; + case BuiltinTypeSpec.Type.Float: + ig.Emit (OpCodes.Ldind_R4); + break; + case BuiltinTypeSpec.Type.Double: + ig.Emit (OpCodes.Ldind_R8); + break; + case BuiltinTypeSpec.Type.IntPtr: + ig.Emit (OpCodes.Ldind_I); + break; + default: + switch (type.Kind) { + case MemberKind.Struct: + case MemberKind.TypeParameter: + if (IsAnonymousStoreyMutateRequired) + type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type); + + ig.Emit (OpCodes.Ldobj, type.GetMetaInfo ()); + break; + case MemberKind.PointerType: + ig.Emit (OpCodes.Ldind_I); + break; + default: + ig.Emit (OpCodes.Ldind_Ref); + break; + } + break; + } + } + + public void EmitNull () + { + ig.Emit (OpCodes.Ldnull); + } + + public void EmitArgumentAddress (int pos) + { + if (!IsStatic) + ++pos; + + if (pos > byte.MaxValue) + ig.Emit (OpCodes.Ldarga, pos); + else + ig.Emit (OpCodes.Ldarga_S, (byte) pos); + } + + public void EmitArgumentLoad (int pos) + { + if (!IsStatic) + ++pos; + + switch (pos) { + case 0: ig.Emit (OpCodes.Ldarg_0); break; + case 1: ig.Emit (OpCodes.Ldarg_1); break; + case 2: ig.Emit (OpCodes.Ldarg_2); break; + case 3: ig.Emit (OpCodes.Ldarg_3); break; + default: + if (pos > byte.MaxValue) + ig.Emit (OpCodes.Ldarg, pos); + else + ig.Emit (OpCodes.Ldarg_S, (byte) pos); + break; + } + } + + public void EmitArgumentStore (int pos) + { + if (!IsStatic) + ++pos; + + if (pos > byte.MaxValue) + ig.Emit (OpCodes.Starg, pos); + else + ig.Emit (OpCodes.Starg_S, (byte) pos); + } + + // + // The stack contains the pointer and the value of type `type' + // + public void EmitStoreFromPtr (TypeSpec type) + { + if (type.IsEnum) + type = EnumSpec.GetUnderlyingType (type); + + switch (type.BuiltinType) { + case BuiltinTypeSpec.Type.Int: + case BuiltinTypeSpec.Type.UInt: + ig.Emit (OpCodes.Stind_I4); + return; + case BuiltinTypeSpec.Type.Long: + case BuiltinTypeSpec.Type.ULong: + ig.Emit (OpCodes.Stind_I8); + return; + case BuiltinTypeSpec.Type.Char: + case BuiltinTypeSpec.Type.Short: + case BuiltinTypeSpec.Type.UShort: + ig.Emit (OpCodes.Stind_I2); + return; + case BuiltinTypeSpec.Type.Float: + ig.Emit (OpCodes.Stind_R4); + return; + case BuiltinTypeSpec.Type.Double: + ig.Emit (OpCodes.Stind_R8); + return; + case BuiltinTypeSpec.Type.Byte: + case BuiltinTypeSpec.Type.SByte: + case BuiltinTypeSpec.Type.Bool: + ig.Emit (OpCodes.Stind_I1); + return; + case BuiltinTypeSpec.Type.IntPtr: + ig.Emit (OpCodes.Stind_I); + return; + } + + switch (type.Kind) { + case MemberKind.Struct: + case MemberKind.TypeParameter: + if (IsAnonymousStoreyMutateRequired) + type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type); + + ig.Emit (OpCodes.Stobj, type.GetMetaInfo ()); + break; + default: + ig.Emit (OpCodes.Stind_Ref); + break; + } + } + + public void EmitThis () + { + ig.Emit (OpCodes.Ldarg_0); + } + + public void EmitEpilogue () + { + if (epilogue_expressions == null) + return; + + foreach (var e in epilogue_expressions) + e.EmitCleanup (this); + + epilogue_expressions = null; + } + + /// + /// Returns a temporary storage for a variable of type t as + /// a local variable in the current body. + /// + public LocalBuilder GetTemporaryLocal (TypeSpec t) + { + if (temporary_storage != null) { + object o; + if (temporary_storage.TryGetValue (t, out o)) { + if (o is Stack) { + var s = (Stack) o; + o = s.Count == 0 ? null : s.Pop (); + } else { + temporary_storage.Remove (t); + } + } + if (o != null) + return (LocalBuilder) o; + } + return DeclareLocal (t, false); + } + + public void FreeTemporaryLocal (LocalBuilder b, TypeSpec t) + { + if (temporary_storage == null) { + temporary_storage = new Dictionary (ReferenceEquality.Default); + temporary_storage.Add (t, b); + return; + } + object o; + + if (!temporary_storage.TryGetValue (t, out o)) { + temporary_storage.Add (t, b); + return; + } + var s = o as Stack; + if (s == null) { + s = new Stack (); + s.Push ((LocalBuilder)o); + temporary_storage [t] = s; + } + s.Push (b); + } + + /// + /// ReturnValue creates on demand the LocalBuilder for the + /// return value from the function. By default this is not + /// used. This is only required when returns are found inside + /// Try or Catch statements. + /// + /// This method is typically invoked from the Emit phase, so + /// we allow the creation of a return label if it was not + /// requested during the resolution phase. Could be cleaned + /// up, but it would replicate a lot of logic in the Emit phase + /// of the code that uses it. + /// + public LocalBuilder TemporaryReturn () + { + if (return_value == null){ + return_value = DeclareLocal (return_type, false); + } + + return return_value; + } + } + + public class ConditionalAccessContext + { + public ConditionalAccessContext (TypeSpec type, Label endLabel) + { + Type = type; + EndLabel = endLabel; + } + + public bool Statement { get; set; } + public Label EndLabel { get; private set; } + public TypeSpec Type { get; private set; } + } + + struct CallEmitter + { + public Expression InstanceExpression; + + // + // When call has to leave an extra copy of all arguments on the stack + // + public bool DuplicateArguments; + + // + // Does not emit InstanceExpression load when InstanceExpressionOnStack + // is set. Used by compound assignments. + // + public bool InstanceExpressionOnStack; + + // + // Any of arguments contains await expression + // + public bool HasAwaitArguments; + + public bool ConditionalAccess; + + // + // When dealing with await arguments the original arguments are converted + // into a new set with hoisted stack results + // + public Arguments EmittedArguments; + + public void Emit (EmitContext ec, MethodSpec method, Arguments Arguments, Location loc) + { + EmitPredefined (ec, method, Arguments, false, loc); + } + + public void EmitStatement (EmitContext ec, MethodSpec method, Arguments Arguments, Location loc) + { + EmitPredefined (ec, method, Arguments, true, loc); + } + + public void EmitPredefined (EmitContext ec, MethodSpec method, Arguments Arguments, bool statement = false, Location? loc = null) + { + Expression instance_copy = null; + + if (!HasAwaitArguments && ec.HasSet (BuilderContext.Options.AsyncBody)) { + HasAwaitArguments = Arguments != null && Arguments.ContainsEmitWithAwait (); + if (HasAwaitArguments && InstanceExpressionOnStack) { + throw new NotSupportedException (); + } + } + + OpCode call_op; + LocalTemporary lt = null; + + if (method.IsStatic) { + call_op = OpCodes.Call; + } else { + call_op = IsVirtualCallRequired (InstanceExpression, method) ? OpCodes.Callvirt : OpCodes.Call; + + if (HasAwaitArguments) { + instance_copy = InstanceExpression.EmitToField (ec); + var ie = new InstanceEmitter (instance_copy, IsAddressCall (instance_copy, call_op, method.DeclaringType)); + + if (Arguments == null) { + ie.EmitLoad (ec); + } + } else if (!InstanceExpressionOnStack) { + var ie = new InstanceEmitter (InstanceExpression, IsAddressCall (InstanceExpression, call_op, method.DeclaringType)); + ie.Emit (ec, ConditionalAccess); + + if (DuplicateArguments) { + ec.Emit (OpCodes.Dup); + if (Arguments != null && Arguments.Count != 0) { + lt = new LocalTemporary (ie.GetStackType (ec)); + lt.Store (ec); + instance_copy = lt; + } + } + } + } + + if (Arguments != null && !InstanceExpressionOnStack) { + EmittedArguments = Arguments.Emit (ec, DuplicateArguments, HasAwaitArguments); + if (EmittedArguments != null) { + if (instance_copy != null) { + var ie = new InstanceEmitter (instance_copy, IsAddressCall (instance_copy, call_op, method.DeclaringType)); + ie.Emit (ec, ConditionalAccess); + + if (lt != null) + lt.Release (ec); + } + + EmittedArguments.Emit (ec); + } + } + + if (call_op == OpCodes.Callvirt && (InstanceExpression.Type.IsGenericParameter || InstanceExpression.Type.IsStructOrEnum)) { + ec.Emit (OpCodes.Constrained, InstanceExpression.Type); + } + + if (loc != null) { + // + // Emit explicit sequence point for expressions like Foo.Bar () to help debugger to + // break at right place when LHS expression can be stepped-into + // + ec.MarkCallEntry (loc.Value); + } + + // + // Set instance expression to actual result expression. When it contains await it can be + // picked up by caller + // + InstanceExpression = instance_copy; + + if (method.Parameters.HasArglist) { + var varargs_types = GetVarargsTypes (method, Arguments); + ec.Emit (call_op, method, varargs_types); + } else { + // + // If you have: + // this.DoFoo (); + // and DoFoo is not virtual, you can omit the callvirt, + // because you don't need the null checking behavior. + // + ec.Emit (call_op, method); + } + + // + // Pop the return value if there is one and stack should be empty + // + if (statement && method.ReturnType.Kind != MemberKind.Void) + ec.Emit (OpCodes.Pop); + } + + static MetaType[] GetVarargsTypes (MethodSpec method, Arguments arguments) + { + AParametersCollection pd = method.Parameters; + + Argument a = arguments[pd.Count - 1]; + Arglist list = (Arglist) a.Expr; + + return list.ArgumentTypes; + } + + // + // Used to decide whether call or callvirt is needed + // + static bool IsVirtualCallRequired (Expression instance, MethodSpec method) + { + // + // There are 2 scenarious where we emit callvirt + // + // Case 1: A method is virtual and it's not used to call base + // Case 2: A method instance expression can be null. In this casen callvirt ensures + // correct NRE exception when the method is called + // + var decl_type = method.DeclaringType; + if (decl_type.IsStruct || decl_type.IsEnum) + return false; + + if (instance is BaseThis) + return false; + + // + // It's non-virtual and will never be null and it can be determined + // whether it's known value or reference type by verifier + // + if (!method.IsVirtual && Expression.IsNeverNull (instance) && !instance.Type.IsGenericParameter) + return false; + + return true; + } + + static bool IsAddressCall (Expression instance, OpCode callOpcode, TypeSpec declaringType) + { + var instance_type = instance.Type; + return (instance_type.IsStructOrEnum && (callOpcode == OpCodes.Callvirt || (callOpcode == OpCodes.Call && declaringType.IsStruct))) || + instance_type.IsGenericParameter || declaringType.IsNullableType; + } + } + + public struct InstanceEmitter + { + readonly Expression instance; + readonly bool addressRequired; + + public InstanceEmitter (Expression instance, bool addressLoad) + { + this.instance = instance; + this.addressRequired = addressLoad; + } + + public void Emit (EmitContext ec, bool conditionalAccess) + { + Label NullOperatorLabel; + Nullable.Unwrap unwrap; + + if (conditionalAccess && Expression.IsNeverNull (instance)) + conditionalAccess = false; + + if (conditionalAccess) { + NullOperatorLabel = ec.DefineLabel (); + unwrap = instance as Nullable.Unwrap; + } else { + NullOperatorLabel = new Label (); + unwrap = null; + } + + IMemoryLocation instance_address = null; + bool conditional_access_dup = false; + + if (unwrap != null) { + unwrap.Store (ec); + unwrap.EmitCheck (ec); + ec.Emit (OpCodes.Brtrue_S, NullOperatorLabel); + } else { + if (conditionalAccess && addressRequired) { + // + // Don't allocate temp variable when instance load is cheap and load and load-address + // operate on same memory + // + instance_address = instance as VariableReference; + if (instance_address == null) + instance_address = instance as LocalTemporary; + + if (instance_address == null) { + EmitLoad (ec); + ec.Emit (OpCodes.Dup); + ec.EmitLoadFromPtr (instance.Type); + + conditional_access_dup = true; + } else { + instance.Emit (ec); + } + + if (instance.Type.Kind == MemberKind.TypeParameter) + ec.Emit (OpCodes.Box, instance.Type); + } else { + EmitLoad (ec); + + if (conditionalAccess) { + conditional_access_dup = !IsInexpensiveLoad (); + if (conditional_access_dup) + ec.Emit (OpCodes.Dup); + } + } + + if (conditionalAccess) { + ec.Emit (OpCodes.Brtrue_S, NullOperatorLabel); + + if (conditional_access_dup) + ec.Emit (OpCodes.Pop); + } + } + + if (conditionalAccess) { + if (!ec.ConditionalAccess.Statement) { + if (ec.ConditionalAccess.Type.IsNullableType) + Nullable.LiftedNull.Create (ec.ConditionalAccess.Type, Location.Null).Emit (ec); + else + ec.EmitNull (); + } + + ec.Emit (OpCodes.Br, ec.ConditionalAccess.EndLabel); + ec.MarkLabel (NullOperatorLabel); + + if (instance_address != null) { + instance_address.AddressOf (ec, AddressOp.Load); + } else if (unwrap != null) { + unwrap.Emit (ec); + var tmp = ec.GetTemporaryLocal (unwrap.Type); + ec.Emit (OpCodes.Stloc, tmp); + ec.Emit (OpCodes.Ldloca, tmp); + ec.FreeTemporaryLocal (tmp, unwrap.Type); + } else if (!conditional_access_dup) { + instance.Emit (ec); + } + } + } + + public void EmitLoad (EmitContext ec) + { + var instance_type = instance.Type; + + // + // Push the instance expression + // + if (addressRequired) { + // + // If the expression implements IMemoryLocation, then + // we can optimize and use AddressOf on the + // return. + // + // If not we have to use some temporary storage for + // it. + var iml = instance as IMemoryLocation; + if (iml != null) { + iml.AddressOf (ec, AddressOp.Load); + } else { + LocalTemporary temp = new LocalTemporary (instance_type); + instance.Emit (ec); + temp.Store (ec); + temp.AddressOf (ec, AddressOp.Load); + } + + return; + } + + instance.Emit (ec); + + // Only to make verifier happy + if (RequiresBoxing ()) + ec.Emit (OpCodes.Box, instance_type); + } + + public TypeSpec GetStackType (EmitContext ec) + { + var instance_type = instance.Type; + + if (addressRequired) + return ReferenceContainer.MakeType (ec.Module, instance_type); + + if (instance_type.IsStructOrEnum) + return ec.Module.Compiler.BuiltinTypes.Object; + + return instance_type; + } + + bool RequiresBoxing () + { + var instance_type = instance.Type; + if (instance_type.IsGenericParameter && !(instance is This) && TypeSpec.IsReferenceType (instance_type)) + return true; + + if (instance_type.IsStructOrEnum) + return true; + + return false; + } + + bool IsInexpensiveLoad () + { + if (instance is Constant) + return instance.IsSideEffectFree; + + if (RequiresBoxing ()) + return false; + + var vr = instance as VariableReference; + if (vr != null) + return !vr.IsRef; + + if (instance is LocalTemporary) + return true; + + var fe = instance as FieldExpr; + if (fe != null) + return fe.IsStatic || fe.InstanceExpression is This; + + return false; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/complete.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/complete.cs new file mode 100644 index 000000000..3504302d5 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/complete.cs @@ -0,0 +1,226 @@ +// +// complete.cs: Expression that are used for completion suggestions. +// +// Author: +// Miguel de Icaza (miguel@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// Copyright 2001, 2002, 2003 Ximian, Inc. +// Copyright 2003-2009 Novell, Inc. +// Copyright 2011 Xamarin Inc +// +// Completion* classes derive from ExpressionStatement as this allows +// them to pass through the parser in many conditions that require +// statements even when the expression is incomplete (for example +// completing inside a lambda +// +using System.Collections.Generic; +using System.Linq; + +namespace Mono.CSharp { + + // + // A common base class for Completing expressions, it + // is just a very simple ExpressionStatement + // + public abstract class CompletingExpression : ExpressionStatement + { + public static void AppendResults (List results, string prefix, IEnumerable names) + { + foreach (string name in names) { + if (name == null) + continue; + + if (prefix != null && !name.StartsWith (prefix)) + continue; + + if (results.Contains (name)) + continue; + + if (prefix != null) + results.Add (name.Substring (prefix.Length)); + else + results.Add (name); + } + } + + public override bool ContainsEmitWithAwait () + { + return false; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + return null; + } + + public override void EmitStatement (EmitContext ec) + { + // Do nothing + } + + public override void Emit (EmitContext ec) + { + // Do nothing + } + } + + public class CompletionSimpleName : CompletingExpression { + public string Prefix; + + public CompletionSimpleName (string prefix, Location l) + { + this.loc = l; + this.Prefix = prefix; + } + + protected override Expression DoResolve (ResolveContext ec) + { + var results = new List (); + + ec.CurrentMemberDefinition.GetCompletionStartingWith (Prefix, results); + + throw new CompletionResult (Prefix, results.Distinct ().Select (l => l.Substring (Prefix.Length)).ToArray ()); + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + // Nothing + } + } + + public class CompletionMemberAccess : CompletingExpression { + Expression expr; + string partial_name; + TypeArguments targs; + + public CompletionMemberAccess (Expression e, string partial_name, Location l) + { + this.expr = e; + this.loc = l; + this.partial_name = partial_name; + } + + public CompletionMemberAccess (Expression e, string partial_name, TypeArguments targs, Location l) + { + this.expr = e; + this.loc = l; + this.partial_name = partial_name; + this.targs = targs; + } + + protected override Expression DoResolve (ResolveContext rc) + { + var sn = expr as SimpleName; + const ResolveFlags flags = ResolveFlags.VariableOrValue | ResolveFlags.Type; + + if (sn != null) { + expr = sn.LookupNameExpression (rc, MemberLookupRestrictions.ReadAccess | MemberLookupRestrictions.ExactArity); + + // + // Resolve expression which does have type set as we need expression type + // with disable flow analysis as we don't know whether left side expression + // is used as variable or type + // + if (expr is VariableReference || expr is ConstantExpr || expr is Linq.TransparentMemberAccess) { + expr = expr.Resolve (rc); + } else if (expr is TypeParameterExpr) { + expr.Error_UnexpectedKind (rc, flags, sn.Location); + expr = null; + } + } else { + expr = expr.Resolve (rc, flags); + } + + if (expr == null) + return null; + + TypeSpec expr_type = expr.Type; + if (expr_type.IsPointer || expr_type.Kind == MemberKind.Void || expr_type == InternalType.NullLiteral || expr_type == InternalType.AnonymousMethod) { + expr.Error_OperatorCannotBeApplied (rc, loc, ".", expr_type); + return null; + } + + if (targs != null) { + if (!targs.Resolve (rc)) + return null; + } + + var results = new List (); + var nexpr = expr as NamespaceExpression; + if (nexpr != null) { + string namespaced_partial; + + if (partial_name == null) + namespaced_partial = nexpr.Namespace.Name; + else + namespaced_partial = nexpr.Namespace.Name + "." + partial_name; + + rc.CurrentMemberDefinition.GetCompletionStartingWith (namespaced_partial, results); + if (partial_name != null) + results = results.Select (l => l.Substring (partial_name.Length)).ToList (); + } else { + var r = MemberCache.GetCompletitionMembers (rc, expr_type, partial_name).Select (l => l.Name); + AppendResults (results, partial_name, r); + } + + throw new CompletionResult (partial_name == null ? "" : partial_name, results.Distinct ().ToArray ()); + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + CompletionMemberAccess target = (CompletionMemberAccess) t; + + if (targs != null) + target.targs = targs.Clone (); + + target.expr = expr.Clone (clonectx); + } + } + + public class CompletionElementInitializer : CompletingExpression { + string partial_name; + + public CompletionElementInitializer (string partial_name, Location l) + { + this.partial_name = partial_name; + this.loc = l; + } + + protected override Expression DoResolve (ResolveContext ec) + { + var members = MemberCache.GetCompletitionMembers (ec, ec.CurrentInitializerVariable.Type, partial_name); + +// TODO: Does this mean exact match only ? +// if (partial_name != null && results.Count > 0 && result [0] == "") +// throw new CompletionResult ("", new string [] { "=" }); + + var results = members.Where (l => (l.Kind & (MemberKind.Field | MemberKind.Property)) != 0).Select (l => l.Name).ToList (); + if (partial_name != null) { + var temp = new List (); + AppendResults (temp, partial_name, results); + results = temp; + } + + throw new CompletionResult (partial_name == null ? "" : partial_name, results.Distinct ().ToArray ()); + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + // Nothing + } + } + + public class EmptyCompletion : CompletingExpression + { + protected override void CloneTo (CloneContext clonectx, Expression target) + { + } + + protected override Expression DoResolve (ResolveContext rc) + { + throw new CompletionResult ("", new string [0]); + } + } + +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/const.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/const.cs new file mode 100644 index 000000000..eef90b346 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/const.cs @@ -0,0 +1,237 @@ +// +// const.cs: Constant declarations. +// +// Author: +// Miguel de Icaza (miguel@ximian.com) +// Marek Safar (marek.safar@seznam.cz) +// +// Copyright 2001-2003 Ximian, Inc. +// Copyright 2003-2008 Novell, Inc. +// + +#if STATIC +using IKVM.Reflection; +#else +using System.Reflection; +#endif + +namespace Mono.CSharp { + + public class Const : FieldBase + { + const Modifiers AllowedModifiers = + Modifiers.NEW | + Modifiers.PUBLIC | + Modifiers.PROTECTED | + Modifiers.INTERNAL | + Modifiers.PRIVATE; + + public Const (TypeDefinition parent, FullNamedExpression type, Modifiers mod_flags, MemberName name, Attributes attrs) + : base (parent, type, mod_flags, AllowedModifiers, name, attrs) + { + ModFlags |= Modifiers.STATIC; + } + + /// + /// Defines the constant in the @parent + /// + public override bool Define () + { + if (!base.Define ()) + return false; + + if (!member_type.IsConstantCompatible) { + Error_InvalidConstantType (member_type, Location, Report); + } + + FieldAttributes field_attr = FieldAttributes.Static | ModifiersExtensions.FieldAttr (ModFlags); + // Decimals cannot be emitted into the constant blob. So, convert to 'readonly'. + if (member_type.BuiltinType == BuiltinTypeSpec.Type.Decimal) { + field_attr |= FieldAttributes.InitOnly; + } else { + field_attr |= FieldAttributes.Literal; + } + + FieldBuilder = Parent.TypeBuilder.DefineField (Name, MemberType.GetMetaInfo (), field_attr); + spec = new ConstSpec (Parent.Definition, this, MemberType, FieldBuilder, ModFlags, initializer); + + Parent.MemberCache.AddMember (spec); + + if ((field_attr & FieldAttributes.InitOnly) != 0) + Parent.PartialContainer.RegisterFieldForInitialization (this, + new FieldInitializer (this, initializer, Location)); + + if (declarators != null) { + var t = new TypeExpression (MemberType, TypeExpression.Location); + foreach (var d in declarators) { + var c = new Const (Parent, t, ModFlags & ~Modifiers.STATIC, new MemberName (d.Name.Value, d.Name.Location), OptAttributes); + c.initializer = d.Initializer; + ((ConstInitializer) c.initializer).Name = d.Name.Value; + c.Define (); + Parent.PartialContainer.Members.Add (c); + } + } + + return true; + } + + public void DefineValue () + { + var rc = new ResolveContext (this); + ((ConstSpec) spec).GetConstant (rc); + } + + /// + /// Emits the field value by evaluating the expression + /// + public override void Emit () + { + var c = ((ConstSpec) spec).Value as Constant; + if (c.Type.BuiltinType == BuiltinTypeSpec.Type.Decimal) { + Module.PredefinedAttributes.DecimalConstant.EmitAttribute (FieldBuilder, (decimal) c.GetValue (), c.Location); + } else { + FieldBuilder.SetConstant (c.GetValue ()); + } + + base.Emit (); + } + + public static void Error_InvalidConstantType (TypeSpec t, Location loc, Report Report) + { + if (t.IsGenericParameter) { + Report.Error (1959, loc, + "Type parameter `{0}' cannot be declared const", t.GetSignatureForError ()); + } else { + Report.Error (283, loc, + "The type `{0}' cannot be declared const", t.GetSignatureForError ()); + } + } + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + } + + public class ConstSpec : FieldSpec + { + Expression value; + + public ConstSpec (TypeSpec declaringType, IMemberDefinition definition, TypeSpec memberType, FieldInfo fi, Modifiers mod, Expression value) + : base (declaringType, definition, memberType, fi, mod) + { + this.value = value; + } + + // + // This expresion is guarantee to be a constant at emit phase only + // + public Expression Value { + get { + return value; + } + } + + // + // For compiled constants we have to resolve the value as there could be constant dependecies. This + // is needed for imported constants too to get the right context type + // + public Constant GetConstant (ResolveContext rc) + { + if (value.eclass != ExprClass.Value) + value = value.Resolve (rc); + + return (Constant) value; + } + } + + public class ConstInitializer : ShimExpression + { + bool in_transit; + readonly FieldBase field; + + public ConstInitializer (FieldBase field, Expression value, Location loc) + : base (value) + { + this.loc = loc; + this.field = field; + } + + public string Name { get; set; } + + protected override Expression DoResolve (ResolveContext unused) + { + if (type != null) + return expr; + + var opt = ResolveContext.Options.ConstantScope; + if (field is EnumMember) + opt |= ResolveContext.Options.EnumScope; + + // + // Use a context in which the constant was declared and + // not the one in which is referenced + // + var rc = new ResolveContext (field, opt); + expr = DoResolveInitializer (rc); + type = expr.Type; + + return expr; + } + + protected virtual Expression DoResolveInitializer (ResolveContext rc) + { + if (in_transit) { + field.Compiler.Report.Error (110, expr.Location, + "The evaluation of the constant value for `{0}' involves a circular definition", + GetSignatureForError ()); + + expr = null; + } else { + in_transit = true; + expr = expr.Resolve (rc); + } + + in_transit = false; + + if (expr != null) { + Constant c = expr as Constant; + if (c != null) + c = field.ConvertInitializer (rc, c); + + if (c == null) { + if (TypeSpec.IsReferenceType (field.MemberType)) + Error_ConstantCanBeInitializedWithNullOnly (rc, field.MemberType, expr.Location, GetSignatureForError ()); + else if (!(expr is Constant)) + Error_ExpressionMustBeConstant (rc, expr.Location, GetSignatureForError ()); + else + expr.Error_ValueCannotBeConverted (rc, field.MemberType, false); + } + + expr = c; + } + + if (expr == null) { + expr = New.Constantify (field.MemberType, Location); + if (expr == null) + expr = Constant.CreateConstantFromValue (field.MemberType, null, Location); + expr = expr.Resolve (rc); + } + + return expr; + } + + public override string GetSignatureForError () + { + if (Name == null) + return field.GetSignatureForError (); + + return field.Parent.GetSignatureForError () + "." + Name; + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/constant.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/constant.cs new file mode 100644 index 000000000..a50baacc7 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/constant.cs @@ -0,0 +1,2496 @@ +// +// constant.cs: Constants. +// +// Author: +// Miguel de Icaza (miguel@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// Copyright 2001-2003 Ximian, Inc. +// Copyright 2003-2008 Novell, Inc. +// Copyright 2011-2013 Xamarin Inc +// + +using System; +using System.Globalization; + +#if STATIC +using IKVM.Reflection.Emit; +#else +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp { + + /// + /// Base class for constants and literals. + /// + public abstract class Constant : Expression + { + static readonly NumberFormatInfo nfi = CultureInfo.InvariantCulture.NumberFormat; + + protected Constant (Location loc) + { + this.loc = loc; + } + + override public string ToString () + { + return this.GetType ().Name + " (" + GetValueAsLiteral () + ")"; + } + + /// + /// This is used to obtain the actual value of the literal + /// cast into an object. + /// + public abstract object GetValue (); + + public abstract long GetValueAsLong (); + + public abstract string GetValueAsLiteral (); + +#if !STATIC + // + // Returns an object value which is typed to contant type + // + public virtual object GetTypedValue () + { + return GetValue (); + } +#endif + + public override void Error_ValueCannotBeConverted (ResolveContext ec, TypeSpec target, bool expl) + { + if (!expl && IsLiteral && + BuiltinTypeSpec.IsPrimitiveTypeOrDecimal (target) && + BuiltinTypeSpec.IsPrimitiveTypeOrDecimal (type)) { + ec.Report.Error (31, loc, "Constant value `{0}' cannot be converted to a `{1}'", + GetValueAsLiteral (), target.GetSignatureForError ()); + } else { + base.Error_ValueCannotBeConverted (ec, target, expl); + } + } + + public Constant ImplicitConversionRequired (ResolveContext ec, TypeSpec type) + { + Constant c = ConvertImplicitly (type); + if (c == null) + Error_ValueCannotBeConverted (ec, type, false); + + return c; + } + + public override bool ContainsEmitWithAwait () + { + return false; + } + + public virtual Constant ConvertImplicitly (TypeSpec type) + { + if (this.type == type) + return this; + + if (!Convert.ImplicitNumericConversionExists (this.type, type)) + return null; + + bool fail; + object constant_value = ChangeType (GetValue (), type, out fail); + if (fail){ + // + // We should always catch the error before this is ever + // reached, by calling Convert.ImplicitStandardConversionExists + // + throw new InternalErrorException ("Missing constant conversion between `{0}' and `{1}'", + Type.GetSignatureForError (), type.GetSignatureForError ()); + } + + return CreateConstantFromValue (type, constant_value, loc); + } + + // + // Returns a constant instance based on Type + // + public static Constant CreateConstantFromValue (TypeSpec t, object v, Location loc) + { + switch (t.BuiltinType) { + case BuiltinTypeSpec.Type.Int: + return new IntConstant (t, (int) v, loc); + case BuiltinTypeSpec.Type.String: + return new StringConstant (t, (string) v, loc); + case BuiltinTypeSpec.Type.UInt: + return new UIntConstant (t, (uint) v, loc); + case BuiltinTypeSpec.Type.Long: + return new LongConstant (t, (long) v, loc); + case BuiltinTypeSpec.Type.ULong: + return new ULongConstant (t, (ulong) v, loc); + case BuiltinTypeSpec.Type.Float: + return new FloatConstant (t, (float) v, loc); + case BuiltinTypeSpec.Type.Double: + return new DoubleConstant (t, (double) v, loc); + case BuiltinTypeSpec.Type.Short: + return new ShortConstant (t, (short) v, loc); + case BuiltinTypeSpec.Type.UShort: + return new UShortConstant (t, (ushort) v, loc); + case BuiltinTypeSpec.Type.SByte: + return new SByteConstant (t, (sbyte) v, loc); + case BuiltinTypeSpec.Type.Byte: + return new ByteConstant (t, (byte) v, loc); + case BuiltinTypeSpec.Type.Char: + return new CharConstant (t, (char) v, loc); + case BuiltinTypeSpec.Type.Bool: + return new BoolConstant (t, (bool) v, loc); + case BuiltinTypeSpec.Type.Decimal: + return new DecimalConstant (t, (decimal) v, loc); + } + + if (t.IsEnum) { + var real_type = EnumSpec.GetUnderlyingType (t); + return new EnumConstant (CreateConstantFromValue (real_type, v, loc), t); + } + + if (v == null) { + if (t.IsNullableType) + return Nullable.LiftedNull.Create (t, loc); + + if (TypeSpec.IsReferenceType (t)) + return new NullConstant (t, loc); + } + +#if STATIC + throw new InternalErrorException ("Constant value `{0}' has unexpected underlying type `{1}'", v, t.GetSignatureForError ()); +#else + return null; +#endif + } + + // + // Returns a constant instance based on value and type. This is probing version of + // CreateConstantFromValue + // + public static Constant ExtractConstantFromValue (TypeSpec t, object v, Location loc) + { + switch (t.BuiltinType) { + case BuiltinTypeSpec.Type.Int: + if (v is int) + return new IntConstant (t, (int) v, loc); + break; + case BuiltinTypeSpec.Type.String: + if (v is string) + return new StringConstant (t, (string) v, loc); + break; + case BuiltinTypeSpec.Type.UInt: + if (v is uint) + return new UIntConstant (t, (uint) v, loc); + break; + case BuiltinTypeSpec.Type.Long: + if (v is long) + return new LongConstant (t, (long) v, loc); + break; + case BuiltinTypeSpec.Type.ULong: + if (v is ulong) + return new ULongConstant (t, (ulong) v, loc); + break; + case BuiltinTypeSpec.Type.Float: + if (v is float) + return new FloatConstant (t, (float) v, loc); + break; + case BuiltinTypeSpec.Type.Double: + if (v is double) + return new DoubleConstant (t, (double) v, loc); + break; + case BuiltinTypeSpec.Type.Short: + if (v is short) + return new ShortConstant (t, (short) v, loc); + break; + case BuiltinTypeSpec.Type.UShort: + if (v is ushort) + return new UShortConstant (t, (ushort) v, loc); + break; + case BuiltinTypeSpec.Type.SByte: + if (v is sbyte) + return new SByteConstant (t, (sbyte) v, loc); + break; + case BuiltinTypeSpec.Type.Byte: + if (v is byte) + return new ByteConstant (t, (byte) v, loc); + break; + case BuiltinTypeSpec.Type.Char: + if (v is char) + return new CharConstant (t, (char) v, loc); + break; + case BuiltinTypeSpec.Type.Bool: + if (v is bool) + return new BoolConstant (t, (bool) v, loc); + break; + case BuiltinTypeSpec.Type.Decimal: + if (v is decimal) + return new DecimalConstant (t, (decimal) v, loc); + break; + } + + if (t.IsEnum) { + var real_type = EnumSpec.GetUnderlyingType (t); + return new EnumConstant (CreateConstantFromValue (real_type, v, loc), t); + } + + if (v == null) { + if (t.IsNullableType) + return Nullable.LiftedNull.Create (t, loc); + + if (TypeSpec.IsReferenceType (t)) + return new NullConstant (t, loc); + } + + return null; + } + + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Arguments args = new Arguments (2); + args.Add (new Argument (this)); + args.Add (new Argument (new TypeOf (type, loc))); + + return CreateExpressionFactoryCall (ec, "Constant", args); + } + + /// + /// Maybe ConvertTo name is better. It tries to convert `this' constant to target_type. + /// It throws OverflowException + /// + // DON'T CALL THIS METHOD DIRECTLY AS IT DOES NOT HANDLE ENUMS + public abstract Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type); + + // This is a custom version of Convert.ChangeType() which works + // with the TypeBuilder defined types when compiling corlib. + static object ChangeType (object value, TypeSpec targetType, out bool error) + { + IConvertible convert_value = value as IConvertible; + + if (convert_value == null) { + error = true; + return null; + } + + // + // We cannot rely on build-in type conversions as they are + // more limited than what C# supports. + // See char -> float/decimal/double conversion + // + error = false; + try { + switch (targetType.BuiltinType) { + case BuiltinTypeSpec.Type.Bool: + return convert_value.ToBoolean (nfi); + case BuiltinTypeSpec.Type.Byte: + return convert_value.ToByte (nfi); + case BuiltinTypeSpec.Type.Char: + return convert_value.ToChar (nfi); + case BuiltinTypeSpec.Type.Short: + return convert_value.ToInt16 (nfi); + case BuiltinTypeSpec.Type.Int: + return convert_value.ToInt32 (nfi); + case BuiltinTypeSpec.Type.Long: + return convert_value.ToInt64 (nfi); + case BuiltinTypeSpec.Type.SByte: + return convert_value.ToSByte (nfi); + case BuiltinTypeSpec.Type.Decimal: + if (convert_value.GetType () == typeof (char)) + return (decimal) convert_value.ToInt32 (nfi); + return convert_value.ToDecimal (nfi); + case BuiltinTypeSpec.Type.Double: + if (convert_value.GetType () == typeof (char)) + return (double) convert_value.ToInt32 (nfi); + return convert_value.ToDouble (nfi); + case BuiltinTypeSpec.Type.Float: + if (convert_value.GetType () == typeof (char)) + return (float) convert_value.ToInt32 (nfi); + return convert_value.ToSingle (nfi); + case BuiltinTypeSpec.Type.String: + return convert_value.ToString (nfi); + case BuiltinTypeSpec.Type.UShort: + return convert_value.ToUInt16 (nfi); + case BuiltinTypeSpec.Type.UInt: + return convert_value.ToUInt32 (nfi); + case BuiltinTypeSpec.Type.ULong: + return convert_value.ToUInt64 (nfi); + case BuiltinTypeSpec.Type.Object: + return value; + } + } catch { + } + + error = true; + return null; + } + + protected override Expression DoResolve (ResolveContext rc) + { + return this; + } + + // + // Attempts to do a compile-time folding of a constant cast and handles + // error reporting for constant overlows only, on normal conversion + // errors returns null + // + public Constant Reduce (ResolveContext ec, TypeSpec target_type) + { + try { + return TryReduceConstant (ec, target_type); + } catch (OverflowException) { + if (ec.ConstantCheckState && Type.BuiltinType != BuiltinTypeSpec.Type.Decimal) { + ec.Report.Error (221, loc, + "Constant value `{0}' cannot be converted to a `{1}' (use `unchecked' syntax to override)", + GetValueAsLiteral (), target_type.GetSignatureForError ()); + } else { + Error_ValueCannotBeConverted (ec, target_type, false); + } + + return New.Constantify (target_type, loc); + } + } + + public Constant TryReduce (ResolveContext rc, TypeSpec targetType) + { + try { + return TryReduceConstant (rc, targetType); + } catch (OverflowException) { + return null; + } + } + + Constant TryReduceConstant (ResolveContext ec, TypeSpec target_type) + { + if (Type == target_type) { + // + // Reducing literal value produces a new constant. Syntactically 10 is not same as (int)10 + // + if (IsLiteral) + return CreateConstantFromValue (target_type, GetValue (), loc); + + return this; + } + + Constant c; + if (target_type.IsEnum) { + c = TryReduceConstant (ec, EnumSpec.GetUnderlyingType (target_type)); + if (c == null) + return null; + + return new EnumConstant (c, target_type); + } + + return ConvertExplicitly (ec.ConstantCheckState, target_type); + } + + /// + /// Need to pass type as the constant can require a boxing + /// and in such case no optimization is possible + /// + public bool IsDefaultInitializer (TypeSpec type) + { + if (type == Type) + return IsDefaultValue; + + return this is NullLiteral; + } + + public abstract bool IsDefaultValue { + get; + } + + public abstract bool IsNegative { + get; + } + + // + // When constant is declared as literal + // + public virtual bool IsLiteral { + get { return false; } + } + + public virtual bool IsOneInteger { + get { return false; } + } + + public override bool IsSideEffectFree { + get { + return true; + } + } + + // + // Returns true iff 1) the stack type of this is one of Object, + // int32, int64 and 2) this == 0 or this == null. + // + public virtual bool IsZeroInteger { + get { return false; } + } + + public override void EmitSideEffect (EmitContext ec) + { + // do nothing + } + + public sealed override Expression Clone (CloneContext clonectx) + { + // No cloning is not needed for constants + return this; + } + + protected override void CloneTo (CloneContext clonectx, Expression target) + { + throw new NotSupportedException ("should not be reached"); + } + + public override System.Linq.Expressions.Expression MakeExpression (BuilderContext ctx) + { +#if STATIC + return base.MakeExpression (ctx); +#else + return System.Linq.Expressions.Expression.Constant (GetTypedValue (), type.GetMetaInfo ()); +#endif + } + + public new bool Resolve (ResolveContext rc) + { + // It exists only as hint not to call Resolve on constants + return true; + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + + } + + public abstract class IntegralConstant : Constant + { + protected IntegralConstant (TypeSpec type, Location loc) + : base (loc) + { + this.type = type; + eclass = ExprClass.Value; + } + + public override void Error_ValueCannotBeConverted (ResolveContext ec, TypeSpec target, bool expl) + { + try { + ConvertExplicitly (true, target); + base.Error_ValueCannotBeConverted (ec, target, expl); + } + catch + { + ec.Report.Error (31, loc, "Constant value `{0}' cannot be converted to a `{1}'", + GetValue ().ToString (), target.GetSignatureForError ()); + } + } + + public override string GetValueAsLiteral () + { + return GetValue ().ToString (); + } + + public abstract Constant Increment (); + } + + public class BoolConstant : Constant { + public readonly bool Value; + + public BoolConstant (BuiltinTypes types, bool val, Location loc) + : this (types.Bool, val, loc) + { + } + + public BoolConstant (TypeSpec type, bool val, Location loc) + : base (loc) + { + eclass = ExprClass.Value; + this.type = type; + + Value = val; + } + + public override object GetValue () + { + return (object) Value; + } + + public override string GetValueAsLiteral () + { + return Value ? "true" : "false"; + } + + public override long GetValueAsLong () + { + return Value ? 1 : 0; + } + + public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) + { + enc.Encode (Value); + } + + public override void Emit (EmitContext ec) + { + if (Value) + ec.EmitInt (1); + else + ec.EmitInt (0); + } + + public override bool IsDefaultValue { + get { + return !Value; + } + } + + public override bool IsNegative { + get { + return false; + } + } + + public override bool IsZeroInteger { + get { return Value == false; } + } + + public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) + { + return null; + } + + } + + public class ByteConstant : IntegralConstant + { + public readonly byte Value; + + public ByteConstant (BuiltinTypes types, byte v, Location loc) + : this (types.Byte, v, loc) + { + } + + public ByteConstant (TypeSpec type, byte v, Location loc) + : base (type, loc) + { + Value = v; + } + + public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) + { + enc.Encode (Value); + } + + public override void Emit (EmitContext ec) + { + ec.EmitInt (Value); + } + + public override object GetValue () + { + return Value; + } + + public override long GetValueAsLong () + { + return Value; + } + + public override Constant Increment () + { + return new ByteConstant (type, checked ((byte)(Value + 1)), loc); + } + + public override bool IsDefaultValue { + get { + return Value == 0; + } + } + + public override bool IsOneInteger { + get { + return Value == 1; + } + } + + public override bool IsNegative { + get { + return false; + } + } + + public override bool IsZeroInteger { + get { return Value == 0; } + } + + public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) + { + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + if (in_checked_context){ + if (Value > SByte.MaxValue) + throw new OverflowException (); + } + return new SByteConstant (target_type, (sbyte) Value, Location); + case BuiltinTypeSpec.Type.Short: + return new ShortConstant (target_type, (short) Value, Location); + case BuiltinTypeSpec.Type.UShort: + return new UShortConstant (target_type, (ushort) Value, Location); + case BuiltinTypeSpec.Type.Int: + return new IntConstant (target_type, (int) Value, Location); + case BuiltinTypeSpec.Type.UInt: + return new UIntConstant (target_type, (uint) Value, Location); + case BuiltinTypeSpec.Type.Long: + return new LongConstant (target_type, (long) Value, Location); + case BuiltinTypeSpec.Type.ULong: + return new ULongConstant (target_type, (ulong) Value, Location); + case BuiltinTypeSpec.Type.Float: + return new FloatConstant (target_type, (float) Value, Location); + case BuiltinTypeSpec.Type.Double: + return new DoubleConstant (target_type, (double) Value, Location); + case BuiltinTypeSpec.Type.Char: + return new CharConstant (target_type, (char) Value, Location); + case BuiltinTypeSpec.Type.Decimal: + return new DecimalConstant (target_type, (decimal) Value, Location); + } + + return null; + } + + } + + public class CharConstant : Constant { + public readonly char Value; + + public CharConstant (BuiltinTypes types, char v, Location loc) + : this (types.Char, v, loc) + { + } + + public CharConstant (TypeSpec type, char v, Location loc) + : base (loc) + { + this.type = type; + eclass = ExprClass.Value; + + Value = v; + } + + public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) + { + enc.Encode ((ushort) Value); + } + + public override void Emit (EmitContext ec) + { + ec.EmitInt (Value); + } + + static string descape (char c) + { + switch (c){ + case '\a': + return "\\a"; + case '\b': + return "\\b"; + case '\n': + return "\\n"; + case '\t': + return "\\t"; + case '\v': + return "\\v"; + case '\r': + return "\\r"; + case '\\': + return "\\\\"; + case '\f': + return "\\f"; + case '\0': + return "\\0"; + case '"': + return "\\\""; + case '\'': + return "\\\'"; + } + return c.ToString (); + } + + public override object GetValue () + { + return Value; + } + + public override long GetValueAsLong () + { + return Value; + } + + public override string GetValueAsLiteral () + { + return "\"" + descape (Value) + "\""; + } + + public override bool IsDefaultValue { + get { + return Value == 0; + } + } + + public override bool IsNegative { + get { + return false; + } + } + + public override bool IsZeroInteger { + get { return Value == '\0'; } + } + + public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) + { + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.Byte: + if (in_checked_context) { + if (Value < Byte.MinValue || Value > Byte.MaxValue) + throw new OverflowException (); + } + return new ByteConstant (target_type, (byte) Value, Location); + case BuiltinTypeSpec.Type.SByte: + if (in_checked_context) { + if (Value > SByte.MaxValue) + throw new OverflowException (); + } + return new SByteConstant (target_type, (sbyte) Value, Location); + + case BuiltinTypeSpec.Type.Short: + if (in_checked_context) { + if (Value > Int16.MaxValue) + throw new OverflowException (); + } + return new ShortConstant (target_type, (short) Value, Location); + case BuiltinTypeSpec.Type.Int: + return new IntConstant (target_type, (int) Value, Location); + case BuiltinTypeSpec.Type.UInt: + return new UIntConstant (target_type, (uint) Value, Location); + case BuiltinTypeSpec.Type.Long: + return new LongConstant (target_type, (long) Value, Location); + case BuiltinTypeSpec.Type.ULong: + return new ULongConstant (target_type, (ulong) Value, Location); + case BuiltinTypeSpec.Type.Float: + return new FloatConstant (target_type, (float) Value, Location); + case BuiltinTypeSpec.Type.Double: + return new DoubleConstant (target_type, (double) Value, Location); + case BuiltinTypeSpec.Type.Decimal: + return new DecimalConstant (target_type, (decimal) Value, Location); + } + + return null; + } + + } + + public class SByteConstant : IntegralConstant + { + public readonly sbyte Value; + + public SByteConstant (BuiltinTypes types, sbyte v, Location loc) + : this (types.SByte, v, loc) + { + } + + public SByteConstant (TypeSpec type, sbyte v, Location loc) + : base (type, loc) + { + Value = v; + } + + public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) + { + enc.Encode (Value); + } + + public override void Emit (EmitContext ec) + { + ec.EmitInt (Value); + } + + public override object GetValue () + { + return Value; + } + + public override long GetValueAsLong () + { + return Value; + } + + public override Constant Increment () + { + return new SByteConstant (type, checked((sbyte)(Value + 1)), loc); + } + + public override bool IsDefaultValue { + get { + return Value == 0; + } + } + + public override bool IsNegative { + get { + return Value < 0; + } + } + + public override bool IsOneInteger { + get { + return Value == 1; + } + } + + public override bool IsZeroInteger { + get { return Value == 0; } + } + + public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) + { + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.Byte: + if (in_checked_context && Value < 0) + throw new OverflowException (); + return new ByteConstant (target_type, (byte) Value, Location); + case BuiltinTypeSpec.Type.Short: + return new ShortConstant (target_type, (short) Value, Location); + case BuiltinTypeSpec.Type.UShort: + if (in_checked_context && Value < 0) + throw new OverflowException (); + return new UShortConstant (target_type, (ushort) Value, Location); + case BuiltinTypeSpec.Type.Int: + return new IntConstant (target_type, (int) Value, Location); + case BuiltinTypeSpec.Type.UInt: + if (in_checked_context && Value < 0) + throw new OverflowException (); + return new UIntConstant (target_type, (uint) Value, Location); + case BuiltinTypeSpec.Type.Long: + return new LongConstant (target_type, (long) Value, Location); + case BuiltinTypeSpec.Type.ULong: + if (in_checked_context && Value < 0) + throw new OverflowException (); + return new ULongConstant (target_type, (ulong) Value, Location); + case BuiltinTypeSpec.Type.Float: + return new FloatConstant (target_type, (float) Value, Location); + case BuiltinTypeSpec.Type.Double: + return new DoubleConstant (target_type, (double) Value, Location); + case BuiltinTypeSpec.Type.Char: + if (in_checked_context && Value < 0) + throw new OverflowException (); + return new CharConstant (target_type, (char) Value, Location); + case BuiltinTypeSpec.Type.Decimal: + return new DecimalConstant (target_type, (decimal) Value, Location); + } + + return null; + } + + } + + public class ShortConstant : IntegralConstant { + public readonly short Value; + + public ShortConstant (BuiltinTypes types, short v, Location loc) + : this (types.Short, v, loc) + { + } + + public ShortConstant (TypeSpec type, short v, Location loc) + : base (type, loc) + { + Value = v; + } + + public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) + { + enc.Encode (Value); + } + + public override void Emit (EmitContext ec) + { + ec.EmitInt (Value); + } + + public override object GetValue () + { + return Value; + } + + public override long GetValueAsLong () + { + return Value; + } + + public override Constant Increment () + { + return new ShortConstant (type, checked((short)(Value + 1)), loc); + } + + public override bool IsDefaultValue { + get { + return Value == 0; + } + } + + public override bool IsZeroInteger { + get { return Value == 0; } + } + + public override bool IsNegative { + get { + return Value < 0; + } + } + + public override bool IsOneInteger { + get { + return Value == 1; + } + } + + public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) + { + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.Byte: + if (in_checked_context) { + if (Value < Byte.MinValue || Value > Byte.MaxValue) + throw new OverflowException (); + } + return new ByteConstant (target_type, (byte) Value, Location); + case BuiltinTypeSpec.Type.SByte: + if (in_checked_context) { + if (Value < SByte.MinValue || Value > SByte.MaxValue) + throw new OverflowException (); + } + return new SByteConstant (target_type, (sbyte) Value, Location); + case BuiltinTypeSpec.Type.UShort: + if (in_checked_context && Value < 0) + throw new OverflowException (); + + return new UShortConstant (target_type, (ushort) Value, Location); + case BuiltinTypeSpec.Type.Int: + return new IntConstant (target_type, (int) Value, Location); + case BuiltinTypeSpec.Type.UInt: + if (in_checked_context && Value < 0) + throw new OverflowException (); + return new UIntConstant (target_type, (uint) Value, Location); + case BuiltinTypeSpec.Type.Long: + return new LongConstant (target_type, (long) Value, Location); + case BuiltinTypeSpec.Type.ULong: + if (in_checked_context && Value < 0) + throw new OverflowException (); + return new ULongConstant (target_type, (ulong) Value, Location); + case BuiltinTypeSpec.Type.Float: + return new FloatConstant (target_type, (float) Value, Location); + case BuiltinTypeSpec.Type.Double: + return new DoubleConstant (target_type, (double) Value, Location); + case BuiltinTypeSpec.Type.Char: + if (in_checked_context) { + if (Value < Char.MinValue) + throw new OverflowException (); + } + return new CharConstant (target_type, (char) Value, Location); + case BuiltinTypeSpec.Type.Decimal: + return new DecimalConstant (target_type, (decimal) Value, Location); + } + + return null; + } + + } + + public class UShortConstant : IntegralConstant + { + public readonly ushort Value; + + public UShortConstant (BuiltinTypes types, ushort v, Location loc) + : this (types.UShort, v, loc) + { + } + + public UShortConstant (TypeSpec type, ushort v, Location loc) + : base (type, loc) + { + Value = v; + } + + public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) + { + enc.Encode (Value); + } + + public override void Emit (EmitContext ec) + { + ec.EmitInt (Value); + } + + public override object GetValue () + { + return Value; + } + + public override long GetValueAsLong () + { + return Value; + } + + public override Constant Increment () + { + return new UShortConstant (type, checked((ushort)(Value + 1)), loc); + } + + public override bool IsDefaultValue { + get { + return Value == 0; + } + } + + public override bool IsNegative { + get { + return false; + } + } + + public override bool IsOneInteger { + get { + return Value == 1; + } + } + + public override bool IsZeroInteger { + get { return Value == 0; } + } + + public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) + { + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.Byte: + if (in_checked_context) { + if (Value > Byte.MaxValue) + throw new OverflowException (); + } + return new ByteConstant (target_type, (byte) Value, Location); + case BuiltinTypeSpec.Type.SByte: + if (in_checked_context) { + if (Value > SByte.MaxValue) + throw new OverflowException (); + } + return new SByteConstant (target_type, (sbyte) Value, Location); + case BuiltinTypeSpec.Type.Short: + if (in_checked_context) { + if (Value > Int16.MaxValue) + throw new OverflowException (); + } + return new ShortConstant (target_type, (short) Value, Location); + case BuiltinTypeSpec.Type.Int: + return new IntConstant (target_type, (int) Value, Location); + case BuiltinTypeSpec.Type.UInt: + return new UIntConstant (target_type, (uint) Value, Location); + case BuiltinTypeSpec.Type.Long: + return new LongConstant (target_type, (long) Value, Location); + case BuiltinTypeSpec.Type.ULong: + return new ULongConstant (target_type, (ulong) Value, Location); + case BuiltinTypeSpec.Type.Float: + return new FloatConstant (target_type, (float) Value, Location); + case BuiltinTypeSpec.Type.Double: + return new DoubleConstant (target_type, (double) Value, Location); + case BuiltinTypeSpec.Type.Char: + if (in_checked_context) { + if (Value > Char.MaxValue) + throw new OverflowException (); + } + return new CharConstant (target_type, (char) Value, Location); + case BuiltinTypeSpec.Type.Decimal: + return new DecimalConstant (target_type, (decimal) Value, Location); + } + + return null; + } + } + + public class IntConstant : IntegralConstant + { + public readonly int Value; + + public IntConstant (BuiltinTypes types, int v, Location loc) + : this (types.Int, v, loc) + { + } + + public IntConstant (TypeSpec type, int v, Location loc) + : base (type, loc) + { + Value = v; + } + + public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) + { + enc.Encode (Value); + } + + public override void Emit (EmitContext ec) + { + ec.EmitInt (Value); + } + + public override object GetValue () + { + return Value; + } + + public override long GetValueAsLong () + { + return Value; + } + + public override Constant Increment () + { + return new IntConstant (type, checked(Value + 1), loc); + } + + public override bool IsDefaultValue { + get { + return Value == 0; + } + } + + public override bool IsNegative { + get { + return Value < 0; + } + } + + public override bool IsOneInteger { + get { + return Value == 1; + } + } + + public override bool IsZeroInteger { + get { return Value == 0; } + } + + public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) + { + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.Byte: + if (in_checked_context) { + if (Value < Byte.MinValue || Value > Byte.MaxValue) + throw new OverflowException (); + } + return new ByteConstant (target_type, (byte) Value, Location); + case BuiltinTypeSpec.Type.SByte: + if (in_checked_context) { + if (Value < SByte.MinValue || Value > SByte.MaxValue) + throw new OverflowException (); + } + return new SByteConstant (target_type, (sbyte) Value, Location); + case BuiltinTypeSpec.Type.Short: + if (in_checked_context) { + if (Value < Int16.MinValue || Value > Int16.MaxValue) + throw new OverflowException (); + } + return new ShortConstant (target_type, (short) Value, Location); + case BuiltinTypeSpec.Type.UShort: + if (in_checked_context) { + if (Value < UInt16.MinValue || Value > UInt16.MaxValue) + throw new OverflowException (); + } + return new UShortConstant (target_type, (ushort) Value, Location); + case BuiltinTypeSpec.Type.UInt: + if (in_checked_context) { + if (Value < UInt32.MinValue) + throw new OverflowException (); + } + return new UIntConstant (target_type, (uint) Value, Location); + case BuiltinTypeSpec.Type.Long: + return new LongConstant (target_type, (long) Value, Location); + case BuiltinTypeSpec.Type.ULong: + if (in_checked_context && Value < 0) + throw new OverflowException (); + return new ULongConstant (target_type, (ulong) Value, Location); + case BuiltinTypeSpec.Type.Float: + return new FloatConstant (target_type, (float) Value, Location); + case BuiltinTypeSpec.Type.Double: + return new DoubleConstant (target_type, (double) Value, Location); + case BuiltinTypeSpec.Type.Char: + if (in_checked_context) { + if (Value < Char.MinValue || Value > Char.MaxValue) + throw new OverflowException (); + } + return new CharConstant (target_type, (char) Value, Location); + case BuiltinTypeSpec.Type.Decimal: + return new DecimalConstant (target_type, (decimal) Value, Location); + } + + return null; + } + + public override Constant ConvertImplicitly (TypeSpec type) + { + if (this.type == type) + return this; + + Constant c = TryImplicitIntConversion (type); + if (c != null) + return c; //.Resolve (rc); + + return base.ConvertImplicitly (type); + } + + /// + /// Attempts to perform an implicit constant conversion of the IntConstant + /// into a different data type using casts (See Implicit Constant + /// Expression Conversions) + /// + Constant TryImplicitIntConversion (TypeSpec target_type) + { + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + if (Value >= SByte.MinValue && Value <= SByte.MaxValue) + return new SByteConstant (target_type, (sbyte) Value, loc); + break; + case BuiltinTypeSpec.Type.Byte: + if (Value >= Byte.MinValue && Value <= Byte.MaxValue) + return new ByteConstant (target_type, (byte) Value, loc); + break; + case BuiltinTypeSpec.Type.Short: + if (Value >= Int16.MinValue && Value <= Int16.MaxValue) + return new ShortConstant (target_type, (short) Value, loc); + break; + case BuiltinTypeSpec.Type.UShort: + if (Value >= UInt16.MinValue && Value <= UInt16.MaxValue) + return new UShortConstant (target_type, (ushort) Value, loc); + break; + case BuiltinTypeSpec.Type.UInt: + if (Value >= 0) + return new UIntConstant (target_type, (uint) Value, loc); + break; + case BuiltinTypeSpec.Type.ULong: + // + // we can optimize this case: a positive int32 + // always fits on a uint64. But we need an opcode + // to do it. + // + if (Value >= 0) + return new ULongConstant (target_type, (ulong) Value, loc); + break; + case BuiltinTypeSpec.Type.Double: + return new DoubleConstant (target_type, (double) Value, loc); + case BuiltinTypeSpec.Type.Float: + return new FloatConstant (target_type, (float) Value, loc); + } + + return null; + } + } + + public class UIntConstant : IntegralConstant { + public readonly uint Value; + + public UIntConstant (BuiltinTypes types, uint v, Location loc) + : this (types.UInt, v, loc) + { + } + + public UIntConstant (TypeSpec type, uint v, Location loc) + : base (type, loc) + { + Value = v; + } + + public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) + { + enc.Encode (Value); + } + + public override void Emit (EmitContext ec) + { + ec.EmitInt (unchecked ((int) Value)); + } + + public override object GetValue () + { + return Value; + } + + public override long GetValueAsLong () + { + return Value; + } + + public override Constant Increment () + { + return new UIntConstant (type, checked(Value + 1), loc); + } + + public override bool IsDefaultValue { + get { + return Value == 0; + } + } + + public override bool IsNegative { + get { + return false; + } + } + + public override bool IsOneInteger { + get { + return Value == 1; + } + } + + public override bool IsZeroInteger { + get { return Value == 0; } + } + + public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) + { + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.Byte: + if (in_checked_context) { + if (Value < 0 || Value > byte.MaxValue) + throw new OverflowException (); + } + return new ByteConstant (target_type, (byte) Value, Location); + case BuiltinTypeSpec.Type.SByte: + if (in_checked_context) { + if (Value > SByte.MaxValue) + throw new OverflowException (); + } + return new SByteConstant (target_type, (sbyte) Value, Location); + case BuiltinTypeSpec.Type.Short: + if (in_checked_context) { + if (Value > Int16.MaxValue) + throw new OverflowException (); + } + return new ShortConstant (target_type, (short) Value, Location); + case BuiltinTypeSpec.Type.UShort: + if (in_checked_context) { + if (Value < UInt16.MinValue || Value > UInt16.MaxValue) + throw new OverflowException (); + } + return new UShortConstant (target_type, (ushort) Value, Location); + case BuiltinTypeSpec.Type.Int: + if (in_checked_context) { + if (Value > Int32.MaxValue) + throw new OverflowException (); + } + return new IntConstant (target_type, (int) Value, Location); + case BuiltinTypeSpec.Type.Long: + return new LongConstant (target_type, (long) Value, Location); + case BuiltinTypeSpec.Type.ULong: + return new ULongConstant (target_type, (ulong) Value, Location); + case BuiltinTypeSpec.Type.Float: + return new FloatConstant (target_type, (float) Value, Location); + case BuiltinTypeSpec.Type.Double: + return new DoubleConstant (target_type, (double) Value, Location); + case BuiltinTypeSpec.Type.Char: + if (in_checked_context) { + if (Value < Char.MinValue || Value > Char.MaxValue) + throw new OverflowException (); + } + return new CharConstant (target_type, (char) Value, Location); + case BuiltinTypeSpec.Type.Decimal: + return new DecimalConstant (target_type, (decimal) Value, Location); + } + + return null; + } + + } + + public class LongConstant : IntegralConstant { + public readonly long Value; + + public LongConstant (BuiltinTypes types, long v, Location loc) + : this (types.Long, v, loc) + { + } + + public LongConstant (TypeSpec type, long v, Location loc) + : base (type, loc) + { + Value = v; + } + + public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) + { + enc.Encode (Value); + } + + public override void Emit (EmitContext ec) + { + ec.EmitLong (Value); + } + + public override object GetValue () + { + return Value; + } + + public override long GetValueAsLong () + { + return Value; + } + + public override Constant Increment () + { + return new LongConstant (type, checked(Value + 1), loc); + } + + public override bool IsDefaultValue { + get { + return Value == 0; + } + } + + public override bool IsNegative { + get { + return Value < 0; + } + } + + public override bool IsOneInteger { + get { + return Value == 1; + } + } + + public override bool IsZeroInteger { + get { return Value == 0; } + } + + public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) + { + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.Byte: + if (in_checked_context) { + if (Value < Byte.MinValue || Value > Byte.MaxValue) + throw new OverflowException (); + } + return new ByteConstant (target_type, (byte) Value, Location); + case BuiltinTypeSpec.Type.SByte: + if (in_checked_context) { + if (Value < SByte.MinValue || Value > SByte.MaxValue) + throw new OverflowException (); + } + return new SByteConstant (target_type, (sbyte) Value, Location); + case BuiltinTypeSpec.Type.Short: + if (in_checked_context) { + if (Value < Int16.MinValue || Value > Int16.MaxValue) + throw new OverflowException (); + } + return new ShortConstant (target_type, (short) Value, Location); + case BuiltinTypeSpec.Type.UShort: + if (in_checked_context) { + if (Value < UInt16.MinValue || Value > UInt16.MaxValue) + throw new OverflowException (); + } + return new UShortConstant (target_type, (ushort) Value, Location); + case BuiltinTypeSpec.Type.Int: + if (in_checked_context) { + if (Value < Int32.MinValue || Value > Int32.MaxValue) + throw new OverflowException (); + } + return new IntConstant (target_type, (int) Value, Location); + case BuiltinTypeSpec.Type.UInt: + if (in_checked_context) { + if (Value < UInt32.MinValue || Value > UInt32.MaxValue) + throw new OverflowException (); + } + return new UIntConstant (target_type, (uint) Value, Location); + case BuiltinTypeSpec.Type.ULong: + if (in_checked_context && Value < 0) + throw new OverflowException (); + return new ULongConstant (target_type, (ulong) Value, Location); + case BuiltinTypeSpec.Type.Float: + return new FloatConstant (target_type, (float) Value, Location); + case BuiltinTypeSpec.Type.Double: + return new DoubleConstant (target_type, (double) Value, Location); + case BuiltinTypeSpec.Type.Char: + if (in_checked_context) { + if (Value < Char.MinValue || Value > Char.MaxValue) + throw new OverflowException (); + } + return new CharConstant (target_type, (char) Value, Location); + case BuiltinTypeSpec.Type.Decimal: + return new DecimalConstant (target_type, (decimal) Value, Location); + } + + return null; + } + + public override Constant ConvertImplicitly (TypeSpec type) + { + if (Value >= 0 && type.BuiltinType == BuiltinTypeSpec.Type.ULong) { + return new ULongConstant (type, (ulong) Value, loc); + } + + return base.ConvertImplicitly (type); + } + } + + public class ULongConstant : IntegralConstant { + public readonly ulong Value; + + public ULongConstant (BuiltinTypes types, ulong v, Location loc) + : this (types.ULong, v, loc) + { + } + + public ULongConstant (TypeSpec type, ulong v, Location loc) + : base (type, loc) + { + Value = v; + } + + public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) + { + enc.Encode (Value); + } + + public override void Emit (EmitContext ec) + { + ec.EmitLong (unchecked ((long) Value)); + } + + public override object GetValue () + { + return Value; + } + + public override long GetValueAsLong () + { + return (long) Value; + } + + public override Constant Increment () + { + return new ULongConstant (type, checked(Value + 1), loc); + } + + public override bool IsDefaultValue { + get { + return Value == 0; + } + } + + public override bool IsNegative { + get { + return false; + } + } + + public override bool IsOneInteger { + get { + return Value == 1; + } + } + + public override bool IsZeroInteger { + get { return Value == 0; } + } + + public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) + { + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.Byte: + if (in_checked_context && Value > Byte.MaxValue) + throw new OverflowException (); + return new ByteConstant (target_type, (byte) Value, Location); + case BuiltinTypeSpec.Type.SByte: + if (in_checked_context && Value > ((ulong) SByte.MaxValue)) + throw new OverflowException (); + return new SByteConstant (target_type, (sbyte) Value, Location); + case BuiltinTypeSpec.Type.Short: + if (in_checked_context && Value > ((ulong) Int16.MaxValue)) + throw new OverflowException (); + return new ShortConstant (target_type, (short) Value, Location); + case BuiltinTypeSpec.Type.UShort: + if (in_checked_context && Value > UInt16.MaxValue) + throw new OverflowException (); + return new UShortConstant (target_type, (ushort) Value, Location); + case BuiltinTypeSpec.Type.Int: + if (in_checked_context && Value > UInt32.MaxValue) + throw new OverflowException (); + return new IntConstant (target_type, (int) Value, Location); + case BuiltinTypeSpec.Type.UInt: + if (in_checked_context && Value > UInt32.MaxValue) + throw new OverflowException (); + return new UIntConstant (target_type, (uint) Value, Location); + case BuiltinTypeSpec.Type.Long: + if (in_checked_context && Value > Int64.MaxValue) + throw new OverflowException (); + return new LongConstant (target_type, (long) Value, Location); + case BuiltinTypeSpec.Type.Float: + return new FloatConstant (target_type, (float) Value, Location); + case BuiltinTypeSpec.Type.Double: + return new DoubleConstant (target_type, (double) Value, Location); + case BuiltinTypeSpec.Type.Char: + if (in_checked_context && Value > Char.MaxValue) + throw new OverflowException (); + return new CharConstant (target_type, (char) Value, Location); + case BuiltinTypeSpec.Type.Decimal: + return new DecimalConstant (target_type, (decimal) Value, Location); + } + + return null; + } + + } + + public class FloatConstant : Constant { + // + // Store constant value as double because float constant operations + // need to work on double value to match JIT + // + public readonly double DoubleValue; + + public FloatConstant (BuiltinTypes types, double v, Location loc) + : this (types.Float, v, loc) + { + } + + public FloatConstant (TypeSpec type, double v, Location loc) + : base (loc) + { + this.type = type; + eclass = ExprClass.Value; + + DoubleValue = v; + } + + public override Constant ConvertImplicitly (TypeSpec type) + { + if (type.BuiltinType == BuiltinTypeSpec.Type.Double) + return new DoubleConstant (type, DoubleValue, loc); + + return base.ConvertImplicitly (type); + } + + public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) + { + enc.Encode (Value); + } + + public override void Emit (EmitContext ec) + { + ec.Emit (OpCodes.Ldc_R4, Value); + } + + public float Value { + get { + return (float) DoubleValue; + } + } + + public override object GetValue () + { + return Value; + } + + public override string GetValueAsLiteral () + { + return Value.ToString (); + } + + public override long GetValueAsLong () + { + throw new NotSupportedException (); + } + + public override bool IsDefaultValue { + get { + return Value == 0; + } + } + + public override bool IsNegative { + get { + return Value < 0; + } + } + + public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) + { + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.Byte: + if (in_checked_context) { + if (Value < byte.MinValue || Value > byte.MaxValue || float.IsNaN (Value)) + throw new OverflowException (); + } + return new ByteConstant (target_type, (byte) DoubleValue, Location); + case BuiltinTypeSpec.Type.SByte: + if (in_checked_context) { + if (Value < sbyte.MinValue || Value > sbyte.MaxValue || float.IsNaN (Value)) + throw new OverflowException (); + } + return new SByteConstant (target_type, (sbyte) DoubleValue, Location); + case BuiltinTypeSpec.Type.Short: + if (in_checked_context) { + if (Value < short.MinValue || Value > short.MaxValue || float.IsNaN (Value)) + throw new OverflowException (); + } + return new ShortConstant (target_type, (short) DoubleValue, Location); + case BuiltinTypeSpec.Type.UShort: + if (in_checked_context) { + if (Value < ushort.MinValue || Value > ushort.MaxValue || float.IsNaN (Value)) + throw new OverflowException (); + } + return new UShortConstant (target_type, (ushort) DoubleValue, Location); + case BuiltinTypeSpec.Type.Int: + if (in_checked_context) { + if (Value < int.MinValue || Value > int.MaxValue || float.IsNaN (Value)) + throw new OverflowException (); + } + return new IntConstant (target_type, (int) DoubleValue, Location); + case BuiltinTypeSpec.Type.UInt: + if (in_checked_context) { + if (Value < uint.MinValue || Value > uint.MaxValue || float.IsNaN (Value)) + throw new OverflowException (); + } + return new UIntConstant (target_type, (uint) DoubleValue, Location); + case BuiltinTypeSpec.Type.Long: + if (in_checked_context) { + if (Value < long.MinValue || Value > long.MaxValue || float.IsNaN (Value)) + throw new OverflowException (); + } + return new LongConstant (target_type, (long) DoubleValue, Location); + case BuiltinTypeSpec.Type.ULong: + if (in_checked_context) { + if (Value < ulong.MinValue || Value > ulong.MaxValue || float.IsNaN (Value)) + throw new OverflowException (); + } + return new ULongConstant (target_type, (ulong) DoubleValue, Location); + case BuiltinTypeSpec.Type.Double: + return new DoubleConstant (target_type, DoubleValue, Location); + case BuiltinTypeSpec.Type.Char: + if (in_checked_context) { + if (Value < (float) char.MinValue || Value > (float) char.MaxValue || float.IsNaN (Value)) + throw new OverflowException (); + } + return new CharConstant (target_type, (char) DoubleValue, Location); + case BuiltinTypeSpec.Type.Decimal: + return new DecimalConstant (target_type, (decimal) DoubleValue, Location); + } + + return null; + } + + } + + public class DoubleConstant : Constant + { + public readonly double Value; + + public DoubleConstant (BuiltinTypes types, double v, Location loc) + : this (types.Double, v, loc) + { + } + + public DoubleConstant (TypeSpec type, double v, Location loc) + : base (loc) + { + this.type = type; + eclass = ExprClass.Value; + + Value = v; + } + + public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) + { + enc.Encode (Value); + } + + public override void Emit (EmitContext ec) + { + ec.Emit (OpCodes.Ldc_R8, Value); + } + + public override object GetValue () + { + return Value; + } + + public override string GetValueAsLiteral () + { + return Value.ToString (); + } + + public override long GetValueAsLong () + { + throw new NotSupportedException (); + } + + public override bool IsDefaultValue { + get { + return Value == 0; + } + } + + public override bool IsNegative { + get { + return Value < 0; + } + } + + public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) + { + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.Byte: + if (in_checked_context) { + if (Value < Byte.MinValue || Value > Byte.MaxValue || double.IsNaN (Value)) + throw new OverflowException (); + } + return new ByteConstant (target_type, (byte) Value, Location); + case BuiltinTypeSpec.Type.SByte: + if (in_checked_context) { + if (Value < SByte.MinValue || Value > SByte.MaxValue || double.IsNaN (Value)) + throw new OverflowException (); + } + return new SByteConstant (target_type, (sbyte) Value, Location); + case BuiltinTypeSpec.Type.Short: + if (in_checked_context) { + if (Value < short.MinValue || Value > short.MaxValue || double.IsNaN (Value)) + throw new OverflowException (); + } + return new ShortConstant (target_type, (short) Value, Location); + case BuiltinTypeSpec.Type.UShort: + if (in_checked_context) { + if (Value < ushort.MinValue || Value > ushort.MaxValue || double.IsNaN (Value)) + throw new OverflowException (); + } + return new UShortConstant (target_type, (ushort) Value, Location); + case BuiltinTypeSpec.Type.Int: + if (in_checked_context) { + if (Value < int.MinValue || Value > int.MaxValue || double.IsNaN (Value)) + throw new OverflowException (); + } + return new IntConstant (target_type, (int) Value, Location); + case BuiltinTypeSpec.Type.UInt: + if (in_checked_context) { + if (Value < uint.MinValue || Value > uint.MaxValue || double.IsNaN (Value)) + throw new OverflowException (); + } + return new UIntConstant (target_type, (uint) Value, Location); + case BuiltinTypeSpec.Type.Long: + if (in_checked_context) { + if (Value < long.MinValue || Value > long.MaxValue || double.IsNaN (Value)) + throw new OverflowException (); + } + return new LongConstant (target_type, (long) Value, Location); + case BuiltinTypeSpec.Type.ULong: + if (in_checked_context) { + if (Value < ulong.MinValue || Value > ulong.MaxValue || double.IsNaN (Value)) + throw new OverflowException (); + } + return new ULongConstant (target_type, (ulong) Value, Location); + case BuiltinTypeSpec.Type.Float: + return new FloatConstant (target_type, (float) Value, Location); + case BuiltinTypeSpec.Type.Char: + if (in_checked_context) { + if (Value < (double) char.MinValue || Value > (double) char.MaxValue || double.IsNaN (Value)) + throw new OverflowException (); + } + return new CharConstant (target_type, (char) Value, Location); + case BuiltinTypeSpec.Type.Decimal: + return new DecimalConstant (target_type, (decimal) Value, Location); + } + + return null; + } + + } + + public class DecimalConstant : Constant { + public readonly decimal Value; + + public DecimalConstant (BuiltinTypes types, decimal d, Location loc) + : this (types.Decimal, d, loc) + { + } + + public DecimalConstant (TypeSpec type, decimal d, Location loc) + : base (loc) + { + this.type = type; + eclass = ExprClass.Value; + + Value = d; + } + + public override void Emit (EmitContext ec) + { + MethodSpec m; + + int [] words = decimal.GetBits (Value); + int power = (words [3] >> 16) & 0xff; + + if (power == 0) { + if (Value <= int.MaxValue && Value >= int.MinValue) { + m = ec.Module.PredefinedMembers.DecimalCtorInt.Resolve (loc); + if (m == null) { + return; + } + + ec.EmitInt ((int) Value); + ec.Emit (OpCodes.Newobj, m); + return; + } + + if (Value <= long.MaxValue && Value >= long.MinValue) { + m = ec.Module.PredefinedMembers.DecimalCtorLong.Resolve (loc); + if (m == null) { + return; + } + + ec.EmitLong ((long) Value); + ec.Emit (OpCodes.Newobj, m); + return; + } + } + + ec.EmitInt (words [0]); + ec.EmitInt (words [1]); + ec.EmitInt (words [2]); + + // sign + ec.EmitInt (words [3] >> 31); + + // power + ec.EmitInt (power); + + m = ec.Module.PredefinedMembers.DecimalCtor.Resolve (loc); + if (m != null) { + ec.Emit (OpCodes.Newobj, m); + } + } + + public override bool IsDefaultValue { + get { + return Value == 0; + } + } + + public override bool IsNegative { + get { + return Value < 0; + } + } + + public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) + { + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + return new SByteConstant (target_type, (sbyte) Value, loc); + case BuiltinTypeSpec.Type.Byte: + return new ByteConstant (target_type, (byte) Value, loc); + case BuiltinTypeSpec.Type.Short: + return new ShortConstant (target_type, (short) Value, loc); + case BuiltinTypeSpec.Type.UShort: + return new UShortConstant (target_type, (ushort) Value, loc); + case BuiltinTypeSpec.Type.Int: + return new IntConstant (target_type, (int) Value, loc); + case BuiltinTypeSpec.Type.UInt: + return new UIntConstant (target_type, (uint) Value, loc); + case BuiltinTypeSpec.Type.Long: + return new LongConstant (target_type, (long) Value, loc); + case BuiltinTypeSpec.Type.ULong: + return new ULongConstant (target_type, (ulong) Value, loc); + case BuiltinTypeSpec.Type.Char: + return new CharConstant (target_type, (char) Value, loc); + case BuiltinTypeSpec.Type.Float: + return new FloatConstant (target_type, (float) Value, loc); + case BuiltinTypeSpec.Type.Double: + return new DoubleConstant (target_type, (double) Value, loc); + } + + return null; + } + + public override object GetValue () + { + return Value; + } + + public override string GetValueAsLiteral () + { + return Value.ToString () + "M"; + } + + public override long GetValueAsLong () + { + throw new NotSupportedException (); + } + } + + public class StringConstant : Constant { + public StringConstant (BuiltinTypes types, string s, Location loc) + : this (types.String, s, loc) + { + } + + public StringConstant (TypeSpec type, string s, Location loc) + : base (loc) + { + this.type = type; + eclass = ExprClass.Value; + + Value = s; + } + + protected StringConstant (Location loc) + : base (loc) + { + } + + public string Value { get; protected set; } + + public override object GetValue () + { + return Value; + } + + public override string GetValueAsLiteral () + { + // FIXME: Escape the string. + return "\"" + Value + "\""; + } + + public override long GetValueAsLong () + { + throw new NotSupportedException (); + } + + public override void Emit (EmitContext ec) + { + if (Value == null) { + ec.EmitNull (); + return; + } + + // + // Use string.Empty for both literals and constants even if + // it's not allowed at language level + // + if (Value.Length == 0 && ec.Module.Compiler.Settings.Optimize) { + var string_type = ec.BuiltinTypes.String; + if (ec.CurrentType != string_type) { + var m = ec.Module.PredefinedMembers.StringEmpty.Get (); + if (m != null) { + ec.Emit (OpCodes.Ldsfld, m); + return; + } + } + } + + ec.Emit (OpCodes.Ldstr, Value); + } + + public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) + { + // cast to object + if (type != targetType) + enc.Encode (type); + + enc.Encode (Value); + } + + public override bool IsDefaultValue { + get { + return Value == null; + } + } + + public override bool IsNegative { + get { + return false; + } + } + + public override bool IsNull { + get { + return IsDefaultValue; + } + } + + public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) + { + return null; + } + + public override Constant ConvertImplicitly (TypeSpec type) + { + if (IsDefaultValue && type.BuiltinType == BuiltinTypeSpec.Type.Object) + return new NullConstant (type, loc); + + return base.ConvertImplicitly (type); + } + } + + class NameOf : StringConstant + { + readonly SimpleName name; + + public NameOf (SimpleName name) + : base (name.Location) + { + this.name = name; + } + + protected override Expression DoResolve (ResolveContext rc) + { + throw new NotSupportedException (); + } + + bool ResolveArgumentExpression (ResolveContext rc, Expression expr) + { + var sn = expr as SimpleName; + if (sn != null) { + Value = sn.Name; + + if (rc.Module.Compiler.Settings.Version < LanguageVersion.V_6) + rc.Report.FeatureIsNotAvailable (rc.Module.Compiler, Location, "nameof operator"); + + if (sn.HasTypeArguments) { + // TODO: csc compatible but unhelpful error message + rc.Report.Error (1001, loc, "Identifier expected"); + return true; + } + + sn.LookupNameExpression (rc, MemberLookupRestrictions.IgnoreArity | MemberLookupRestrictions.IgnoreAmbiguity); + return true; + } + + var ma = expr as MemberAccess; + if (ma != null) { + FullNamedExpression fne = ma.LeftExpression as ATypeNameExpression; + if (fne == null) { + var qam = ma as QualifiedAliasMember; + if (qam == null) + return false; + + fne = qam.CreateExpressionFromAlias (rc); + if (fne == null) + return true; + } + + Value = ma.Name; + + if (rc.Module.Compiler.Settings.Version < LanguageVersion.V_6) + rc.Report.FeatureIsNotAvailable (rc.Module.Compiler, Location, "nameof operator"); + + if (ma.HasTypeArguments) { + // TODO: csc compatible but unhelpful error message + rc.Report.Error (1001, loc, "Identifier expected"); + return true; + } + + var left = fne.ResolveAsTypeOrNamespace (rc, true); + if (left == null) + return true; + + var ns = left as NamespaceExpression; + if (ns != null) { + FullNamedExpression retval = ns.LookupTypeOrNamespace (rc, ma.Name, 0, LookupMode.NameOf, loc); + if (retval == null) + ns.Error_NamespaceDoesNotExist (rc, ma.Name, 0); + + return true; + } + + if (left.Type.IsGenericOrParentIsGeneric && left.Type.GetDefinition () != left.Type) { + rc.Report.Error (8071, loc, "Type arguments are not allowed in the nameof operator"); + } + + var mexpr = MemberLookup (rc, false, left.Type, ma.Name, 0, MemberLookupRestrictions.IgnoreArity | MemberLookupRestrictions.IgnoreAmbiguity, loc); + if (mexpr == null) { + ma.Error_IdentifierNotFound (rc, left.Type); + return true; + } + + return true; + } + + return false; + } + + public Expression ResolveOverload (ResolveContext rc, Arguments args) + { + if (args == null || args.Count != 1) { + name.Error_NameDoesNotExist (rc); + return null; + } + + var arg = args [0]; + var res = ResolveArgumentExpression (rc, arg.Expr); + if (!res) { + name.Error_NameDoesNotExist (rc); + return null; + } + + type = rc.BuiltinTypes.String; + eclass = ExprClass.Value; + return this; + } + } + + // + // Null constant can have its own type, think of `default (Foo)' + // + public class NullConstant : Constant + { + public NullConstant (TypeSpec type, Location loc) + : base (loc) + { + eclass = ExprClass.Value; + this.type = type; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + if (type == InternalType.NullLiteral || type.BuiltinType == BuiltinTypeSpec.Type.Object) { + // Optimized version, also avoids referencing literal internal type + Arguments args = new Arguments (1); + args.Add (new Argument (this)); + return CreateExpressionFactoryCall (ec, "Constant", args); + } + + return base.CreateExpressionTree (ec); + } + + public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) + { + switch (targetType.BuiltinType) { + case BuiltinTypeSpec.Type.Object: + // Type it as string cast + enc.Encode (rc.Module.Compiler.BuiltinTypes.String); + goto case BuiltinTypeSpec.Type.String; + case BuiltinTypeSpec.Type.String: + case BuiltinTypeSpec.Type.Type: + enc.Encode (byte.MaxValue); + return; + default: + var ac = targetType as ArrayContainer; + if (ac != null && ac.Rank == 1 && !ac.Element.IsArray) { + enc.Encode (uint.MaxValue); + return; + } + + break; + } + + base.EncodeAttributeValue (rc, enc, targetType, parameterType); + } + + public override void Emit (EmitContext ec) + { + ec.EmitNull (); + + // Only to make verifier happy + if (type.IsGenericParameter) + ec.Emit (OpCodes.Unbox_Any, type); + } + + public override string ExprClassName { + get { + return GetSignatureForError (); + } + } + + public override Constant ConvertExplicitly (bool inCheckedContext, TypeSpec targetType) + { + if (targetType.IsPointer) { + if (IsLiteral || this is NullPointer) + return new NullPointer (targetType, loc); + + return null; + } + + // Exlude internal compiler types + if (targetType.Kind == MemberKind.InternalCompilerType && targetType.BuiltinType != BuiltinTypeSpec.Type.Dynamic) + return null; + + if (!IsLiteral && !Convert.ImplicitStandardConversionExists (this, targetType)) + return null; + + if (TypeSpec.IsReferenceType (targetType)) + return new NullConstant (targetType, loc); + + if (targetType.IsNullableType) + return Nullable.LiftedNull.Create (targetType, loc); + + return null; + } + + public override Constant ConvertImplicitly (TypeSpec targetType) + { + return ConvertExplicitly (false, targetType); + } + + public override string GetSignatureForError () + { + return "null"; + } + + public override object GetValue () + { + return null; + } + + public override string GetValueAsLiteral () + { + return GetSignatureForError (); + } + + public override long GetValueAsLong () + { + throw new NotSupportedException (); + } + + public override bool IsDefaultValue { + get { return true; } + } + + public override bool IsNegative { + get { return false; } + } + + public override bool IsNull { + get { return true; } + } + + public override bool IsZeroInteger { + get { return true; } + } + } + + + // + // A null constant in a pointer context + // + class NullPointer : NullConstant + { + public NullPointer (TypeSpec type, Location loc) + : base (type, loc) + { + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Error_PointerInsideExpressionTree (ec); + return base.CreateExpressionTree (ec); + } + + public override void Emit (EmitContext ec) + { + // + // Emits null pointer + // + ec.EmitInt (0); + ec.Emit (OpCodes.Conv_U); + } + } + + /// + /// The value is constant, but when emitted has a side effect. This is + /// used by BitwiseAnd to ensure that the second expression is invoked + /// regardless of the value of the left side. + /// + public class SideEffectConstant : Constant + { + public readonly Constant value; + Expression side_effect; + + public SideEffectConstant (Constant value, Expression side_effect, Location loc) + : base (loc) + { + this.value = value; + type = value.Type; + eclass = ExprClass.Value; + + while (side_effect is SideEffectConstant) + side_effect = ((SideEffectConstant) side_effect).side_effect; + this.side_effect = side_effect; + } + + public override bool IsSideEffectFree { + get { + return false; + } + } + + public override bool ContainsEmitWithAwait () + { + return side_effect.ContainsEmitWithAwait (); + } + + public override object GetValue () + { + return value.GetValue (); + } + + public override string GetValueAsLiteral () + { + return value.GetValueAsLiteral (); + } + + public override long GetValueAsLong () + { + return value.GetValueAsLong (); + } + + public override void Emit (EmitContext ec) + { + side_effect.EmitSideEffect (ec); + value.Emit (ec); + } + + public override void EmitSideEffect (EmitContext ec) + { + side_effect.EmitSideEffect (ec); + value.EmitSideEffect (ec); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + side_effect.FlowAnalysis (fc); + } + + public override bool IsDefaultValue { + get { return value.IsDefaultValue; } + } + + public override bool IsNegative { + get { return value.IsNegative; } + } + + public override bool IsZeroInteger { + get { return value.IsZeroInteger; } + } + + public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) + { + Constant new_value = value.ConvertExplicitly (in_checked_context, target_type); + if (new_value == null) + return null; + + var c = new SideEffectConstant (new_value, side_effect, new_value.Location); + c.type = target_type; + c.eclass = eclass; + return c; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/context.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/context.cs new file mode 100644 index 000000000..230aa1257 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/context.cs @@ -0,0 +1,751 @@ +// +// context.cs: Various compiler contexts. +// +// Author: +// Marek Safar (marek.safar@gmail.com) +// Miguel de Icaza (miguel@ximian.com) +// +// Copyright 2001, 2002, 2003 Ximian, Inc. +// Copyright 2004-2009 Novell, Inc. +// Copyright 2011 Xamarin Inc. +// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Security.Cryptography; +using System.Diagnostics; + +namespace Mono.CSharp +{ + public enum LookupMode + { + Normal = 0, + Probing = 1, + IgnoreAccessibility = 2, + NameOf = 3 + } + + // + // Implemented by elements which can act as independent contexts + // during resolve phase. Used mostly for lookups. + // + public interface IMemberContext : IModuleContext + { + // + // A scope type context, it can be inflated for generic types + // + TypeSpec CurrentType { get; } + + // + // A scope type parameters either VAR or MVAR + // + TypeParameters CurrentTypeParameters { get; } + + // + // A member definition of the context. For partial types definition use + // CurrentTypeDefinition.PartialContainer otherwise the context is local + // + // TODO: Obsolete it in this context, dynamic context cannot guarantee sensible value + // + MemberCore CurrentMemberDefinition { get; } + + bool IsObsolete { get; } + bool IsUnsafe { get; } + bool IsStatic { get; } + + string GetSignatureForError (); + + ExtensionMethodCandidates LookupExtensionMethod (TypeSpec extensionType, string name, int arity); + FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc); + FullNamedExpression LookupNamespaceAlias (string name); + } + + public interface IModuleContext + { + ModuleContainer Module { get; } + } + + // + // Block or statement resolving context + // + public class BlockContext : ResolveContext + { + readonly TypeSpec return_type; + + // + // Tracks the last offset used by VariableInfo + // + public int AssignmentInfoOffset; + + public BlockContext (IMemberContext mc, ExplicitBlock block, TypeSpec returnType) + : base (mc) + { + if (returnType == null) + throw new ArgumentNullException ("returnType"); + + this.return_type = returnType; + + // TODO: check for null value + CurrentBlock = block; + } + + public BlockContext (ResolveContext rc, ExplicitBlock block, TypeSpec returnType) + : this (rc.MemberContext, block, returnType) + { + if (rc.IsUnsafe) + flags |= ResolveContext.Options.UnsafeScope; + + if (rc.HasSet (ResolveContext.Options.CheckedScope)) + flags |= ResolveContext.Options.CheckedScope; + + if (!rc.ConstantCheckState) + flags &= ~Options.ConstantCheckState; + + if (rc.IsInProbingMode) + flags |= ResolveContext.Options.ProbingMode; + + if (rc.HasSet (ResolveContext.Options.FieldInitializerScope)) + flags |= ResolveContext.Options.FieldInitializerScope; + + if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion)) + flags |= ResolveContext.Options.ExpressionTreeConversion; + + if (rc.HasSet (ResolveContext.Options.BaseInitializer)) + flags |= ResolveContext.Options.BaseInitializer; + } + + public ExceptionStatement CurrentTryBlock { get; set; } + + public LoopStatement EnclosingLoop { get; set; } + + public LoopStatement EnclosingLoopOrSwitch { get; set; } + + public Switch Switch { get; set; } + + public TypeSpec ReturnType { + get { return return_type; } + } + } + + // + // Expression resolving context + // + public class ResolveContext : IMemberContext + { + [Flags] + public enum Options + { + /// + /// This flag tracks the `checked' state of the compilation, + /// it controls whether we should generate code that does overflow + /// checking, or if we generate code that ignores overflows. + /// + /// The default setting comes from the command line option to generate + /// checked or unchecked code plus any source code changes using the + /// checked/unchecked statements or expressions. Contrast this with + /// the ConstantCheckState flag. + /// + CheckedScope = 1 << 0, + + /// + /// The constant check state is always set to `true' and cant be changed + /// from the command line. The source code can change this setting with + /// the `checked' and `unchecked' statements and expressions. + /// + ConstantCheckState = 1 << 1, + + AllCheckStateFlags = CheckedScope | ConstantCheckState, + + // + // unsafe { ... } scope + // + UnsafeScope = 1 << 2, + CatchScope = 1 << 3, + FinallyScope = 1 << 4, + FieldInitializerScope = 1 << 5, + CompoundAssignmentScope = 1 << 6, + FixedInitializerScope = 1 << 7, + BaseInitializer = 1 << 8, + + // + // Inside an enum definition, we do not resolve enumeration values + // to their enumerations, but rather to the underlying type/value + // This is so EnumVal + EnumValB can be evaluated. + // + // There is no "E operator + (E x, E y)", so during an enum evaluation + // we relax the rules + // + EnumScope = 1 << 9, + + ConstantScope = 1 << 10, + + ConstructorScope = 1 << 11, + + UsingInitializerScope = 1 << 12, + + LockScope = 1 << 13, + + TryScope = 1 << 14, + + TryWithCatchScope = 1 << 15, + + ConditionalAccessReceiver = 1 << 16, + + /// + /// Indicates the current context is in probing mode, no errors are reported. + /// + ProbingMode = 1 << 22, + + // + // Return and ContextualReturn statements will set the ReturnType + // value based on the expression types of each return statement + // instead of the method return type which is initially null. + // + InferReturnType = 1 << 23, + + OmitDebuggingInfo = 1 << 24, + + ExpressionTreeConversion = 1 << 25, + + InvokeSpecialName = 1 << 26 + } + + // utility helper for CheckExpr, UnCheckExpr, Checked and Unchecked statements + // it's public so that we can use a struct at the callsite + public struct FlagsHandle : IDisposable + { + readonly ResolveContext ec; + readonly Options invmask, oldval; + + public FlagsHandle (ResolveContext ec, Options flagsToSet) + : this (ec, flagsToSet, flagsToSet) + { + } + + internal FlagsHandle (ResolveContext ec, Options mask, Options val) + { + this.ec = ec; + invmask = ~mask; + oldval = ec.flags & mask; + ec.flags = (ec.flags & invmask) | (val & mask); + +// if ((mask & Options.ProbingMode) != 0) +// ec.Report.DisableReporting (); + } + + public void Dispose () + { +// if ((invmask & Options.ProbingMode) == 0) +// ec.Report.EnableReporting (); + + ec.flags = (ec.flags & invmask) | oldval; + } + } + + protected Options flags; + + // + // Whether we are inside an anonymous method. + // + public AnonymousExpression CurrentAnonymousMethod; + + // + // Holds a varible used during collection or object initialization. + // + public Expression CurrentInitializerVariable; + + public Block CurrentBlock; + + public readonly IMemberContext MemberContext; + + public ResolveContext (IMemberContext mc) + { + if (mc == null) + throw new ArgumentNullException (); + + MemberContext = mc; + + // + // The default setting comes from the command line option + // + if (mc.Module.Compiler.Settings.Checked) + flags |= Options.CheckedScope; + + // + // The constant check state is always set to true + // + flags |= Options.ConstantCheckState; + } + + public ResolveContext (IMemberContext mc, Options options) + : this (mc) + { + flags |= options; + } + + #region Properties + + public BuiltinTypes BuiltinTypes { + get { + return MemberContext.Module.Compiler.BuiltinTypes; + } + } + + public virtual ExplicitBlock ConstructorBlock { + get { + return CurrentBlock.Explicit; + } + } + + // + // The current iterator + // + public Iterator CurrentIterator { + get { return CurrentAnonymousMethod as Iterator; } + } + + public TypeSpec CurrentType { + get { return MemberContext.CurrentType; } + } + + public TypeParameters CurrentTypeParameters { + get { return MemberContext.CurrentTypeParameters; } + } + + public MemberCore CurrentMemberDefinition { + get { return MemberContext.CurrentMemberDefinition; } + } + + public bool ConstantCheckState { + get { return (flags & Options.ConstantCheckState) != 0; } + } + + public bool IsInProbingMode { + get { + return (flags & Options.ProbingMode) != 0; + } + } + + public bool IsObsolete { + get { + // Disables obsolete checks when probing is on + return MemberContext.IsObsolete; + } + } + + public bool IsStatic { + get { + return MemberContext.IsStatic; + } + } + + public bool IsUnsafe { + get { + return HasSet (Options.UnsafeScope) || MemberContext.IsUnsafe; + } + } + + public bool IsRuntimeBinder { + get { + return Module.Compiler.IsRuntimeBinder; + } + } + + public bool IsVariableCapturingRequired { + get { + return !IsInProbingMode; + } + } + + public ModuleContainer Module { + get { + return MemberContext.Module; + } + } + + public Report Report { + get { + return Module.Compiler.Report; + } + } + + #endregion + + public bool MustCaptureVariable (INamedBlockVariable local) + { + if (CurrentAnonymousMethod == null) + return false; + + // + // Capture only if this or any of child blocks contain yield + // or it's a parameter + // + if (CurrentAnonymousMethod.IsIterator) + return local.IsParameter || local.Block.Explicit.HasYield; + + // + // Capture only if this or any of child blocks contain await + // or it's a parameter or we need to access variable from + // different parameter block + // + if (CurrentAnonymousMethod is AsyncInitializer) + return local.IsParameter || local.Block.Explicit.HasAwait || CurrentBlock.Explicit.HasAwait || + local.Block.ParametersBlock != CurrentBlock.ParametersBlock.Original; + + return local.Block.ParametersBlock != CurrentBlock.ParametersBlock.Original; + } + + public bool HasSet (Options options) + { + return (this.flags & options) == options; + } + + public bool HasAny (Options options) + { + return (this.flags & options) != 0; + } + + + // Temporarily set all the given flags to the given value. Should be used in an 'using' statement + public FlagsHandle Set (Options options) + { + return new FlagsHandle (this, options); + } + + public FlagsHandle With (Options options, bool enable) + { + return new FlagsHandle (this, options, enable ? options : 0); + } + + #region IMemberContext Members + + public string GetSignatureForError () + { + return MemberContext.GetSignatureForError (); + } + + public ExtensionMethodCandidates LookupExtensionMethod (TypeSpec extensionType, string name, int arity) + { + return MemberContext.LookupExtensionMethod (extensionType, name, arity); + } + + public FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc) + { + return MemberContext.LookupNamespaceOrType (name, arity, mode, loc); + } + + public FullNamedExpression LookupNamespaceAlias (string name) + { + return MemberContext.LookupNamespaceAlias (name); + } + + #endregion + } + + public class FlowAnalysisContext + { + readonly CompilerContext ctx; + DefiniteAssignmentBitSet conditional_access; + + public FlowAnalysisContext (CompilerContext ctx, ParametersBlock parametersBlock, int definiteAssignmentLength) + { + this.ctx = ctx; + this.ParametersBlock = parametersBlock; + + DefiniteAssignment = definiteAssignmentLength == 0 ? + DefiniteAssignmentBitSet.Empty : + new DefiniteAssignmentBitSet (definiteAssignmentLength); + } + + public DefiniteAssignmentBitSet DefiniteAssignment { get; set; } + + public DefiniteAssignmentBitSet DefiniteAssignmentOnTrue { get; set; } + + public DefiniteAssignmentBitSet DefiniteAssignmentOnFalse { get; set; } + + public List LabelStack { get; set; } + + public ParametersBlock ParametersBlock { get; set; } + + public Report Report { + get { + return ctx.Report; + } + } + + public DefiniteAssignmentBitSet SwitchInitialDefinitiveAssignment { get; set; } + + public TryFinally TryFinally { get; set; } + + public bool UnreachableReported { get; set; } + + public DefiniteAssignmentBitSet BranchDefiniteAssignment () + { + var dat = DefiniteAssignment; + if (dat != DefiniteAssignmentBitSet.Empty) + DefiniteAssignment = new DefiniteAssignmentBitSet (dat); + return dat; + } + + public void BranchConditionalAccessDefiniteAssignment () + { + if (conditional_access == null) + conditional_access = BranchDefiniteAssignment (); + } + + public void ConditionalAccessEnd () + { + Debug.Assert (conditional_access != null); + DefiniteAssignment = conditional_access; + conditional_access = null; + } + + public bool IsDefinitelyAssigned (VariableInfo variable) + { + return variable.IsAssigned (DefiniteAssignment); + } + + public bool IsStructFieldDefinitelyAssigned (VariableInfo variable, string name) + { + return variable.IsStructFieldAssigned (DefiniteAssignment, name); + } + + public void SetVariableAssigned (VariableInfo variable, bool generatedAssignment = false) + { + variable.SetAssigned (DefiniteAssignment, generatedAssignment); + } + + public void SetStructFieldAssigned (VariableInfo variable, string name) + { + variable.SetStructFieldAssigned (DefiniteAssignment, name); + } + } + + + // + // This class is used during the Statement.Clone operation + // to remap objects that have been cloned. + // + // Since blocks are cloned by Block.Clone, we need a way for + // expressions that must reference the block to be cloned + // pointing to the new cloned block. + // + public class CloneContext + { + Dictionary block_map = new Dictionary (); + + public void AddBlockMap (Block from, Block to) + { + block_map.Add (from, to); + } + + public Block LookupBlock (Block from) + { + Block result; + if (!block_map.TryGetValue (from, out result)) { + result = (Block) from.Clone (this); + } + + return result; + } + + /// + /// Remaps block to cloned copy if one exists. + /// + public Block RemapBlockCopy (Block from) + { + Block mapped_to; + if (!block_map.TryGetValue (from, out mapped_to)) + return from; + + return mapped_to; + } + } + + // + // Main compiler context + // + public class CompilerContext + { + static readonly TimeReporter DisabledTimeReporter = new TimeReporter (false); + + readonly Report report; + readonly BuiltinTypes builtin_types; + readonly CompilerSettings settings; + + Dictionary all_source_files; + + public CompilerContext (CompilerSettings settings, ReportPrinter reportPrinter) + { + this.settings = settings; + this.report = new Report (this, reportPrinter); + this.builtin_types = new BuiltinTypes (); + this.TimeReporter = DisabledTimeReporter; + } + + #region Properties + + public BuiltinTypes BuiltinTypes { + get { + return builtin_types; + } + } + + // Used for special handling of runtime dynamic context mostly + // by error reporting but also by member accessibility checks + public bool IsRuntimeBinder { + get; set; + } + + public Report Report { + get { + return report; + } + } + + public CompilerSettings Settings { + get { + return settings; + } + } + + public List SourceFiles { + get { + return settings.SourceFiles; + } + } + + internal TimeReporter TimeReporter { + get; set; + } + + #endregion + + // + // This is used when we encounter a #line preprocessing directive during parsing + // to register additional source file names + // + public SourceFile LookupFile (CompilationSourceFile comp_unit, string name) + { + if (all_source_files == null) { + all_source_files = new Dictionary (); + foreach (var source in SourceFiles) + all_source_files[source.FullPathName] = source; + } + + string path; + if (!Path.IsPathRooted (name)) { + var loc = comp_unit.SourceFile; + string root = Path.GetDirectoryName (loc.FullPathName); + path = Path.GetFullPath (Path.Combine (root, name)); + var dir = Path.GetDirectoryName (loc.Name); + if (!string.IsNullOrEmpty (dir)) + name = Path.Combine (dir, name); + } else + path = name; + + SourceFile retval; + if (all_source_files.TryGetValue (path, out retval)) + return retval; + + retval = new SourceFile (name, path, all_source_files.Count + 1); + Location.AddFile (retval); + all_source_files.Add (path, retval); + return retval; + } + } + + // + // Generic code emitter context + // + public class BuilderContext + { + [Flags] + public enum Options + { + /// + /// This flag tracks the `checked' state of the compilation, + /// it controls whether we should generate code that does overflow + /// checking, or if we generate code that ignores overflows. + /// + /// The default setting comes from the command line option to generate + /// checked or unchecked code plus any source code changes using the + /// checked/unchecked statements or expressions. Contrast this with + /// the ConstantCheckState flag. + /// + CheckedScope = 1 << 0, + + AccurateDebugInfo = 1 << 1, + + OmitDebugInfo = 1 << 2, + + ConstructorScope = 1 << 3, + + AsyncBody = 1 << 4, + } + + // utility helper for CheckExpr, UnCheckExpr, Checked and Unchecked statements + // it's public so that we can use a struct at the callsite + public struct FlagsHandle : IDisposable + { + readonly BuilderContext ec; + readonly Options invmask, oldval; + + public FlagsHandle (BuilderContext ec, Options flagsToSet) + : this (ec, flagsToSet, flagsToSet) + { + } + + internal FlagsHandle (BuilderContext ec, Options mask, Options val) + { + this.ec = ec; + invmask = ~mask; + oldval = ec.flags & mask; + ec.flags = (ec.flags & invmask) | (val & mask); + } + + public void Dispose () + { + ec.flags = (ec.flags & invmask) | oldval; + } + } + + protected Options flags; + + public bool HasSet (Options options) + { + return (this.flags & options) == options; + } + + // Temporarily set all the given flags to the given value. Should be used in an 'using' statement + public FlagsHandle With (Options options, bool enable) + { + return new FlagsHandle (this, options, enable ? options : 0); + } + } + + // + // Parser session objects. We could recreate all these objects for each parser + // instance but the best parser performance the session object can be reused + // + public class ParserSession + { + MD5 md5; + + public readonly char[] StreamReaderBuffer = new char[SeekableStreamReader.DefaultReadAheadSize * 2]; + public readonly Dictionary[] Identifiers = new Dictionary[Tokenizer.MaxIdentifierLength + 1]; + public readonly List ParametersStack = new List (4); + public readonly char[] IDBuilder = new char[Tokenizer.MaxIdentifierLength]; + public readonly char[] NumberBuilder = new char[Tokenizer.MaxNumberLength]; + + public LocationsBag LocationsBag { get; set; } + public bool UseJayGlobalArrays { get; set; } + public LocatedToken[] LocatedTokens { get; set; } + + public MD5 GetChecksumAlgorithm () + { + return md5 ?? (md5 = MD5.Create ()); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/convert.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/convert.cs new file mode 100644 index 000000000..065c9a7e3 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/convert.cs @@ -0,0 +1,2266 @@ +// +// conversion.cs: various routines for implementing conversions. +// +// Authors: +// Miguel de Icaza (miguel@ximian.com) +// Ravi Pratap (ravi@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// Copyright 2001, 2002, 2003 Ximian, Inc. +// Copyright 2003-2008 Novell, Inc. +// Copyright 2011 Xamarin Inc (http://www.xamarin.com) +// + +using System; +using System.Collections.Generic; + +#if STATIC +using IKVM.Reflection.Emit; +#else +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp { + + // + // A container class for all the conversion operations + // + static class Convert + { + [Flags] + public enum UserConversionRestriction + { + None = 0, + ImplicitOnly = 1, + ProbingOnly = 1 << 1, + NullableSourceOnly = 1 << 2 + + } + // + // From a one-dimensional array-type S[] to System.Collections.IList and base + // interfaces of this interface, provided there is an implicit reference conversion + // from S to T. + // + static bool ArrayToIList (ArrayContainer array, TypeSpec list, bool isExplicit) + { + if (array.Rank != 1 || !list.IsArrayGenericInterface) + return false; + + var arg_type = list.TypeArguments[0]; + if (array.Element == arg_type) + return true; + + // + // Reject conversion from T[] to IList even if T has U dependency + // + if (arg_type.IsGenericParameter) + return false; + + if (isExplicit) + return ExplicitReferenceConversionExists (array.Element, arg_type); + + return ImplicitReferenceConversionExists (array.Element, arg_type); + } + + static bool IList_To_Array(TypeSpec list, ArrayContainer array) + { + if (array.Rank != 1 || !list.IsArrayGenericInterface) + return false; + + var arg_type = list.TypeArguments[0]; + if (array.Element == arg_type) + return true; + + return ImplicitReferenceConversionExists (array.Element, arg_type) || ExplicitReferenceConversionExists (array.Element, arg_type); + } + + public static Expression ImplicitTypeParameterConversion (Expression expr, TypeParameterSpec expr_type, TypeSpec target_type) + { + // + // From T to a type parameter U, provided T depends on U + // + if (target_type.IsGenericParameter) { + if (expr_type.TypeArguments != null && expr_type.HasDependencyOn (target_type)) { + if (expr == null) + return EmptyExpression.Null; + + if (expr_type.IsReferenceType && !((TypeParameterSpec) target_type).IsReferenceType) + return new BoxedCast (expr, target_type); + + return new ClassCast (expr, target_type); + } + + return null; + } + + // + // LAMESPEC: From T to dynamic type because it's like T to object + // + if (target_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + if (expr == null) + return EmptyExpression.Null; + + if (expr_type.IsReferenceType) + return new ClassCast (expr, target_type); + + return new BoxedCast (expr, target_type); + } + + // + // From T to its effective base class C + // From T to any base class of C (it cannot contain dynamic or be of dynamic type) + // From T to any interface implemented by C + // + var base_type = expr_type.GetEffectiveBase (); + if (base_type == target_type || TypeSpec.IsBaseClass (base_type, target_type, false) || base_type.ImplementsInterface (target_type, true)) { + if (expr == null) + return EmptyExpression.Null; + + if (expr_type.IsReferenceType) + return new ClassCast (expr, target_type); + + return new BoxedCast (expr, target_type); + } + + if (target_type.IsInterface && expr_type.IsConvertibleToInterface (target_type)) { + if (expr == null) + return EmptyExpression.Null; + + if (expr_type.IsReferenceType) + return new ClassCast (expr, target_type); + + return new BoxedCast (expr, target_type); + } + + return null; + } + + static Expression ExplicitTypeParameterConversionFromT (Expression source, TypeSpec source_type, TypeSpec target_type) + { + var target_tp = target_type as TypeParameterSpec; + if (target_tp != null) { + // + // From a type parameter U to T, provided T depends on U + // + if (target_tp.TypeArguments != null && target_tp.HasDependencyOn (source_type)) { + return source == null ? EmptyExpression.Null : new ClassCast (source, target_type); + } + } + + // + // From T to any interface-type I provided there is not already an implicit conversion from T to I + // + if (target_type.IsInterface) + return source == null ? EmptyExpression.Null : new ClassCast (source, target_type, true); + + return null; + } + + static Expression ExplicitTypeParameterConversionToT (Expression source, TypeSpec source_type, TypeParameterSpec target_type) + { + // + // From the effective base class C of T to T and from any base class of C to T + // + var effective = target_type.GetEffectiveBase (); + if (TypeSpecComparer.IsEqual (effective, source_type) || TypeSpec.IsBaseClass (effective, source_type, false)) + return source == null ? EmptyExpression.Null : new ClassCast (source, target_type); + + return null; + } + + public static Expression ImplicitReferenceConversion (Expression expr, TypeSpec target_type, bool explicit_cast) + { + TypeSpec expr_type = expr.Type; + + if (expr_type.Kind == MemberKind.TypeParameter) + return ImplicitTypeParameterConversion (expr, (TypeParameterSpec) expr.Type, target_type); + + // + // from the null type to any reference-type. + // + NullLiteral nl = expr as NullLiteral; + if (nl != null) { + return nl.ConvertImplicitly (target_type); + } + + if (ImplicitReferenceConversionExists (expr_type, target_type)) { + // + // Avoid wrapping implicitly convertible reference type + // + if (!explicit_cast) + return expr; + + return EmptyCast.Create (expr, target_type); + } + + return null; + } + + // + // Implicit reference conversions + // + public static bool ImplicitReferenceConversionExists (TypeSpec expr_type, TypeSpec target_type) + { + return ImplicitReferenceConversionExists (expr_type, target_type, true); + } + + public static bool ImplicitReferenceConversionExists (TypeSpec expr_type, TypeSpec target_type, bool refOnlyTypeParameter) + { + // It's here only to speed things up + if (target_type.IsStruct) + return false; + + switch (expr_type.Kind) { + case MemberKind.TypeParameter: + return ImplicitTypeParameterConversion (null, (TypeParameterSpec) expr_type, target_type) != null && + (!refOnlyTypeParameter || TypeSpec.IsReferenceType (expr_type)); + + case MemberKind.Class: + // + // From any class-type to dynamic (+object to speed up common path) + // + if (target_type.BuiltinType == BuiltinTypeSpec.Type.Object || target_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) + return true; + + if (target_type.IsClass) { + // + // Identity conversion, including dynamic erasure + // + if (TypeSpecComparer.IsEqual (expr_type, target_type)) + return true; + + // + // From any class-type S to any class-type T, provided S is derived from T + // + return TypeSpec.IsBaseClass (expr_type, target_type, true); + } + + // + // From any class-type S to any interface-type T, provided S implements T + // + if (target_type.IsInterface) + return expr_type.ImplementsInterface (target_type, true); + + return false; + + case MemberKind.ArrayType: + // + // Identity array conversion + // + if (expr_type == target_type) + return true; + + // + // From any array-type to System.Array + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.Array: + case BuiltinTypeSpec.Type.Object: + case BuiltinTypeSpec.Type.Dynamic: + return true; + } + + var expr_type_array = (ArrayContainer) expr_type; + var target_type_array = target_type as ArrayContainer; + + // + // From an array-type S to an array-type of type T + // + if (target_type_array != null && expr_type_array.Rank == target_type_array.Rank) { + + // + // Both SE and TE are reference-types. TE check is defered + // to ImplicitReferenceConversionExists + // + TypeSpec expr_element_type = expr_type_array.Element; + if (!TypeSpec.IsReferenceType (expr_element_type)) + return false; + + // + // An implicit reference conversion exists from SE to TE + // + return ImplicitReferenceConversionExists (expr_element_type, target_type_array.Element); + } + + // + // From any array-type to the interfaces it implements + // + if (target_type.IsInterface) { + if (expr_type.ImplementsInterface (target_type, false)) + return true; + + // from an array-type of type T to IList + if (ArrayToIList (expr_type_array, target_type, false)) + return true; + } + + return false; + + case MemberKind.Delegate: + // + // From any delegate-type to System.Delegate (and its base types) + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.Delegate: + case BuiltinTypeSpec.Type.MulticastDelegate: + case BuiltinTypeSpec.Type.Object: + case BuiltinTypeSpec.Type.Dynamic: + return true; + } + + // + // Identity conversion, including dynamic erasure + // + if (TypeSpecComparer.IsEqual (expr_type, target_type)) + return true; + + // + // From any delegate-type to the interfaces it implements + // From any reference-type to an delegate type if is variance-convertible + // + return expr_type.ImplementsInterface (target_type, false) || TypeSpecComparer.Variant.IsEqual (expr_type, target_type); + + case MemberKind.Interface: + // + // Identity conversion, including dynamic erasure + // + if (TypeSpecComparer.IsEqual (expr_type, target_type)) + return true; + + // + // From any interface type S to interface-type T + // From any reference-type to an interface if is variance-convertible + // + if (target_type.IsInterface) + return TypeSpecComparer.Variant.IsEqual (expr_type, target_type) || expr_type.ImplementsInterface (target_type, true); + + return target_type.BuiltinType == BuiltinTypeSpec.Type.Object || target_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic; + + case MemberKind.InternalCompilerType: + // + // from the null literal to any reference-type. + // + if (expr_type == InternalType.NullLiteral) { + // Exlude internal compiler types + if (target_type.Kind == MemberKind.InternalCompilerType) + return target_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic; + + return TypeSpec.IsReferenceType (target_type) || target_type.Kind == MemberKind.PointerType; + } + + // + // Implicit dynamic conversion + // + if (expr_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + switch (target_type.Kind) { + case MemberKind.ArrayType: + case MemberKind.Class: + case MemberKind.Delegate: + case MemberKind.Interface: + case MemberKind.TypeParameter: + return true; + } + + // dynamic to __arglist + if (target_type == InternalType.Arglist) + return true; + + return false; + } + + break; + } + + return false; + } + + public static Expression ImplicitBoxingConversion (Expression expr, TypeSpec expr_type, TypeSpec target_type) + { + switch (target_type.BuiltinType) { + // + // From any non-nullable-value-type to the type object and dynamic + // + case BuiltinTypeSpec.Type.Object: + case BuiltinTypeSpec.Type.Dynamic: + // + // From any non-nullable-value-type to the type System.ValueType + // + case BuiltinTypeSpec.Type.ValueType: + // + // No ned to check for nullable type as underlying type is always convertible + // + if (!TypeSpec.IsValueType (expr_type)) + return null; + + return expr == null ? EmptyExpression.Null : new BoxedCast (expr, target_type); + + case BuiltinTypeSpec.Type.Enum: + // + // From any enum-type to the type System.Enum. + // + if (expr_type.IsEnum) + return expr == null ? EmptyExpression.Null : new BoxedCast (expr, target_type); + + break; + } + + // + // From a nullable-type to a reference type, if a boxing conversion exists from + // the underlying type to the reference type + // + if (expr_type.IsNullableType) { + if (!TypeSpec.IsReferenceType (target_type)) + return null; + + var res = ImplicitBoxingConversion (expr, Nullable.NullableInfo.GetUnderlyingType (expr_type), target_type); + + // "cast" underlying type to target type to emit correct InvalidCastException when + // underlying hierarchy changes without recompilation + if (res != null && expr != null) + res = new UnboxCast (res, target_type); + + return res; + } + + // + // A value type has a boxing conversion to an interface type I if it has a boxing conversion + // to an interface or delegate type I0 and I0 is variance-convertible to I + // + if (target_type.IsInterface && TypeSpec.IsValueType (expr_type) && expr_type.ImplementsInterface (target_type, true)) { + return expr == null ? EmptyExpression.Null : new BoxedCast (expr, target_type); + } + + return null; + } + + public static Expression ImplicitNulableConversion (ResolveContext ec, Expression expr, TypeSpec target_type) + { + TypeSpec expr_type = expr.Type; + + // + // From null to any nullable type + // + if (expr_type == InternalType.NullLiteral) + return ec == null ? EmptyExpression.Null : Nullable.LiftedNull.Create (target_type, expr.Location); + + // S -> T? + TypeSpec t_el = Nullable.NullableInfo.GetUnderlyingType (target_type); + + // S? -> T? + if (expr_type.IsNullableType) + expr_type = Nullable.NullableInfo.GetUnderlyingType (expr_type); + + // + // Predefined implicit identity or implicit numeric conversion + // has to exist between underlying type S and underlying type T + // + + // conversion exists only mode + if (ec == null) { + if (TypeSpecComparer.IsEqual (expr_type, t_el)) + return EmptyExpression.Null; + + if (expr is Constant) + return ((Constant) expr).ConvertImplicitly (t_el); + + return ImplicitNumericConversion (null, expr_type, t_el); + } + + Expression unwrap; + if (expr_type != expr.Type) + unwrap = Nullable.Unwrap.Create (expr); + else + unwrap = expr; + + Expression conv = unwrap; + if (!TypeSpecComparer.IsEqual (expr_type, t_el)) { + if (conv is Constant) + conv = ((Constant)conv).ConvertImplicitly (t_el); + else + conv = ImplicitNumericConversion (conv, expr_type, t_el); + + if (conv == null) + return null; + } + + if (expr_type != expr.Type) + return new Nullable.LiftedConversion (conv, unwrap, target_type).Resolve (ec); + + return Nullable.Wrap.Create (conv, target_type); + } + + /// + /// Implicit Numeric Conversions. + /// + /// expr is the expression to convert, returns a new expression of type + /// target_type or null if an implicit conversion is not possible. + /// + public static Expression ImplicitNumericConversion (Expression expr, TypeSpec target_type) + { + return ImplicitNumericConversion (expr, expr.Type, target_type); + } + + public static bool ImplicitNumericConversionExists (TypeSpec expr_type, TypeSpec target_type) + { + return ImplicitNumericConversion (null, expr_type, target_type) != null; + } + + static Expression ImplicitNumericConversion (Expression expr, TypeSpec expr_type, TypeSpec target_type) + { + switch (expr_type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + // + // From sbyte to short, int, long, float, double, decimal + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.Int: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_I4); + case BuiltinTypeSpec.Type.Long: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_I8); + case BuiltinTypeSpec.Type.Double: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_R8); + case BuiltinTypeSpec.Type.Float: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_R4); + case BuiltinTypeSpec.Type.Short: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_I2); + case BuiltinTypeSpec.Type.Decimal: + return expr == null ? EmptyExpression.Null : new OperatorCast (expr, target_type); + + } + + break; + case BuiltinTypeSpec.Type.Byte: + // + // From byte to short, ushort, int, uint, long, ulong, float, double, decimal + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.Int: + case BuiltinTypeSpec.Type.UInt: + case BuiltinTypeSpec.Type.Short: + case BuiltinTypeSpec.Type.UShort: + return expr == null ? EmptyExpression.Null : EmptyCast.Create (expr, target_type); + case BuiltinTypeSpec.Type.ULong: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_U8); + case BuiltinTypeSpec.Type.Long: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_I8); + case BuiltinTypeSpec.Type.Float: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_R4); + case BuiltinTypeSpec.Type.Double: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_R8); + case BuiltinTypeSpec.Type.Decimal: + return expr == null ? EmptyExpression.Null : new OperatorCast (expr, target_type); + } + break; + case BuiltinTypeSpec.Type.Short: + // + // From short to int, long, float, double, decimal + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.Int: + return expr == null ? EmptyExpression.Null : EmptyCast.Create (expr, target_type); + case BuiltinTypeSpec.Type.Long: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_I8); + case BuiltinTypeSpec.Type.Double: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_R8); + case BuiltinTypeSpec.Type.Float: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_R4); + case BuiltinTypeSpec.Type.Decimal: + return expr == null ? EmptyExpression.Null : new OperatorCast (expr, target_type); + } + break; + case BuiltinTypeSpec.Type.UShort: + // + // From ushort to int, uint, long, ulong, float, double, decimal + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.Int: + case BuiltinTypeSpec.Type.UInt: + return expr == null ? EmptyExpression.Null : EmptyCast.Create (expr, target_type); + case BuiltinTypeSpec.Type.ULong: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_U8); + case BuiltinTypeSpec.Type.Long: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_I8); + case BuiltinTypeSpec.Type.Double: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_R8); + case BuiltinTypeSpec.Type.Float: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_R4); + case BuiltinTypeSpec.Type.Decimal: + return expr == null ? EmptyExpression.Null : new OperatorCast (expr, target_type); + } + break; + case BuiltinTypeSpec.Type.Int: + // + // From int to long, float, double, decimal + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.Long: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_I8); + case BuiltinTypeSpec.Type.Double: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_R8); + case BuiltinTypeSpec.Type.Float: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_R4); + case BuiltinTypeSpec.Type.Decimal: + return expr == null ? EmptyExpression.Null : new OperatorCast (expr, target_type); + } + break; + case BuiltinTypeSpec.Type.UInt: + // + // From uint to long, ulong, float, double, decimal + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.Long: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_U8); + case BuiltinTypeSpec.Type.ULong: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_U8); + case BuiltinTypeSpec.Type.Double: + return expr == null ? EmptyExpression.Null : new OpcodeCastDuplex (expr, target_type, OpCodes.Conv_R_Un, OpCodes.Conv_R8); + case BuiltinTypeSpec.Type.Float: + return expr == null ? EmptyExpression.Null : new OpcodeCastDuplex (expr, target_type, OpCodes.Conv_R_Un, OpCodes.Conv_R4); + case BuiltinTypeSpec.Type.Decimal: + return expr == null ? EmptyExpression.Null : new OperatorCast (expr, target_type); + } + break; + case BuiltinTypeSpec.Type.Long: + // + // From long to float, double, decimal + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.Double: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_R8); + case BuiltinTypeSpec.Type.Float: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_R4); + case BuiltinTypeSpec.Type.Decimal: + return expr == null ? EmptyExpression.Null : new OperatorCast (expr, target_type); + } + break; + case BuiltinTypeSpec.Type.ULong: + // + // From ulong to float, double, decimal + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.Double: + return expr == null ? EmptyExpression.Null : new OpcodeCastDuplex (expr, target_type, OpCodes.Conv_R_Un, OpCodes.Conv_R8); + case BuiltinTypeSpec.Type.Float: + return expr == null ? EmptyExpression.Null : new OpcodeCastDuplex (expr, target_type, OpCodes.Conv_R_Un, OpCodes.Conv_R4); + case BuiltinTypeSpec.Type.Decimal: + return expr == null ? EmptyExpression.Null : new OperatorCast (expr, target_type); + } + break; + case BuiltinTypeSpec.Type.Char: + // + // From char to ushort, int, uint, long, ulong, float, double, decimal + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.UShort: + case BuiltinTypeSpec.Type.Int: + case BuiltinTypeSpec.Type.UInt: + return expr == null ? EmptyExpression.Null : EmptyCast.Create (expr, target_type); + case BuiltinTypeSpec.Type.ULong: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_U8); + case BuiltinTypeSpec.Type.Long: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_I8); + case BuiltinTypeSpec.Type.Float: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_R4); + case BuiltinTypeSpec.Type.Double: + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_R8); + case BuiltinTypeSpec.Type.Decimal: + return expr == null ? EmptyExpression.Null : new OperatorCast (expr, target_type); + } + break; + case BuiltinTypeSpec.Type.Float: + // + // float to double + // + if (target_type.BuiltinType == BuiltinTypeSpec.Type.Double) + return expr == null ? EmptyExpression.Null : new OpcodeCast (expr, target_type, OpCodes.Conv_R8); + break; + } + + return null; + } + + // + // Full version of implicit conversion + // + public static bool ImplicitConversionExists (ResolveContext ec, Expression expr, TypeSpec target_type) + { + if (ImplicitStandardConversionExists (ec, expr, target_type)) + return true; + + if (expr.Type == InternalType.AnonymousMethod) { + if (!target_type.IsDelegate && !target_type.IsExpressionTreeType) + return false; + + AnonymousMethodExpression ame = (AnonymousMethodExpression) expr; + return ame.ImplicitStandardConversionExists (ec, target_type); + } + + // Conversion from __arglist to System.ArgIterator + if (expr.Type == InternalType.Arglist) + return target_type == ec.Module.PredefinedTypes.ArgIterator.TypeSpec; + + return UserDefinedConversion (ec, expr, target_type, + UserConversionRestriction.ImplicitOnly | UserConversionRestriction.ProbingOnly, Location.Null) != null; + } + + public static bool ImplicitStandardConversionExists (ResolveContext rc, Expression expr, TypeSpec target_type) + { + if (expr.eclass == ExprClass.MethodGroup) { + if (target_type.IsDelegate && rc.Module.Compiler.Settings.Version != LanguageVersion.ISO_1) { + MethodGroupExpr mg = expr as MethodGroupExpr; + if (mg != null) + return DelegateCreation.ImplicitStandardConversionExists (rc, mg, target_type); + } + + return false; + } + + return ImplicitStandardConversionExists (expr, target_type); + } + + // + // Implicit standard conversion (only core conversions are used here) + // + public static bool ImplicitStandardConversionExists (Expression expr, TypeSpec target_type) + { + // + // Identity conversions + // Implicit numeric conversions + // Implicit nullable conversions + // Implicit reference conversions + // Boxing conversions + // Implicit constant expression conversions + // Implicit conversions involving type parameters + // + + TypeSpec expr_type = expr.Type; + + if (expr_type == target_type) + return true; + + if (target_type.IsNullableType) + return ImplicitNulableConversion (null, expr, target_type) != null; + + if (ImplicitNumericConversion (null, expr_type, target_type) != null) + return true; + + if (ImplicitReferenceConversionExists (expr_type, target_type, false)) + return true; + + if (ImplicitBoxingConversion (null, expr_type, target_type) != null) + return true; + + // + // Implicit Constant Expression Conversions + // + if (expr is IntConstant){ + int value = ((IntConstant) expr).Value; + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + if (value >= SByte.MinValue && value <= SByte.MaxValue) + return true; + break; + case BuiltinTypeSpec.Type.Byte: + if (value >= 0 && value <= Byte.MaxValue) + return true; + break; + case BuiltinTypeSpec.Type.Short: + if (value >= Int16.MinValue && value <= Int16.MaxValue) + return true; + break; + case BuiltinTypeSpec.Type.UShort: + if (value >= UInt16.MinValue && value <= UInt16.MaxValue) + return true; + break; + case BuiltinTypeSpec.Type.UInt: + if (value >= 0) + return true; + break; + case BuiltinTypeSpec.Type.ULong: + // + // we can optimize this case: a positive int32 + // always fits on a uint64. But we need an opcode + // to do it. + // + if (value >= 0) + return true; + + break; + } + } + + if (expr is LongConstant && target_type.BuiltinType == BuiltinTypeSpec.Type.ULong){ + // + // Try the implicit constant expression conversion + // from long to ulong, instead of a nice routine, + // we just inline it + // + long v = ((LongConstant) expr).Value; + if (v >= 0) + return true; + } + + if (expr is IntegralConstant && target_type.IsEnum) { + var i = (IntegralConstant) expr; + // + // LAMESPEC: csc allows any constant like 0 values to be converted, including const float f = 0.0 + // + // An implicit enumeration conversion permits the decimal-integer-literal 0 + // to be converted to any enum-type and to any nullable-type whose underlying + // type is an enum-type + // + return i.IsZeroInteger; + } + + // + // Implicit dynamic conversion for remaining value types. It should probably + // go somewhere else + // + if (expr_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + switch (target_type.Kind) { + case MemberKind.Struct: + case MemberKind.Enum: + return true; + } + + return false; + } + + // + // In an unsafe context implicit conversions is extended to include + // + // From any pointer-type to the type void* + // From the null literal to any pointer-type. + // + // LAMESPEC: The specification claims this conversion is allowed in implicit conversion but + // in reality implicit standard conversion uses it + // + if (target_type.IsPointer && expr.Type.IsPointer && ((PointerContainer) target_type).Element.Kind == MemberKind.Void) + return true; + + // + // Struct identity conversion, including dynamic erasure + // + if (expr_type.IsStruct && TypeSpecComparer.IsEqual (expr_type, target_type)) + return true; + + return false; + } + + /// + /// Finds "most encompassed type" according to the spec (13.4.2) + /// amongst the methods in the MethodGroupExpr + /// + public static TypeSpec FindMostEncompassedType (IList types) + { + TypeSpec best = null; + EmptyExpression expr; + + foreach (TypeSpec t in types) { + if (best == null) { + best = t; + continue; + } + + expr = new EmptyExpression (t); + if (ImplicitStandardConversionExists (expr, best)) + best = t; + } + + expr = new EmptyExpression (best); + foreach (TypeSpec t in types) { + if (best == t) + continue; + if (!ImplicitStandardConversionExists (expr, t)) { + best = null; + break; + } + } + + return best; + } + + // + // Finds the most encompassing type (type into which all other + // types can convert to) amongst the types in the given set + // + static TypeSpec FindMostEncompassingType (IList types) + { + if (types.Count == 0) + return null; + + if (types.Count == 1) + return types [0]; + + TypeSpec best = null; + for (int i = 0; i < types.Count; ++i) { + int ii = 0; + for (; ii < types.Count; ++ii) { + if (ii == i) + continue; + + var expr = new EmptyExpression (types[ii]); + if (!ImplicitStandardConversionExists (expr, types [i])) { + ii = 0; + break; + } + } + + if (ii == 0) + continue; + + if (best == null) { + best = types[i]; + continue; + } + + // Indicates multiple best types + return InternalType.FakeInternalType; + } + + return best; + } + + // + // Finds the most specific source Sx according to the rules of the spec (13.4.4) + // by making use of FindMostEncomp* methods. Applies the correct rules separately + // for explicit and implicit conversion operators. + // + static TypeSpec FindMostSpecificSource (ResolveContext rc, List list, TypeSpec sourceType, Expression source, bool apply_explicit_conv_rules) + { + TypeSpec[] src_types_set = null; + + // + // Try exact match first, if any operator converts from S then Sx = S + // + for (int i = 0; i < list.Count; ++i) { + TypeSpec param_type = list [i].Parameters.Types [0]; + + if (param_type == sourceType) + return param_type; + + if (src_types_set == null) + src_types_set = new TypeSpec [list.Count]; + + src_types_set [i] = param_type; + } + + // + // Explicit Conv rules + // + if (apply_explicit_conv_rules) { + var candidate_set = new List (); + + foreach (TypeSpec param_type in src_types_set){ + if (ImplicitStandardConversionExists (rc, source, param_type)) + candidate_set.Add (param_type); + } + + if (candidate_set.Count != 0) { + if (source.eclass == ExprClass.MethodGroup) + return InternalType.FakeInternalType; + + return FindMostEncompassedType (candidate_set); + } + } + + // + // Final case + // + if (apply_explicit_conv_rules) + return FindMostEncompassingType (src_types_set); + else + return FindMostEncompassedType (src_types_set); + } + + /// + /// Finds the most specific target Tx according to section 13.4.4 + /// + static public TypeSpec FindMostSpecificTarget (IList list, + TypeSpec target, bool apply_explicit_conv_rules) + { + List tgt_types_set = null; + + // + // If any operator converts to T then Tx = T + // + foreach (var mi in list){ + TypeSpec ret_type = mi.ReturnType; + if (ret_type == target) + return ret_type; + + if (tgt_types_set == null) { + tgt_types_set = new List (list.Count); + } else if (tgt_types_set.Contains (ret_type)) { + continue; + } + + tgt_types_set.Add (ret_type); + } + + // + // Explicit conv rules + // + if (apply_explicit_conv_rules) { + var candidate_set = new List (); + + foreach (TypeSpec ret_type in tgt_types_set) { + var expr = new EmptyExpression (ret_type); + + if (ImplicitStandardConversionExists (expr, target)) + candidate_set.Add (ret_type); + } + + if (candidate_set.Count != 0) + return FindMostEncompassingType (candidate_set); + } + + // + // Okay, final case ! + // + if (apply_explicit_conv_rules) + return FindMostEncompassedType (tgt_types_set); + else + return FindMostEncompassingType (tgt_types_set); + } + + /// + /// User-defined Implicit conversions + /// + static public Expression ImplicitUserConversion (ResolveContext ec, Expression source, TypeSpec target, Location loc) + { + return UserDefinedConversion (ec, source, target, UserConversionRestriction.ImplicitOnly, loc); + } + + /// + /// User-defined Explicit conversions + /// + static Expression ExplicitUserConversion (ResolveContext ec, Expression source, TypeSpec target, Location loc) + { + return UserDefinedConversion (ec, source, target, 0, loc); + } + + static void FindApplicableUserDefinedConversionOperators (ResolveContext rc, IList operators, Expression source, TypeSpec target, UserConversionRestriction restr, ref List candidates) + { + if (source.Type.IsInterface) { + // Neither A nor B are interface-types + return; + } + + // For a conversion operator to be applicable, it must be possible + // to perform a standard conversion from the source type to + // the operand type of the operator, and it must be possible + // to perform a standard conversion from the result type of + // the operator to the target type. + + Expression texpr = null; + + foreach (MethodSpec op in operators) { + + // Can be null because MemberCache.GetUserOperator does not resize the array + if (op == null) + continue; + + var t = op.Parameters.Types[0]; + if (source.Type != t && !ImplicitStandardConversionExists (rc, source, t)) { + if ((restr & UserConversionRestriction.ImplicitOnly) != 0) + continue; + + if (!ImplicitStandardConversionExists (new EmptyExpression (t), source.Type)) + continue; + } + + if ((restr & UserConversionRestriction.NullableSourceOnly) != 0 && !t.IsNullableType) + continue; + + t = op.ReturnType; + + if (t.IsInterface) + continue; + + if (target != t) { + if (t.IsNullableType) + t = Nullable.NullableInfo.GetUnderlyingType (t); + + if (!ImplicitStandardConversionExists (new EmptyExpression (t), target)) { + if ((restr & UserConversionRestriction.ImplicitOnly) != 0) + continue; + + if (texpr == null) + texpr = new EmptyExpression (target); + + if (!ImplicitStandardConversionExists (texpr, t)) + continue; + } + } + + if (candidates == null) + candidates = new List (); + + candidates.Add (op); + } + } + + // + // User-defined conversions + // + public static Expression UserDefinedConversion (ResolveContext rc, Expression source, TypeSpec target, UserConversionRestriction restr, Location loc) + { + List candidates = null; + + // + // If S or T are nullable types, source_type and target_type are their underlying types + // otherwise source_type and target_type are equal to S and T respectively. + // + TypeSpec source_type = source.Type; + TypeSpec target_type = target; + Expression source_type_expr; + bool nullable_source = false; + var implicitOnly = (restr & UserConversionRestriction.ImplicitOnly) != 0; + + if (source_type.IsNullableType) { + // No unwrapping conversion S? -> T for non-reference types + if (implicitOnly && !TypeSpec.IsReferenceType (target_type) && !target_type.IsNullableType) { + source_type_expr = source; + } else { + source_type_expr = Nullable.Unwrap.CreateUnwrapped (source); + source_type = source_type_expr.Type; + nullable_source = true; + } + } else { + source_type_expr = source; + } + + if (target_type.IsNullableType) + target_type = Nullable.NullableInfo.GetUnderlyingType (target_type); + + // Only these containers can contain a user defined implicit or explicit operators + const MemberKind user_conversion_kinds = MemberKind.Class | MemberKind.Struct | MemberKind.TypeParameter; + + if ((source_type.Kind & user_conversion_kinds) != 0 && source_type.BuiltinType != BuiltinTypeSpec.Type.Decimal) { + bool declared_only = source_type.IsStruct; + + var operators = MemberCache.GetUserOperator (source_type, Operator.OpType.Implicit, declared_only); + if (operators != null) { + FindApplicableUserDefinedConversionOperators (rc, operators, source_type_expr, target_type, restr, ref candidates); + } + + if (!implicitOnly) { + operators = MemberCache.GetUserOperator (source_type, Operator.OpType.Explicit, declared_only); + if (operators != null) { + FindApplicableUserDefinedConversionOperators (rc, operators, source_type_expr, target_type, restr, ref candidates); + } + } + } + + if ((target.Kind & user_conversion_kinds) != 0 && target_type.BuiltinType != BuiltinTypeSpec.Type.Decimal) { + bool declared_only = target.IsStruct || implicitOnly; + + var operators = MemberCache.GetUserOperator (target_type, Operator.OpType.Implicit, declared_only); + if (operators != null) { + FindApplicableUserDefinedConversionOperators (rc, operators, source_type_expr, target_type, restr, ref candidates); + } + + if (!implicitOnly) { + operators = MemberCache.GetUserOperator (target_type, Operator.OpType.Explicit, declared_only); + if (operators != null) { + FindApplicableUserDefinedConversionOperators (rc, operators, source_type_expr, target_type, restr, ref candidates); + } + } + } + + if (candidates == null) + return null; + + // + // Find the most specific conversion operator + // + MethodSpec most_specific_operator; + TypeSpec s_x, t_x; + if (candidates.Count == 1) { + most_specific_operator = candidates[0]; + s_x = most_specific_operator.Parameters.Types[0]; + t_x = most_specific_operator.ReturnType; + } else { + // + // Pass original source type to find the best match against input type and + // not the unwrapped expression + // + s_x = FindMostSpecificSource (rc, candidates, source.Type, source_type_expr, !implicitOnly); + if (s_x == null) + return null; + + t_x = FindMostSpecificTarget (candidates, target, !implicitOnly); + if (t_x == null) + return null; + + most_specific_operator = null; + for (int i = 0; i < candidates.Count; ++i) { + if (candidates[i].ReturnType == t_x && candidates[i].Parameters.Types[0] == s_x) { + most_specific_operator = candidates[i]; + break; + } + } + + if (most_specific_operator == null) { + // + // Unless running in probing more + // + if ((restr & UserConversionRestriction.ProbingOnly) == 0) { + MethodSpec ambig_arg = candidates [0]; + most_specific_operator = candidates [1]; + /* + foreach (var candidate in candidates) { + if (candidate.ReturnType == t_x) + most_specific_operator = candidate; + else if (candidate.Parameters.Types[0] == s_x) + ambig_arg = candidate; + } + */ + rc.Report.Error (457, loc, + "Ambiguous user defined operators `{0}' and `{1}' when converting from `{2}' to `{3}'", + ambig_arg.GetSignatureForError (), most_specific_operator.GetSignatureForError (), + source.Type.GetSignatureForError (), target.GetSignatureForError ()); + } + + return ErrorExpression.Instance; + } + } + + // + // Convert input type when it's different to best operator argument + // + if (s_x != source_type) { + var c = source as Constant; + if (c != null) { + source = c.Reduce (rc, s_x); + if (source == null) + c = null; + } + + if (c == null) { + source = implicitOnly ? + ImplicitConversionStandard (rc, source_type_expr, s_x, loc) : + ExplicitConversionStandard (rc, source_type_expr, s_x, loc); + } + } else { + source = source_type_expr; + } + + source = new UserCast (most_specific_operator, source, loc).Resolve (rc); + + // + // Convert result type when it's different to best operator return type + // + if (t_x != target_type) { + // + // User operator is of T? + // + if (t_x.IsNullableType && (target.IsNullableType || !implicitOnly)) { + // + // User operator return type does not match target type we need + // yet another conversion. This should happen for promoted numeric + // types only + // + if (t_x != target) { + var unwrap = Nullable.Unwrap.CreateUnwrapped (source); + + source = implicitOnly ? + ImplicitConversionStandard (rc, unwrap, target_type, loc) : + ExplicitConversionStandard (rc, unwrap, target_type, loc); + + if (source == null) + return null; + + if (target.IsNullableType) + source = new Nullable.LiftedConversion (source, unwrap, target).Resolve (rc); + } + } else { + source = implicitOnly ? + ImplicitConversionStandard (rc, source, target_type, loc) : + ExplicitConversionStandard (rc, source, target_type, loc); + + if (source == null) + return null; + } + } + + + // + // Source expression is of nullable type and underlying conversion returns + // only non-nullable type we need to lift it manually + // + if (nullable_source && !s_x.IsNullableType) + return new Nullable.LiftedConversion (source, source_type_expr, target).Resolve (rc); + + // + // Target is of nullable type but source type is not, wrap the result expression + // + if (target.IsNullableType && !t_x.IsNullableType) + source = Nullable.Wrap.Create (source, target); + + return source; + } + + /// + /// Converts implicitly the resolved expression `expr' into the + /// `target_type'. It returns a new expression that can be used + /// in a context that expects a `target_type'. + /// + static public Expression ImplicitConversion (ResolveContext ec, Expression expr, + TypeSpec target_type, Location loc) + { + Expression e; + + if (target_type == null) + throw new Exception ("Target type is null"); + + e = ImplicitConversionStandard (ec, expr, target_type, loc); + if (e != null) + return e; + + e = ImplicitUserConversion (ec, expr, target_type, loc); + if (e != null) + return e; + + return null; + } + + + /// + /// Attempts to apply the `Standard Implicit + /// Conversion' rules to the expression `expr' into + /// the `target_type'. It returns a new expression + /// that can be used in a context that expects a + /// `target_type'. + /// + /// This is different from `ImplicitConversion' in that the + /// user defined implicit conversions are excluded. + /// + static public Expression ImplicitConversionStandard (ResolveContext ec, Expression expr, + TypeSpec target_type, Location loc) + { + return ImplicitConversionStandard (ec, expr, target_type, loc, false); + } + + static Expression ImplicitConversionStandard (ResolveContext ec, Expression expr, TypeSpec target_type, Location loc, bool explicit_cast) + { + if (expr.eclass == ExprClass.MethodGroup){ + if (!target_type.IsDelegate){ + return null; + } + + // + // Only allow anonymous method conversions on post ISO_1 + // + if (ec.Module.Compiler.Settings.Version != LanguageVersion.ISO_1){ + MethodGroupExpr mg = expr as MethodGroupExpr; + if (mg != null) + return new ImplicitDelegateCreation (target_type, mg, loc).Resolve (ec); + } + } + + TypeSpec expr_type = expr.Type; + Expression e; + + if (expr_type == target_type) { + if (expr_type != InternalType.NullLiteral && expr_type != InternalType.AnonymousMethod) + return expr; + return null; + } + + if (expr_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + switch (target_type.Kind) { + case MemberKind.ArrayType: + case MemberKind.Class: + if (target_type.BuiltinType == BuiltinTypeSpec.Type.Object) + return EmptyCast.Create (expr, target_type); + + goto case MemberKind.Struct; + case MemberKind.Struct: + case MemberKind.Delegate: + case MemberKind.Enum: + case MemberKind.Interface: + case MemberKind.TypeParameter: + Arguments args = new Arguments (1); + args.Add (new Argument (expr)); + return new DynamicConversion (target_type, explicit_cast ? CSharpBinderFlags.ConvertExplicit : 0, args, loc).Resolve (ec); + } + + return null; + } + + if (target_type.IsNullableType) + return ImplicitNulableConversion (ec, expr, target_type); + + // + // Attempt to do the implicit constant expression conversions + // + Constant c = expr as Constant; + if (c != null) { + try { + c = c.ConvertImplicitly (target_type); + } catch { + throw new InternalErrorException ("Conversion error", loc); + } + if (c != null) + return c; + } + + e = ImplicitNumericConversion (expr, expr_type, target_type); + if (e != null) + return e; + + e = ImplicitReferenceConversion (expr, target_type, explicit_cast); + if (e != null) + return e; + + e = ImplicitBoxingConversion (expr, expr_type, target_type); + if (e != null) + return e; + + if (expr is IntegralConstant && target_type.IsEnum){ + var i = (IntegralConstant) expr; + // + // LAMESPEC: csc allows any constant like 0 values to be converted, including const float f = 0.0 + // + // An implicit enumeration conversion permits the decimal-integer-literal 0 + // to be converted to any enum-type and to any nullable-type whose underlying + // type is an enum-type + // + if (i.IsZeroInteger) { + // Recreate 0 literal to remove any collected conversions + return new EnumConstant (new IntLiteral (ec.BuiltinTypes, 0, i.Location), target_type); + } + } + + var target_pc = target_type as PointerContainer; + if (target_pc != null) { + if (expr_type.IsPointer) { + // + // Pointer types are same when they have same element types + // + if (expr_type == target_pc) + return expr; + + if (target_pc.Element.Kind == MemberKind.Void) + return EmptyCast.Create (expr, target_type); + + //return null; + } + + if (expr_type == InternalType.NullLiteral) + return new NullPointer (target_type, loc); + } + + if (expr_type == InternalType.AnonymousMethod){ + AnonymousMethodExpression ame = (AnonymousMethodExpression) expr; + Expression am = ame.Compatible (ec, target_type); + if (am != null) + return am.Resolve (ec); + + // Avoid CS1503 after CS1661 + return ErrorExpression.Instance; + } + + if (expr_type == InternalType.Arglist && target_type == ec.Module.PredefinedTypes.ArgIterator.TypeSpec) + return expr; + + // + // dynamic erasure conversion on value types + // + if (expr_type.IsStruct && TypeSpecComparer.IsEqual (expr_type, target_type)) + return expr_type == target_type ? expr : EmptyCast.Create (expr, target_type); + + return null; + } + + /// + /// Attempts to implicitly convert `source' into `target_type', using + /// ImplicitConversion. If there is no implicit conversion, then + /// an error is signaled + /// + static public Expression ImplicitConversionRequired (ResolveContext ec, Expression source, + TypeSpec target_type, Location loc) + { + Expression e = ImplicitConversion (ec, source, target_type, loc); + if (e != null) + return e; + + source.Error_ValueCannotBeConverted (ec, target_type, false); + + return null; + } + + /// + /// Performs the explicit numeric conversions + /// + /// There are a few conversions that are not part of the C# standard, + /// they were interim hacks in the C# compiler that were supposed to + /// become explicit operators in the UIntPtr class and IntPtr class, + /// but for historical reasons it did not happen, so the C# compiler + /// ended up with these special hacks. + /// + /// See bug 59800 for details. + /// + /// The conversion are: + /// UIntPtr->SByte + /// UIntPtr->Int16 + /// UIntPtr->Int32 + /// IntPtr->UInt64 + /// UInt64->IntPtr + /// SByte->UIntPtr + /// Int16->UIntPtr + /// + /// + public static Expression ExplicitNumericConversion (ResolveContext rc, Expression expr, TypeSpec target_type) + { + // Not all predefined explicit numeric conversion are + // defined here, for some of them (mostly IntPtr/UIntPtr) we + // defer to user-operator handling which is now perfect but + // works for now + // + // LAMESPEC: Undocumented IntPtr/UIntPtr conversions + // IntPtr -> uint uses int + // UIntPtr -> long uses ulong + // + + switch (expr.Type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + // + // From sbyte to byte, ushort, uint, ulong, char, uintptr + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.Byte: + return new ConvCast (expr, target_type, ConvCast.Mode.I1_U1); + case BuiltinTypeSpec.Type.UShort: + return new ConvCast (expr, target_type, ConvCast.Mode.I1_U2); + case BuiltinTypeSpec.Type.UInt: + return new ConvCast (expr, target_type, ConvCast.Mode.I1_U4); + case BuiltinTypeSpec.Type.ULong: + return new ConvCast (expr, target_type, ConvCast.Mode.I1_U8); + case BuiltinTypeSpec.Type.Char: + return new ConvCast (expr, target_type, ConvCast.Mode.I1_CH); + + // One of the built-in conversions that belonged in the class library + case BuiltinTypeSpec.Type.UIntPtr: + return new OperatorCast (new ConvCast (expr, rc.BuiltinTypes.ULong, ConvCast.Mode.I1_U8), target_type, target_type, true); + } + break; + case BuiltinTypeSpec.Type.Byte: + // + // From byte to sbyte and char + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + return new ConvCast (expr, target_type, ConvCast.Mode.U1_I1); + case BuiltinTypeSpec.Type.Char: + return new ConvCast (expr, target_type, ConvCast.Mode.U1_CH); + } + break; + case BuiltinTypeSpec.Type.Short: + // + // From short to sbyte, byte, ushort, uint, ulong, char, uintptr + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + return new ConvCast (expr, target_type, ConvCast.Mode.I2_I1); + case BuiltinTypeSpec.Type.Byte: + return new ConvCast (expr, target_type, ConvCast.Mode.I2_U1); + case BuiltinTypeSpec.Type.UShort: + return new ConvCast (expr, target_type, ConvCast.Mode.I2_U2); + case BuiltinTypeSpec.Type.UInt: + return new ConvCast (expr, target_type, ConvCast.Mode.I2_U4); + case BuiltinTypeSpec.Type.ULong: + return new ConvCast (expr, target_type, ConvCast.Mode.I2_U8); + case BuiltinTypeSpec.Type.Char: + return new ConvCast (expr, target_type, ConvCast.Mode.I2_CH); + + // One of the built-in conversions that belonged in the class library + case BuiltinTypeSpec.Type.UIntPtr: + return new OperatorCast (new ConvCast (expr, rc.BuiltinTypes.ULong, ConvCast.Mode.I2_U8), target_type, target_type, true); + } + break; + case BuiltinTypeSpec.Type.UShort: + // + // From ushort to sbyte, byte, short, char + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + return new ConvCast (expr, target_type, ConvCast.Mode.U2_I1); + case BuiltinTypeSpec.Type.Byte: + return new ConvCast (expr, target_type, ConvCast.Mode.U2_U1); + case BuiltinTypeSpec.Type.Short: + return new ConvCast (expr, target_type, ConvCast.Mode.U2_I2); + case BuiltinTypeSpec.Type.Char: + return new ConvCast (expr, target_type, ConvCast.Mode.U2_CH); + } + break; + case BuiltinTypeSpec.Type.Int: + // + // From int to sbyte, byte, short, ushort, uint, ulong, char, uintptr + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + return new ConvCast (expr, target_type, ConvCast.Mode.I4_I1); + case BuiltinTypeSpec.Type.Byte: + return new ConvCast (expr, target_type, ConvCast.Mode.I4_U1); + case BuiltinTypeSpec.Type.Short: + return new ConvCast (expr, target_type, ConvCast.Mode.I4_I2); + case BuiltinTypeSpec.Type.UShort: + return new ConvCast (expr, target_type, ConvCast.Mode.I4_U2); + case BuiltinTypeSpec.Type.UInt: + return new ConvCast (expr, target_type, ConvCast.Mode.I4_U4); + case BuiltinTypeSpec.Type.ULong: + return new ConvCast (expr, target_type, ConvCast.Mode.I4_U8); + case BuiltinTypeSpec.Type.Char: + return new ConvCast (expr, target_type, ConvCast.Mode.I4_CH); + + // One of the built-in conversions that belonged in the class library + case BuiltinTypeSpec.Type.UIntPtr: + return new OperatorCast (new ConvCast (expr, rc.BuiltinTypes.ULong, ConvCast.Mode.I2_U8), target_type, target_type, true); + } + break; + case BuiltinTypeSpec.Type.UInt: + // + // From uint to sbyte, byte, short, ushort, int, char + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + return new ConvCast (expr, target_type, ConvCast.Mode.U4_I1); + case BuiltinTypeSpec.Type.Byte: + return new ConvCast (expr, target_type, ConvCast.Mode.U4_U1); + case BuiltinTypeSpec.Type.Short: + return new ConvCast (expr, target_type, ConvCast.Mode.U4_I2); + case BuiltinTypeSpec.Type.UShort: + return new ConvCast (expr, target_type, ConvCast.Mode.U4_U2); + case BuiltinTypeSpec.Type.Int: + return new ConvCast (expr, target_type, ConvCast.Mode.U4_I4); + case BuiltinTypeSpec.Type.Char: + return new ConvCast (expr, target_type, ConvCast.Mode.U4_CH); + } + break; + case BuiltinTypeSpec.Type.Long: + // + // From long to sbyte, byte, short, ushort, int, uint, ulong, char + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + return new ConvCast (expr, target_type, ConvCast.Mode.I8_I1); + case BuiltinTypeSpec.Type.Byte: + return new ConvCast (expr, target_type, ConvCast.Mode.I8_U1); + case BuiltinTypeSpec.Type.Short: + return new ConvCast (expr, target_type, ConvCast.Mode.I8_I2); + case BuiltinTypeSpec.Type.UShort: + return new ConvCast (expr, target_type, ConvCast.Mode.I8_U2); + case BuiltinTypeSpec.Type.Int: + return new ConvCast (expr, target_type, ConvCast.Mode.I8_I4); + case BuiltinTypeSpec.Type.UInt: + return new ConvCast (expr, target_type, ConvCast.Mode.I8_U4); + case BuiltinTypeSpec.Type.ULong: + return new ConvCast (expr, target_type, ConvCast.Mode.I8_U8); + case BuiltinTypeSpec.Type.Char: + return new ConvCast (expr, target_type, ConvCast.Mode.I8_CH); + } + break; + case BuiltinTypeSpec.Type.ULong: + // + // From ulong to sbyte, byte, short, ushort, int, uint, long, char + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + return new ConvCast (expr, target_type, ConvCast.Mode.U8_I1); + case BuiltinTypeSpec.Type.Byte: + return new ConvCast (expr, target_type, ConvCast.Mode.U8_U1); + case BuiltinTypeSpec.Type.Short: + return new ConvCast (expr, target_type, ConvCast.Mode.U8_I2); + case BuiltinTypeSpec.Type.UShort: + return new ConvCast (expr, target_type, ConvCast.Mode.U8_U2); + case BuiltinTypeSpec.Type.Int: + return new ConvCast (expr, target_type, ConvCast.Mode.U8_I4); + case BuiltinTypeSpec.Type.UInt: + return new ConvCast (expr, target_type, ConvCast.Mode.U8_U4); + case BuiltinTypeSpec.Type.Long: + return new ConvCast (expr, target_type, ConvCast.Mode.U8_I8); + case BuiltinTypeSpec.Type.Char: + return new ConvCast (expr, target_type, ConvCast.Mode.U8_CH); + + // One of the built-in conversions that belonged in the class library + case BuiltinTypeSpec.Type.IntPtr: + return new OperatorCast (EmptyCast.Create (expr, rc.BuiltinTypes.Long), target_type, true); + } + break; + case BuiltinTypeSpec.Type.Char: + // + // From char to sbyte, byte, short + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + return new ConvCast (expr, target_type, ConvCast.Mode.CH_I1); + case BuiltinTypeSpec.Type.Byte: + return new ConvCast (expr, target_type, ConvCast.Mode.CH_U1); + case BuiltinTypeSpec.Type.Short: + return new ConvCast (expr, target_type, ConvCast.Mode.CH_I2); + } + break; + case BuiltinTypeSpec.Type.Float: + // + // From float to sbyte, byte, short, + // ushort, int, uint, long, ulong, char + // or decimal + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + return new ConvCast (expr, target_type, ConvCast.Mode.R4_I1); + case BuiltinTypeSpec.Type.Byte: + return new ConvCast (expr, target_type, ConvCast.Mode.R4_U1); + case BuiltinTypeSpec.Type.Short: + return new ConvCast (expr, target_type, ConvCast.Mode.R4_I2); + case BuiltinTypeSpec.Type.UShort: + return new ConvCast (expr, target_type, ConvCast.Mode.R4_U2); + case BuiltinTypeSpec.Type.Int: + return new ConvCast (expr, target_type, ConvCast.Mode.R4_I4); + case BuiltinTypeSpec.Type.UInt: + return new ConvCast (expr, target_type, ConvCast.Mode.R4_U4); + case BuiltinTypeSpec.Type.Long: + return new ConvCast (expr, target_type, ConvCast.Mode.R4_I8); + case BuiltinTypeSpec.Type.ULong: + return new ConvCast (expr, target_type, ConvCast.Mode.R4_U8); + case BuiltinTypeSpec.Type.Char: + return new ConvCast (expr, target_type, ConvCast.Mode.R4_CH); + case BuiltinTypeSpec.Type.Decimal: + return new OperatorCast (expr, target_type, true); + } + break; + case BuiltinTypeSpec.Type.Double: + // + // From double to sbyte, byte, short, + // ushort, int, uint, long, ulong, + // char, float or decimal + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + return new ConvCast (expr, target_type, ConvCast.Mode.R8_I1); + case BuiltinTypeSpec.Type.Byte: + return new ConvCast (expr, target_type, ConvCast.Mode.R8_U1); + case BuiltinTypeSpec.Type.Short: + return new ConvCast (expr, target_type, ConvCast.Mode.R8_I2); + case BuiltinTypeSpec.Type.UShort: + return new ConvCast (expr, target_type, ConvCast.Mode.R8_U2); + case BuiltinTypeSpec.Type.Int: + return new ConvCast (expr, target_type, ConvCast.Mode.R8_I4); + case BuiltinTypeSpec.Type.UInt: + return new ConvCast (expr, target_type, ConvCast.Mode.R8_U4); + case BuiltinTypeSpec.Type.Long: + return new ConvCast (expr, target_type, ConvCast.Mode.R8_I8); + case BuiltinTypeSpec.Type.ULong: + return new ConvCast (expr, target_type, ConvCast.Mode.R8_U8); + case BuiltinTypeSpec.Type.Char: + return new ConvCast (expr, target_type, ConvCast.Mode.R8_CH); + case BuiltinTypeSpec.Type.Float: + return new ConvCast (expr, target_type, ConvCast.Mode.R8_R4); + case BuiltinTypeSpec.Type.Decimal: + return new OperatorCast (expr, target_type, true); + } + break; + case BuiltinTypeSpec.Type.UIntPtr: + // + // Various built-in conversions that belonged in the class library + // + // from uintptr to sbyte, short, int32 + // + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + return new ConvCast (new OperatorCast (expr, expr.Type, rc.BuiltinTypes.UInt, true), target_type, ConvCast.Mode.U4_I1); + case BuiltinTypeSpec.Type.Short: + return new ConvCast (new OperatorCast (expr, expr.Type, rc.BuiltinTypes.UInt, true), target_type, ConvCast.Mode.U4_I2); + case BuiltinTypeSpec.Type.Int: + return EmptyCast.Create (new OperatorCast (expr, expr.Type, rc.BuiltinTypes.UInt, true), target_type); + case BuiltinTypeSpec.Type.UInt: + return new OperatorCast (expr, expr.Type, target_type, true); + case BuiltinTypeSpec.Type.Long: + return EmptyCast.Create (new OperatorCast (expr, expr.Type, rc.BuiltinTypes.ULong, true), target_type); + } + break; + case BuiltinTypeSpec.Type.IntPtr: + if (target_type.BuiltinType == BuiltinTypeSpec.Type.UInt) + return EmptyCast.Create (new OperatorCast (expr, expr.Type, rc.BuiltinTypes.Int, true), target_type); + if (target_type.BuiltinType == BuiltinTypeSpec.Type.ULong) + return EmptyCast.Create (new OperatorCast (expr, expr.Type, rc.BuiltinTypes.Long, true), target_type); + + break; + case BuiltinTypeSpec.Type.Decimal: + // From decimal to sbyte, byte, short, + // ushort, int, uint, long, ulong, char, + // float, or double + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + case BuiltinTypeSpec.Type.Byte: + case BuiltinTypeSpec.Type.Short: + case BuiltinTypeSpec.Type.UShort: + case BuiltinTypeSpec.Type.Int: + case BuiltinTypeSpec.Type.UInt: + case BuiltinTypeSpec.Type.Long: + case BuiltinTypeSpec.Type.ULong: + case BuiltinTypeSpec.Type.Char: + case BuiltinTypeSpec.Type.Float: + case BuiltinTypeSpec.Type.Double: + return new OperatorCast (expr, expr.Type, target_type, true); + } + + break; + } + + return null; + } + + /// + /// Returns whether an explicit reference conversion can be performed + /// from source_type to target_type + /// + public static bool ExplicitReferenceConversionExists (TypeSpec source_type, TypeSpec target_type) + { + Expression e = ExplicitReferenceConversion (null, source_type, target_type); + if (e == null) + return false; + + if (e == EmptyExpression.Null) + return true; + + throw new InternalErrorException ("Invalid probing conversion result"); + } + + /// + /// Implements Explicit Reference conversions + /// + static Expression ExplicitReferenceConversion (Expression source, TypeSpec source_type, TypeSpec target_type) + { + // + // From object to a generic parameter + // + if (source_type.BuiltinType == BuiltinTypeSpec.Type.Object && TypeManager.IsGenericParameter (target_type)) + return source == null ? EmptyExpression.Null : new UnboxCast (source, target_type); + + // + // Explicit type parameter conversion from T + // + if (source_type.Kind == MemberKind.TypeParameter) + return ExplicitTypeParameterConversionFromT (source, source_type, target_type); + + bool target_is_value_type = target_type.Kind == MemberKind.Struct || target_type.Kind == MemberKind.Enum; + + // + // Unboxing conversion from System.ValueType to any non-nullable-value-type + // + if (source_type.BuiltinType == BuiltinTypeSpec.Type.ValueType && target_is_value_type) + return source == null ? EmptyExpression.Null : new UnboxCast (source, target_type); + + // + // From object or dynamic to any reference type or value type (unboxing) + // + if (source_type.BuiltinType == BuiltinTypeSpec.Type.Object || source_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + if (target_type.IsPointer) + return null; + + return + source == null ? EmptyExpression.Null : + target_is_value_type ? new UnboxCast (source, target_type) : + source is Constant ? (Expression) new EmptyConstantCast ((Constant) source, target_type) : + new ClassCast (source, target_type); + } + + // + // From any class S to any class-type T, provided S is a base class of T + // + if (source_type.Kind == MemberKind.Class && TypeSpec.IsBaseClass (target_type, source_type, true)) + return source == null ? EmptyExpression.Null : new ClassCast (source, target_type); + + // + // From any interface-type S to to any class type T, provided T is not + // sealed, or provided T implements S. + // + // This also covers Explicit conversions involving type parameters + // section From any interface type to T + // + if (source_type.Kind == MemberKind.Interface) { + if (!target_type.IsSealed || target_type.ImplementsInterface (source_type, true)) { + if (source == null) + return EmptyExpression.Null; + + // + // Unboxing conversion from any interface-type to any non-nullable-value-type that + // implements the interface-type + // + return target_is_value_type ? new UnboxCast (source, target_type) : (Expression) new ClassCast (source, target_type); + } + + // + // From System.Collections.Generic.IList and its base interfaces to a one-dimensional + // array type S[], provided there is an implicit or explicit reference conversion from S to T. + // + var target_array = target_type as ArrayContainer; + if (target_array != null && IList_To_Array (source_type, target_array)) + return source == null ? EmptyExpression.Null : new ClassCast (source, target_type); + + return null; + } + + var source_array = source_type as ArrayContainer; + if (source_array != null) { + var target_array = target_type as ArrayContainer; + if (target_array != null) { + // + // From System.Array to any array-type + // + if (source_type.BuiltinType == BuiltinTypeSpec.Type.Array) + return source == null ? EmptyExpression.Null : new ClassCast (source, target_type); + + // + // From an array type S with an element type Se to an array type T with an + // element type Te provided all the following are true: + // * S and T differe only in element type, in other words, S and T + // have the same number of dimensions. + // * Both Se and Te are reference types + // * An explicit reference conversions exist from Se to Te + // + if (source_array.Rank == target_array.Rank) { + + source_type = source_array.Element; + var target_element = target_array.Element; + + // + // LAMESPEC: Type parameters are special cased somehow but + // only when both source and target elements are type parameters + // + if ((source_type.Kind & target_element.Kind & MemberKind.TypeParameter) == MemberKind.TypeParameter) { + // + // Conversion is allowed unless source element type has struct constrain + // + if (TypeSpec.IsValueType (source_type)) + return null; + } else { + if (!TypeSpec.IsReferenceType (source_type)) + return null; + } + + if (!TypeSpec.IsReferenceType (target_element)) + return null; + + if (ExplicitReferenceConversionExists (source_type, target_element)) + return source == null ? EmptyExpression.Null : new ClassCast (source, target_type); + + return null; + } + } + + // + // From a single-dimensional array type S[] to System.Collections.Generic.IList and its base interfaces, + // provided that there is an explicit reference conversion from S to T + // + if (ArrayToIList (source_array, target_type, true)) + return source == null ? EmptyExpression.Null : new ClassCast (source, target_type); + + return null; + } + + // + // From any class type S to any interface T, provides S is not sealed + // and provided S does not implement T. + // + if (target_type.IsInterface && !source_type.IsSealed && !source_type.ImplementsInterface (target_type, true)) { + return source == null ? EmptyExpression.Null : new ClassCast (source, target_type); + } + + // + // From System delegate to any delegate-type + // + if (source_type.BuiltinType == BuiltinTypeSpec.Type.Delegate && target_type.IsDelegate) + return source == null ? EmptyExpression.Null : new ClassCast (source, target_type); + + // + // From variant generic delegate to same variant generic delegate type + // + if (source_type.IsDelegate && target_type.IsDelegate && source_type.MemberDefinition == target_type.MemberDefinition) { + var tparams = source_type.MemberDefinition.TypeParameters; + var targs_src = source_type.TypeArguments; + var targs_dst = target_type.TypeArguments; + int i; + for (i = 0; i < tparams.Length; ++i) { + // + // If TP is invariant, types have to be identical + // + if (TypeSpecComparer.IsEqual (targs_src[i], targs_dst[i])) + continue; + + if (tparams[i].Variance == Variance.Covariant) { + // + //If TP is covariant, an implicit or explicit identity or reference conversion is required + // + if (ImplicitReferenceConversionExists (targs_src[i], targs_dst[i])) + continue; + + if (ExplicitReferenceConversionExists (targs_src[i], targs_dst[i])) + continue; + + } else if (tparams[i].Variance == Variance.Contravariant) { + // + //If TP is contravariant, both are either identical or reference types + // + if (TypeSpec.IsReferenceType (targs_src[i]) && TypeSpec.IsReferenceType (targs_dst[i])) + continue; + } + + break; + } + + if (i == tparams.Length) + return source == null ? EmptyExpression.Null : new ClassCast (source, target_type); + } + + var tps = target_type as TypeParameterSpec; + if (tps != null) + return ExplicitTypeParameterConversionToT (source, source_type, tps); + + return null; + } + + /// + /// Performs an explicit conversion of the expression `expr' whose + /// type is expr.Type to `target_type'. + /// + static public Expression ExplicitConversionCore (ResolveContext ec, Expression expr, + TypeSpec target_type, Location loc) + { + TypeSpec expr_type = expr.Type; + + // Explicit conversion includes implicit conversion and it used for enum underlying types too + Expression ne = ImplicitConversionStandard (ec, expr, target_type, loc, true); + if (ne != null) + return ne; + + if (expr_type.IsEnum) { + TypeSpec real_target = target_type.IsEnum ? EnumSpec.GetUnderlyingType (target_type) : target_type; + Expression underlying = EmptyCast.Create (expr, EnumSpec.GetUnderlyingType (expr_type)); + if (underlying.Type == real_target) + ne = underlying; + + if (ne == null) + ne = ImplicitNumericConversion (underlying, real_target); + + if (ne == null) + ne = ExplicitNumericConversion (ec, underlying, real_target); + + // + // LAMESPEC: IntPtr and UIntPtr conversion to any Enum is allowed + // + if (ne == null && (real_target.BuiltinType == BuiltinTypeSpec.Type.IntPtr || real_target.BuiltinType == BuiltinTypeSpec.Type.UIntPtr)) + ne = ExplicitUserConversion (ec, underlying, real_target, loc); + + return ne != null ? EmptyCast.Create (ne, target_type) : null; + } + + if (target_type.IsEnum) { + // + // System.Enum can be unboxed to any enum-type + // + if (expr_type.BuiltinType == BuiltinTypeSpec.Type.Enum) + return new UnboxCast (expr, target_type); + + TypeSpec real_target = target_type.IsEnum ? EnumSpec.GetUnderlyingType (target_type) : target_type; + + if (expr_type == real_target) + return EmptyCast.Create (expr, target_type); + + Constant c = expr as Constant; + if (c != null) { + c = c.TryReduce (ec, real_target); + if (c != null) + return c; + } else { + ne = ImplicitNumericConversion (expr, real_target); + if (ne != null) + return EmptyCast.Create (ne, target_type); + + ne = ExplicitNumericConversion (ec, expr, real_target); + if (ne != null) + return EmptyCast.Create (ne, target_type); + + // + // LAMESPEC: IntPtr and UIntPtr conversion to any Enum is allowed + // + if (expr_type.BuiltinType == BuiltinTypeSpec.Type.IntPtr || expr_type.BuiltinType == BuiltinTypeSpec.Type.UIntPtr) { + ne = ExplicitUserConversion (ec, expr, real_target, loc); + if (ne != null) + return ExplicitConversionCore (ec, ne, target_type, loc); + } + } + } else { + ne = ExplicitNumericConversion (ec, expr, target_type); + if (ne != null) + return ne; + } + + // + // Skip the ExplicitReferenceConversion because we can not convert + // from Null to a ValueType, and ExplicitReference wont check against + // null literal explicitly + // + if (expr_type != InternalType.NullLiteral) { + ne = ExplicitReferenceConversion (expr, expr_type, target_type); + if (ne != null) + return ne; + } + + if (ec.IsUnsafe){ + ne = ExplicitUnsafe (expr, target_type); + if (ne != null) + return ne; + } + + return null; + } + + public static Expression ExplicitUnsafe (Expression expr, TypeSpec target_type) + { + TypeSpec expr_type = expr.Type; + + if (target_type.IsPointer){ + if (expr_type.IsPointer) + return EmptyCast.Create (expr, target_type); + + switch (expr_type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + case BuiltinTypeSpec.Type.Short: + case BuiltinTypeSpec.Type.Int: + return new OpcodeCast (expr, target_type, OpCodes.Conv_I); + + case BuiltinTypeSpec.Type.UShort: + case BuiltinTypeSpec.Type.UInt: + case BuiltinTypeSpec.Type.Byte: + return new OpcodeCast (expr, target_type, OpCodes.Conv_U); + + case BuiltinTypeSpec.Type.Long: + return new ConvCast (expr, target_type, ConvCast.Mode.I8_I); + + case BuiltinTypeSpec.Type.ULong: + return new ConvCast (expr, target_type, ConvCast.Mode.U8_I); + } + } + + if (expr_type.IsPointer){ + switch (target_type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + return new OpcodeCast (expr, target_type, OpCodes.Conv_I1); + case BuiltinTypeSpec.Type.Byte: + return new OpcodeCast (expr, target_type, OpCodes.Conv_U1); + case BuiltinTypeSpec.Type.Short: + return new OpcodeCast (expr, target_type, OpCodes.Conv_I2); + case BuiltinTypeSpec.Type.UShort: + return new OpcodeCast (expr, target_type, OpCodes.Conv_U2); + case BuiltinTypeSpec.Type.Int: + return new OpcodeCast (expr, target_type, OpCodes.Conv_I4); + case BuiltinTypeSpec.Type.UInt: + return new OpcodeCast (expr, target_type, OpCodes.Conv_U4); + case BuiltinTypeSpec.Type.Long: + return new ConvCast (expr, target_type, ConvCast.Mode.I_I8); + case BuiltinTypeSpec.Type.ULong: + return new OpcodeCast (expr, target_type, OpCodes.Conv_U8); + } + } + return null; + } + + /// + /// Same as ExplicitConversion, only it doesn't include user defined conversions + /// + static public Expression ExplicitConversionStandard (ResolveContext ec, Expression expr, + TypeSpec target_type, Location l) + { + int errors = ec.Report.Errors; + Expression ne = ImplicitConversionStandard (ec, expr, target_type, l); + if (ec.Report.Errors > errors) + return null; + + if (ne != null) + return ne; + + ne = ExplicitNumericConversion (ec, expr, target_type); + if (ne != null) + return ne; + + ne = ExplicitReferenceConversion (expr, expr.Type, target_type); + if (ne != null) + return ne; + + if (ec.IsUnsafe && expr.Type.IsPointer && target_type.IsPointer && ((PointerContainer)expr.Type).Element.Kind == MemberKind.Void) + return EmptyCast.Create (expr, target_type); + + expr.Error_ValueCannotBeConverted (ec, target_type, true); + return null; + } + + /// + /// Performs an explicit conversion of the expression `expr' whose + /// type is expr.Type to `target_type'. + /// + static public Expression ExplicitConversion (ResolveContext ec, Expression expr, + TypeSpec target_type, Location loc) + { + Expression e = ExplicitConversionCore (ec, expr, target_type, loc); + if (e != null) { + // + // Don't eliminate explicit precission casts + // + if (e == expr) { + if (target_type.BuiltinType == BuiltinTypeSpec.Type.Float) + return new OpcodeCast (expr, target_type, OpCodes.Conv_R4); + + if (target_type.BuiltinType == BuiltinTypeSpec.Type.Double) + return new OpcodeCast (expr, target_type, OpCodes.Conv_R8); + } + + return e; + } + + TypeSpec expr_type = expr.Type; + if (target_type.IsNullableType) { + TypeSpec target; + + if (expr_type.IsNullableType) { + target = Nullable.NullableInfo.GetUnderlyingType (target_type); + Expression unwrap = Nullable.Unwrap.Create (expr); + e = ExplicitConversion (ec, unwrap, target, expr.Location); + if (e == null) + return null; + + return new Nullable.LiftedConversion (e, unwrap, target_type).Resolve (ec); + } + if (expr_type.BuiltinType == BuiltinTypeSpec.Type.Object) { + return new UnboxCast (expr, target_type); + } + + target = TypeManager.GetTypeArguments (target_type) [0]; + e = ExplicitConversionCore (ec, expr, target, loc); + if (e != null) + return TypeSpec.IsReferenceType (expr.Type) ? new UnboxCast (expr, target_type) : Nullable.Wrap.Create (e, target_type); + } else if (expr_type.IsNullableType) { + e = ImplicitBoxingConversion (expr, Nullable.NullableInfo.GetUnderlyingType (expr_type), target_type); + if (e != null) + return e; + + e = Nullable.Unwrap.Create (expr, false); + e = ExplicitConversionCore (ec, e, target_type, loc); + if (e != null) + return EmptyCast.Create (e, target_type); + } + + e = ExplicitUserConversion (ec, expr, target_type, loc); + + if (e != null) + return e; + + expr.Error_ValueCannotBeConverted (ec, target_type, true); + return null; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/cs-parser.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/cs-parser.cs new file mode 100644 index 000000000..868c1394f --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/cs-parser.cs @@ -0,0 +1,16238 @@ +// created by jay 0.7 (c) 1998 Axel.Schreiner@informatik.uni-osnabrueck.de + +#line 2 "cs-parser.jay" +// +// cs-parser.jay: The Parser for the C# compiler +// +// Authors: Miguel de Icaza (miguel@gnome.org) +// Ravi Pratap (ravi@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// Dual Licensed under the terms of the GNU GPL and the MIT X11 license +// +// (C) 2001 Ximian, Inc (http://www.ximian.com) +// (C) 2004-2011 Novell, Inc +// Copyright 2011-2012 Xamarin Inc. +// + +using System.Text; +using System.IO; +using System; +using System.Collections.Generic; + +namespace Mono.CSharp +{ + /// + /// The C# Parser + /// + public class CSharpParser + { + [Flags] + enum ParameterModifierType + { + Ref = 1 << 1, + Out = 1 << 2, + This = 1 << 3, + Params = 1 << 4, + Arglist = 1 << 5, + DefaultValue = 1 << 6, + + All = Ref | Out | This | Params | Arglist | DefaultValue, + PrimaryConstructor = Ref | Out | Params | DefaultValue + } + + static readonly object ModifierNone = 0; + + NamespaceContainer current_namespace; + TypeContainer current_container; + TypeDefinition current_type; + PropertyBase current_property; + EventProperty current_event; + EventField current_event_field; + FieldBase current_field; + + /// + /// Current block is used to add statements as we find + /// them. + /// + Block current_block; + + BlockVariable current_variable; + + Delegate current_delegate; + + AnonymousMethodExpression current_anonymous_method; + + /// + /// This is used by the unary_expression code to resolve + /// a name against a parameter. + /// + + // FIXME: This is very ugly and it's very hard to reset it correctly + // on all places, especially when some parameters are autogenerated. + ParametersCompiled current_local_parameters; + + bool parsing_anonymous_method; + + bool async_block; + + /// + /// An out-of-band stack. + /// + Stack oob_stack; + + /// + /// Controls the verbosity of the errors produced by the parser + /// + int yacc_verbose_flag; + + /// + /// Used by the interactive shell, flags whether EOF was reached + /// and an error was produced + /// + public bool UnexpectedEOF; + + /// + /// The current file. + /// + readonly CompilationSourceFile file; + + /// + /// Temporary Xml documentation cache. + /// For enum types, we need one more temporary store. + /// + string tmpComment; + string enumTypeComment; + + /// Current attribute target + string current_attr_target; + + ParameterModifierType valid_param_mod; + + bool default_parameter_used; + + /// When using the interactive parser, this holds the + /// resulting expression + public Class InteractiveResult; + + // + // Keeps track of global data changes to undo on parser error + // + public Undo undo; + + bool? interactive_async; + + Stack linq_clause_blocks; + + ModuleContainer module; + + readonly CompilerContext compiler; + readonly LanguageVersion lang_version; + readonly bool doc_support; + readonly CompilerSettings settings; + readonly Report report; + + // + // Instead of allocating carrier array everytime we + // share the bucket for very common constructs which can never + // be recursive + // + List parameters_bucket; + + // + // Full AST support members + // + LocationsBag lbag; + List> mod_locations; + Location parameterModifierLocation, savedLocation, savedEventAssignLocation; + Location savedAttrParenOpenLocation, savedAttrParenCloseLocation, savedOperatorLocation; + Stack> locationListStack = new Stack> (); // used for type parameters + Stack opt_intoStack = new Stack (); + + bool HadAttributeParens; + List attributeArgumentCommas = new List (); + List parameterListCommas = new List (); + Stack location_stack; +#line default + + /** error output stream. + It should be changeable. + */ + public System.IO.TextWriter ErrorOutput = System.Console.Out; + + /** simplified error message. + @see yyerror + */ + public void yyerror (string message) { + yyerror(message, null); + } +#pragma warning disable 649 + /* An EOF token */ + public int eof_token; +#pragma warning restore 649 + /** (syntax) error message. + Can be overwritten to control message format. + @param message text to be displayed. + @param expected vector of acceptable tokens, if available. + */ + public void yyerror (string message, string[] expected) { + if ((yacc_verbose_flag > 0) && (expected != null) && (expected.Length > 0)) { + ErrorOutput.Write (message+", expecting"); + for (int n = 0; n < expected.Length; ++ n) + ErrorOutput.Write (" "+expected[n]); + ErrorOutput.WriteLine (); + } else + ErrorOutput.WriteLine (message); + } + + /** debugging support, requires the package jay.yydebug. + Set to null to suppress debugging messages. + */ +//t internal yydebug.yyDebug debug; + + protected const int yyFinal = 7; +//t // Put this array into a separate class so it is only initialized if debugging is actually used +//t // Use MarshalByRefObject to disable inlining +//t class YYRules : MarshalByRefObject { +//t public static readonly string [] yyRule = { +//t "$accept : compilation_unit", +//t "compilation_unit : outer_declaration opt_EOF", +//t "$$1 :", +//t "compilation_unit : interactive_parsing $$1 opt_EOF", +//t "compilation_unit : documentation_parsing", +//t "outer_declaration : opt_extern_alias_directives opt_using_directives", +//t "outer_declaration : opt_extern_alias_directives opt_using_directives namespace_or_type_declarations opt_attributes", +//t "outer_declaration : opt_extern_alias_directives opt_using_directives attribute_sections", +//t "outer_declaration : error", +//t "opt_EOF :", +//t "opt_EOF : EOF", +//t "extern_alias_directives : extern_alias_directive", +//t "extern_alias_directives : extern_alias_directives extern_alias_directive", +//t "extern_alias_directive : EXTERN_ALIAS IDENTIFIER IDENTIFIER SEMICOLON", +//t "extern_alias_directive : EXTERN_ALIAS error", +//t "using_directives : using_directive", +//t "using_directives : using_directives using_directive", +//t "using_directive : using_namespace", +//t "using_namespace : USING namespace_or_type_expr SEMICOLON", +//t "using_namespace : USING IDENTIFIER ASSIGN namespace_or_type_expr SEMICOLON", +//t "using_namespace : USING error", +//t "$$2 :", +//t "$$3 :", +//t "namespace_declaration : opt_attributes NAMESPACE namespace_name $$2 OPEN_BRACE $$3 opt_extern_alias_directives opt_using_directives opt_namespace_or_type_declarations CLOSE_BRACE opt_semicolon_error", +//t "namespace_declaration : opt_attributes NAMESPACE namespace_name", +//t "opt_semicolon_error :", +//t "opt_semicolon_error : SEMICOLON", +//t "opt_semicolon_error : error", +//t "namespace_name : IDENTIFIER", +//t "namespace_name : namespace_name DOT IDENTIFIER", +//t "namespace_name : error", +//t "opt_semicolon :", +//t "opt_semicolon : SEMICOLON", +//t "opt_comma :", +//t "opt_comma : COMMA", +//t "opt_using_directives :", +//t "opt_using_directives : using_directives", +//t "opt_extern_alias_directives :", +//t "opt_extern_alias_directives : extern_alias_directives", +//t "opt_namespace_or_type_declarations :", +//t "opt_namespace_or_type_declarations : namespace_or_type_declarations", +//t "namespace_or_type_declarations : namespace_or_type_declaration", +//t "namespace_or_type_declarations : namespace_or_type_declarations namespace_or_type_declaration", +//t "namespace_or_type_declaration : type_declaration", +//t "namespace_or_type_declaration : namespace_declaration", +//t "namespace_or_type_declaration : attribute_sections CLOSE_BRACE", +//t "type_declaration : class_declaration", +//t "type_declaration : struct_declaration", +//t "type_declaration : interface_declaration", +//t "type_declaration : enum_declaration", +//t "type_declaration : delegate_declaration", +//t "opt_attributes :", +//t "opt_attributes : attribute_sections", +//t "attribute_sections : attribute_section", +//t "attribute_sections : attribute_sections attribute_section", +//t "$$4 :", +//t "attribute_section : OPEN_BRACKET $$4 attribute_section_cont", +//t "$$5 :", +//t "attribute_section_cont : attribute_target COLON $$5 attribute_list opt_comma CLOSE_BRACKET", +//t "attribute_section_cont : attribute_list opt_comma CLOSE_BRACKET", +//t "attribute_section_cont : IDENTIFIER error", +//t "attribute_section_cont : error", +//t "attribute_target : IDENTIFIER", +//t "attribute_target : EVENT", +//t "attribute_target : RETURN", +//t "attribute_list : attribute", +//t "attribute_list : attribute_list COMMA attribute", +//t "$$6 :", +//t "attribute : attribute_name $$6 opt_attribute_arguments", +//t "attribute_name : namespace_or_type_expr", +//t "opt_attribute_arguments :", +//t "opt_attribute_arguments : OPEN_PARENS attribute_arguments CLOSE_PARENS", +//t "attribute_arguments :", +//t "attribute_arguments : positional_or_named_argument", +//t "attribute_arguments : named_attribute_argument", +//t "attribute_arguments : attribute_arguments COMMA positional_or_named_argument", +//t "attribute_arguments : attribute_arguments COMMA named_attribute_argument", +//t "positional_or_named_argument : expression", +//t "positional_or_named_argument : named_argument", +//t "positional_or_named_argument : error", +//t "$$7 :", +//t "named_attribute_argument : IDENTIFIER ASSIGN $$7 expression", +//t "named_argument : identifier_inside_body COLON opt_named_modifier expression_or_error", +//t "opt_named_modifier :", +//t "opt_named_modifier : REF", +//t "opt_named_modifier : OUT", +//t "opt_class_member_declarations :", +//t "opt_class_member_declarations : class_member_declarations", +//t "class_member_declarations : class_member_declaration", +//t "class_member_declarations : class_member_declarations class_member_declaration", +//t "class_member_declaration : constant_declaration", +//t "class_member_declaration : field_declaration", +//t "class_member_declaration : method_declaration", +//t "class_member_declaration : property_declaration", +//t "class_member_declaration : event_declaration", +//t "class_member_declaration : indexer_declaration", +//t "class_member_declaration : operator_declaration", +//t "class_member_declaration : constructor_declaration", +//t "class_member_declaration : primary_constructor_body", +//t "class_member_declaration : destructor_declaration", +//t "class_member_declaration : type_declaration", +//t "class_member_declaration : attributes_without_members", +//t "class_member_declaration : incomplete_member", +//t "class_member_declaration : error", +//t "$$8 :", +//t "primary_constructor_body : OPEN_BRACE $$8 opt_statement_list block_end", +//t "$$9 :", +//t "$$10 :", +//t "$$11 :", +//t "$$12 :", +//t "$$13 :", +//t "struct_declaration : opt_attributes opt_modifiers opt_partial STRUCT $$9 type_declaration_name $$10 opt_primary_parameters opt_class_base opt_type_parameter_constraints_clauses $$11 OPEN_BRACE $$12 opt_class_member_declarations CLOSE_BRACE $$13 opt_semicolon", +//t "struct_declaration : opt_attributes opt_modifiers opt_partial STRUCT error", +//t "$$14 :", +//t "constant_declaration : opt_attributes opt_modifiers CONST type IDENTIFIER $$14 constant_initializer opt_constant_declarators SEMICOLON", +//t "constant_declaration : opt_attributes opt_modifiers CONST type error", +//t "opt_constant_declarators :", +//t "opt_constant_declarators : constant_declarators", +//t "constant_declarators : constant_declarator", +//t "constant_declarators : constant_declarators constant_declarator", +//t "constant_declarator : COMMA IDENTIFIER constant_initializer", +//t "$$15 :", +//t "constant_initializer : ASSIGN $$15 constant_initializer_expr", +//t "constant_initializer : error", +//t "constant_initializer_expr : constant_expression", +//t "constant_initializer_expr : array_initializer", +//t "$$16 :", +//t "field_declaration : opt_attributes opt_modifiers member_type IDENTIFIER $$16 opt_field_initializer opt_field_declarators SEMICOLON", +//t "$$17 :", +//t "field_declaration : opt_attributes opt_modifiers FIXED simple_type IDENTIFIER $$17 fixed_field_size opt_fixed_field_declarators SEMICOLON", +//t "field_declaration : opt_attributes opt_modifiers FIXED simple_type error SEMICOLON", +//t "opt_field_initializer :", +//t "$$18 :", +//t "opt_field_initializer : ASSIGN $$18 variable_initializer", +//t "opt_field_declarators :", +//t "opt_field_declarators : field_declarators", +//t "field_declarators : field_declarator", +//t "field_declarators : field_declarators field_declarator", +//t "field_declarator : COMMA IDENTIFIER", +//t "$$19 :", +//t "field_declarator : COMMA IDENTIFIER ASSIGN $$19 variable_initializer", +//t "opt_fixed_field_declarators :", +//t "opt_fixed_field_declarators : fixed_field_declarators", +//t "fixed_field_declarators : fixed_field_declarator", +//t "fixed_field_declarators : fixed_field_declarators fixed_field_declarator", +//t "fixed_field_declarator : COMMA IDENTIFIER fixed_field_size", +//t "$$20 :", +//t "fixed_field_size : OPEN_BRACKET $$20 expression CLOSE_BRACKET", +//t "fixed_field_size : OPEN_BRACKET error", +//t "variable_initializer : expression", +//t "variable_initializer : array_initializer", +//t "variable_initializer : error", +//t "$$21 :", +//t "method_declaration : method_header $$21 method_body_expression_block", +//t "$$22 :", +//t "$$23 :", +//t "method_header : opt_attributes opt_modifiers member_type method_declaration_name OPEN_PARENS $$22 opt_formal_parameter_list CLOSE_PARENS $$23 opt_type_parameter_constraints_clauses", +//t "$$24 :", +//t "$$25 :", +//t "$$26 :", +//t "method_header : opt_attributes opt_modifiers PARTIAL VOID $$24 method_declaration_name OPEN_PARENS $$25 opt_formal_parameter_list CLOSE_PARENS $$26 opt_type_parameter_constraints_clauses", +//t "method_header : opt_attributes opt_modifiers member_type modifiers method_declaration_name OPEN_PARENS opt_formal_parameter_list CLOSE_PARENS", +//t "method_header : opt_attributes opt_modifiers member_type method_declaration_name error", +//t "method_body_expression_block : method_body", +//t "method_body_expression_block : expression_block", +//t "method_body : block", +//t "method_body : SEMICOLON", +//t "$$27 :", +//t "expression_block : ARROW $$27 expression SEMICOLON", +//t "opt_formal_parameter_list :", +//t "opt_formal_parameter_list : formal_parameter_list", +//t "formal_parameter_list : fixed_parameters", +//t "formal_parameter_list : fixed_parameters COMMA parameter_array", +//t "formal_parameter_list : fixed_parameters COMMA arglist_modifier", +//t "formal_parameter_list : parameter_array COMMA error", +//t "formal_parameter_list : fixed_parameters COMMA parameter_array COMMA error", +//t "formal_parameter_list : arglist_modifier COMMA error", +//t "formal_parameter_list : fixed_parameters COMMA ARGLIST COMMA error", +//t "formal_parameter_list : parameter_array", +//t "formal_parameter_list : arglist_modifier", +//t "formal_parameter_list : error", +//t "fixed_parameters : fixed_parameter", +//t "fixed_parameters : fixed_parameters COMMA fixed_parameter", +//t "fixed_parameter : opt_attributes opt_parameter_modifier parameter_type identifier_inside_body", +//t "fixed_parameter : opt_attributes opt_parameter_modifier parameter_type identifier_inside_body OPEN_BRACKET CLOSE_BRACKET", +//t "fixed_parameter : attribute_sections error", +//t "fixed_parameter : opt_attributes opt_parameter_modifier parameter_type error", +//t "$$28 :", +//t "fixed_parameter : opt_attributes opt_parameter_modifier parameter_type identifier_inside_body ASSIGN $$28 constant_expression", +//t "opt_parameter_modifier :", +//t "opt_parameter_modifier : parameter_modifiers", +//t "parameter_modifiers : parameter_modifier", +//t "parameter_modifiers : parameter_modifiers parameter_modifier", +//t "parameter_modifier : REF", +//t "parameter_modifier : OUT", +//t "parameter_modifier : THIS", +//t "parameter_array : opt_attributes params_modifier type IDENTIFIER", +//t "parameter_array : opt_attributes params_modifier type IDENTIFIER ASSIGN constant_expression", +//t "parameter_array : opt_attributes params_modifier type error", +//t "params_modifier : PARAMS", +//t "params_modifier : PARAMS parameter_modifier", +//t "params_modifier : PARAMS params_modifier", +//t "arglist_modifier : ARGLIST", +//t "$$29 :", +//t "$$30 :", +//t "$$31 :", +//t "$$32 :", +//t "property_declaration : opt_attributes opt_modifiers member_type member_declaration_name $$29 OPEN_BRACE $$30 accessor_declarations $$31 CLOSE_BRACE $$32 opt_property_initializer", +//t "$$33 :", +//t "property_declaration : opt_attributes opt_modifiers member_type member_declaration_name $$33 expression_block", +//t "opt_property_initializer :", +//t "$$34 :", +//t "opt_property_initializer : ASSIGN $$34 property_initializer SEMICOLON", +//t "property_initializer : expression", +//t "property_initializer : array_initializer", +//t "$$35 :", +//t "$$36 :", +//t "indexer_declaration : opt_attributes opt_modifiers member_type indexer_declaration_name OPEN_BRACKET $$35 opt_formal_parameter_list CLOSE_BRACKET $$36 indexer_body", +//t "indexer_body : OPEN_BRACE accessor_declarations CLOSE_BRACE", +//t "indexer_body : expression_block", +//t "accessor_declarations : get_accessor_declaration", +//t "accessor_declarations : get_accessor_declaration accessor_declarations", +//t "accessor_declarations : set_accessor_declaration", +//t "accessor_declarations : set_accessor_declaration accessor_declarations", +//t "accessor_declarations : error", +//t "$$37 :", +//t "get_accessor_declaration : opt_attributes opt_modifiers GET $$37 accessor_body", +//t "$$38 :", +//t "set_accessor_declaration : opt_attributes opt_modifiers SET $$38 accessor_body", +//t "accessor_body : block", +//t "accessor_body : SEMICOLON", +//t "accessor_body : error", +//t "$$39 :", +//t "$$40 :", +//t "$$41 :", +//t "$$42 :", +//t "interface_declaration : opt_attributes opt_modifiers opt_partial INTERFACE $$39 type_declaration_name $$40 opt_class_base opt_type_parameter_constraints_clauses $$41 OPEN_BRACE opt_interface_member_declarations CLOSE_BRACE $$42 opt_semicolon", +//t "interface_declaration : opt_attributes opt_modifiers opt_partial INTERFACE error", +//t "opt_interface_member_declarations :", +//t "opt_interface_member_declarations : interface_member_declarations", +//t "interface_member_declarations : interface_member_declaration", +//t "interface_member_declarations : interface_member_declarations interface_member_declaration", +//t "interface_member_declaration : constant_declaration", +//t "interface_member_declaration : field_declaration", +//t "interface_member_declaration : method_declaration", +//t "interface_member_declaration : property_declaration", +//t "interface_member_declaration : event_declaration", +//t "interface_member_declaration : indexer_declaration", +//t "interface_member_declaration : operator_declaration", +//t "interface_member_declaration : constructor_declaration", +//t "interface_member_declaration : type_declaration", +//t "$$43 :", +//t "operator_declaration : opt_attributes opt_modifiers operator_declarator $$43 method_body_expression_block", +//t "operator_type : type_expression_or_array", +//t "operator_type : VOID", +//t "$$44 :", +//t "operator_declarator : operator_type OPERATOR overloadable_operator OPEN_PARENS $$44 opt_formal_parameter_list CLOSE_PARENS", +//t "operator_declarator : conversion_operator_declarator", +//t "overloadable_operator : BANG", +//t "overloadable_operator : TILDE", +//t "overloadable_operator : OP_INC", +//t "overloadable_operator : OP_DEC", +//t "overloadable_operator : TRUE", +//t "overloadable_operator : FALSE", +//t "overloadable_operator : PLUS", +//t "overloadable_operator : MINUS", +//t "overloadable_operator : STAR", +//t "overloadable_operator : DIV", +//t "overloadable_operator : PERCENT", +//t "overloadable_operator : BITWISE_AND", +//t "overloadable_operator : BITWISE_OR", +//t "overloadable_operator : CARRET", +//t "overloadable_operator : OP_SHIFT_LEFT", +//t "overloadable_operator : OP_SHIFT_RIGHT", +//t "overloadable_operator : OP_EQ", +//t "overloadable_operator : OP_NE", +//t "overloadable_operator : OP_GT", +//t "overloadable_operator : OP_LT", +//t "overloadable_operator : OP_GE", +//t "overloadable_operator : OP_LE", +//t "$$45 :", +//t "conversion_operator_declarator : IMPLICIT OPERATOR type OPEN_PARENS $$45 opt_formal_parameter_list CLOSE_PARENS", +//t "$$46 :", +//t "conversion_operator_declarator : EXPLICIT OPERATOR type OPEN_PARENS $$46 opt_formal_parameter_list CLOSE_PARENS", +//t "conversion_operator_declarator : IMPLICIT error", +//t "conversion_operator_declarator : EXPLICIT error", +//t "constructor_declaration : constructor_declarator constructor_body", +//t "$$47 :", +//t "$$48 :", +//t "constructor_declarator : opt_attributes opt_modifiers IDENTIFIER $$47 OPEN_PARENS opt_formal_parameter_list CLOSE_PARENS $$48 opt_constructor_initializer", +//t "constructor_body : block_prepared", +//t "constructor_body : SEMICOLON", +//t "opt_constructor_initializer :", +//t "opt_constructor_initializer : constructor_initializer", +//t "$$49 :", +//t "constructor_initializer : COLON BASE OPEN_PARENS $$49 opt_argument_list CLOSE_PARENS", +//t "$$50 :", +//t "constructor_initializer : COLON THIS OPEN_PARENS $$50 opt_argument_list CLOSE_PARENS", +//t "constructor_initializer : COLON error", +//t "constructor_initializer : error", +//t "$$51 :", +//t "destructor_declaration : opt_attributes opt_modifiers TILDE $$51 IDENTIFIER OPEN_PARENS CLOSE_PARENS method_body", +//t "$$52 :", +//t "event_declaration : opt_attributes opt_modifiers EVENT type member_declaration_name $$52 opt_event_initializer opt_event_declarators SEMICOLON", +//t "$$53 :", +//t "$$54 :", +//t "event_declaration : opt_attributes opt_modifiers EVENT type member_declaration_name OPEN_BRACE $$53 event_accessor_declarations $$54 CLOSE_BRACE", +//t "event_declaration : opt_attributes opt_modifiers EVENT type error", +//t "opt_event_initializer :", +//t "$$55 :", +//t "opt_event_initializer : ASSIGN $$55 event_variable_initializer", +//t "opt_event_declarators :", +//t "opt_event_declarators : event_declarators", +//t "event_declarators : event_declarator", +//t "event_declarators : event_declarators event_declarator", +//t "event_declarator : COMMA IDENTIFIER", +//t "$$56 :", +//t "event_declarator : COMMA IDENTIFIER ASSIGN $$56 event_variable_initializer", +//t "$$57 :", +//t "event_variable_initializer : $$57 variable_initializer", +//t "event_accessor_declarations : add_accessor_declaration remove_accessor_declaration", +//t "event_accessor_declarations : remove_accessor_declaration add_accessor_declaration", +//t "event_accessor_declarations : add_accessor_declaration", +//t "event_accessor_declarations : remove_accessor_declaration", +//t "event_accessor_declarations : error", +//t "$$58 :", +//t "add_accessor_declaration : opt_attributes opt_modifiers ADD $$58 event_accessor_block", +//t "$$59 :", +//t "remove_accessor_declaration : opt_attributes opt_modifiers REMOVE $$59 event_accessor_block", +//t "event_accessor_block : opt_semicolon", +//t "event_accessor_block : block", +//t "attributes_without_members : attribute_sections CLOSE_BRACE", +//t "incomplete_member : opt_attributes opt_modifiers member_type CLOSE_BRACE", +//t "$$60 :", +//t "$$61 :", +//t "$$62 :", +//t "enum_declaration : opt_attributes opt_modifiers ENUM type_declaration_name opt_enum_base $$60 OPEN_BRACE $$61 opt_enum_member_declarations $$62 CLOSE_BRACE opt_semicolon", +//t "opt_enum_base :", +//t "opt_enum_base : COLON type", +//t "opt_enum_base : COLON error", +//t "opt_enum_member_declarations :", +//t "opt_enum_member_declarations : enum_member_declarations", +//t "opt_enum_member_declarations : enum_member_declarations COMMA", +//t "enum_member_declarations : enum_member_declaration", +//t "enum_member_declarations : enum_member_declarations COMMA enum_member_declaration", +//t "enum_member_declaration : opt_attributes IDENTIFIER", +//t "$$63 :", +//t "enum_member_declaration : opt_attributes IDENTIFIER $$63 ASSIGN constant_expression", +//t "enum_member_declaration : opt_attributes IDENTIFIER error", +//t "enum_member_declaration : attributes_without_members", +//t "$$64 :", +//t "$$65 :", +//t "$$66 :", +//t "delegate_declaration : opt_attributes opt_modifiers DELEGATE member_type type_declaration_name OPEN_PARENS $$64 opt_formal_parameter_list CLOSE_PARENS $$65 opt_type_parameter_constraints_clauses $$66 SEMICOLON", +//t "opt_nullable :", +//t "opt_nullable : INTERR_NULLABLE", +//t "namespace_or_type_expr : member_name", +//t "namespace_or_type_expr : qualified_alias_member IDENTIFIER opt_type_argument_list", +//t "namespace_or_type_expr : qualified_alias_member IDENTIFIER generic_dimension", +//t "member_name : simple_name_expr", +//t "member_name : namespace_or_type_expr DOT IDENTIFIER opt_type_argument_list", +//t "member_name : namespace_or_type_expr DOT IDENTIFIER generic_dimension", +//t "simple_name_expr : IDENTIFIER opt_type_argument_list", +//t "simple_name_expr : IDENTIFIER generic_dimension", +//t "opt_type_argument_list :", +//t "opt_type_argument_list : OP_GENERICS_LT type_arguments OP_GENERICS_GT", +//t "opt_type_argument_list : OP_GENERICS_LT error", +//t "type_arguments : type", +//t "type_arguments : type_arguments COMMA type", +//t "$$67 :", +//t "type_declaration_name : IDENTIFIER $$67 opt_type_parameter_list", +//t "member_declaration_name : method_declaration_name", +//t "method_declaration_name : type_declaration_name", +//t "method_declaration_name : explicit_interface IDENTIFIER opt_type_parameter_list", +//t "indexer_declaration_name : THIS", +//t "indexer_declaration_name : explicit_interface THIS", +//t "explicit_interface : IDENTIFIER opt_type_argument_list DOT", +//t "explicit_interface : qualified_alias_member IDENTIFIER opt_type_argument_list DOT", +//t "explicit_interface : explicit_interface IDENTIFIER opt_type_argument_list DOT", +//t "opt_type_parameter_list :", +//t "opt_type_parameter_list : OP_GENERICS_LT_DECL type_parameters OP_GENERICS_GT", +//t "type_parameters : type_parameter", +//t "type_parameters : type_parameters COMMA type_parameter", +//t "type_parameter : opt_attributes opt_type_parameter_variance IDENTIFIER", +//t "type_parameter : error", +//t "type_and_void : type_expression_or_array", +//t "type_and_void : VOID", +//t "member_type : type_and_void", +//t "type : type_expression_or_array", +//t "type : void_invalid", +//t "simple_type : type_expression", +//t "simple_type : void_invalid", +//t "parameter_type : type_expression_or_array", +//t "parameter_type : VOID", +//t "type_expression_or_array : type_expression", +//t "type_expression_or_array : type_expression rank_specifiers", +//t "type_expression : namespace_or_type_expr opt_nullable", +//t "type_expression : namespace_or_type_expr pointer_stars", +//t "type_expression : builtin_type_expression", +//t "void_invalid : VOID", +//t "builtin_type_expression : builtin_types opt_nullable", +//t "builtin_type_expression : builtin_types pointer_stars", +//t "builtin_type_expression : VOID pointer_stars", +//t "type_list : base_type_name", +//t "type_list : type_list COMMA base_type_name", +//t "base_type_name : type", +//t "builtin_types : OBJECT", +//t "builtin_types : STRING", +//t "builtin_types : BOOL", +//t "builtin_types : DECIMAL", +//t "builtin_types : FLOAT", +//t "builtin_types : DOUBLE", +//t "builtin_types : integral_type", +//t "integral_type : SBYTE", +//t "integral_type : BYTE", +//t "integral_type : SHORT", +//t "integral_type : USHORT", +//t "integral_type : INT", +//t "integral_type : UINT", +//t "integral_type : LONG", +//t "integral_type : ULONG", +//t "integral_type : CHAR", +//t "primary_expression : primary_expression_or_type", +//t "primary_expression : literal", +//t "primary_expression : array_creation_expression", +//t "primary_expression : parenthesized_expression", +//t "primary_expression : default_value_expression", +//t "primary_expression : invocation_expression", +//t "primary_expression : element_access", +//t "primary_expression : this_access", +//t "primary_expression : base_access", +//t "primary_expression : post_increment_expression", +//t "primary_expression : post_decrement_expression", +//t "primary_expression : object_or_delegate_creation_expression", +//t "primary_expression : anonymous_type_expression", +//t "primary_expression : typeof_expression", +//t "primary_expression : sizeof_expression", +//t "primary_expression : checked_expression", +//t "primary_expression : unchecked_expression", +//t "primary_expression : pointer_member_access", +//t "primary_expression : anonymous_method_expression", +//t "primary_expression : undocumented_expressions", +//t "primary_expression_or_type : simple_name_expr", +//t "primary_expression_or_type : IDENTIFIER GENERATE_COMPLETION", +//t "primary_expression_or_type : member_access", +//t "literal : boolean_literal", +//t "literal : LITERAL", +//t "literal : NULL", +//t "boolean_literal : TRUE", +//t "boolean_literal : FALSE", +//t "open_parens_any : OPEN_PARENS", +//t "open_parens_any : OPEN_PARENS_CAST", +//t "close_parens : CLOSE_PARENS", +//t "close_parens : COMPLETE_COMPLETION", +//t "parenthesized_expression : OPEN_PARENS expression CLOSE_PARENS", +//t "parenthesized_expression : OPEN_PARENS expression COMPLETE_COMPLETION", +//t "member_access : primary_expression DOT identifier_inside_body opt_type_argument_list", +//t "member_access : primary_expression DOT identifier_inside_body generic_dimension", +//t "member_access : primary_expression INTERR_OPERATOR DOT identifier_inside_body opt_type_argument_list", +//t "member_access : builtin_types DOT identifier_inside_body opt_type_argument_list", +//t "member_access : BASE DOT identifier_inside_body opt_type_argument_list", +//t "member_access : AWAIT DOT identifier_inside_body opt_type_argument_list", +//t "member_access : qualified_alias_member identifier_inside_body opt_type_argument_list", +//t "member_access : qualified_alias_member identifier_inside_body generic_dimension", +//t "member_access : primary_expression DOT GENERATE_COMPLETION", +//t "member_access : primary_expression DOT IDENTIFIER GENERATE_COMPLETION", +//t "member_access : builtin_types DOT GENERATE_COMPLETION", +//t "member_access : builtin_types DOT IDENTIFIER GENERATE_COMPLETION", +//t "invocation_expression : primary_expression open_parens_any opt_argument_list close_parens", +//t "invocation_expression : primary_expression open_parens_any argument_list error", +//t "invocation_expression : primary_expression open_parens_any error", +//t "opt_object_or_collection_initializer :", +//t "opt_object_or_collection_initializer : object_or_collection_initializer", +//t "object_or_collection_initializer : OPEN_BRACE opt_member_initializer_list close_brace_or_complete_completion", +//t "object_or_collection_initializer : OPEN_BRACE member_initializer_list COMMA CLOSE_BRACE", +//t "opt_member_initializer_list :", +//t "opt_member_initializer_list : member_initializer_list", +//t "member_initializer_list : member_initializer", +//t "member_initializer_list : member_initializer_list COMMA member_initializer", +//t "member_initializer_list : member_initializer_list error", +//t "member_initializer : IDENTIFIER ASSIGN initializer_value", +//t "member_initializer : AWAIT ASSIGN initializer_value", +//t "member_initializer : GENERATE_COMPLETION", +//t "member_initializer : non_assignment_expression opt_COMPLETE_COMPLETION", +//t "member_initializer : OPEN_BRACE expression_list CLOSE_BRACE", +//t "member_initializer : OPEN_BRACKET_EXPR expression_list CLOSE_BRACKET ASSIGN initializer_value", +//t "member_initializer : OPEN_BRACE CLOSE_BRACE", +//t "initializer_value : expression", +//t "initializer_value : object_or_collection_initializer", +//t "opt_argument_list :", +//t "opt_argument_list : argument_list", +//t "argument_list : argument_or_named_argument", +//t "argument_list : argument_list COMMA argument", +//t "argument_list : argument_list COMMA named_argument", +//t "argument_list : argument_list COMMA error", +//t "argument_list : COMMA error", +//t "argument : expression", +//t "argument : non_simple_argument", +//t "argument_or_named_argument : argument", +//t "argument_or_named_argument : named_argument", +//t "non_simple_argument : REF variable_reference", +//t "non_simple_argument : OUT variable_reference", +//t "non_simple_argument : ARGLIST OPEN_PARENS argument_list CLOSE_PARENS", +//t "non_simple_argument : ARGLIST OPEN_PARENS CLOSE_PARENS", +//t "variable_reference : expression", +//t "element_access : primary_expression OPEN_BRACKET_EXPR expression_list_arguments CLOSE_BRACKET", +//t "element_access : primary_expression INTERR_OPERATOR OPEN_BRACKET_EXPR expression_list_arguments CLOSE_BRACKET", +//t "element_access : primary_expression OPEN_BRACKET_EXPR expression_list_arguments error", +//t "element_access : primary_expression OPEN_BRACKET_EXPR error", +//t "expression_list : expression_or_error", +//t "expression_list : expression_list COMMA expression_or_error", +//t "expression_list_arguments : expression_list_argument", +//t "expression_list_arguments : expression_list_arguments COMMA expression_list_argument", +//t "expression_list_argument : expression", +//t "expression_list_argument : named_argument", +//t "this_access : THIS", +//t "base_access : BASE OPEN_BRACKET_EXPR expression_list_arguments CLOSE_BRACKET", +//t "base_access : BASE OPEN_BRACKET error", +//t "post_increment_expression : primary_expression OP_INC", +//t "post_decrement_expression : primary_expression OP_DEC", +//t "object_or_delegate_creation_expression : NEW new_expr_type open_parens_any opt_argument_list CLOSE_PARENS opt_object_or_collection_initializer", +//t "object_or_delegate_creation_expression : NEW new_expr_type object_or_collection_initializer", +//t "array_creation_expression : NEW new_expr_type OPEN_BRACKET_EXPR expression_list CLOSE_BRACKET opt_rank_specifier opt_array_initializer", +//t "array_creation_expression : NEW new_expr_type rank_specifiers opt_array_initializer", +//t "array_creation_expression : NEW rank_specifier array_initializer", +//t "array_creation_expression : NEW new_expr_type OPEN_BRACKET CLOSE_BRACKET OPEN_BRACKET_EXPR error CLOSE_BRACKET", +//t "array_creation_expression : NEW new_expr_type error", +//t "$$68 :", +//t "new_expr_type : $$68 simple_type", +//t "anonymous_type_expression : NEW OPEN_BRACE anonymous_type_parameters_opt_comma CLOSE_BRACE", +//t "anonymous_type_expression : NEW OPEN_BRACE GENERATE_COMPLETION", +//t "anonymous_type_parameters_opt_comma : anonymous_type_parameters_opt", +//t "anonymous_type_parameters_opt_comma : anonymous_type_parameters COMMA", +//t "anonymous_type_parameters_opt :", +//t "anonymous_type_parameters_opt : anonymous_type_parameters", +//t "anonymous_type_parameters : anonymous_type_parameter", +//t "anonymous_type_parameters : anonymous_type_parameters COMMA anonymous_type_parameter", +//t "anonymous_type_parameters : COMPLETE_COMPLETION", +//t "anonymous_type_parameters : anonymous_type_parameter COMPLETE_COMPLETION", +//t "anonymous_type_parameter : identifier_inside_body ASSIGN variable_initializer", +//t "anonymous_type_parameter : identifier_inside_body", +//t "anonymous_type_parameter : member_access", +//t "anonymous_type_parameter : error", +//t "opt_rank_specifier :", +//t "opt_rank_specifier : rank_specifiers", +//t "rank_specifiers : rank_specifier", +//t "rank_specifiers : rank_specifier rank_specifiers", +//t "rank_specifier : OPEN_BRACKET CLOSE_BRACKET", +//t "rank_specifier : OPEN_BRACKET dim_separators CLOSE_BRACKET", +//t "dim_separators : COMMA", +//t "dim_separators : dim_separators COMMA", +//t "opt_array_initializer :", +//t "opt_array_initializer : array_initializer", +//t "array_initializer : OPEN_BRACE CLOSE_BRACE", +//t "array_initializer : OPEN_BRACE variable_initializer_list opt_comma CLOSE_BRACE", +//t "variable_initializer_list : variable_initializer", +//t "variable_initializer_list : variable_initializer_list COMMA variable_initializer", +//t "typeof_expression : TYPEOF open_parens_any typeof_type_expression CLOSE_PARENS", +//t "typeof_type_expression : type_and_void", +//t "typeof_type_expression : error", +//t "generic_dimension : GENERIC_DIMENSION", +//t "qualified_alias_member : IDENTIFIER DOUBLE_COLON", +//t "sizeof_expression : SIZEOF open_parens_any type CLOSE_PARENS", +//t "sizeof_expression : SIZEOF open_parens_any type error", +//t "checked_expression : CHECKED open_parens_any expression CLOSE_PARENS", +//t "checked_expression : CHECKED error", +//t "unchecked_expression : UNCHECKED open_parens_any expression CLOSE_PARENS", +//t "unchecked_expression : UNCHECKED error", +//t "pointer_member_access : primary_expression OP_PTR IDENTIFIER opt_type_argument_list", +//t "$$69 :", +//t "anonymous_method_expression : DELEGATE opt_anonymous_method_signature $$69 block", +//t "$$70 :", +//t "anonymous_method_expression : ASYNC DELEGATE opt_anonymous_method_signature $$70 block", +//t "opt_anonymous_method_signature :", +//t "opt_anonymous_method_signature : anonymous_method_signature", +//t "$$71 :", +//t "anonymous_method_signature : OPEN_PARENS $$71 opt_formal_parameter_list CLOSE_PARENS", +//t "default_value_expression : DEFAULT open_parens_any type CLOSE_PARENS", +//t "unary_expression : primary_expression", +//t "unary_expression : BANG prefixed_unary_expression", +//t "unary_expression : TILDE prefixed_unary_expression", +//t "unary_expression : OPEN_PARENS_CAST type CLOSE_PARENS prefixed_unary_expression", +//t "unary_expression : AWAIT prefixed_unary_expression", +//t "unary_expression : BANG error", +//t "unary_expression : TILDE error", +//t "unary_expression : OPEN_PARENS_CAST type CLOSE_PARENS error", +//t "unary_expression : AWAIT error", +//t "prefixed_unary_expression : unary_expression", +//t "prefixed_unary_expression : PLUS prefixed_unary_expression", +//t "prefixed_unary_expression : MINUS prefixed_unary_expression", +//t "prefixed_unary_expression : OP_INC prefixed_unary_expression", +//t "prefixed_unary_expression : OP_DEC prefixed_unary_expression", +//t "prefixed_unary_expression : STAR prefixed_unary_expression", +//t "prefixed_unary_expression : BITWISE_AND prefixed_unary_expression", +//t "prefixed_unary_expression : PLUS error", +//t "prefixed_unary_expression : MINUS error", +//t "prefixed_unary_expression : OP_INC error", +//t "prefixed_unary_expression : OP_DEC error", +//t "prefixed_unary_expression : STAR error", +//t "prefixed_unary_expression : BITWISE_AND error", +//t "multiplicative_expression : prefixed_unary_expression", +//t "multiplicative_expression : multiplicative_expression STAR prefixed_unary_expression", +//t "multiplicative_expression : multiplicative_expression DIV prefixed_unary_expression", +//t "multiplicative_expression : multiplicative_expression PERCENT prefixed_unary_expression", +//t "multiplicative_expression : multiplicative_expression STAR error", +//t "multiplicative_expression : multiplicative_expression DIV error", +//t "multiplicative_expression : multiplicative_expression PERCENT error", +//t "additive_expression : multiplicative_expression", +//t "additive_expression : additive_expression PLUS multiplicative_expression", +//t "additive_expression : additive_expression MINUS multiplicative_expression", +//t "additive_expression : additive_expression PLUS error", +//t "additive_expression : additive_expression MINUS error", +//t "additive_expression : additive_expression AS type", +//t "additive_expression : additive_expression IS is_match_expr opt_identifier", +//t "additive_expression : additive_expression AS error", +//t "additive_expression : additive_expression IS error", +//t "additive_expression : AWAIT IS type", +//t "additive_expression : AWAIT AS type", +//t "is_match_expr : match_type", +//t "is_match_expr : match_type rank_specifiers", +//t "is_match_expr : literal", +//t "is_match_expr : PLUS prefixed_unary_expression", +//t "is_match_expr : MINUS prefixed_unary_expression", +//t "match_type : primary_expression_or_type opt_nullable", +//t "match_type : primary_expression_or_type pointer_stars", +//t "match_type : builtin_type_expression", +//t "match_type : void_invalid", +//t "shift_expression : additive_expression", +//t "shift_expression : shift_expression OP_SHIFT_LEFT additive_expression", +//t "shift_expression : shift_expression OP_SHIFT_RIGHT additive_expression", +//t "shift_expression : shift_expression OP_SHIFT_LEFT error", +//t "shift_expression : shift_expression OP_SHIFT_RIGHT error", +//t "relational_expression : shift_expression", +//t "relational_expression : relational_expression OP_LT shift_expression", +//t "relational_expression : relational_expression OP_GT shift_expression", +//t "relational_expression : relational_expression OP_LE shift_expression", +//t "relational_expression : relational_expression OP_GE shift_expression", +//t "relational_expression : relational_expression OP_LT error", +//t "relational_expression : relational_expression OP_GT error", +//t "relational_expression : relational_expression OP_LE error", +//t "relational_expression : relational_expression OP_GE error", +//t "equality_expression : relational_expression", +//t "equality_expression : equality_expression OP_EQ relational_expression", +//t "equality_expression : equality_expression OP_NE relational_expression", +//t "equality_expression : equality_expression OP_EQ error", +//t "equality_expression : equality_expression OP_NE error", +//t "and_expression : equality_expression", +//t "and_expression : and_expression BITWISE_AND equality_expression", +//t "and_expression : and_expression BITWISE_AND error", +//t "exclusive_or_expression : and_expression", +//t "exclusive_or_expression : exclusive_or_expression CARRET and_expression", +//t "exclusive_or_expression : exclusive_or_expression CARRET error", +//t "inclusive_or_expression : exclusive_or_expression", +//t "inclusive_or_expression : inclusive_or_expression BITWISE_OR exclusive_or_expression", +//t "inclusive_or_expression : inclusive_or_expression BITWISE_OR error", +//t "conditional_and_expression : inclusive_or_expression", +//t "conditional_and_expression : conditional_and_expression OP_AND inclusive_or_expression", +//t "conditional_and_expression : conditional_and_expression OP_AND error", +//t "conditional_or_expression : conditional_and_expression", +//t "conditional_or_expression : conditional_or_expression OP_OR conditional_and_expression", +//t "conditional_or_expression : conditional_or_expression OP_OR error", +//t "null_coalescing_expression : conditional_or_expression", +//t "null_coalescing_expression : conditional_or_expression OP_COALESCING null_coalescing_expression", +//t "conditional_expression : null_coalescing_expression", +//t "conditional_expression : null_coalescing_expression INTERR expression COLON expression", +//t "conditional_expression : null_coalescing_expression INTERR expression error", +//t "conditional_expression : null_coalescing_expression INTERR expression COLON error", +//t "conditional_expression : null_coalescing_expression INTERR expression COLON CLOSE_BRACE", +//t "assignment_expression : prefixed_unary_expression ASSIGN expression", +//t "assignment_expression : prefixed_unary_expression OP_MULT_ASSIGN expression", +//t "assignment_expression : prefixed_unary_expression OP_DIV_ASSIGN expression", +//t "assignment_expression : prefixed_unary_expression OP_MOD_ASSIGN expression", +//t "assignment_expression : prefixed_unary_expression OP_ADD_ASSIGN expression", +//t "assignment_expression : prefixed_unary_expression OP_SUB_ASSIGN expression", +//t "assignment_expression : prefixed_unary_expression OP_SHIFT_LEFT_ASSIGN expression", +//t "assignment_expression : prefixed_unary_expression OP_SHIFT_RIGHT_ASSIGN expression", +//t "assignment_expression : prefixed_unary_expression OP_AND_ASSIGN expression", +//t "assignment_expression : prefixed_unary_expression OP_OR_ASSIGN expression", +//t "assignment_expression : prefixed_unary_expression OP_XOR_ASSIGN expression", +//t "lambda_parameter_list : lambda_parameter", +//t "lambda_parameter_list : lambda_parameter_list COMMA lambda_parameter", +//t "lambda_parameter : parameter_modifier parameter_type identifier_inside_body", +//t "lambda_parameter : parameter_type identifier_inside_body", +//t "lambda_parameter : IDENTIFIER", +//t "lambda_parameter : AWAIT", +//t "opt_lambda_parameter_list :", +//t "opt_lambda_parameter_list : lambda_parameter_list", +//t "$$72 :", +//t "lambda_expression_body : $$72 expression", +//t "lambda_expression_body : block", +//t "lambda_expression_body : error", +//t "expression_or_error : expression", +//t "expression_or_error : error", +//t "$$73 :", +//t "lambda_expression : IDENTIFIER ARROW $$73 lambda_expression_body", +//t "$$74 :", +//t "lambda_expression : AWAIT ARROW $$74 lambda_expression_body", +//t "$$75 :", +//t "lambda_expression : ASYNC identifier_inside_body ARROW $$75 lambda_expression_body", +//t "$$76 :", +//t "$$77 :", +//t "lambda_expression : OPEN_PARENS_LAMBDA $$76 opt_lambda_parameter_list CLOSE_PARENS ARROW $$77 lambda_expression_body", +//t "$$78 :", +//t "$$79 :", +//t "lambda_expression : ASYNC OPEN_PARENS_LAMBDA $$78 opt_lambda_parameter_list CLOSE_PARENS ARROW $$79 lambda_expression_body", +//t "expression : assignment_expression", +//t "expression : non_assignment_expression", +//t "non_assignment_expression : conditional_expression", +//t "non_assignment_expression : lambda_expression", +//t "non_assignment_expression : query_expression", +//t "non_assignment_expression : ARGLIST", +//t "undocumented_expressions : REFVALUE OPEN_PARENS non_assignment_expression COMMA type CLOSE_PARENS", +//t "undocumented_expressions : REFTYPE open_parens_any expression CLOSE_PARENS", +//t "undocumented_expressions : MAKEREF open_parens_any expression CLOSE_PARENS", +//t "constant_expression : expression", +//t "boolean_expression : expression", +//t "opt_primary_parameters :", +//t "opt_primary_parameters : primary_parameters", +//t "primary_parameters : OPEN_PARENS opt_formal_parameter_list CLOSE_PARENS", +//t "opt_primary_parameters_with_class_base :", +//t "opt_primary_parameters_with_class_base : class_base", +//t "opt_primary_parameters_with_class_base : primary_parameters", +//t "opt_primary_parameters_with_class_base : primary_parameters class_base", +//t "$$80 :", +//t "opt_primary_parameters_with_class_base : primary_parameters class_base OPEN_PARENS $$80 opt_argument_list CLOSE_PARENS", +//t "$$81 :", +//t "$$82 :", +//t "$$83 :", +//t "$$84 :", +//t "class_declaration : opt_attributes opt_modifiers opt_partial CLASS $$81 type_declaration_name $$82 opt_primary_parameters_with_class_base opt_type_parameter_constraints_clauses $$83 OPEN_BRACE opt_class_member_declarations CLOSE_BRACE $$84 opt_semicolon", +//t "opt_partial :", +//t "opt_partial : PARTIAL", +//t "opt_modifiers :", +//t "opt_modifiers : modifiers", +//t "modifiers : modifier", +//t "modifiers : modifiers modifier", +//t "modifier : NEW", +//t "modifier : PUBLIC", +//t "modifier : PROTECTED", +//t "modifier : INTERNAL", +//t "modifier : PRIVATE", +//t "modifier : ABSTRACT", +//t "modifier : SEALED", +//t "modifier : STATIC", +//t "modifier : READONLY", +//t "modifier : VIRTUAL", +//t "modifier : OVERRIDE", +//t "modifier : EXTERN", +//t "modifier : VOLATILE", +//t "modifier : UNSAFE", +//t "modifier : ASYNC", +//t "opt_class_base :", +//t "opt_class_base : class_base", +//t "class_base : COLON type_list", +//t "class_base : COLON type_list error", +//t "opt_type_parameter_constraints_clauses :", +//t "opt_type_parameter_constraints_clauses : type_parameter_constraints_clauses", +//t "type_parameter_constraints_clauses : type_parameter_constraints_clause", +//t "type_parameter_constraints_clauses : type_parameter_constraints_clauses type_parameter_constraints_clause", +//t "type_parameter_constraints_clause : WHERE IDENTIFIER COLON type_parameter_constraints", +//t "type_parameter_constraints_clause : WHERE IDENTIFIER error", +//t "type_parameter_constraints : type_parameter_constraint", +//t "type_parameter_constraints : type_parameter_constraints COMMA type_parameter_constraint", +//t "type_parameter_constraint : type", +//t "type_parameter_constraint : NEW OPEN_PARENS CLOSE_PARENS", +//t "type_parameter_constraint : CLASS", +//t "type_parameter_constraint : STRUCT", +//t "opt_type_parameter_variance :", +//t "opt_type_parameter_variance : type_parameter_variance", +//t "type_parameter_variance : OUT", +//t "type_parameter_variance : IN", +//t "$$85 :", +//t "block : OPEN_BRACE $$85 opt_statement_list block_end", +//t "block_end : CLOSE_BRACE", +//t "block_end : COMPLETE_COMPLETION", +//t "$$86 :", +//t "block_prepared : OPEN_BRACE $$86 opt_statement_list CLOSE_BRACE", +//t "block_prepared : CLOSE_BRACE", +//t "$$87 :", +//t "block_prepared_strict : OPEN_BRACE $$87 opt_statement_list CLOSE_BRACE", +//t "opt_statement_list :", +//t "opt_statement_list : statement_list", +//t "statement_list : statement", +//t "statement_list : statement_list statement", +//t "statement : block_variable_declaration", +//t "statement : valid_declaration_statement", +//t "statement : labeled_statement", +//t "statement : IDENTIFIER error", +//t "statement : error", +//t "interactive_statement_list : interactive_statement", +//t "interactive_statement_list : interactive_statement_list interactive_statement", +//t "interactive_statement : block_variable_declaration", +//t "interactive_statement : interactive_valid_declaration_statement", +//t "interactive_statement : labeled_statement", +//t "valid_declaration_statement : block", +//t "valid_declaration_statement : empty_statement", +//t "valid_declaration_statement : expression_statement", +//t "valid_declaration_statement : selection_statement", +//t "valid_declaration_statement : iteration_statement", +//t "valid_declaration_statement : jump_statement", +//t "valid_declaration_statement : try_statement", +//t "valid_declaration_statement : checked_statement", +//t "valid_declaration_statement : unchecked_statement", +//t "valid_declaration_statement : lock_statement", +//t "valid_declaration_statement : using_statement", +//t "valid_declaration_statement : unsafe_statement", +//t "valid_declaration_statement : fixed_statement", +//t "interactive_valid_declaration_statement : block", +//t "interactive_valid_declaration_statement : empty_statement", +//t "interactive_valid_declaration_statement : interactive_expression_statement", +//t "interactive_valid_declaration_statement : selection_statement", +//t "interactive_valid_declaration_statement : iteration_statement", +//t "interactive_valid_declaration_statement : jump_statement", +//t "interactive_valid_declaration_statement : try_statement", +//t "interactive_valid_declaration_statement : checked_statement", +//t "interactive_valid_declaration_statement : unchecked_statement", +//t "interactive_valid_declaration_statement : lock_statement", +//t "interactive_valid_declaration_statement : using_statement", +//t "interactive_valid_declaration_statement : unsafe_statement", +//t "interactive_valid_declaration_statement : fixed_statement", +//t "embedded_statement : valid_declaration_statement", +//t "embedded_statement : block_variable_declaration", +//t "embedded_statement : labeled_statement", +//t "embedded_statement : error", +//t "empty_statement : SEMICOLON", +//t "$$88 :", +//t "labeled_statement : identifier_inside_body COLON $$88 statement", +//t "variable_type : variable_type_simple", +//t "variable_type : variable_type_simple rank_specifiers", +//t "variable_type_simple : primary_expression_or_type opt_nullable", +//t "variable_type_simple : primary_expression_or_type pointer_stars", +//t "variable_type_simple : builtin_type_expression", +//t "variable_type_simple : void_invalid", +//t "pointer_stars : pointer_star", +//t "pointer_stars : pointer_star pointer_stars", +//t "pointer_star : STAR", +//t "identifier_inside_body : IDENTIFIER", +//t "identifier_inside_body : AWAIT", +//t "$$89 :", +//t "block_variable_declaration : variable_type identifier_inside_body $$89 opt_local_variable_initializer opt_variable_declarators semicolon_or_handle_error_close_brace", +//t "$$90 :", +//t "block_variable_declaration : CONST variable_type identifier_inside_body $$90 const_variable_initializer opt_const_declarators SEMICOLON", +//t "semicolon_or_handle_error_close_brace : SEMICOLON", +//t "semicolon_or_handle_error_close_brace : CLOSE_BRACE", +//t "opt_local_variable_initializer :", +//t "opt_local_variable_initializer : ASSIGN block_variable_initializer", +//t "opt_local_variable_initializer : error", +//t "opt_variable_declarators :", +//t "opt_variable_declarators : variable_declarators", +//t "opt_using_or_fixed_variable_declarators :", +//t "opt_using_or_fixed_variable_declarators : variable_declarators", +//t "variable_declarators : variable_declarator", +//t "variable_declarators : variable_declarators variable_declarator", +//t "variable_declarator : COMMA identifier_inside_body", +//t "variable_declarator : COMMA identifier_inside_body ASSIGN block_variable_initializer", +//t "const_variable_initializer :", +//t "const_variable_initializer : ASSIGN constant_initializer_expr", +//t "opt_const_declarators :", +//t "opt_const_declarators : const_declarators", +//t "const_declarators : const_declarator", +//t "const_declarators : const_declarators const_declarator", +//t "const_declarator : COMMA identifier_inside_body ASSIGN constant_initializer_expr", +//t "block_variable_initializer : variable_initializer", +//t "block_variable_initializer : STACKALLOC simple_type OPEN_BRACKET_EXPR expression CLOSE_BRACKET", +//t "block_variable_initializer : STACKALLOC simple_type", +//t "expression_statement : statement_expression SEMICOLON", +//t "expression_statement : statement_expression COMPLETE_COMPLETION", +//t "expression_statement : statement_expression CLOSE_BRACE", +//t "interactive_expression_statement : interactive_statement_expression SEMICOLON", +//t "interactive_expression_statement : interactive_statement_expression COMPLETE_COMPLETION", +//t "statement_expression : expression", +//t "interactive_statement_expression : expression", +//t "interactive_statement_expression : error", +//t "selection_statement : if_statement", +//t "selection_statement : switch_statement", +//t "if_statement : IF open_parens_any boolean_expression CLOSE_PARENS embedded_statement", +//t "if_statement : IF open_parens_any boolean_expression CLOSE_PARENS embedded_statement ELSE embedded_statement", +//t "if_statement : IF open_parens_any boolean_expression error", +//t "$$91 :", +//t "switch_statement : SWITCH open_parens_any expression CLOSE_PARENS OPEN_BRACE $$91 opt_switch_sections CLOSE_BRACE", +//t "switch_statement : SWITCH open_parens_any expression error", +//t "opt_switch_sections :", +//t "opt_switch_sections : switch_sections", +//t "switch_sections : switch_section", +//t "switch_sections : switch_sections switch_section", +//t "switch_sections : error", +//t "switch_section : switch_labels statement_list", +//t "switch_labels : switch_label", +//t "switch_labels : switch_labels switch_label", +//t "switch_label : CASE constant_expression COLON", +//t "switch_label : CASE constant_expression error", +//t "switch_label : DEFAULT_COLON", +//t "iteration_statement : while_statement", +//t "iteration_statement : do_statement", +//t "iteration_statement : for_statement", +//t "iteration_statement : foreach_statement", +//t "while_statement : WHILE open_parens_any boolean_expression CLOSE_PARENS embedded_statement", +//t "while_statement : WHILE open_parens_any boolean_expression error", +//t "do_statement : DO embedded_statement WHILE open_parens_any boolean_expression CLOSE_PARENS SEMICOLON", +//t "do_statement : DO embedded_statement error", +//t "do_statement : DO embedded_statement WHILE open_parens_any boolean_expression error", +//t "$$92 :", +//t "for_statement : FOR open_parens_any $$92 for_statement_cont", +//t "$$93 :", +//t "for_statement_cont : opt_for_initializer SEMICOLON $$93 for_statement_condition", +//t "for_statement_cont : opt_for_initializer CLOSE_PARENS", +//t "$$94 :", +//t "for_statement_condition : opt_for_condition SEMICOLON $$94 for_statement_end", +//t "for_statement_condition : boolean_expression CLOSE_PARENS", +//t "for_statement_end : opt_for_iterator CLOSE_PARENS embedded_statement", +//t "for_statement_end : error", +//t "opt_for_initializer :", +//t "opt_for_initializer : for_initializer", +//t "$$95 :", +//t "for_initializer : variable_type identifier_inside_body $$95 opt_local_variable_initializer opt_variable_declarators", +//t "for_initializer : statement_expression_list", +//t "opt_for_condition :", +//t "opt_for_condition : boolean_expression", +//t "opt_for_iterator :", +//t "opt_for_iterator : for_iterator", +//t "for_iterator : statement_expression_list", +//t "statement_expression_list : statement_expression", +//t "statement_expression_list : statement_expression_list COMMA statement_expression", +//t "foreach_statement : FOREACH open_parens_any type error", +//t "foreach_statement : FOREACH open_parens_any type identifier_inside_body error", +//t "$$96 :", +//t "foreach_statement : FOREACH open_parens_any type identifier_inside_body IN expression CLOSE_PARENS $$96 embedded_statement", +//t "foreach_statement : FOREACH open_parens_any type identifier_inside_body error", +//t "foreach_statement : FOREACH open_parens_any type error", +//t "jump_statement : break_statement", +//t "jump_statement : continue_statement", +//t "jump_statement : goto_statement", +//t "jump_statement : return_statement", +//t "jump_statement : throw_statement", +//t "jump_statement : yield_statement", +//t "break_statement : BREAK SEMICOLON", +//t "continue_statement : CONTINUE SEMICOLON", +//t "continue_statement : CONTINUE error", +//t "goto_statement : GOTO identifier_inside_body SEMICOLON", +//t "goto_statement : GOTO CASE constant_expression SEMICOLON", +//t "goto_statement : GOTO DEFAULT SEMICOLON", +//t "return_statement : RETURN opt_expression SEMICOLON", +//t "return_statement : RETURN expression error", +//t "return_statement : RETURN error", +//t "throw_statement : THROW opt_expression SEMICOLON", +//t "throw_statement : THROW expression error", +//t "throw_statement : THROW error", +//t "yield_statement : identifier_inside_body RETURN opt_expression SEMICOLON", +//t "yield_statement : identifier_inside_body RETURN expression error", +//t "yield_statement : identifier_inside_body BREAK SEMICOLON", +//t "opt_expression :", +//t "opt_expression : expression", +//t "try_statement : TRY block catch_clauses", +//t "try_statement : TRY block FINALLY block", +//t "try_statement : TRY block catch_clauses FINALLY block", +//t "try_statement : TRY block error", +//t "catch_clauses : catch_clause", +//t "catch_clauses : catch_clauses catch_clause", +//t "opt_identifier :", +//t "opt_identifier : identifier_inside_body", +//t "catch_clause : CATCH opt_catch_filter block", +//t "$$97 :", +//t "catch_clause : CATCH open_parens_any type opt_identifier CLOSE_PARENS $$97 opt_catch_filter block_prepared", +//t "catch_clause : CATCH open_parens_any error", +//t "catch_clause : CATCH open_parens_any type opt_identifier CLOSE_PARENS error", +//t "opt_catch_filter :", +//t "opt_catch_filter : IF open_parens_any expression CLOSE_PARENS", +//t "checked_statement : CHECKED block", +//t "unchecked_statement : UNCHECKED block", +//t "$$98 :", +//t "unsafe_statement : UNSAFE $$98 block", +//t "lock_statement : LOCK open_parens_any expression CLOSE_PARENS embedded_statement", +//t "lock_statement : LOCK open_parens_any expression error", +//t "$$99 :", +//t "$$100 :", +//t "fixed_statement : FIXED open_parens_any variable_type identifier_inside_body $$99 using_or_fixed_variable_initializer opt_using_or_fixed_variable_declarators CLOSE_PARENS $$100 embedded_statement", +//t "$$101 :", +//t "$$102 :", +//t "using_statement : USING open_parens_any variable_type identifier_inside_body $$101 using_initialization CLOSE_PARENS $$102 embedded_statement", +//t "using_statement : USING open_parens_any expression CLOSE_PARENS embedded_statement", +//t "using_statement : USING open_parens_any expression error", +//t "using_initialization : using_or_fixed_variable_initializer opt_using_or_fixed_variable_declarators", +//t "using_initialization : error", +//t "using_or_fixed_variable_initializer :", +//t "using_or_fixed_variable_initializer : ASSIGN variable_initializer", +//t "query_expression : first_from_clause query_body", +//t "query_expression : nested_from_clause query_body", +//t "query_expression : first_from_clause COMPLETE_COMPLETION", +//t "query_expression : nested_from_clause COMPLETE_COMPLETION", +//t "first_from_clause : FROM_FIRST identifier_inside_body IN expression", +//t "first_from_clause : FROM_FIRST type identifier_inside_body IN expression", +//t "nested_from_clause : FROM identifier_inside_body IN expression", +//t "nested_from_clause : FROM type identifier_inside_body IN expression", +//t "$$103 :", +//t "from_clause : FROM identifier_inside_body IN $$103 expression_or_error", +//t "$$104 :", +//t "from_clause : FROM type identifier_inside_body IN $$104 expression_or_error", +//t "query_body : query_body_clauses select_or_group_clause opt_query_continuation", +//t "query_body : select_or_group_clause opt_query_continuation", +//t "query_body : query_body_clauses COMPLETE_COMPLETION", +//t "query_body : query_body_clauses error", +//t "query_body : error", +//t "$$105 :", +//t "select_or_group_clause : SELECT $$105 expression_or_error", +//t "$$106 :", +//t "$$107 :", +//t "select_or_group_clause : GROUP $$106 expression_or_error $$107 by_expression", +//t "by_expression : BY expression_or_error", +//t "by_expression : error", +//t "query_body_clauses : query_body_clause", +//t "query_body_clauses : query_body_clauses query_body_clause", +//t "query_body_clause : from_clause", +//t "query_body_clause : let_clause", +//t "query_body_clause : where_clause", +//t "query_body_clause : join_clause", +//t "query_body_clause : orderby_clause", +//t "$$108 :", +//t "let_clause : LET identifier_inside_body ASSIGN $$108 expression_or_error", +//t "$$109 :", +//t "where_clause : WHERE $$109 expression_or_error", +//t "$$110 :", +//t "$$111 :", +//t "$$112 :", +//t "join_clause : JOIN identifier_inside_body IN $$110 expression_or_error ON $$111 expression_or_error EQUALS $$112 expression_or_error opt_join_into", +//t "$$113 :", +//t "$$114 :", +//t "$$115 :", +//t "join_clause : JOIN type identifier_inside_body IN $$113 expression_or_error ON $$114 expression_or_error EQUALS $$115 expression_or_error opt_join_into", +//t "opt_join_into :", +//t "opt_join_into : INTO identifier_inside_body", +//t "$$116 :", +//t "orderby_clause : ORDERBY $$116 orderings", +//t "orderings : order_by", +//t "$$117 :", +//t "orderings : order_by COMMA $$117 orderings_then_by", +//t "orderings_then_by : then_by", +//t "$$118 :", +//t "orderings_then_by : orderings_then_by COMMA $$118 then_by", +//t "order_by : expression", +//t "order_by : expression ASCENDING", +//t "order_by : expression DESCENDING", +//t "then_by : expression", +//t "then_by : expression ASCENDING", +//t "then_by : expression DESCENDING", +//t "opt_query_continuation :", +//t "$$119 :", +//t "opt_query_continuation : INTO identifier_inside_body $$119 query_body", +//t "interactive_parsing : EVAL_STATEMENT_PARSER EOF", +//t "interactive_parsing : EVAL_USING_DECLARATIONS_UNIT_PARSER using_directives opt_COMPLETE_COMPLETION", +//t "$$120 :", +//t "interactive_parsing : EVAL_STATEMENT_PARSER $$120 interactive_statement_list opt_COMPLETE_COMPLETION", +//t "interactive_parsing : EVAL_COMPILATION_UNIT_PARSER interactive_compilation_unit", +//t "interactive_compilation_unit : opt_extern_alias_directives opt_using_directives", +//t "interactive_compilation_unit : opt_extern_alias_directives opt_using_directives namespace_or_type_declarations", +//t "opt_COMPLETE_COMPLETION :", +//t "opt_COMPLETE_COMPLETION : COMPLETE_COMPLETION", +//t "close_brace_or_complete_completion : CLOSE_BRACE", +//t "close_brace_or_complete_completion : COMPLETE_COMPLETION", +//t "documentation_parsing : DOC_SEE doc_cref", +//t "doc_cref : doc_type_declaration_name opt_doc_method_sig", +//t "doc_cref : builtin_types opt_doc_method_sig", +//t "doc_cref : VOID opt_doc_method_sig", +//t "doc_cref : builtin_types DOT IDENTIFIER opt_doc_method_sig", +//t "doc_cref : doc_type_declaration_name DOT THIS", +//t "$$121 :", +//t "doc_cref : doc_type_declaration_name DOT THIS OPEN_BRACKET $$121 opt_doc_parameters CLOSE_BRACKET", +//t "doc_cref : EXPLICIT OPERATOR type opt_doc_method_sig", +//t "doc_cref : IMPLICIT OPERATOR type opt_doc_method_sig", +//t "doc_cref : OPERATOR overloadable_operator opt_doc_method_sig", +//t "doc_type_declaration_name : type_declaration_name", +//t "doc_type_declaration_name : doc_type_declaration_name DOT type_declaration_name", +//t "opt_doc_method_sig :", +//t "$$122 :", +//t "opt_doc_method_sig : OPEN_PARENS $$122 opt_doc_parameters CLOSE_PARENS", +//t "opt_doc_parameters :", +//t "opt_doc_parameters : doc_parameters", +//t "doc_parameters : doc_parameter", +//t "doc_parameters : doc_parameters COMMA doc_parameter", +//t "doc_parameter : opt_parameter_modifier parameter_type", +//t }; +//t public static string getRule (int index) { +//t return yyRule [index]; +//t } +//t} + protected static readonly string [] yyNames = { + "end-of-file",null,null,null,null,null,null,null,null,null,null,null, + null,null,null,null,null,null,null,null,null,null,null,null,null,null, + null,null,null,null,null,null,null,null,null,null,null,null,null,null, + null,null,null,null,null,null,null,null,null,null,null,null,null,null, + null,null,null,null,null,null,null,null,null,null,null,null,null,null, + null,null,null,null,null,null,null,null,null,null,null,null,null,null, + null,null,null,null,null,null,null,null,null,null,null,null,null,null, + null,null,null,null,null,null,null,null,null,null,null,null,null,null, + null,null,null,null,null,null,null,null,null,null,null,null,null,null, + null,null,null,null,null,null,null,null,null,null,null,null,null,null, + null,null,null,null,null,null,null,null,null,null,null,null,null,null, + null,null,null,null,null,null,null,null,null,null,null,null,null,null, + null,null,null,null,null,null,null,null,null,null,null,null,null,null, + null,null,null,null,null,null,null,null,null,null,null,null,null,null, + null,null,null,null,null,null,null,null,null,null,null,null,null,null, + null,null,null,null,null,null,null,null,null,null,null,null,null,null, + null,null,null,null,null,null,null,null,null,null,null,null,null,null, + null,null,null,null,null,null,null,null,null,null,null,null,null,null, + null,null,null,null,null,null,null,"EOF","NONE","ERROR", + "FIRST_KEYWORD","ABSTRACT","AS","ADD","BASE","BOOL","BREAK","BYTE", + "CASE","CATCH","CHAR","CHECKED","CLASS","CONST","CONTINUE","DECIMAL", + "DEFAULT","DELEGATE","DO","DOUBLE","ELSE","ENUM","EVENT","EXPLICIT", + "EXTERN","FALSE","FINALLY","FIXED","FLOAT","FOR","FOREACH","GOTO", + "IF","IMPLICIT","IN","INT","INTERFACE","INTERNAL","IS","LOCK","LONG", + "NAMESPACE","NEW","NULL","OBJECT","OPERATOR","OUT","OVERRIDE", + "PARAMS","PRIVATE","PROTECTED","PUBLIC","READONLY","REF","RETURN", + "REMOVE","SBYTE","SEALED","SHORT","SIZEOF","STACKALLOC","STATIC", + "STRING","STRUCT","SWITCH","THIS","THROW","TRUE","TRY","TYPEOF", + "UINT","ULONG","UNCHECKED","UNSAFE","USHORT","USING","VIRTUAL","VOID", + "VOLATILE","WHERE","WHILE","ARGLIST","PARTIAL","ARROW","FROM", + "FROM_FIRST","JOIN","ON","EQUALS","SELECT","GROUP","BY","LET", + "ORDERBY","ASCENDING","DESCENDING","INTO","INTERR_NULLABLE", + "EXTERN_ALIAS","REFVALUE","REFTYPE","MAKEREF","ASYNC","AWAIT", + "INTERR_OPERATOR","GET","SET","LAST_KEYWORD","OPEN_BRACE", + "CLOSE_BRACE","OPEN_BRACKET","CLOSE_BRACKET","OPEN_PARENS", + "CLOSE_PARENS","DOT","COMMA","COLON","SEMICOLON","TILDE","PLUS", + "MINUS","BANG","ASSIGN","OP_LT","OP_GT","BITWISE_AND","BITWISE_OR", + "STAR","PERCENT","DIV","CARRET","INTERR","DOUBLE_COLON","OP_INC", + "OP_DEC","OP_SHIFT_LEFT","OP_SHIFT_RIGHT","OP_LE","OP_GE","OP_EQ", + "OP_NE","OP_AND","OP_OR","OP_MULT_ASSIGN","OP_DIV_ASSIGN", + "OP_MOD_ASSIGN","OP_ADD_ASSIGN","OP_SUB_ASSIGN", + "OP_SHIFT_LEFT_ASSIGN","OP_SHIFT_RIGHT_ASSIGN","OP_AND_ASSIGN", + "OP_XOR_ASSIGN","OP_OR_ASSIGN","OP_PTR","OP_COALESCING", + "OP_GENERICS_LT","OP_GENERICS_LT_DECL","OP_GENERICS_GT","LITERAL", + "IDENTIFIER","OPEN_PARENS_LAMBDA","OPEN_PARENS_CAST", + "GENERIC_DIMENSION","DEFAULT_COLON","OPEN_BRACKET_EXPR", + "EVAL_STATEMENT_PARSER","EVAL_COMPILATION_UNIT_PARSER", + "EVAL_USING_DECLARATIONS_UNIT_PARSER","DOC_SEE","GENERATE_COMPLETION", + "COMPLETE_COMPLETION","UMINUS", + }; + + /** index-checked interface to yyNames[]. + @param token single character or %token value. + @return token name or [illegal] or [unknown]. + */ +//t public static string yyname (int token) { +//t if ((token < 0) || (token > yyNames.Length)) return "[illegal]"; +//t string name; +//t if ((name = yyNames[token]) != null) return name; +//t return "[unknown]"; +//t } + +#pragma warning disable 414 + int yyExpectingState; +#pragma warning restore 414 + /** computes list of expected tokens on error by tracing the tables. + @param state for which to compute the list. + @return list of token names. + */ + protected int [] yyExpectingTokens (int state){ + int token, n, len = 0; + bool[] ok = new bool[yyNames.Length]; + if ((n = yySindex[state]) != 0) + for (token = n < 0 ? -n : 0; + (token < yyNames.Length) && (n+token < yyTable.Length); ++ token) + if (yyCheck[n+token] == token && !ok[token] && yyNames[token] != null) { + ++ len; + ok[token] = true; + } + if ((n = yyRindex[state]) != 0) + for (token = n < 0 ? -n : 0; + (token < yyNames.Length) && (n+token < yyTable.Length); ++ token) + if (yyCheck[n+token] == token && !ok[token] && yyNames[token] != null) { + ++ len; + ok[token] = true; + } + int [] result = new int [len]; + for (n = token = 0; n < len; ++ token) + if (ok[token]) result[n++] = token; + return result; + } + protected string[] yyExpecting (int state) { + int [] tokens = yyExpectingTokens (state); + string [] result = new string[tokens.Length]; + for (int n = 0; n < tokens.Length; n++) + result[n++] = yyNames[tokens [n]]; + return result; + } + + /** the generated parser, with debugging messages. + Maintains a state and a value stack, currently with fixed maximum size. + @param yyLex scanner. + @param yydebug debug message writer implementing yyDebug, or null. + @return result of the last reduction, if any. + @throws yyException on irrecoverable parse error. + */ + internal Object yyparse (yyParser.yyInput yyLex, Object yyd) + { +//t this.debug = (yydebug.yyDebug)yyd; + return yyparse(yyLex); + } + + /** initial size and increment of the state/value stack [default 256]. + This is not final so that it can be overwritten outside of invocations + of yyparse(). + */ + protected int yyMax; + + /** executed at the beginning of a reduce action. + Used as $$ = yyDefault($1), prior to the user-specified action, if any. + Can be overwritten to provide deep copy, etc. + @param first value for $1, or null. + @return first. + */ + protected Object yyDefault (Object first) { + return first; + } + + static int[] global_yyStates; + static object[] global_yyVals; +#pragma warning disable 649 + protected bool use_global_stacks; +#pragma warning restore 649 + object[] yyVals; // value stack + object yyVal; // value stack ptr + int yyToken; // current input + int yyTop; + + /** the generated parser. + Maintains a state and a value stack, currently with fixed maximum size. + @param yyLex scanner. + @return result of the last reduction, if any. + @throws yyException on irrecoverable parse error. + */ + internal Object yyparse (yyParser.yyInput yyLex) + { + if (yyMax <= 0) yyMax = 256; // initial size + int yyState = 0; // state stack ptr + int [] yyStates; // state stack + yyVal = null; + yyToken = -1; + int yyErrorFlag = 0; // #tks to shift + if (use_global_stacks && global_yyStates != null) { + yyVals = global_yyVals; + yyStates = global_yyStates; + } else { + yyVals = new object [yyMax]; + yyStates = new int [yyMax]; + if (use_global_stacks) { + global_yyVals = yyVals; + global_yyStates = yyStates; + } + } + + /*yyLoop:*/ for (yyTop = 0;; ++ yyTop) { + if (yyTop >= yyStates.Length) { // dynamically increase + global::System.Array.Resize (ref yyStates, yyStates.Length+yyMax); + global::System.Array.Resize (ref yyVals, yyVals.Length+yyMax); + } + yyStates[yyTop] = yyState; + yyVals[yyTop] = yyVal; +//t if (debug != null) debug.push(yyState, yyVal); + + /*yyDiscarded:*/ while (true) { // discarding a token does not change stack + int yyN; + if ((yyN = yyDefRed[yyState]) == 0) { // else [default] reduce (yyN) + if (yyToken < 0) { + yyToken = yyLex.advance() ? yyLex.token() : 0; +//t if (debug != null) +//t debug.lex(yyState, yyToken, yyname(yyToken), yyLex.value()); + } + if ((yyN = yySindex[yyState]) != 0 && ((yyN += yyToken) >= 0) + && (yyN < yyTable.Length) && (yyCheck[yyN] == yyToken)) { +//t if (debug != null) +//t debug.shift(yyState, yyTable[yyN], yyErrorFlag-1); + yyState = yyTable[yyN]; // shift to yyN + yyVal = yyLex.value(); + yyToken = -1; + if (yyErrorFlag > 0) -- yyErrorFlag; + goto continue_yyLoop; + } + if ((yyN = yyRindex[yyState]) != 0 && (yyN += yyToken) >= 0 + && yyN < yyTable.Length && yyCheck[yyN] == yyToken) + yyN = yyTable[yyN]; // reduce (yyN) + else + switch (yyErrorFlag) { + + case 0: + yyExpectingState = yyState; + // yyerror(String.Format ("syntax error, got token `{0}'", yyname (yyToken)), yyExpecting(yyState)); +//t if (debug != null) debug.error("syntax error"); + if (yyToken == 0 /*eof*/ || yyToken == eof_token) throw new yyParser.yyUnexpectedEof (); + goto case 1; + case 1: case 2: + yyErrorFlag = 3; + do { + if ((yyN = yySindex[yyStates[yyTop]]) != 0 + && (yyN += Token.yyErrorCode) >= 0 && yyN < yyTable.Length + && yyCheck[yyN] == Token.yyErrorCode) { +//t if (debug != null) +//t debug.shift(yyStates[yyTop], yyTable[yyN], 3); + yyState = yyTable[yyN]; + yyVal = yyLex.value(); + goto continue_yyLoop; + } +//t if (debug != null) debug.pop(yyStates[yyTop]); + } while (-- yyTop >= 0); +//t if (debug != null) debug.reject(); + throw new yyParser.yyException("irrecoverable syntax error"); + + case 3: + if (yyToken == 0) { +//t if (debug != null) debug.reject(); + throw new yyParser.yyException("irrecoverable syntax error at end-of-file"); + } +//t if (debug != null) +//t debug.discard(yyState, yyToken, yyname(yyToken), +//t yyLex.value()); + yyToken = -1; + goto continue_yyDiscarded; // leave stack alone + } + } + int yyV = yyTop + 1-yyLen[yyN]; +//t if (debug != null) +//t debug.reduce(yyState, yyStates[yyV-1], yyN, YYRules.getRule (yyN), yyLen[yyN]); + yyVal = yyV > yyTop ? null : yyVals[yyV]; // yyVal = yyDefault(yyV > yyTop ? null : yyVals[yyV]); + switch (yyN) { +case 1: +#line 389 "cs-parser.jay" + { + Lexer.check_incorrect_doc_comment (); + } + break; +case 2: +#line 390 "cs-parser.jay" + { Lexer.CompleteOnEOF = false; } + break; +case 6: + case_6(); + break; +case 7: +#line 409 "cs-parser.jay" + { + module.AddAttributes ((Attributes) yyVals[0+yyTop], current_namespace); + } + break; +case 8: + case_8(); + break; +case 13: + case_13(); + break; +case 14: +#line 454 "cs-parser.jay" + { + Error_SyntaxError (yyToken); + } + break; +case 17: + case_17(); + break; +case 18: + case_18(); + break; +case 19: + case_19(); + break; +case 20: + case_20(); + break; +case 21: + case_21(); + break; +case 22: + case_22(); + break; +case 23: + case_23(); + break; +case 24: + case_24(); + break; +case 27: + case_27(); + break; +case 28: + case_28(); + break; +case 29: + case_29(); + break; +case 30: + case_30(); + break; +case 43: + case_43(); + break; +case 44: +#line 638 "cs-parser.jay" + { + current_namespace.DeclarationFound = true; + } + break; +case 45: + case_45(); + break; +case 53: + case_53(); + break; +case 54: + case_54(); + break; +case 55: + case_55(); + break; +case 56: + case_56(); + break; +case 57: + case_57(); + break; +case 58: + case_58(); + break; +case 59: + case_59(); + break; +case 60: + case_60(); + break; +case 61: + case_61(); + break; +case 62: + case_62(); + break; +case 63: +#line 763 "cs-parser.jay" + { yyVal = "event"; PushLocation (GetLocation (yyVals[0+yyTop])); } + break; +case 64: +#line 764 "cs-parser.jay" + { yyVal = "return"; PushLocation (GetLocation (yyVals[0+yyTop])); } + break; +case 65: +#line 771 "cs-parser.jay" + { + yyVal = new List (4) { (Attribute) yyVals[0+yyTop] }; + } + break; +case 66: + case_66(); + break; +case 67: +#line 788 "cs-parser.jay" + { + ++lexer.parsing_block; + } + break; +case 68: + case_68(); + break; +case 70: +#line 816 "cs-parser.jay" + { yyVal = null; HadAttributeParens = false; } + break; +case 71: + case_71(); + break; +case 72: +#line 828 "cs-parser.jay" + { yyVal = null; } + break; +case 73: + case_73(); + break; +case 74: + case_74(); + break; +case 75: + case_75(); + break; +case 76: + case_76(); + break; +case 77: +#line 872 "cs-parser.jay" + { + yyVal = new Argument ((Expression) yyVals[0+yyTop]); + } + break; +case 79: + case_79(); + break; +case 80: +#line 885 "cs-parser.jay" + { + ++lexer.parsing_block; + } + break; +case 81: + case_81(); + break; +case 82: + case_82(); + break; +case 83: +#line 911 "cs-parser.jay" + { yyVal = null; } + break; +case 84: +#line 915 "cs-parser.jay" + { + yyVal = Argument.AType.Ref; + } + break; +case 85: +#line 919 "cs-parser.jay" + { + yyVal = Argument.AType.Out; + } + break; +case 88: + case_88(); + break; +case 89: + case_89(); + break; +case 103: + case_103(); + break; +case 104: + case_104(); + break; +case 105: + case_105(); + break; +case 106: +#line 996 "cs-parser.jay" + { + } + break; +case 107: + case_107(); + break; +case 108: + case_108(); + break; +case 109: + case_109(); + break; +case 110: + case_110(); + break; +case 111: + case_111(); + break; +case 112: +#line 1046 "cs-parser.jay" + { + Error_SyntaxError (yyToken); + } + break; +case 113: + case_113(); + break; +case 114: + case_114(); + break; +case 115: + case_115(); + break; +case 118: +#line 1095 "cs-parser.jay" + { + current_field.AddDeclarator ((FieldDeclarator) yyVals[0+yyTop]); + } + break; +case 119: +#line 1099 "cs-parser.jay" + { + current_field.AddDeclarator ((FieldDeclarator) yyVals[0+yyTop]); + } + break; +case 120: + case_120(); + break; +case 121: +#line 1115 "cs-parser.jay" + { + ++lexer.parsing_block; + } + break; +case 122: + case_122(); + break; +case 123: + case_123(); + break; +case 126: + case_126(); + break; +case 127: + case_127(); + break; +case 128: + case_128(); + break; +case 129: + case_129(); + break; +case 130: +#line 1194 "cs-parser.jay" + { + report.Error (1641, GetLocation (yyVals[-1+yyTop]), "A fixed size buffer field must have the array size specifier after the field name"); + } + break; +case 132: + case_132(); + break; +case 133: + case_133(); + break; +case 136: +#line 1224 "cs-parser.jay" + { + current_field.AddDeclarator ((FieldDeclarator) yyVals[0+yyTop]); + } + break; +case 137: +#line 1228 "cs-parser.jay" + { + current_field.AddDeclarator ((FieldDeclarator) yyVals[0+yyTop]); + } + break; +case 138: + case_138(); + break; +case 139: +#line 1241 "cs-parser.jay" + { + ++lexer.parsing_block; + } + break; +case 140: + case_140(); + break; +case 143: +#line 1260 "cs-parser.jay" + { + current_field.AddDeclarator ((FieldDeclarator) yyVals[0+yyTop]); + } + break; +case 144: +#line 1264 "cs-parser.jay" + { + current_field.AddDeclarator ((FieldDeclarator) yyVals[0+yyTop]); + } + break; +case 145: + case_145(); + break; +case 146: +#line 1280 "cs-parser.jay" + { + ++lexer.parsing_block; + } + break; +case 147: + case_147(); + break; +case 148: + case_148(); + break; +case 151: + case_151(); + break; +case 152: + case_152(); + break; +case 153: + case_153(); + break; +case 154: +#line 1348 "cs-parser.jay" + { + valid_param_mod = ParameterModifierType.All; + } + break; +case 155: + case_155(); + break; +case 156: + case_156(); + break; +case 157: +#line 1387 "cs-parser.jay" + { + lexer.parsing_generic_declaration = true; + } + break; +case 158: + case_158(); + break; +case 159: +#line 1397 "cs-parser.jay" + { + lexer.ConstraintsParsing = true; + } + break; +case 160: + case_160(); + break; +case 161: + case_161(); + break; +case 162: + case_162(); + break; +case 166: +#line 1475 "cs-parser.jay" + { savedLocation = GetLocation (yyVals[0+yyTop]); yyVal = null; } + break; +case 167: + case_167(); + break; +case 168: + case_168(); + break; +case 169: +#line 1499 "cs-parser.jay" + { yyVal = ParametersCompiled.EmptyReadOnlyParameters; } + break; +case 171: + case_171(); + break; +case 172: + case_172(); + break; +case 173: + case_173(); + break; +case 174: + case_174(); + break; +case 175: + case_175(); + break; +case 176: + case_176(); + break; +case 177: + case_177(); + break; +case 178: +#line 1571 "cs-parser.jay" + { + yyVal = new ParametersCompiled (new Parameter[] { (Parameter) yyVals[0+yyTop] } ); + } + break; +case 179: +#line 1575 "cs-parser.jay" + { + yyVal = new ParametersCompiled (new Parameter [] { new ArglistParameter (GetLocation (yyVals[0+yyTop])) }, true); + } + break; +case 180: + case_180(); + break; +case 181: + case_181(); + break; +case 182: + case_182(); + break; +case 183: + case_183(); + break; +case 184: + case_184(); + break; +case 185: + case_185(); + break; +case 186: + case_186(); + break; +case 187: +#line 1656 "cs-parser.jay" + { + ++lexer.parsing_block; + } + break; +case 188: + case_188(); + break; +case 189: +#line 1697 "cs-parser.jay" + { yyVal = Parameter.Modifier.NONE; } + break; +case 191: +#line 1705 "cs-parser.jay" + { + yyVal = yyVals[0+yyTop]; + } + break; +case 192: + case_192(); + break; +case 193: + case_193(); + break; +case 194: + case_194(); + break; +case 195: + case_195(); + break; +case 196: + case_196(); + break; +case 197: + case_197(); + break; +case 198: + case_198(); + break; +case 199: + case_199(); + break; +case 200: + case_200(); + break; +case 201: +#line 1799 "cs-parser.jay" + { + Error_DuplicateParameterModifier (GetLocation (yyVals[-1+yyTop]), Parameter.Modifier.PARAMS); + } + break; +case 202: + case_202(); + break; +case 203: + case_203(); + break; +case 204: + case_204(); + break; +case 205: + case_205(); + break; +case 206: + case_206(); + break; +case 207: +#line 1849 "cs-parser.jay" + { + current_property = null; + } + break; +case 208: + case_208(); + break; +case 209: + case_209(); + break; +case 211: + case_211(); + break; +case 212: + case_212(); + break; +case 215: +#line 1911 "cs-parser.jay" + { + valid_param_mod = ParameterModifierType.Params | ParameterModifierType.DefaultValue; + } + break; +case 216: + case_216(); + break; +case 217: + case_217(); + break; +case 218: +#line 1957 "cs-parser.jay" + { + lbag.AppendToMember (current_property, GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + break; +case 219: + case_219(); + break; +case 224: + case_224(); + break; +case 225: + case_225(); + break; +case 226: + case_226(); + break; +case 227: + case_227(); + break; +case 228: + case_228(); + break; +case 230: + case_230(); + break; +case 231: + case_231(); + break; +case 232: +#line 2098 "cs-parser.jay" + { + } + break; +case 233: + case_233(); + break; +case 234: + case_234(); + break; +case 235: + case_235(); + break; +case 236: + case_236(); + break; +case 237: +#line 2138 "cs-parser.jay" + { + Error_SyntaxError (yyToken); + } + break; +case 240: + case_240(); + break; +case 241: + case_241(); + break; +case 242: +#line 2163 "cs-parser.jay" + { + report.Error (525, GetLocation (yyVals[0+yyTop]), "Interfaces cannot contain fields or constants"); + } + break; +case 243: +#line 2167 "cs-parser.jay" + { + report.Error (525, GetLocation (yyVals[0+yyTop]), "Interfaces cannot contain fields or constants"); + } + break; +case 248: +#line 2175 "cs-parser.jay" + { + report.Error (567, GetLocation (yyVals[0+yyTop]), "Interfaces cannot contain operators"); + } + break; +case 249: +#line 2179 "cs-parser.jay" + { + report.Error (526, GetLocation (yyVals[0+yyTop]), "Interfaces cannot contain contructors"); + } + break; +case 250: +#line 2183 "cs-parser.jay" + { + report.Error (524, GetLocation (yyVals[0+yyTop]), "Interfaces cannot declare classes, structs, interfaces, delegates, or enumerations"); + } + break; +case 251: +#line 2189 "cs-parser.jay" + { + } + break; +case 252: + case_252(); + break; +case 254: + case_254(); + break; +case 255: +#line 2233 "cs-parser.jay" + { + valid_param_mod = ParameterModifierType.DefaultValue; + } + break; +case 256: + case_256(); + break; +case 258: +#line 2279 "cs-parser.jay" + { yyVal = Operator.OpType.LogicalNot; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 259: +#line 2280 "cs-parser.jay" + { yyVal = Operator.OpType.OnesComplement; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 260: +#line 2281 "cs-parser.jay" + { yyVal = Operator.OpType.Increment; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 261: +#line 2282 "cs-parser.jay" + { yyVal = Operator.OpType.Decrement; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 262: +#line 2283 "cs-parser.jay" + { yyVal = Operator.OpType.True; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 263: +#line 2284 "cs-parser.jay" + { yyVal = Operator.OpType.False; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 264: +#line 2286 "cs-parser.jay" + { yyVal = Operator.OpType.Addition; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 265: +#line 2287 "cs-parser.jay" + { yyVal = Operator.OpType.Subtraction; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 266: +#line 2289 "cs-parser.jay" + { yyVal = Operator.OpType.Multiply; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 267: +#line 2290 "cs-parser.jay" + { yyVal = Operator.OpType.Division; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 268: +#line 2291 "cs-parser.jay" + { yyVal = Operator.OpType.Modulus; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 269: +#line 2292 "cs-parser.jay" + { yyVal = Operator.OpType.BitwiseAnd; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 270: +#line 2293 "cs-parser.jay" + { yyVal = Operator.OpType.BitwiseOr; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 271: +#line 2294 "cs-parser.jay" + { yyVal = Operator.OpType.ExclusiveOr; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 272: +#line 2295 "cs-parser.jay" + { yyVal = Operator.OpType.LeftShift; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 273: +#line 2296 "cs-parser.jay" + { yyVal = Operator.OpType.RightShift; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 274: +#line 2297 "cs-parser.jay" + { yyVal = Operator.OpType.Equality; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 275: +#line 2298 "cs-parser.jay" + { yyVal = Operator.OpType.Inequality; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 276: +#line 2299 "cs-parser.jay" + { yyVal = Operator.OpType.GreaterThan; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 277: +#line 2300 "cs-parser.jay" + { yyVal = Operator.OpType.LessThan; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 278: +#line 2301 "cs-parser.jay" + { yyVal = Operator.OpType.GreaterThanOrEqual; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 279: +#line 2302 "cs-parser.jay" + { yyVal = Operator.OpType.LessThanOrEqual; savedOperatorLocation = GetLocation (yyVals[0+yyTop]); } + break; +case 280: +#line 2309 "cs-parser.jay" + { + valid_param_mod = ParameterModifierType.DefaultValue; + } + break; +case 281: + case_281(); + break; +case 282: +#line 2332 "cs-parser.jay" + { + valid_param_mod = ParameterModifierType.DefaultValue; + } + break; +case 283: + case_283(); + break; +case 284: + case_284(); + break; +case 285: + case_285(); + break; +case 286: + case_286(); + break; +case 287: + case_287(); + break; +case 288: + case_288(); + break; +case 289: + case_289(); + break; +case 291: +#line 2442 "cs-parser.jay" + { current_block = null; yyVal = null; } + break; +case 294: +#line 2454 "cs-parser.jay" + { + ++lexer.parsing_block; + } + break; +case 295: + case_295(); + break; +case 296: +#line 2464 "cs-parser.jay" + { + ++lexer.parsing_block; + } + break; +case 297: + case_297(); + break; +case 298: + case_298(); + break; +case 299: + case_299(); + break; +case 300: + case_300(); + break; +case 301: + case_301(); + break; +case 302: + case_302(); + break; +case 303: + case_303(); + break; +case 304: + case_304(); + break; +case 305: + case_305(); + break; +case 306: + case_306(); + break; +case 307: + case_307(); + break; +case 309: +#line 2591 "cs-parser.jay" + { + ++lexer.parsing_block; + } + break; +case 310: + case_310(); + break; +case 313: +#line 2609 "cs-parser.jay" + { + current_event_field.AddDeclarator ((FieldDeclarator) yyVals[0+yyTop]); + } + break; +case 314: +#line 2613 "cs-parser.jay" + { + current_event_field.AddDeclarator ((FieldDeclarator) yyVals[0+yyTop]); + } + break; +case 315: + case_315(); + break; +case 316: +#line 2626 "cs-parser.jay" + { + ++lexer.parsing_block; + } + break; +case 317: + case_317(); + break; +case 318: + case_318(); + break; +case 319: +#line 2651 "cs-parser.jay" + { + yyVal = yyVals[0+yyTop]; + } + break; +case 322: + case_322(); + break; +case 323: + case_323(); + break; +case 324: + case_324(); + break; +case 325: + case_325(); + break; +case 326: + case_326(); + break; +case 327: + case_327(); + break; +case 328: + case_328(); + break; +case 329: + case_329(); + break; +case 331: + case_331(); + break; +case 332: + case_332(); + break; +case 333: + case_333(); + break; +case 334: + case_334(); + break; +case 335: + case_335(); + break; +case 336: + case_336(); + break; +case 338: + case_338(); + break; +case 339: + case_339(); + break; +case 342: +#line 2839 "cs-parser.jay" + { + lbag.AppendToMember (current_container, GetLocation (yyVals[0+yyTop])); + } + break; +case 344: + case_344(); + break; +case 345: + case_345(); + break; +case 346: + case_346(); + break; +case 347: + case_347(); + break; +case 348: + case_348(); + break; +case 350: +#line 2913 "cs-parser.jay" + { + valid_param_mod = ParameterModifierType.Ref | ParameterModifierType.Out | ParameterModifierType.Params | ParameterModifierType.DefaultValue; + } + break; +case 351: + case_351(); + break; +case 352: +#line 2932 "cs-parser.jay" + { + lexer.ConstraintsParsing = false; + } + break; +case 353: + case_353(); + break; +case 355: + case_355(); + break; +case 357: + case_357(); + break; +case 358: + case_358(); + break; +case 360: + case_360(); + break; +case 361: + case_361(); + break; +case 362: + case_362(); + break; +case 363: + case_363(); + break; +case 365: + case_365(); + break; +case 366: + case_366(); + break; +case 367: + case_367(); + break; +case 368: + case_368(); + break; +case 369: +#line 3062 "cs-parser.jay" + { + lexer.parsing_generic_declaration = true; + } + break; +case 370: + case_370(); + break; +case 371: + case_371(); + break; +case 373: + case_373(); + break; +case 374: + case_374(); + break; +case 375: + case_375(); + break; +case 376: + case_376(); + break; +case 377: + case_377(); + break; +case 378: + case_378(); + break; +case 380: + case_380(); + break; +case 381: + case_381(); + break; +case 382: + case_382(); + break; +case 383: + case_383(); + break; +case 384: + case_384(); + break; +case 386: +#line 3187 "cs-parser.jay" + { + yyVal = new TypeExpression (compiler.BuiltinTypes.Void, GetLocation (yyVals[0+yyTop])); + } + break; +case 387: +#line 3194 "cs-parser.jay" + { + lexer.parsing_generic_declaration = true; + } + break; +case 393: + case_393(); + break; +case 395: +#line 3224 "cs-parser.jay" + { + yyVal = new ComposedCast ((FullNamedExpression) yyVals[-1+yyTop], (ComposedTypeSpecifier) yyVals[0+yyTop]); + } + break; +case 396: + case_396(); + break; +case 397: +#line 3243 "cs-parser.jay" + { + yyVal = new ComposedCast ((ATypeNameExpression) yyVals[-1+yyTop], (ComposedTypeSpecifier) yyVals[0+yyTop]); + } + break; +case 399: + case_399(); + break; +case 400: + case_400(); + break; +case 401: +#line 3264 "cs-parser.jay" + { + yyVal = new ComposedCast ((FullNamedExpression) yyVals[-1+yyTop], (ComposedTypeSpecifier) yyVals[0+yyTop]); + } + break; +case 402: +#line 3268 "cs-parser.jay" + { + yyVal = new ComposedCast (new TypeExpression (compiler.BuiltinTypes.Void, GetLocation (yyVals[-1+yyTop])), (ComposedTypeSpecifier) yyVals[0+yyTop]); + } + break; +case 403: + case_403(); + break; +case 404: + case_404(); + break; +case 405: + case_405(); + break; +case 406: +#line 3302 "cs-parser.jay" + { yyVal = new TypeExpression (compiler.BuiltinTypes.Object, GetLocation (yyVals[0+yyTop])); } + break; +case 407: +#line 3303 "cs-parser.jay" + { yyVal = new TypeExpression (compiler.BuiltinTypes.String, GetLocation (yyVals[0+yyTop])); } + break; +case 408: +#line 3304 "cs-parser.jay" + { yyVal = new TypeExpression (compiler.BuiltinTypes.Bool, GetLocation (yyVals[0+yyTop])); } + break; +case 409: +#line 3305 "cs-parser.jay" + { yyVal = new TypeExpression (compiler.BuiltinTypes.Decimal, GetLocation (yyVals[0+yyTop])); } + break; +case 410: +#line 3306 "cs-parser.jay" + { yyVal = new TypeExpression (compiler.BuiltinTypes.Float, GetLocation (yyVals[0+yyTop])); } + break; +case 411: +#line 3307 "cs-parser.jay" + { yyVal = new TypeExpression (compiler.BuiltinTypes.Double, GetLocation (yyVals[0+yyTop])); } + break; +case 413: +#line 3312 "cs-parser.jay" + { yyVal = new TypeExpression (compiler.BuiltinTypes.SByte, GetLocation (yyVals[0+yyTop])); } + break; +case 414: +#line 3313 "cs-parser.jay" + { yyVal = new TypeExpression (compiler.BuiltinTypes.Byte, GetLocation (yyVals[0+yyTop])); } + break; +case 415: +#line 3314 "cs-parser.jay" + { yyVal = new TypeExpression (compiler.BuiltinTypes.Short, GetLocation (yyVals[0+yyTop])); } + break; +case 416: +#line 3315 "cs-parser.jay" + { yyVal = new TypeExpression (compiler.BuiltinTypes.UShort, GetLocation (yyVals[0+yyTop])); } + break; +case 417: +#line 3316 "cs-parser.jay" + { yyVal = new TypeExpression (compiler.BuiltinTypes.Int, GetLocation (yyVals[0+yyTop])); } + break; +case 418: +#line 3317 "cs-parser.jay" + { yyVal = new TypeExpression (compiler.BuiltinTypes.UInt, GetLocation (yyVals[0+yyTop])); } + break; +case 419: +#line 3318 "cs-parser.jay" + { yyVal = new TypeExpression (compiler.BuiltinTypes.Long, GetLocation (yyVals[0+yyTop])); } + break; +case 420: +#line 3319 "cs-parser.jay" + { yyVal = new TypeExpression (compiler.BuiltinTypes.ULong, GetLocation (yyVals[0+yyTop])); } + break; +case 421: +#line 3320 "cs-parser.jay" + { yyVal = new TypeExpression (compiler.BuiltinTypes.Char, GetLocation (yyVals[0+yyTop])); } + break; +case 443: + case_443(); + break; +case 447: +#line 3363 "cs-parser.jay" + { yyVal = new NullLiteral (GetLocation (yyVals[0+yyTop])); } + break; +case 448: +#line 3367 "cs-parser.jay" + { yyVal = new BoolLiteral (compiler.BuiltinTypes, true, GetLocation (yyVals[0+yyTop])); } + break; +case 449: +#line 3368 "cs-parser.jay" + { yyVal = new BoolLiteral (compiler.BuiltinTypes, false, GetLocation (yyVals[0+yyTop])); } + break; +case 454: + case_454(); + break; +case 455: +#line 3401 "cs-parser.jay" + { + yyVal = new ParenthesizedExpression ((Expression) yyVals[-1+yyTop], GetLocation (yyVals[-2+yyTop])); + } + break; +case 456: + case_456(); + break; +case 457: + case_457(); + break; +case 458: + case_458(); + break; +case 459: + case_459(); + break; +case 460: + case_460(); + break; +case 461: + case_461(); + break; +case 462: + case_462(); + break; +case 463: + case_463(); + break; +case 464: +#line 3465 "cs-parser.jay" + { + yyVal = new CompletionMemberAccess ((Expression) yyVals[-2+yyTop], null,GetLocation (yyVals[0+yyTop])); + } + break; +case 465: + case_465(); + break; +case 466: +#line 3473 "cs-parser.jay" + { + yyVal = new CompletionMemberAccess ((Expression) yyVals[-2+yyTop], null, lexer.Location); + } + break; +case 467: + case_467(); + break; +case 468: + case_468(); + break; +case 469: + case_469(); + break; +case 470: + case_470(); + break; +case 471: +#line 3503 "cs-parser.jay" + { yyVal = null; } + break; +case 473: + case_473(); + break; +case 474: + case_474(); + break; +case 475: +#line 3525 "cs-parser.jay" + { yyVal = null; } + break; +case 476: +#line 3529 "cs-parser.jay" + { + yyVal = yyVals[0+yyTop]; + } + break; +case 477: + case_477(); + break; +case 478: + case_478(); + break; +case 479: + case_479(); + break; +case 480: + case_480(); + break; +case 481: + case_481(); + break; +case 482: +#line 3568 "cs-parser.jay" + { + yyVal = new CompletionElementInitializer (null, GetLocation (yyVals[0+yyTop])); + } + break; +case 483: + case_483(); + break; +case 484: + case_484(); + break; +case 485: + case_485(); + break; +case 486: + case_486(); + break; +case 489: +#line 3608 "cs-parser.jay" + { yyVal = null; } + break; +case 491: + case_491(); + break; +case 492: + case_492(); + break; +case 493: + case_493(); + break; +case 494: + case_494(); + break; +case 495: + case_495(); + break; +case 496: +#line 3662 "cs-parser.jay" + { + yyVal = new Argument ((Expression) yyVals[0+yyTop]); + } + break; +case 500: + case_500(); + break; +case 501: + case_501(); + break; +case 502: + case_502(); + break; +case 503: + case_503(); + break; +case 505: + case_505(); + break; +case 506: + case_506(); + break; +case 507: + case_507(); + break; +case 508: + case_508(); + break; +case 509: + case_509(); + break; +case 510: + case_510(); + break; +case 511: + case_511(); + break; +case 512: + case_512(); + break; +case 513: +#line 3766 "cs-parser.jay" + { + yyVal = new Argument ((Expression) yyVals[0+yyTop]); + } + break; +case 515: +#line 3774 "cs-parser.jay" + { + yyVal = new This (GetLocation (yyVals[0+yyTop])); + } + break; +case 516: + case_516(); + break; +case 517: + case_517(); + break; +case 518: +#line 3794 "cs-parser.jay" + { + yyVal = new UnaryMutator (UnaryMutator.Mode.PostIncrement, (Expression) yyVals[-1+yyTop], GetLocation (yyVals[0+yyTop])); + } + break; +case 519: +#line 3801 "cs-parser.jay" + { + yyVal = new UnaryMutator (UnaryMutator.Mode.PostDecrement, (Expression) yyVals[-1+yyTop], GetLocation (yyVals[0+yyTop])); + } + break; +case 520: + case_520(); + break; +case 521: + case_521(); + break; +case 522: + case_522(); + break; +case 523: + case_523(); + break; +case 524: + case_524(); + break; +case 525: + case_525(); + break; +case 526: + case_526(); + break; +case 527: +#line 3868 "cs-parser.jay" + { + ++lexer.parsing_type; + } + break; +case 528: + case_528(); + break; +case 529: + case_529(); + break; +case 530: +#line 3890 "cs-parser.jay" + { + yyVal = new EmptyCompletion (); + } + break; +case 533: +#line 3899 "cs-parser.jay" + { yyVal = null; } + break; +case 535: + case_535(); + break; +case 536: + case_536(); + break; +case 537: +#line 3921 "cs-parser.jay" + { + yyVal = new EmptyCompletion (); + } + break; +case 538: +#line 3925 "cs-parser.jay" + { + yyVal = yyVals[-1+yyTop]; + } + break; +case 539: + case_539(); + break; +case 540: + case_540(); + break; +case 541: + case_541(); + break; +case 542: + case_542(); + break; +case 546: + case_546(); + break; +case 547: + case_547(); + break; +case 548: + case_548(); + break; +case 549: +#line 3985 "cs-parser.jay" + { + yyVal = 2; + } + break; +case 550: +#line 3989 "cs-parser.jay" + { + yyVal = ((int) yyVals[-1+yyTop]) + 1; + } + break; +case 551: +#line 3996 "cs-parser.jay" + { + yyVal = null; + } + break; +case 552: +#line 4000 "cs-parser.jay" + { + yyVal = yyVals[0+yyTop]; + } + break; +case 553: + case_553(); + break; +case 554: + case_554(); + break; +case 555: + case_555(); + break; +case 556: + case_556(); + break; +case 557: + case_557(); + break; +case 559: + case_559(); + break; +case 560: + case_560(); + break; +case 561: + case_561(); + break; +case 562: + case_562(); + break; +case 563: + case_563(); + break; +case 564: + case_564(); + break; +case 565: + case_565(); + break; +case 566: + case_566(); + break; +case 567: + case_567(); + break; +case 568: + case_568(); + break; +case 569: +#line 4133 "cs-parser.jay" + { + start_anonymous (false, (ParametersCompiled) yyVals[0+yyTop], false, GetLocation (yyVals[-1+yyTop])); + } + break; +case 570: + case_570(); + break; +case 571: +#line 4146 "cs-parser.jay" + { + start_anonymous (false, (ParametersCompiled) yyVals[0+yyTop], true, GetLocation (yyVals[-2+yyTop])); + } + break; +case 572: + case_572(); + break; +case 573: +#line 4163 "cs-parser.jay" + { + yyVal = ParametersCompiled.Undefined; + } + break; +case 575: +#line 4171 "cs-parser.jay" + { + valid_param_mod = ParameterModifierType.Ref | ParameterModifierType.Out; + } + break; +case 576: + case_576(); + break; +case 577: + case_577(); + break; +case 579: +#line 4197 "cs-parser.jay" + { + yyVal = new Unary (Unary.Operator.LogicalNot, (Expression) yyVals[0+yyTop], GetLocation (yyVals[-1+yyTop])); + } + break; +case 580: +#line 4201 "cs-parser.jay" + { + yyVal = new Unary (Unary.Operator.OnesComplement, (Expression) yyVals[0+yyTop], GetLocation (yyVals[-1+yyTop])); + } + break; +case 581: + case_581(); + break; +case 582: + case_582(); + break; +case 583: + case_583(); + break; +case 584: + case_584(); + break; +case 585: + case_585(); + break; +case 586: + case_586(); + break; +case 588: +#line 4265 "cs-parser.jay" + { + yyVal = new Unary (Unary.Operator.UnaryPlus, (Expression) yyVals[0+yyTop], GetLocation (yyVals[-1+yyTop])); + } + break; +case 589: +#line 4269 "cs-parser.jay" + { + yyVal = new Unary (Unary.Operator.UnaryNegation, (Expression) yyVals[0+yyTop], GetLocation (yyVals[-1+yyTop])); + } + break; +case 590: +#line 4273 "cs-parser.jay" + { + yyVal = new UnaryMutator (UnaryMutator.Mode.PreIncrement, (Expression) yyVals[0+yyTop], GetLocation (yyVals[-1+yyTop])); + } + break; +case 591: +#line 4277 "cs-parser.jay" + { + yyVal = new UnaryMutator (UnaryMutator.Mode.PreDecrement, (Expression) yyVals[0+yyTop], GetLocation (yyVals[-1+yyTop])); + } + break; +case 592: +#line 4281 "cs-parser.jay" + { + yyVal = new Indirection ((Expression) yyVals[0+yyTop], GetLocation (yyVals[-1+yyTop])); + } + break; +case 593: +#line 4285 "cs-parser.jay" + { + yyVal = new Unary (Unary.Operator.AddressOf, (Expression) yyVals[0+yyTop], GetLocation (yyVals[-1+yyTop])); + } + break; +case 594: + case_594(); + break; +case 595: + case_595(); + break; +case 596: + case_596(); + break; +case 597: + case_597(); + break; +case 598: + case_598(); + break; +case 599: + case_599(); + break; +case 601: + case_601(); + break; +case 602: + case_602(); + break; +case 603: + case_603(); + break; +case 604: + case_604(); + break; +case 605: + case_605(); + break; +case 606: + case_606(); + break; +case 608: + case_608(); + break; +case 609: + case_609(); + break; +case 610: + case_610(); + break; +case 611: + case_611(); + break; +case 612: +#line 4393 "cs-parser.jay" + { + yyVal = new As ((Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop], GetLocation (yyVals[-1+yyTop])); + } + break; +case 613: + case_613(); + break; +case 614: + case_614(); + break; +case 615: + case_615(); + break; +case 616: + case_616(); + break; +case 617: + case_617(); + break; +case 619: + case_619(); + break; +case 621: +#line 4445 "cs-parser.jay" + { + yyVal = new Unary (Unary.Operator.UnaryPlus, (Expression) yyVals[0+yyTop], GetLocation (yyVals[-1+yyTop])); + } + break; +case 622: +#line 4449 "cs-parser.jay" + { + yyVal = new Unary (Unary.Operator.UnaryNegation, (Expression) yyVals[0+yyTop], GetLocation (yyVals[-1+yyTop])); + } + break; +case 623: + case_623(); + break; +case 624: + case_624(); + break; +case 628: + case_628(); + break; +case 629: + case_629(); + break; +case 630: + case_630(); + break; +case 631: + case_631(); + break; +case 633: + case_633(); + break; +case 634: + case_634(); + break; +case 635: + case_635(); + break; +case 636: + case_636(); + break; +case 637: + case_637(); + break; +case 638: + case_638(); + break; +case 639: + case_639(); + break; +case 640: + case_640(); + break; +case 642: + case_642(); + break; +case 643: + case_643(); + break; +case 644: + case_644(); + break; +case 645: + case_645(); + break; +case 647: + case_647(); + break; +case 648: + case_648(); + break; +case 650: + case_650(); + break; +case 651: + case_651(); + break; +case 653: + case_653(); + break; +case 654: + case_654(); + break; +case 656: + case_656(); + break; +case 657: + case_657(); + break; +case 659: + case_659(); + break; +case 660: + case_660(); + break; +case 662: + case_662(); + break; +case 664: + case_664(); + break; +case 665: + case_665(); + break; +case 666: + case_666(); + break; +case 667: + case_667(); + break; +case 668: + case_668(); + break; +case 669: + case_669(); + break; +case 670: + case_670(); + break; +case 671: + case_671(); + break; +case 672: + case_672(); + break; +case 673: + case_673(); + break; +case 674: + case_674(); + break; +case 675: + case_675(); + break; +case 676: + case_676(); + break; +case 677: + case_677(); + break; +case 678: + case_678(); + break; +case 679: + case_679(); + break; +case 680: + case_680(); + break; +case 681: + case_681(); + break; +case 682: + case_682(); + break; +case 683: + case_683(); + break; +case 684: + case_684(); + break; +case 685: +#line 4821 "cs-parser.jay" + { yyVal = ParametersCompiled.EmptyReadOnlyParameters; } + break; +case 686: + case_686(); + break; +case 687: +#line 4832 "cs-parser.jay" + { + start_block (Location.Null); + } + break; +case 688: + case_688(); + break; +case 690: + case_690(); + break; +case 692: + case_692(); + break; +case 693: + case_693(); + break; +case 694: + case_694(); + break; +case 695: + case_695(); + break; +case 696: + case_696(); + break; +case 697: + case_697(); + break; +case 698: + case_698(); + break; +case 699: +#line 4899 "cs-parser.jay" + { + valid_param_mod = ParameterModifierType.Ref | ParameterModifierType.Out; + } + break; +case 700: + case_700(); + break; +case 701: + case_701(); + break; +case 702: +#line 4913 "cs-parser.jay" + { + valid_param_mod = ParameterModifierType.Ref | ParameterModifierType.Out; + } + break; +case 703: + case_703(); + break; +case 704: + case_704(); + break; +case 710: +#line 4938 "cs-parser.jay" + { + yyVal = new ArglistAccess (GetLocation (yyVals[0+yyTop])); + } + break; +case 711: + case_711(); + break; +case 712: + case_712(); + break; +case 713: + case_713(); + break; +case 715: +#line 4967 "cs-parser.jay" + { + yyVal = new BooleanExpression ((Expression) yyVals[0+yyTop]); + } + break; +case 716: +#line 4974 "cs-parser.jay" + { + yyVal = null; + } + break; +case 718: + case_718(); + break; +case 719: +#line 4995 "cs-parser.jay" + { + yyVal = null; + } + break; +case 720: +#line 4999 "cs-parser.jay" + { + yyVal = null; + } + break; +case 721: +#line 5003 "cs-parser.jay" + { + yyVal = yyVals[0+yyTop]; + } + break; +case 722: +#line 5007 "cs-parser.jay" + { + yyVal = yyVals[-1+yyTop]; + } + break; +case 723: + case_723(); + break; +case 724: + case_724(); + break; +case 725: +#line 5032 "cs-parser.jay" + { + } + break; +case 726: + case_726(); + break; +case 727: + case_727(); + break; +case 728: + case_728(); + break; +case 729: + case_729(); + break; +case 730: +#line 5084 "cs-parser.jay" + { yyVal = null; } + break; +case 731: +#line 5086 "cs-parser.jay" + { yyVal = yyVals[0+yyTop]; StoreModifierLocation (Modifiers.PARTIAL, GetLocation (yyVals[0+yyTop])); } + break; +case 732: + case_732(); + break; +case 733: +#line 5099 "cs-parser.jay" + { + lexer.parsing_modifiers = false; + } + break; +case 735: + case_735(); + break; +case 736: + case_736(); + break; +case 737: + case_737(); + break; +case 738: + case_738(); + break; +case 739: + case_739(); + break; +case 740: + case_740(); + break; +case 741: + case_741(); + break; +case 742: + case_742(); + break; +case 743: + case_743(); + break; +case 744: + case_744(); + break; +case 745: + case_745(); + break; +case 746: + case_746(); + break; +case 747: + case_747(); + break; +case 748: + case_748(); + break; +case 749: + case_749(); + break; +case 750: + case_750(); + break; +case 753: + case_753(); + break; +case 754: + case_754(); + break; +case 756: +#line 5229 "cs-parser.jay" + { + yyVal = yyVals[0+yyTop]; + } + break; +case 757: + case_757(); + break; +case 758: + case_758(); + break; +case 759: + case_759(); + break; +case 760: + case_760(); + break; +case 761: + case_761(); + break; +case 762: + case_762(); + break; +case 763: + case_763(); + break; +case 764: + case_764(); + break; +case 765: +#line 5322 "cs-parser.jay" + { + yyVal = new SpecialContraintExpr (SpecialConstraint.Class, GetLocation (yyVals[0+yyTop])); + } + break; +case 766: +#line 5326 "cs-parser.jay" + { + yyVal = new SpecialContraintExpr (SpecialConstraint.Struct, GetLocation (yyVals[0+yyTop])); + } + break; +case 767: +#line 5333 "cs-parser.jay" + { + yyVal = null; + } + break; +case 768: + case_768(); + break; +case 769: + case_769(); + break; +case 770: + case_770(); + break; +case 771: + case_771(); + break; +case 772: +#line 5378 "cs-parser.jay" + { + yyVal = yyVals[0+yyTop]; + } + break; +case 773: + case_773(); + break; +case 774: + case_774(); + break; +case 775: + case_775(); + break; +case 776: + case_776(); + break; +case 777: + case_777(); + break; +case 778: + case_778(); + break; +case 779: + case_779(); + break; +case 784: +#line 5440 "cs-parser.jay" + { + current_block.AddStatement ((Statement) yyVals[0+yyTop]); + } + break; +case 785: +#line 5444 "cs-parser.jay" + { + current_block.AddStatement ((Statement) yyVals[0+yyTop]); + } + break; +case 787: + case_787(); + break; +case 788: + case_788(); + break; +case 791: +#line 5478 "cs-parser.jay" + { + current_block.AddStatement ((Statement) yyVals[0+yyTop]); + } + break; +case 792: +#line 5482 "cs-parser.jay" + { + current_block.AddStatement ((Statement) yyVals[0+yyTop]); + } + break; +case 821: + case_821(); + break; +case 822: + case_822(); + break; +case 823: + case_823(); + break; +case 824: + case_824(); + break; +case 825: + case_825(); + break; +case 828: + case_828(); + break; +case 829: + case_829(); + break; +case 830: + case_830(); + break; +case 834: + case_834(); + break; +case 835: +#line 5633 "cs-parser.jay" + { + yyVal = ComposedTypeSpecifier.CreatePointer (GetLocation (yyVals[0+yyTop])); + } + break; +case 837: +#line 5641 "cs-parser.jay" + { + yyVal = Error_AwaitAsIdentifier (yyVals[0+yyTop]); + } + break; +case 838: + case_838(); + break; +case 839: + case_839(); + break; +case 840: + case_840(); + break; +case 841: + case_841(); + break; +case 843: + case_843(); + break; +case 845: + case_845(); + break; +case 846: + case_846(); + break; +case 850: + case_850(); + break; +case 853: + case_853(); + break; +case 854: + case_854(); + break; +case 855: +#line 5755 "cs-parser.jay" + { + report.Error (145, lexer.Location, "A const field requires a value to be provided"); + } + break; +case 856: + case_856(); + break; +case 861: + case_861(); + break; +case 863: + case_863(); + break; +case 864: + case_864(); + break; +case 865: + case_865(); + break; +case 866: +#line 5805 "cs-parser.jay" + { yyVal = yyVals[-1+yyTop]; } + break; +case 867: + case_867(); + break; +case 868: +#line 5815 "cs-parser.jay" + { yyVal = yyVals[-1+yyTop]; } + break; +case 869: +#line 5816 "cs-parser.jay" + { yyVal = yyVals[-1+yyTop]; } + break; +case 870: + case_870(); + break; +case 871: + case_871(); + break; +case 872: + case_872(); + break; +case 875: + case_875(); + break; +case 876: + case_876(); + break; +case 877: + case_877(); + break; +case 878: +#line 5888 "cs-parser.jay" + { + start_block (GetLocation (yyVals[0+yyTop])); + } + break; +case 879: + case_879(); + break; +case 880: + case_880(); + break; +case 881: +#line 5908 "cs-parser.jay" + { + report.Warning (1522, 1, current_block.StartLocation, "Empty switch block"); + } + break; +case 885: +#line 5918 "cs-parser.jay" + { + Error_SyntaxError (yyToken); + } + break; +case 887: + case_887(); + break; +case 888: +#line 5935 "cs-parser.jay" + { + current_block.AddStatement ((Statement) yyVals[0+yyTop]); + } + break; +case 889: + case_889(); + break; +case 890: + case_890(); + break; +case 891: +#line 5952 "cs-parser.jay" + { + yyVal = new SwitchLabel (null, GetLocation (yyVals[0+yyTop])); + } + break; +case 896: + case_896(); + break; +case 897: + case_897(); + break; +case 898: + case_898(); + break; +case 899: + case_899(); + break; +case 900: + case_900(); + break; +case 901: + case_901(); + break; +case 902: +#line 6013 "cs-parser.jay" + { + yyVal = yyVals[0+yyTop]; + } + break; +case 903: + case_903(); + break; +case 904: +#line 6028 "cs-parser.jay" + { + yyVal = yyVals[0+yyTop]; + } + break; +case 905: + case_905(); + break; +case 906: + case_906(); + break; +case 907: +#line 6049 "cs-parser.jay" + { + yyVal = yyVals[0+yyTop]; + } + break; +case 908: + case_908(); + break; +case 909: + case_909(); + break; +case 910: + case_910(); + break; +case 911: +#line 6083 "cs-parser.jay" + { yyVal = new EmptyStatement (lexer.Location); } + break; +case 913: + case_913(); + break; +case 914: + case_914(); + break; +case 916: +#line 6107 "cs-parser.jay" + { yyVal = null; } + break; +case 918: +#line 6112 "cs-parser.jay" + { yyVal = new EmptyStatement (lexer.Location); } + break; +case 922: + case_922(); + break; +case 923: + case_923(); + break; +case 924: + case_924(); + break; +case 925: + case_925(); + break; +case 926: + case_926(); + break; +case 927: + case_927(); + break; +case 928: + case_928(); + break; +case 935: + case_935(); + break; +case 936: + case_936(); + break; +case 937: + case_937(); + break; +case 938: + case_938(); + break; +case 939: + case_939(); + break; +case 940: + case_940(); + break; +case 941: + case_941(); + break; +case 942: + case_942(); + break; +case 943: + case_943(); + break; +case 944: + case_944(); + break; +case 945: + case_945(); + break; +case 946: + case_946(); + break; +case 947: + case_947(); + break; +case 948: + case_948(); + break; +case 949: + case_949(); + break; +case 952: +#line 6358 "cs-parser.jay" + { + yyVal = new TryCatch ((Block) yyVals[-1+yyTop], (List) yyVals[0+yyTop], GetLocation (yyVals[-2+yyTop]), false); + } + break; +case 953: + case_953(); + break; +case 954: + case_954(); + break; +case 955: + case_955(); + break; +case 956: + case_956(); + break; +case 957: + case_957(); + break; +case 960: + case_960(); + break; +case 961: + case_961(); + break; +case 962: + case_962(); + break; +case 963: + case_963(); + break; +case 964: + case_964(); + break; +case 966: + case_966(); + break; +case 967: +#line 6483 "cs-parser.jay" + { + yyVal = new Checked ((Block) yyVals[0+yyTop], GetLocation (yyVals[-1+yyTop])); + } + break; +case 968: +#line 6490 "cs-parser.jay" + { + yyVal = new Unchecked ((Block) yyVals[0+yyTop], GetLocation (yyVals[-1+yyTop])); + } + break; +case 969: + case_969(); + break; +case 970: +#line 6500 "cs-parser.jay" + { + yyVal = new Unsafe ((Block) yyVals[0+yyTop], GetLocation (yyVals[-2+yyTop])); + } + break; +case 971: + case_971(); + break; +case 972: + case_972(); + break; +case 973: + case_973(); + break; +case 974: + case_974(); + break; +case 975: + case_975(); + break; +case 976: + case_976(); + break; +case 977: + case_977(); + break; +case 978: + case_978(); + break; +case 979: + case_979(); + break; +case 980: + case_980(); + break; +case 982: + case_982(); + break; +case 983: +#line 6605 "cs-parser.jay" + { + Error_MissingInitializer (lexer.Location); + } + break; +case 984: + case_984(); + break; +case 985: + case_985(); + break; +case 986: + case_986(); + break; +case 987: + case_987(); + break; +case 988: + case_988(); + break; +case 989: + case_989(); + break; +case 990: + case_990(); + break; +case 991: + case_991(); + break; +case 992: + case_992(); + break; +case 993: +#line 6710 "cs-parser.jay" + { + current_block = new Linq.QueryBlock (current_block, lexer.Location); + } + break; +case 994: + case_994(); + break; +case 995: +#line 6725 "cs-parser.jay" + { + current_block = new Linq.QueryBlock (current_block, lexer.Location); + } + break; +case 996: + case_996(); + break; +case 997: + case_997(); + break; +case 998: + case_998(); + break; +case 1000: + case_1000(); + break; +case 1001: + case_1001(); + break; +case 1002: +#line 6789 "cs-parser.jay" + { + current_block = new Linq.QueryBlock (current_block, lexer.Location); + } + break; +case 1003: + case_1003(); + break; +case 1004: + case_1004(); + break; +case 1005: + case_1005(); + break; +case 1006: + case_1006(); + break; +case 1007: +#line 6828 "cs-parser.jay" + { + yyVal = new object[] { yyVals[0+yyTop], GetLocation (yyVals[-1+yyTop]) }; + } + break; +case 1008: + case_1008(); + break; +case 1010: + case_1010(); + break; +case 1016: +#line 6857 "cs-parser.jay" + { + current_block = new Linq.QueryBlock (current_block, lexer.Location); + } + break; +case 1017: + case_1017(); + break; +case 1018: +#line 6876 "cs-parser.jay" + { + current_block = new Linq.QueryBlock (current_block, lexer.Location); + } + break; +case 1019: + case_1019(); + break; +case 1020: + case_1020(); + break; +case 1021: + case_1021(); + break; +case 1022: + case_1022(); + break; +case 1023: + case_1023(); + break; +case 1024: + case_1024(); + break; +case 1025: + case_1025(); + break; +case 1026: + case_1026(); + break; +case 1027: + case_1027(); + break; +case 1029: + case_1029(); + break; +case 1030: + case_1030(); + break; +case 1031: + case_1031(); + break; +case 1033: + case_1033(); + break; +case 1034: + case_1034(); + break; +case 1036: + case_1036(); + break; +case 1037: + case_1037(); + break; +case 1038: +#line 7077 "cs-parser.jay" + { + yyVal = new Linq.OrderByAscending ((Linq.QueryBlock) current_block, (Expression)yyVals[0+yyTop]); + } + break; +case 1039: + case_1039(); + break; +case 1040: + case_1040(); + break; +case 1041: +#line 7094 "cs-parser.jay" + { + yyVal = new Linq.ThenByAscending ((Linq.QueryBlock) current_block, (Expression)yyVals[0+yyTop]); + } + break; +case 1042: + case_1042(); + break; +case 1043: + case_1043(); + break; +case 1045: + case_1045(); + break; +case 1046: + case_1046(); + break; +case 1049: + case_1049(); + break; +case 1050: + case_1050(); + break; +case 1058: +#line 7219 "cs-parser.jay" + { + module.DocumentationBuilder.ParsedName = (MemberName) yyVals[0+yyTop]; + } + break; +case 1059: +#line 7226 "cs-parser.jay" + { + module.DocumentationBuilder.ParsedParameters = (List)yyVals[0+yyTop]; + } + break; +case 1060: + case_1060(); + break; +case 1061: + case_1061(); + break; +case 1062: + case_1062(); + break; +case 1063: +#line 7249 "cs-parser.jay" + { + yyVal = new MemberName ((MemberName) yyVals[-2+yyTop], MemberCache.IndexerNameAlias, Location.Null); + } + break; +case 1064: +#line 7253 "cs-parser.jay" + { + valid_param_mod = ParameterModifierType.Ref | ParameterModifierType.Out; + } + break; +case 1065: + case_1065(); + break; +case 1066: + case_1066(); + break; +case 1067: + case_1067(); + break; +case 1068: + case_1068(); + break; +case 1070: +#line 7289 "cs-parser.jay" + { + yyVal = new MemberName (((MemberName) yyVals[-2+yyTop]), (MemberName) yyVals[0+yyTop]); + } + break; +case 1072: +#line 7297 "cs-parser.jay" + { + valid_param_mod = ParameterModifierType.Ref | ParameterModifierType.Out; + } + break; +case 1073: +#line 7301 "cs-parser.jay" + { + yyVal = yyVals[-1+yyTop]; + } + break; +case 1074: +#line 7308 "cs-parser.jay" + { + yyVal = new List (0); + } + break; +case 1076: + case_1076(); + break; +case 1077: + case_1077(); + break; +case 1078: + case_1078(); + break; +#line default + } + yyTop -= yyLen[yyN]; + yyState = yyStates[yyTop]; + int yyM = yyLhs[yyN]; + if (yyState == 0 && yyM == 0) { +//t if (debug != null) debug.shift(0, yyFinal); + yyState = yyFinal; + if (yyToken < 0) { + yyToken = yyLex.advance() ? yyLex.token() : 0; +//t if (debug != null) +//t debug.lex(yyState, yyToken,yyname(yyToken), yyLex.value()); + } + if (yyToken == 0) { +//t if (debug != null) debug.accept(yyVal); + return yyVal; + } + goto continue_yyLoop; + } + if (((yyN = yyGindex[yyM]) != 0) && ((yyN += yyState) >= 0) + && (yyN < yyTable.Length) && (yyCheck[yyN] == yyState)) + yyState = yyTable[yyN]; + else + yyState = yyDgoto[yyM]; +//t if (debug != null) debug.shift(yyStates[yyTop], yyState); + goto continue_yyLoop; + continue_yyDiscarded: ; // implements the named-loop continue: 'continue yyDiscarded' + } + continue_yyLoop: ; // implements the named-loop continue: 'continue yyLoop' + } + } + +/* + All more than 3 lines long rules are wrapped into a method +*/ +void case_6() +#line 397 "cs-parser.jay" +{ + if (yyVals[0+yyTop] != null) { + Attributes attrs = (Attributes) yyVals[0+yyTop]; + report.Error (1730, attrs.Attrs [0].Location, + "Assembly and module attributes must precede all other elements except using clauses and extern alias declarations"); + + current_namespace.UnattachedAttributes = attrs; + } + } + +void case_8() +#line 411 "cs-parser.jay" +{ + if (yyToken == Token.EXTERN_ALIAS) + report.Error (439, lexer.Location, "An extern alias declaration must precede all other elements"); + else + Error_SyntaxError (yyToken); + } + +void case_13() +#line 431 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-2+yyTop]; + string s = lt.Value; + if (s != "alias") { + syntax_error (lt.Location, "`alias' expected"); + } else { + if (lang_version == LanguageVersion.ISO_1) + FeatureIsNotAvailable (lt.Location, "external alias"); + + lt = (LocatedToken) yyVals[-1+yyTop]; + if (lt.Value == QualifiedAliasMember.GlobalAlias) { + RootNamespace.Error_GlobalNamespaceRedefined (report, lt.Location); + } + + var na = new UsingExternAlias (new SimpleMemberName (lt.Value, lt.Location), GetLocation (yyVals[-3+yyTop])); + current_namespace.AddUsing (na); + + lbag.AddLocation (na, GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + } + +void case_17() +#line 464 "cs-parser.jay" +{ + if (doc_support) + Lexer.doc_state = XmlCommentState.Allowed; + } + +void case_18() +#line 472 "cs-parser.jay" +{ + var un = new UsingNamespace ((ATypeNameExpression) yyVals[-1+yyTop], GetLocation (yyVals[-2+yyTop])); + current_namespace.AddUsing (un); + + lbag.AddLocation (un, GetLocation (yyVals[0+yyTop])); + } + +void case_19() +#line 479 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-3+yyTop]; + if (lang_version != LanguageVersion.ISO_1 && lt.Value == "global") { + report.Warning (440, 2, lt.Location, + "An alias named `global' will not be used when resolving `global::'. The global namespace will be used instead"); + } + + var un = new UsingAliasNamespace (new SimpleMemberName (lt.Value, lt.Location), (ATypeNameExpression) yyVals[-1+yyTop], GetLocation (yyVals[-4+yyTop])); + current_namespace.AddUsing (un); + lbag.AddLocation (un, GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_20() +#line 491 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = null; + } + +void case_21() +#line 504 "cs-parser.jay" +{ + Attributes attrs = (Attributes) yyVals[-2+yyTop]; + var name = (MemberName) yyVals[0+yyTop]; + if (attrs != null) { + bool valid_global_attrs = true; + if ((current_namespace.DeclarationFound || current_namespace != file)) { + valid_global_attrs = false; + } else { + foreach (var a in attrs.Attrs) { + if (a.ExplicitTarget == "assembly" || a.ExplicitTarget == "module") + continue; + + valid_global_attrs = false; + break; + } + } + + if (!valid_global_attrs) + report.Error (1671, name.Location, "A namespace declaration cannot have modifiers or attributes"); + } + + module.AddAttributes (attrs, current_namespace); + + var ns = new NamespaceContainer (name, current_namespace); + current_namespace.AddTypeContainer (ns); + current_container = current_namespace = ns; + } + +void case_22() +#line 532 "cs-parser.jay" +{ + if (doc_support) + Lexer.doc_state = XmlCommentState.Allowed; + } + +void case_23() +#line 537 "cs-parser.jay" +{ + if (yyVals[0+yyTop] != null) + lbag.AddLocation (current_container, GetLocation (yyVals[-9+yyTop]), GetLocation (yyVals[-6+yyTop]), GetLocation (yyVals[-1+yyTop]), GetLocation (yyVals[0+yyTop])); + else + lbag.AddLocation (current_container, GetLocation (yyVals[-9+yyTop]), GetLocation (yyVals[-6+yyTop]), GetLocation (yyVals[-1+yyTop])); + + current_container = current_namespace = current_namespace.Parent; + } + +void case_24() +#line 546 "cs-parser.jay" +{ + report.Error (1514, lexer.Location, "Unexpected symbol `{0}', expecting `.' or `{{'", GetSymbolName (yyToken)); + + var name = (MemberName) yyVals[0+yyTop]; + var ns = new NamespaceContainer (name, current_namespace); + lbag.AddLocation (ns, GetLocation (yyVals[-1+yyTop])); + current_namespace.AddTypeContainer (ns); + } + +void case_27() +#line 560 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = null; + } + +void case_28() +#line 568 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[0+yyTop]; + yyVal = new MemberName (lt.Value, lt.Location); + } + +void case_29() +#line 573 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[0+yyTop]; + yyVal = new MemberName ((MemberName) yyVals[-2+yyTop], lt.Value, lt.Location); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_30() +#line 579 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = new MemberName ("", lexer.Location); + } + +void case_43() +#line 617 "cs-parser.jay" +{ + if (yyVals[0+yyTop] != null) { + TypeContainer ds = (TypeContainer)yyVals[0+yyTop]; + + if ((ds.ModFlags & (Modifiers.PRIVATE | Modifiers.PROTECTED)) != 0){ + report.Error (1527, ds.Location, + "Namespace elements cannot be explicitly declared as private, protected or protected internal"); + } + + /* Here is a trick, for explicit attributes we don't know where they belong to until*/ + /* we parse succeeding declaration hence we parse them as normal and re-attach them*/ + /* when we know whether they are global (assembly:, module:) or local (type:).*/ + if (ds.OptAttributes != null) { + ds.OptAttributes.ConvertGlobalAttributes (ds, current_namespace, !current_namespace.DeclarationFound && current_namespace == file); + } + } + current_namespace.DeclarationFound = true; + } + +void case_45() +#line 639 "cs-parser.jay" +{ + current_namespace.UnattachedAttributes = (Attributes) yyVals[-1+yyTop]; + report.Error (1518, lexer.Location, "Attributes must be attached to class, delegate, enum, interface or struct"); + lexer.putback ('}'); + } + +void case_53() +#line 672 "cs-parser.jay" +{ + var sect = (List) yyVals[0+yyTop]; + yyVal = new Attributes (sect); + } + +void case_54() +#line 677 "cs-parser.jay" +{ + Attributes attrs = yyVals[-1+yyTop] as Attributes; + var sect = (List) yyVals[0+yyTop]; + if (attrs == null) + attrs = new Attributes (sect); + else if (sect != null) + attrs.AddAttributes (sect); + yyVal = attrs; + } + +void case_55() +#line 690 "cs-parser.jay" +{ + PushLocation (GetLocation (yyVals[0+yyTop])); + lexer.parsing_attribute_section = true; + } + +void case_56() +#line 695 "cs-parser.jay" +{ + lexer.parsing_attribute_section = false; + yyVal = yyVals[0+yyTop]; + } + +void case_57() +#line 703 "cs-parser.jay" +{ + current_attr_target = (string) yyVals[-1+yyTop]; + if (current_attr_target == "assembly" || current_attr_target == "module") { + Lexer.check_incorrect_doc_comment (); + } + } + +void case_58() +#line 710 "cs-parser.jay" +{ + /* when attribute target is invalid*/ + if (current_attr_target == string.Empty) + yyVal = new List (0); + else + yyVal = yyVals[-2+yyTop]; + lbag.InsertLocation (yyVal, 0, GetLocation (yyVals[-4+yyTop])); + lbag.InsertLocation (yyVal, 0, PopLocation ()); + lbag.InsertLocation (yyVal, 0, PopLocation ()); + if (yyVals[-1+yyTop] != null) { + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop]), GetLocation (yyVals[0+yyTop])); + } else { + lbag.AddLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + + current_attr_target = null; + lexer.parsing_attribute_section = false; + } + +void case_59() +#line 729 "cs-parser.jay" +{ + yyVal = yyVals[-2+yyTop]; + lbag.InsertLocation (yyVal, 0, PopLocation ()); + if (yyVals[-1+yyTop] != null) { + lbag.AddLocation (yyVal, GetLocation(yyVals[-1+yyTop]), GetLocation (yyVals[0+yyTop])); + } else { + lbag.AddLocation (yyVal, GetLocation(yyVals[0+yyTop])); + } + } + +void case_60() +#line 739 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + var lt = (LocatedToken) yyVals[-1+yyTop]; + var tne = new SimpleName (lt.Value, null, lt.Location); + + yyVal = new List () { + new Attribute (null, tne, null, GetLocation (yyVals[-1+yyTop]), false) + }; + } + +void case_61() +#line 750 "cs-parser.jay" +{ + CheckAttributeTarget (yyToken, GetTokenName (yyToken), GetLocation (yyVals[0+yyTop])); + yyVal = null; + } + +void case_62() +#line 758 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[0+yyTop]; + yyVal = CheckAttributeTarget (yyToken, lt.Value, lt.Location); + PushLocation (GetLocation (yyVals[0+yyTop])); + } + +void case_66() +#line 773 "cs-parser.jay" +{ + var attrs = (List) yyVals[-2+yyTop]; + if (attrs != null) { + attrs.Add ((Attribute) yyVals[0+yyTop]); + lbag.AddLocation (attrs, GetLocation (yyVals[-1+yyTop])); + } + + yyVal = attrs; + } + +void case_68() +#line 790 "cs-parser.jay" +{ + --lexer.parsing_block; + + var tne = (ATypeNameExpression) yyVals[-2+yyTop]; + if (tne.HasTypeArguments) { + report.Error (404, tne.Location, "Attributes cannot be generic"); + } + Arguments [] arguments = (Arguments []) yyVals[0+yyTop]; + + yyVal = new Attribute (current_attr_target, tne, (Arguments[]) yyVals[0+yyTop], GetLocation (yyVals[-2+yyTop]), lexer.IsEscapedIdentifier (tne)); + if (arguments != null) { + attributeArgumentCommas.Insert (0, savedAttrParenOpenLocation); + attributeArgumentCommas.Add (savedAttrParenCloseLocation); + lbag.AddLocation (yyVal, attributeArgumentCommas); + attributeArgumentCommas.Clear (); + } else if (HadAttributeParens) { + lbag.AddLocation (yyVal, savedAttrParenOpenLocation, savedAttrParenCloseLocation); + } + } + +void case_71() +#line 818 "cs-parser.jay" +{ + savedAttrParenOpenLocation = GetLocation (yyVals[-2+yyTop]); + savedAttrParenCloseLocation = GetLocation (yyVals[0+yyTop]); + yyVal = yyVals[-1+yyTop]; + HadAttributeParens = true; + } + +void case_73() +#line 830 "cs-parser.jay" +{ + Arguments a = new Arguments (4); + a.Add ((Argument) yyVals[0+yyTop]); + yyVal = new Arguments [] { a, null }; + } + +void case_74() +#line 836 "cs-parser.jay" +{ + Arguments a = new Arguments (4); + a.Add ((Argument) yyVals[0+yyTop]); + yyVal = new Arguments [] { null, a }; + } + +void case_75() +#line 842 "cs-parser.jay" +{ + Arguments[] o = (Arguments[]) yyVals[-2+yyTop]; + if (o [1] != null) { + report.Error (1016, ((Argument) yyVals[0+yyTop]).Expr.Location, "Named attribute arguments must appear after the positional arguments"); + o [0] = new Arguments (4); + } + + Arguments args = ((Arguments) o [0]); + if (args.Count > 0 && !(yyVals[0+yyTop] is NamedArgument) && args [args.Count - 1] is NamedArgument) + Error_NamedArgumentExpected ((NamedArgument) args [args.Count - 1]); + + args.Add ((Argument) yyVals[0+yyTop]); + attributeArgumentCommas.Add (GetLocation (yyVals[-1+yyTop])); + } + +void case_76() +#line 857 "cs-parser.jay" +{ + Arguments[] o = (Arguments[]) yyVals[-2+yyTop]; + if (o [1] == null) { + o [1] = new Arguments (4); + } + + ((Arguments) o [1]).Add ((Argument) yyVals[0+yyTop]); + attributeArgumentCommas.Add (GetLocation (yyVals[-1+yyTop])); + } + +void case_79() +#line 875 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = null; + } + +void case_81() +#line 887 "cs-parser.jay" +{ + --lexer.parsing_block; + var lt = (LocatedToken) yyVals[-3+yyTop]; + yyVal = new NamedArgument (lt.Value, lt.Location, (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation(yyVals[-2+yyTop])); + } + +void case_82() +#line 897 "cs-parser.jay" +{ + if (lang_version <= LanguageVersion.V_3) + FeatureIsNotAvailable (GetLocation (yyVals[-3+yyTop]), "named argument"); + + /* Avoid boxing in common case (no modifier)*/ + var arg_mod = yyVals[-1+yyTop] == null ? Argument.AType.None : (Argument.AType) yyVals[-1+yyTop]; + + var lt = (LocatedToken) yyVals[-3+yyTop]; + yyVal = new NamedArgument (lt.Value, lt.Location, (Expression) yyVals[0+yyTop], arg_mod); + lbag.AddLocation (yyVal, GetLocation(yyVals[-2+yyTop])); + } + +void case_88() +#line 929 "cs-parser.jay" +{ + lexer.parsing_modifiers = true; + lexer.parsing_block = 0; + } + +void case_89() +#line 934 "cs-parser.jay" +{ + lexer.parsing_modifiers = true; + lexer.parsing_block = 0; + } + +void case_103() +#line 955 "cs-parser.jay" +{ + report.Error (1519, lexer.Location, "Unexpected symbol `{0}' in class, struct, or interface member declaration", + GetSymbolName (yyToken)); + yyVal = null; + lexer.parsing_generic_declaration = false; + } + +void case_104() +#line 965 "cs-parser.jay" +{ + current_local_parameters = current_type.PrimaryConstructorParameters; + if (current_local_parameters == null) { + report.Error (9010, GetLocation (yyVals[0+yyTop]), "Primary constructor body is not allowed"); + current_local_parameters = ParametersCompiled.EmptyReadOnlyParameters; + } + + ++lexer.parsing_block; + start_block (GetLocation (yyVals[0+yyTop])); + } + +void case_105() +#line 976 "cs-parser.jay" +{ + current_local_parameters = null; + var t = current_type as ClassOrStruct; + if (t != null) { + var b = (ToplevelBlock) yyVals[0+yyTop]; + if (t.PrimaryConstructorBlock != null) { + report.Error (8041, b.StartLocation, "Primary constructor already has a body"); + } else { + t.PrimaryConstructorBlock = b; + } + } + } + +void case_107() +#line 998 "cs-parser.jay" +{ + lexer.ConstraintsParsing = true; + valid_param_mod = ParameterModifierType.PrimaryConstructor; + push_current_container (new Struct (current_container, (MemberName) yyVals[0+yyTop], (Modifiers) yyVals[-4+yyTop], (Attributes) yyVals[-5+yyTop]), yyVals[-3+yyTop]); + lbag.AddMember (current_container, GetModifierLocations (), GetLocation (yyVals[-2+yyTop])); + } + +void case_108() +#line 1007 "cs-parser.jay" +{ + valid_param_mod = 0; + lexer.ConstraintsParsing = false; + + if (yyVals[-2+yyTop] != null) + current_type.PrimaryConstructorParameters = (ParametersCompiled) yyVals[-2+yyTop]; + + if (yyVals[0+yyTop] != null) + current_container.SetConstraints ((List) yyVals[0+yyTop]); + + if (doc_support) + current_container.PartialContainer.DocComment = Lexer.consume_doc_comment (); + + + lexer.parsing_modifiers = true; + } + +void case_109() +#line 1024 "cs-parser.jay" +{ + if (doc_support) + Lexer.doc_state = XmlCommentState.Allowed; + } + +void case_110() +#line 1029 "cs-parser.jay" +{ + --lexer.parsing_declaration; + if (doc_support) + Lexer.doc_state = XmlCommentState.Allowed; + } + +void case_111() +#line 1035 "cs-parser.jay" +{ + if (yyVals[-1+yyTop] == null) { + lbag.AppendToMember (current_container, GetLocation (yyVals[-5+yyTop]), GetLocation (yyVals[-2+yyTop])); + } else { + lbag.AppendToMember (current_container, GetLocation (yyVals[-5+yyTop]), GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + yyVal = pop_current_class (); + } + +void case_113() +#line 1053 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[0+yyTop]; + var mod = (Modifiers) yyVals[-3+yyTop]; + current_field = new Const (current_type, (FullNamedExpression) yyVals[-1+yyTop], mod, new MemberName (lt.Value, lt.Location), (Attributes) yyVals[-4+yyTop]); + current_type.AddMember (current_field); + + if ((mod & Modifiers.STATIC) != 0) { + report.Error (504, current_field.Location, "The constant `{0}' cannot be marked static", current_field.GetSignatureForError ()); + } + + yyVal = current_field; + } + +void case_114() +#line 1066 "cs-parser.jay" +{ + if (doc_support) { + current_field.DocComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + current_field.Initializer = (ConstInitializer) yyVals[-2+yyTop]; + lbag.AddMember (current_field, GetModifierLocations (), GetLocation (yyVals[-6+yyTop]), GetLocation (yyVals[0+yyTop])); + current_field = null; + } + +void case_115() +#line 1079 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + current_type.AddMember (new Const (current_type, (FullNamedExpression) yyVals[-1+yyTop], (Modifiers) yyVals[-3+yyTop], MemberName.Null, (Attributes) yyVals[-4+yyTop])); + } + +void case_120() +#line 1104 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-1+yyTop]; + yyVal = new FieldDeclarator (new SimpleMemberName (lt.Value, lt.Location), (ConstInitializer) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_122() +#line 1117 "cs-parser.jay" +{ + --lexer.parsing_block; + yyVal = new ConstInitializer (current_field, (Expression) yyVals[0+yyTop], GetLocation (yyVals[-2+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_123() +#line 1123 "cs-parser.jay" +{ + report.Error (145, lexer.Location, "A const field requires a value to be provided"); + yyVal = null; + } + +void case_126() +#line 1138 "cs-parser.jay" +{ + lexer.parsing_generic_declaration = false; + + FullNamedExpression type = (FullNamedExpression) yyVals[-1+yyTop]; + if (type.Type != null && type.Type.Kind == MemberKind.Void) + report.Error (670, GetLocation (yyVals[-1+yyTop]), "Fields cannot have void type"); + + var lt = (LocatedToken) yyVals[0+yyTop]; + current_field = new Field (current_type, type, (Modifiers) yyVals[-2+yyTop], new MemberName (lt.Value, lt.Location), (Attributes) yyVals[-3+yyTop]); + current_type.AddField (current_field); + yyVal = current_field; + } + +void case_127() +#line 1153 "cs-parser.jay" +{ + if (doc_support) { + current_field.DocComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + lbag.AddMember (current_field, GetModifierLocations (), GetLocation (yyVals[0+yyTop])); + yyVal = current_field; + current_field = null; + } + +void case_128() +#line 1166 "cs-parser.jay" +{ + if (lang_version < LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation (yyVals[-2+yyTop]), "fixed size buffers"); + + var lt = (LocatedToken) yyVals[0+yyTop]; + current_field = new FixedField (current_type, (FullNamedExpression) yyVals[-1+yyTop], (Modifiers) yyVals[-3+yyTop], + new MemberName (lt.Value, lt.Location), (Attributes) yyVals[-4+yyTop]); + + current_type.AddField (current_field); + } + +void case_129() +#line 1177 "cs-parser.jay" +{ + if (doc_support) { + current_field.DocComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + current_field.Initializer = (ConstInitializer) yyVals[-2+yyTop]; + lbag.AddMember (current_field, GetModifierLocations (), GetLocation (yyVals[-6+yyTop]), GetLocation (yyVals[0+yyTop])); + yyVal = current_field; + current_field = null; + } + +void case_132() +#line 1200 "cs-parser.jay" +{ + ++lexer.parsing_block; + current_local_parameters = ParametersCompiled.EmptyReadOnlyParameters; + start_block (GetLocation (yyVals[0+yyTop])); + } + +void case_133() +#line 1206 "cs-parser.jay" +{ + --lexer.parsing_block; + current_field.Initializer = (Expression) yyVals[0+yyTop]; + lbag.AppendToMember (current_field, GetLocation (yyVals[-2+yyTop])); + end_block (lexer.Location); + current_local_parameters = null; + } + +void case_138() +#line 1233 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[0+yyTop]; + yyVal = new FieldDeclarator (new SimpleMemberName (lt.Value, lt.Location), null); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_140() +#line 1243 "cs-parser.jay" +{ + --lexer.parsing_block; + var lt = (LocatedToken) yyVals[-3+yyTop]; + yyVal = new FieldDeclarator (new SimpleMemberName (lt.Value, lt.Location), (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-4+yyTop]), GetLocation (yyVals[-2+yyTop])); + } + +void case_145() +#line 1269 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-1+yyTop]; + yyVal = new FieldDeclarator (new SimpleMemberName (lt.Value, lt.Location), (ConstInitializer) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_147() +#line 1282 "cs-parser.jay" +{ + --lexer.parsing_block; + yyVal = new ConstInitializer (current_field, (Expression) yyVals[-1+yyTop], GetLocation (yyVals[-3+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_148() +#line 1288 "cs-parser.jay" +{ + report.Error (443, lexer.Location, "Value or constant expected"); + yyVal = null; + } + +void case_151() +#line 1298 "cs-parser.jay" +{ + /* It has to be here for the parent to safely restore artificial block*/ + Error_SyntaxError (yyToken); + yyVal = null; + } + +void case_152() +#line 1307 "cs-parser.jay" +{ + if (doc_support) + Lexer.doc_state = XmlCommentState.NotAllowed; + + /* Was added earlier in the case of body being eof for full ast*/ + } + +void case_153() +#line 1314 "cs-parser.jay" +{ + Method method = (Method) yyVals[-2+yyTop]; + method.Block = (ToplevelBlock) yyVals[0+yyTop]; + async_block = false; + + if (method.Block == null) { + lbag.AppendToMember (method, savedLocation); /* semicolon*/ + method.ParameterInfo.CheckParameters (method); + + if ((method.ModFlags & Modifiers.ASYNC) != 0) { + report.Error (1994, method.Location, "`{0}': The async modifier can only be used with methods that have a body", + method.GetSignatureForError ()); + } + } else { + if (current_container.Kind == MemberKind.Interface) { + report.Error (531, method.Location, "`{0}': interface members cannot have a definition", + method.GetSignatureForError ()); + } + } + + current_local_parameters = null; + + if (doc_support) + Lexer.doc_state = XmlCommentState.Allowed; + } + +void case_155() +#line 1350 "cs-parser.jay" +{ + valid_param_mod = 0; + MemberName name = (MemberName) yyVals[-4+yyTop]; + current_local_parameters = (ParametersCompiled) yyVals[-1+yyTop]; + + var method = Method.Create (current_type, (FullNamedExpression) yyVals[-5+yyTop], (Modifiers) yyVals[-6+yyTop], + name, current_local_parameters, (Attributes) yyVals[-7+yyTop]); + + current_type.AddMember (method); + + async_block = (method.ModFlags & Modifiers.ASYNC) != 0; + + if (doc_support) + method.DocComment = Lexer.consume_doc_comment (); + + lbag.AddMember (method, GetModifierLocations (), GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[0+yyTop])); + yyVal = method; + + lexer.ConstraintsParsing = true; + } + +void case_156() +#line 1371 "cs-parser.jay" +{ + lexer.ConstraintsParsing = false; + + if (yyVals[0+yyTop] != null) { + var method = (Method) yyVals[-1+yyTop]; + method.SetConstraints ((List) yyVals[0+yyTop]); + } + + yyVal = yyVals[-1+yyTop]; + } + +void case_158() +#line 1390 "cs-parser.jay" +{ + lexer.parsing_generic_declaration = false; + valid_param_mod = ParameterModifierType.All; + } + +void case_160() +#line 1399 "cs-parser.jay" +{ + lexer.ConstraintsParsing = false; + valid_param_mod = 0; + + MemberName name = (MemberName) yyVals[-6+yyTop]; + current_local_parameters = (ParametersCompiled) yyVals[-3+yyTop]; + + var modifiers = (Modifiers) yyVals[-10+yyTop]; + modifiers |= Modifiers.PARTIAL; + + var method = Method.Create (current_type, new TypeExpression (compiler.BuiltinTypes.Void, GetLocation (yyVals[-8+yyTop])), + modifiers, name, current_local_parameters, (Attributes) yyVals[-11+yyTop]); + + current_type.AddMember (method); + + async_block = (method.ModFlags & Modifiers.ASYNC) != 0; + + if (yyVals[0+yyTop] != null) + method.SetConstraints ((List) yyVals[0+yyTop]); + + if (doc_support) + method.DocComment = Lexer.consume_doc_comment (); + + StoreModifierLocation (Modifiers.PARTIAL, GetLocation (yyVals[-9+yyTop])); + lbag.AddMember (method, GetModifierLocations (), GetLocation (yyVals[-5+yyTop]), GetLocation (yyVals[-2+yyTop])); + yyVal = method; + } + +void case_161() +#line 1430 "cs-parser.jay" +{ + MemberName name = (MemberName) yyVals[-3+yyTop]; + report.Error (1585, name.Location, + "Member modifier `{0}' must precede the member type and name", ModifiersExtensions.Name ((Modifiers) yyVals[-4+yyTop])); + + var method = Method.Create (current_type, (FullNamedExpression) yyVals[-5+yyTop], + 0, name, (ParametersCompiled) yyVals[-1+yyTop], (Attributes) yyVals[-7+yyTop]); + + current_type.AddMember (method); + + current_local_parameters = (ParametersCompiled) yyVals[-1+yyTop]; + + if (doc_support) + method.DocComment = Lexer.consume_doc_comment (); + + yyVal = method; + } + +void case_162() +#line 1451 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + current_local_parameters = ParametersCompiled.Undefined; + + MemberName name = (MemberName) yyVals[-1+yyTop]; + var method = Method.Create (current_type, (FullNamedExpression) yyVals[-2+yyTop], (Modifiers) yyVals[-3+yyTop], + name, current_local_parameters, (Attributes) yyVals[-4+yyTop]); + + current_type.AddMember (method); + + if (doc_support) + method.DocComment = Lexer.consume_doc_comment (); + + yyVal = method; + } + +void case_167() +#line 1480 "cs-parser.jay" +{ + if (lang_version < LanguageVersion.V_6) { + FeatureIsNotAvailable (GetLocation (yyVals[0+yyTop]), "expression bodied members"); + } + + ++lexer.parsing_block; + start_block (GetLocation (yyVals[0+yyTop])); + } + +void case_168() +#line 1489 "cs-parser.jay" +{ + lexer.parsing_block = 0; + current_block.AddStatement (new ContextualReturn ((Expression) yyVals[-1+yyTop])); + var b = end_block (GetLocation (yyVals[0+yyTop])); + b.IsCompilerGenerated = true; + yyVal = b; + } + +void case_171() +#line 1505 "cs-parser.jay" +{ + var pars_list = (List) yyVals[0+yyTop]; + yyVal = new ParametersCompiled (pars_list.ToArray ()); + lbag.AddLocation (yyVal, parameterListCommas); + } + +void case_172() +#line 1511 "cs-parser.jay" +{ + var pars_list = (List) yyVals[-2+yyTop]; + pars_list.Add ((Parameter) yyVals[0+yyTop]); + parameterListCommas.Add (GetLocation (yyVals[-1+yyTop])); + + yyVal = new ParametersCompiled (pars_list.ToArray ()); + lbag.AddLocation (yyVal, parameterListCommas); + } + +void case_173() +#line 1520 "cs-parser.jay" +{ + var pars_list = (List) yyVals[-2+yyTop]; + pars_list.Add (new ArglistParameter (GetLocation (yyVals[0+yyTop]))); + parameterListCommas.Add (GetLocation (yyVals[-1+yyTop])); + + yyVal = new ParametersCompiled (pars_list.ToArray (), true); + lbag.AddLocation (yyVal, parameterListCommas); + } + +void case_174() +#line 1529 "cs-parser.jay" +{ + if (yyVals[-2+yyTop] != null) + report.Error (231, ((Parameter) yyVals[-2+yyTop]).Location, "A params parameter must be the last parameter in a formal parameter list"); + + yyVal = new ParametersCompiled (new Parameter[] { (Parameter) yyVals[-2+yyTop] } ); + lbag.AddLocation (yyVal, parameterListCommas); + } + +void case_175() +#line 1537 "cs-parser.jay" +{ + if (yyVals[-2+yyTop] != null) + report.Error (231, ((Parameter) yyVals[-2+yyTop]).Location, "A params parameter must be the last parameter in a formal parameter list"); + + var pars_list = (List) yyVals[-4+yyTop]; + pars_list.Add (new ArglistParameter (GetLocation (yyVals[-2+yyTop]))); + parameterListCommas.Add (GetLocation (yyVals[-3+yyTop])); + parameterListCommas.Add (GetLocation (yyVals[-1+yyTop])); + + yyVal = new ParametersCompiled (pars_list.ToArray (), true); + lbag.AddLocation (yyVal, parameterListCommas); + } + +void case_176() +#line 1550 "cs-parser.jay" +{ + report.Error (257, GetLocation (yyVals[-2+yyTop]), "An __arglist parameter must be the last parameter in a formal parameter list"); + + yyVal = new ParametersCompiled (new Parameter [] { new ArglistParameter (GetLocation (yyVals[-2+yyTop])) }, true); + lbag.AddLocation (yyVal, parameterListCommas); + } + +void case_177() +#line 1557 "cs-parser.jay" +{ + report.Error (257, GetLocation (yyVals[-2+yyTop]), "An __arglist parameter must be the last parameter in a formal parameter list"); + + var pars_list = (List) yyVals[-4+yyTop]; + pars_list.Add (new ArglistParameter (GetLocation (yyVals[-2+yyTop]))); + parameterListCommas.Add (GetLocation (yyVals[-3+yyTop])); + parameterListCommas.Add (GetLocation (yyVals[-1+yyTop])); + + yyVal = new ParametersCompiled (pars_list.ToArray (), true); + lbag.AddLocation (yyVal, parameterListCommas); + } + +void case_180() +#line 1577 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = ParametersCompiled.EmptyReadOnlyParameters; + } + +void case_181() +#line 1585 "cs-parser.jay" +{ + parameters_bucket.Clear (); + Parameter p = (Parameter) yyVals[0+yyTop]; + parameters_bucket.Add (p); + parameterListCommas.Clear (); + default_parameter_used = p.HasDefaultValue; + yyVal = parameters_bucket; + } + +void case_182() +#line 1594 "cs-parser.jay" +{ + var pars = (List) yyVals[-2+yyTop]; + Parameter p = (Parameter) yyVals[0+yyTop]; + if (p != null) { + if (p.HasExtensionMethodModifier) + report.Error (1100, p.Location, "The parameter modifier `this' can only be used on the first parameter"); + else if (!p.HasDefaultValue && default_parameter_used) + report.Error (1737, p.Location, "Optional parameter cannot precede required parameters"); + + default_parameter_used |= p.HasDefaultValue; + pars.Add (p); + + parameterListCommas.Add (GetLocation (yyVals[-1+yyTop])); + } + + yyVal = yyVals[-2+yyTop]; + } + +void case_183() +#line 1618 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[0+yyTop]; + yyVal = new Parameter ((FullNamedExpression) yyVals[-1+yyTop], lt.Value, (Parameter.Modifier) yyVals[-2+yyTop], (Attributes) yyVals[-3+yyTop], lt.Location); + lbag.AddLocation (yyVal, parameterModifierLocation); + } + +void case_184() +#line 1627 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-2+yyTop]; + report.Error (1552, lt.Location, "Array type specifier, [], must appear before parameter name"); + yyVal = new Parameter ((FullNamedExpression) yyVals[-3+yyTop], lt.Value, (Parameter.Modifier) yyVals[-4+yyTop], (Attributes) yyVals[-5+yyTop], lt.Location); + lbag.AddLocation (yyVal, parameterModifierLocation); + } + +void case_185() +#line 1634 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + Location l = GetLocation (yyVals[0+yyTop]); + yyVal = new Parameter (null, null, Parameter.Modifier.NONE, (Attributes) yyVals[-1+yyTop], l); + } + +void case_186() +#line 1643 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + Location l = GetLocation (yyVals[0+yyTop]); + yyVal = new Parameter ((FullNamedExpression) yyVals[-1+yyTop], null, (Parameter.Modifier) yyVals[-2+yyTop], (Attributes) yyVals[-3+yyTop], l); + lbag.AddLocation (yyVal, parameterModifierLocation); + } + +void case_188() +#line 1658 "cs-parser.jay" +{ + --lexer.parsing_block; + if (lang_version <= LanguageVersion.V_3) { + FeatureIsNotAvailable (GetLocation (yyVals[-2+yyTop]), "optional parameter"); + } + + Parameter.Modifier mod = (Parameter.Modifier) yyVals[-5+yyTop]; + if (mod != Parameter.Modifier.NONE) { + switch (mod) { + case Parameter.Modifier.REF: + case Parameter.Modifier.OUT: + report.Error (1741, GetLocation (yyVals[-5+yyTop]), "Cannot specify a default value for the `{0}' parameter", + Parameter.GetModifierSignature (mod)); + break; + + case Parameter.Modifier.This: + report.Error (1743, GetLocation (yyVals[-5+yyTop]), "Cannot specify a default value for the `{0}' parameter", + Parameter.GetModifierSignature (mod)); + break; + default: + throw new NotImplementedException (mod.ToString ()); + } + + mod = Parameter.Modifier.NONE; + } + + if ((valid_param_mod & ParameterModifierType.DefaultValue) == 0) + report.Error (1065, GetLocation (yyVals[-2+yyTop]), "Optional parameter is not valid in this context"); + + var lt = (LocatedToken) yyVals[-3+yyTop]; + yyVal = new Parameter ((FullNamedExpression) yyVals[-4+yyTop], lt.Value, mod, (Attributes) yyVals[-6+yyTop], lt.Location); + lbag.AddLocation (yyVal, parameterModifierLocation, GetLocation (yyVals[-2+yyTop])); /* parameterModifierLocation should be ignored when mod == NONE*/ + + if (yyVals[0+yyTop] != null) + ((Parameter) yyVal).DefaultValue = new DefaultParameterValueExpression ((Expression) yyVals[0+yyTop]); + } + +void case_192() +#line 1707 "cs-parser.jay" +{ + Parameter.Modifier p2 = (Parameter.Modifier)yyVals[0+yyTop]; + Parameter.Modifier mod = (Parameter.Modifier)yyVals[-1+yyTop] | p2; + if (((Parameter.Modifier)yyVals[-1+yyTop] & p2) == p2) { + Error_DuplicateParameterModifier (lexer.Location, p2); + } else { + switch (mod & ~Parameter.Modifier.This) { + case Parameter.Modifier.REF: + report.Error (1101, lexer.Location, "The parameter modifiers `this' and `ref' cannot be used altogether"); + break; + case Parameter.Modifier.OUT: + report.Error (1102, lexer.Location, "The parameter modifiers `this' and `out' cannot be used altogether"); + break; + default: + report.Error (1108, lexer.Location, "A parameter cannot have specified more than one modifier"); + break; + } + } + yyVal = mod; + } + +void case_193() +#line 1731 "cs-parser.jay" +{ + if ((valid_param_mod & ParameterModifierType.Ref) == 0) + Error_ParameterModifierNotValid ("ref", GetLocation (yyVals[0+yyTop])); + parameterModifierLocation = GetLocation (yyVals[0+yyTop]); + yyVal = Parameter.Modifier.REF; + } + +void case_194() +#line 1738 "cs-parser.jay" +{ + if ((valid_param_mod & ParameterModifierType.Out) == 0) + Error_ParameterModifierNotValid ("out", GetLocation (yyVals[0+yyTop])); + parameterModifierLocation = GetLocation (yyVals[0+yyTop]); + yyVal = Parameter.Modifier.OUT; + } + +void case_195() +#line 1745 "cs-parser.jay" +{ + if ((valid_param_mod & ParameterModifierType.This) == 0) + Error_ParameterModifierNotValid ("this", GetLocation (yyVals[0+yyTop])); + + if (lang_version <= LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation (yyVals[0+yyTop]), "extension methods"); + parameterModifierLocation = GetLocation (yyVals[0+yyTop]); + yyVal = Parameter.Modifier.This; + } + +void case_196() +#line 1758 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[0+yyTop]; + yyVal = new ParamsParameter ((FullNamedExpression) yyVals[-1+yyTop], lt.Value, (Attributes) yyVals[-3+yyTop], lt.Location); + lbag.AddLocation (yyVal, savedLocation); + } + +void case_197() +#line 1764 "cs-parser.jay" +{ + report.Error (1751, GetLocation (yyVals[-4+yyTop]), "Cannot specify a default value for a parameter array"); + + var lt = (LocatedToken) yyVals[-2+yyTop]; + yyVal = new ParamsParameter ((FullNamedExpression) yyVals[-3+yyTop], lt.Value, (Attributes) yyVals[-5+yyTop], lt.Location); + lbag.AddLocation (yyVal, savedLocation); + } + +void case_198() +#line 1772 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new ParamsParameter ((FullNamedExpression) yyVals[-1+yyTop], null, (Attributes) yyVals[-3+yyTop], Location.Null); + } + +void case_199() +#line 1781 "cs-parser.jay" +{ + if ((valid_param_mod & ParameterModifierType.Params) == 0) + report.Error (1670, (GetLocation (yyVals[0+yyTop])), "The `params' modifier is not allowed in current context"); + savedLocation = GetLocation (yyVals[0+yyTop]); + } + +void case_200() +#line 1787 "cs-parser.jay" +{ + Parameter.Modifier mod = (Parameter.Modifier)yyVals[0+yyTop]; + if ((mod & Parameter.Modifier.This) != 0) { + report.Error (1104, GetLocation (yyVals[-1+yyTop]), "The parameter modifiers `this' and `params' cannot be used altogether"); + } else { + report.Error (1611, GetLocation (yyVals[-1+yyTop]), "The params parameter cannot be declared as ref or out"); + } + savedLocation = GetLocation (yyVals[-1+yyTop]); + } + +void case_202() +#line 1804 "cs-parser.jay" +{ + if ((valid_param_mod & ParameterModifierType.Arglist) == 0) + report.Error (1669, GetLocation (yyVals[0+yyTop]), "__arglist is not valid in this context"); + } + +void case_203() +#line 1815 "cs-parser.jay" +{ + lexer.parsing_generic_declaration = false; + if (doc_support) + tmpComment = Lexer.consume_doc_comment (); + } + +void case_204() +#line 1821 "cs-parser.jay" +{ + var type = (FullNamedExpression) yyVals[-3+yyTop]; + current_property = new Property (current_type, type, (Modifiers) yyVals[-4+yyTop], + (MemberName) yyVals[-2+yyTop], (Attributes) yyVals[-5+yyTop]); + + if (type.Type != null && type.Type.Kind == MemberKind.Void) + report.Error (547, GetLocation (yyVals[-3+yyTop]), "`{0}': property or indexer cannot have void type", current_property.GetSignatureForError ()); + + current_type.AddMember (current_property); + lbag.AddMember (current_property, GetModifierLocations (), GetLocation (yyVals[0+yyTop])); + + lexer.PropertyParsing = true; + } + +void case_205() +#line 1835 "cs-parser.jay" +{ + lexer.PropertyParsing = false; + + if (doc_support) + current_property.DocComment = ConsumeStoredComment (); + } + +void case_206() +#line 1842 "cs-parser.jay" +{ + lbag.AppendToMember (current_property, GetLocation (yyVals[0+yyTop])); + lexer.parsing_modifiers = true; + } + +void case_208() +#line 1854 "cs-parser.jay" +{ + lexer.parsing_generic_declaration = false; + if (doc_support) + tmpComment = Lexer.consume_doc_comment (); + current_local_parameters = ParametersCompiled.EmptyReadOnlyParameters; + } + +void case_209() +#line 1861 "cs-parser.jay" +{ + var type = (FullNamedExpression) yyVals[-3+yyTop]; + var property = new Property (current_type, type, (Modifiers) yyVals[-4+yyTop], + (MemberName) yyVals[-2+yyTop], (Attributes) yyVals[-5+yyTop]); + + property.Get = new Property.GetMethod (property, Modifiers.COMPILER_GENERATED, null, property.Location); + property.Get.Block = (ToplevelBlock) yyVals[0+yyTop]; + + if (current_container.Kind == MemberKind.Interface) { + report.Error (531, property.Get.Block.StartLocation, + "`{0}': interface members cannot have a definition", property.GetSignatureForError ()); + } + + if (type.Type != null && type.Type.Kind == MemberKind.Void) + report.Error (547, GetLocation (yyVals[-3+yyTop]), "`{0}': property or indexer cannot have void type", property.GetSignatureForError ()); + + current_type.AddMember (property); + + current_local_parameters = null; + } + +void case_211() +#line 1886 "cs-parser.jay" +{ + ++lexer.parsing_block; + current_local_parameters = ParametersCompiled.EmptyReadOnlyParameters; + start_block (GetLocation (yyVals[0+yyTop])); + } + +void case_212() +#line 1892 "cs-parser.jay" +{ + --lexer.parsing_block; + ((Property)current_property).Initializer = (Expression) yyVals[-1+yyTop]; + lbag.AppendToMember (current_property, GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[0+yyTop])); + end_block (GetLocation (yyVals[0+yyTop])); + current_local_parameters = null; + } + +void case_216() +#line 1913 "cs-parser.jay" +{ + valid_param_mod = 0; + var type = (FullNamedExpression) yyVals[-5+yyTop]; + Indexer indexer = new Indexer (current_type, type, (MemberName) yyVals[-4+yyTop], (Modifiers) yyVals[-6+yyTop], (ParametersCompiled) yyVals[-1+yyTop], (Attributes) yyVals[-7+yyTop]); + + current_property = indexer; + + current_type.AddIndexer (indexer); + lbag.AddMember (current_property, GetModifierLocations (), GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[0+yyTop])); + + if (type.Type != null && type.Type.Kind == MemberKind.Void) + report.Error (620, GetLocation (yyVals[-5+yyTop]), "`{0}': indexer return type cannot be `void'", indexer.GetSignatureForError ()); + + if (indexer.ParameterInfo.IsEmpty) { + report.Error (1551, GetLocation (yyVals[-3+yyTop]), "Indexers must have at least one parameter"); + } + + if (doc_support) { + tmpComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + lexer.PropertyParsing = true; + current_local_parameters = (ParametersCompiled) yyVals[-1+yyTop]; + } + +void case_217() +#line 1939 "cs-parser.jay" +{ + lexer.PropertyParsing = false; + current_local_parameters = null; + + if (current_property.AccessorFirst != null && current_property.AccessorFirst.Block == null) + ((Indexer) current_property).ParameterInfo.CheckParameters (current_property); + + if (doc_support) + current_property.DocComment = ConsumeStoredComment (); + + current_property = null; + } + +void case_219() +#line 1959 "cs-parser.jay" +{ + current_property.Get = new Indexer.GetIndexerMethod (current_property, Modifiers.COMPILER_GENERATED, current_local_parameters, null, current_property.Location); + current_property.Get.Block = (ToplevelBlock) yyVals[0+yyTop]; + } + +void case_224() +#line 1971 "cs-parser.jay" +{ + if (yyToken == Token.CLOSE_BRACE) { + report.Error (548, lexer.Location, "`{0}': property or indexer must have at least one accessor", current_property.GetSignatureForError ()); + } else { + if (yyToken == Token.SEMICOLON) + report.Error (1597, lexer.Location, "Semicolon after method or accessor block is not valid"); + else + report.Error (1014, GetLocation (yyVals[0+yyTop]), "A get or set accessor expected"); + } + } + +void case_225() +#line 1985 "cs-parser.jay" +{ + if (yyVals[-1+yyTop] != ModifierNone && lang_version == LanguageVersion.ISO_1) { + FeatureIsNotAvailable (GetLocation (yyVals[-1+yyTop]), "access modifiers on properties"); + } + + if (current_property.Get != null) { + report.Error (1007, GetLocation (yyVals[0+yyTop]), "Property accessor already defined"); + } + + if (current_property is Indexer) { + current_property.Get = new Indexer.GetIndexerMethod (current_property, (Modifiers) yyVals[-1+yyTop], ((Indexer)current_property).ParameterInfo.Clone (), + (Attributes) yyVals[-2+yyTop], GetLocation (yyVals[0+yyTop])); + } else { + current_property.Get = new Property.GetMethod (current_property, + (Modifiers) yyVals[-1+yyTop], (Attributes) yyVals[-2+yyTop], GetLocation (yyVals[0+yyTop])); + } + + current_local_parameters = current_property.Get.ParameterInfo; + lexer.PropertyParsing = false; + } + +void case_226() +#line 2006 "cs-parser.jay" +{ + if (yyVals[0+yyTop] != null) { + current_property.Get.Block = (ToplevelBlock) yyVals[0+yyTop]; + + if (current_container.Kind == MemberKind.Interface) { + report.Error (531, current_property.Get.Block.StartLocation, + "`{0}': interface members cannot have a definition", current_property.Get.GetSignatureForError ()); + } + lbag.AddMember (current_property.Get, GetModifierLocations ()); + } else { + lbag.AddMember (current_property.Get, GetModifierLocations (), savedLocation); + } + + current_local_parameters = null; + lexer.PropertyParsing = true; + + if (doc_support) + if (Lexer.doc_state == XmlCommentState.Error) + Lexer.doc_state = XmlCommentState.NotAllowed; + } + +void case_227() +#line 2030 "cs-parser.jay" +{ + if (yyVals[-1+yyTop] != ModifierNone && lang_version == LanguageVersion.ISO_1) { + FeatureIsNotAvailable (GetLocation (yyVals[-1+yyTop]), "access modifiers on properties"); + } + + if (current_property.Set != null) { + report.Error (1007, GetLocation (yyVals[0+yyTop]), "Property accessor already defined"); + } + + if (current_property is Indexer) { + current_property.Set = new Indexer.SetIndexerMethod (current_property, (Modifiers) yyVals[-1+yyTop], + ParametersCompiled.MergeGenerated (compiler, + ((Indexer)current_property).ParameterInfo, true, new Parameter ( + current_property.TypeExpression, "value", Parameter.Modifier.NONE, null, GetLocation (yyVals[0+yyTop])), + null), + (Attributes) yyVals[-2+yyTop], GetLocation (yyVals[0+yyTop])); + } else { + current_property.Set = new Property.SetMethod (current_property, (Modifiers) yyVals[-1+yyTop], + ParametersCompiled.CreateImplicitParameter (current_property.TypeExpression, GetLocation (yyVals[0+yyTop])), + (Attributes) yyVals[-2+yyTop], GetLocation (yyVals[0+yyTop])); + } + + current_local_parameters = current_property.Set.ParameterInfo; + lexer.PropertyParsing = false; + } + +void case_228() +#line 2056 "cs-parser.jay" +{ + if (yyVals[0+yyTop] != null) { + current_property.Set.Block = (ToplevelBlock) yyVals[0+yyTop]; + + if (current_container.Kind == MemberKind.Interface) { + report.Error (531, current_property.Set.Block.StartLocation, + "`{0}': interface members cannot have a definition", current_property.Set.GetSignatureForError ()); + } + lbag.AddMember (current_property.Set, GetModifierLocations ()); + } else { + lbag.AddMember (current_property.Set, GetModifierLocations (), savedLocation); + } + + current_local_parameters = null; + lexer.PropertyParsing = true; + + if (doc_support + && Lexer.doc_state == XmlCommentState.Error) + Lexer.doc_state = XmlCommentState.NotAllowed; + } + +void case_230() +#line 2081 "cs-parser.jay" +{ + savedLocation = GetLocation (yyVals[0+yyTop]); + yyVal = null; + } + +void case_231() +#line 2086 "cs-parser.jay" +{ + Error_SyntaxError (1043, yyToken, "Invalid accessor body"); + yyVal = null; + } + +void case_233() +#line 2100 "cs-parser.jay" +{ + lexer.ConstraintsParsing = true; + push_current_container (new Interface (current_container, (MemberName) yyVals[0+yyTop], (Modifiers) yyVals[-4+yyTop], (Attributes) yyVals[-5+yyTop]), yyVals[-3+yyTop]); + lbag.AddMember (current_container, GetModifierLocations (), GetLocation (yyVals[-2+yyTop])); + } + +void case_234() +#line 2107 "cs-parser.jay" +{ + lexer.ConstraintsParsing = false; + + if (yyVals[0+yyTop] != null) + current_container.SetConstraints ((List) yyVals[0+yyTop]); + + if (doc_support) { + current_container.PartialContainer.DocComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + lexer.parsing_modifiers = true; + } + +void case_235() +#line 2121 "cs-parser.jay" +{ + --lexer.parsing_declaration; + if (doc_support) + Lexer.doc_state = XmlCommentState.Allowed; + } + +void case_236() +#line 2127 "cs-parser.jay" +{ + if (yyVals[0+yyTop] == null) { + lbag.AppendToMember (current_container, GetLocation (yyVals[-4+yyTop]), GetLocation (yyVals[-2+yyTop])); + } else { + lbag.AppendToMember (current_container, GetLocation (yyVals[-4+yyTop]), GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + yyVal = pop_current_class (); + } + +void case_240() +#line 2148 "cs-parser.jay" +{ + lexer.parsing_modifiers = true; + lexer.parsing_block = 0; + } + +void case_241() +#line 2153 "cs-parser.jay" +{ + lexer.parsing_modifiers = true; + lexer.parsing_block = 0; + } + +void case_252() +#line 2191 "cs-parser.jay" +{ + OperatorDeclaration decl = (OperatorDeclaration) yyVals[-2+yyTop]; + if (decl != null) { + Operator op = new Operator ( + current_type, decl.optype, decl.ret_type, (Modifiers) yyVals[-3+yyTop], + current_local_parameters, + (ToplevelBlock) yyVals[0+yyTop], (Attributes) yyVals[-4+yyTop], decl.location); + + if (op.Block == null) + op.ParameterInfo.CheckParameters (op); + + if (doc_support) { + op.DocComment = tmpComment; + Lexer.doc_state = XmlCommentState.Allowed; + } + + /* Note again, checking is done in semantic analysis*/ + current_type.AddOperator (op); + + lbag.AddMember (op, GetModifierLocations (), lbag.GetLocations (decl)); + if (yyVals[0+yyTop] == null) { /* Semicolon*/ + lbag.AddLocation (op, savedLocation); + } + } + + current_local_parameters = null; + } + +void case_254() +#line 2223 "cs-parser.jay" +{ + report.Error (590, GetLocation (yyVals[0+yyTop]), "User-defined operators cannot return void"); + yyVal = new TypeExpression (compiler.BuiltinTypes.Void, GetLocation (yyVals[0+yyTop])); + } + +void case_256() +#line 2235 "cs-parser.jay" +{ + valid_param_mod = 0; + + Location loc = GetLocation (yyVals[-5+yyTop]); + Operator.OpType op = (Operator.OpType) yyVals[-4+yyTop]; + current_local_parameters = (ParametersCompiled)yyVals[-1+yyTop]; + + int p_count = current_local_parameters.Count; + if (p_count == 1) { + if (op == Operator.OpType.Addition) + op = Operator.OpType.UnaryPlus; + else if (op == Operator.OpType.Subtraction) + op = Operator.OpType.UnaryNegation; + } + + if (IsUnaryOperator (op)) { + if (p_count == 2) { + report.Error (1020, loc, "Overloadable binary operator expected"); + } else if (p_count != 1) { + report.Error (1535, loc, "Overloaded unary operator `{0}' takes one parameter", + Operator.GetName (op)); + } + } else { + if (p_count == 1) { + report.Error (1019, loc, "Overloadable unary operator expected"); + } else if (p_count != 2) { + report.Error (1534, loc, "Overloaded binary operator `{0}' takes two parameters", + Operator.GetName (op)); + } + } + + if (doc_support) { + tmpComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.NotAllowed; + } + + yyVal = new OperatorDeclaration (op, (FullNamedExpression) yyVals[-6+yyTop], loc); + lbag.AddLocation (yyVal, GetLocation (yyVals[-5+yyTop]), savedOperatorLocation, GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_281() +#line 2311 "cs-parser.jay" +{ + valid_param_mod = 0; + + Location loc = GetLocation (yyVals[-5+yyTop]); + current_local_parameters = (ParametersCompiled)yyVals[-1+yyTop]; + + if (current_local_parameters.Count != 1) { + report.Error (1535, loc, "Overloaded unary operator `implicit' takes one parameter"); + } + + if (doc_support) { + tmpComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.NotAllowed; + } + + yyVal = new OperatorDeclaration (Operator.OpType.Implicit, (FullNamedExpression) yyVals[-4+yyTop], loc); + lbag.AddLocation (yyVal, GetLocation (yyVals[-6+yyTop]), GetLocation (yyVals[-5+yyTop]), GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_283() +#line 2334 "cs-parser.jay" +{ + valid_param_mod = 0; + + Location loc = GetLocation (yyVals[-5+yyTop]); + current_local_parameters = (ParametersCompiled)yyVals[-1+yyTop]; + + if (current_local_parameters.Count != 1) { + report.Error (1535, loc, "Overloaded unary operator `explicit' takes one parameter"); + } + + if (doc_support) { + tmpComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.NotAllowed; + } + + yyVal = new OperatorDeclaration (Operator.OpType.Explicit, (FullNamedExpression) yyVals[-4+yyTop], loc); + lbag.AddLocation (yyVal, GetLocation (yyVals[-6+yyTop]), GetLocation (yyVals[-5+yyTop]), GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_284() +#line 2353 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + current_local_parameters = ParametersCompiled.EmptyReadOnlyParameters; + yyVal = new OperatorDeclaration (Operator.OpType.Implicit, null, GetLocation (yyVals[-1+yyTop])); + } + +void case_285() +#line 2359 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + current_local_parameters = ParametersCompiled.EmptyReadOnlyParameters; + yyVal = new OperatorDeclaration (Operator.OpType.Explicit, null, GetLocation (yyVals[-1+yyTop])); + } + +void case_286() +#line 2369 "cs-parser.jay" +{ + Constructor c = (Constructor) yyVals[-1+yyTop]; + c.Block = (ToplevelBlock) yyVals[0+yyTop]; + + if (doc_support) + c.DocComment = ConsumeStoredComment (); + + current_local_parameters = null; + if (doc_support) + Lexer.doc_state = XmlCommentState.Allowed; + } + +void case_287() +#line 2386 "cs-parser.jay" +{ + if (doc_support) { + tmpComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + valid_param_mod = ParameterModifierType.All; + } + +void case_288() +#line 2395 "cs-parser.jay" +{ + valid_param_mod = 0; + current_local_parameters = (ParametersCompiled) yyVals[-1+yyTop]; + + var lt = (LocatedToken) yyVals[-4+yyTop]; + var mods = (Modifiers) yyVals[-5+yyTop]; + var c = new Constructor (current_type, lt.Value, mods, (Attributes) yyVals[-6+yyTop], current_local_parameters, lt.Location); + + if (lt.Value != current_container.MemberName.Name) { + report.Error (1520, c.Location, "Class, struct, or interface method must have a return type"); + } else if ((mods & Modifiers.STATIC) != 0) { + if ((mods & Modifiers.AccessibilityMask) != 0){ + report.Error (515, c.Location, + "`{0}': static constructor cannot have an access modifier", + c.GetSignatureForError ()); + } + } + + current_type.AddConstructor (c); + lbag.AddMember (c, GetModifierLocations (), GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + yyVal = c; + + /**/ + /* start block here, so possible anonymous methods inside*/ + /* constructor initializer can get correct parent block*/ + /**/ + start_block (lexer.Location); + } + +void case_289() +#line 2424 "cs-parser.jay" +{ + if (yyVals[0+yyTop] != null) { + var c = (Constructor) yyVals[-1+yyTop]; + c.Initializer = (ConstructorInitializer) yyVals[0+yyTop]; + + if (c.IsStatic) { + report.Error (514, c.Location, + "`{0}': static constructor cannot have an explicit `this' or `base' constructor call", + c.GetSignatureForError ()); + } + } + + yyVal = yyVals[-1+yyTop]; + } + +void case_295() +#line 2456 "cs-parser.jay" +{ + --lexer.parsing_block; + yyVal = new ConstructorBaseInitializer ((Arguments) yyVals[-1+yyTop], GetLocation (yyVals[-4+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-5+yyTop]), GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_297() +#line 2466 "cs-parser.jay" +{ + --lexer.parsing_block; + yyVal = new ConstructorThisInitializer ((Arguments) yyVals[-1+yyTop], GetLocation (yyVals[-4+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-5+yyTop]), GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_298() +#line 2472 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = new ConstructorThisInitializer (null, GetLocation (yyVals[0+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_299() +#line 2478 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = null; + } + +void case_300() +#line 2486 "cs-parser.jay" +{ + if (doc_support) { + tmpComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.NotAllowed; + } + + current_local_parameters = ParametersCompiled.EmptyReadOnlyParameters; + } + +void case_301() +#line 2495 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-3+yyTop]; + if (lt.Value != current_container.MemberName.Name){ + report.Error (574, lt.Location, "Name of destructor must match name of class"); + } else if (current_container.Kind != MemberKind.Class){ + report.Error (575, lt.Location, "Only class types can contain destructor"); + } + + Destructor d = new Destructor (current_type, (Modifiers) yyVals[-6+yyTop], + ParametersCompiled.EmptyReadOnlyParameters, (Attributes) yyVals[-7+yyTop], lt.Location); + d.Identifier = lt.Value; + if (doc_support) + d.DocComment = ConsumeStoredComment (); + + d.Block = (ToplevelBlock) yyVals[0+yyTop]; + current_type.AddMember (d); + lbag.AddMember (d, GetModifierLocations (), GetLocation (yyVals[-5+yyTop]), GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[-1+yyTop])); + + current_local_parameters = null; + } + +void case_302() +#line 2521 "cs-parser.jay" +{ + current_event_field = new EventField (current_type, (FullNamedExpression) yyVals[-1+yyTop], (Modifiers) yyVals[-3+yyTop], (MemberName) yyVals[0+yyTop], (Attributes) yyVals[-4+yyTop]); + current_type.AddMember (current_event_field); + + if (current_event_field.MemberName.ExplicitInterface != null) { + report.Error (71, current_event_field.Location, "`{0}': An explicit interface implementation of an event must use property syntax", + current_event_field.GetSignatureForError ()); + } + + yyVal = current_event_field; + } + +void case_303() +#line 2535 "cs-parser.jay" +{ + if (doc_support) { + current_event_field.DocComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + if (current_event_field.Initializer != null) { + lbag.AddMember (current_event_field, GetModifierLocations (), GetLocation (yyVals[-6+yyTop]), savedEventAssignLocation, GetLocation (yyVals[0+yyTop])); + } else { + lbag.AddMember (current_event_field, GetModifierLocations (), GetLocation (yyVals[-6+yyTop]), GetLocation (yyVals[0+yyTop])); + } + current_event_field = null; + } + +void case_304() +#line 2551 "cs-parser.jay" +{ + current_event = new EventProperty (current_type, (FullNamedExpression) yyVals[-2+yyTop], (Modifiers) yyVals[-4+yyTop], (MemberName) yyVals[-1+yyTop], (Attributes) yyVals[-5+yyTop]); + current_type.AddMember (current_event); + lbag.AddMember (current_event, GetModifierLocations (), GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[0+yyTop])); + + lexer.EventParsing = true; + } + +void case_305() +#line 2559 "cs-parser.jay" +{ + if (current_container.Kind == MemberKind.Interface) + report.Error (69, GetLocation (yyVals[-2+yyTop]), "Event in interface cannot have add or remove accessors"); + + lexer.EventParsing = false; + } + +void case_306() +#line 2566 "cs-parser.jay" +{ + if (doc_support) { + current_event.DocComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + lbag.AppendToMember (current_event, GetLocation (yyVals[-1+yyTop])); + current_event = null; + current_local_parameters = null; + } + +void case_307() +#line 2579 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + current_type.AddMember (new EventField (current_type, (FullNamedExpression) yyVals[-1+yyTop], (Modifiers) yyVals[-3+yyTop], MemberName.Null, (Attributes) yyVals[-4+yyTop])); + } + +void case_310() +#line 2593 "cs-parser.jay" +{ + --lexer.parsing_block; + savedEventAssignLocation = GetLocation (yyVals[-2+yyTop]); + current_event_field.Initializer = (Expression) yyVals[0+yyTop]; + } + +void case_315() +#line 2618 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[0+yyTop]; + yyVal = new FieldDeclarator (new SimpleMemberName (lt.Value, lt.Location), null); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_317() +#line 2628 "cs-parser.jay" +{ + --lexer.parsing_block; + var lt = (LocatedToken) yyVals[-3+yyTop]; + yyVal = new FieldDeclarator (new SimpleMemberName (lt.Value, lt.Location), (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-4+yyTop]), GetLocation (yyVals[-2+yyTop])); + } + +void case_318() +#line 2637 "cs-parser.jay" +{ + if (current_container.Kind == MemberKind.Interface) { + report.Error (68, lexer.Location, "`{0}': event in interface cannot have an initializer", + current_event_field.GetSignatureForError ()); + } + + if ((current_event_field.ModFlags & Modifiers.ABSTRACT) != 0) { + report.Error (74, lexer.Location, "`{0}': abstract event cannot have an initializer", + current_event_field.GetSignatureForError ()); + } + } + +void case_322() +#line 2658 "cs-parser.jay" +{ + report.Error (65, lexer.Location, "`{0}': event property must have both add and remove accessors", + current_event.GetSignatureForError ()); + } + +void case_323() +#line 2663 "cs-parser.jay" +{ + report.Error (65, lexer.Location, "`{0}': event property must have both add and remove accessors", + current_event.GetSignatureForError ()); + } + +void case_324() +#line 2668 "cs-parser.jay" +{ + report.Error (1055, GetLocation (yyVals[0+yyTop]), "An add or remove accessor expected"); + yyVal = null; + } + +void case_325() +#line 2676 "cs-parser.jay" +{ + if (yyVals[-1+yyTop] != ModifierNone) { + report.Error (1609, GetLocation (yyVals[-1+yyTop]), "Modifiers cannot be placed on event accessor declarations"); + } + + current_event.Add = new EventProperty.AddDelegateMethod (current_event, (Attributes) yyVals[-2+yyTop], GetLocation (yyVals[0+yyTop])); + current_local_parameters = current_event.Add.ParameterInfo; + + lbag.AddMember (current_event.Add, GetModifierLocations ()); + lexer.EventParsing = false; + } + +void case_326() +#line 2688 "cs-parser.jay" +{ + lexer.EventParsing = true; + + current_event.Add.Block = (ToplevelBlock) yyVals[0+yyTop]; + + if (current_container.Kind == MemberKind.Interface) { + report.Error (531, current_event.Add.Block.StartLocation, + "`{0}': interface members cannot have a definition", current_event.Add.GetSignatureForError ()); + } + + current_local_parameters = null; + } + +void case_327() +#line 2704 "cs-parser.jay" +{ + if (yyVals[-1+yyTop] != ModifierNone) { + report.Error (1609, GetLocation (yyVals[-1+yyTop]), "Modifiers cannot be placed on event accessor declarations"); + } + + current_event.Remove = new EventProperty.RemoveDelegateMethod (current_event, (Attributes) yyVals[-2+yyTop], GetLocation (yyVals[0+yyTop])); + current_local_parameters = current_event.Remove.ParameterInfo; + + lbag.AddMember (current_event.Remove, GetModifierLocations ()); + lexer.EventParsing = false; + } + +void case_328() +#line 2716 "cs-parser.jay" +{ + lexer.EventParsing = true; + + current_event.Remove.Block = (ToplevelBlock) yyVals[0+yyTop]; + + if (current_container.Kind == MemberKind.Interface) { + report.Error (531, current_event.Remove.Block.StartLocation, + "`{0}': interface members cannot have a definition", current_event.Remove.GetSignatureForError ()); + } + + current_local_parameters = null; + } + +void case_329() +#line 2732 "cs-parser.jay" +{ + report.Error (73, lexer.Location, "An add or remove accessor must have a body"); + yyVal = null; + } + +void case_331() +#line 2741 "cs-parser.jay" +{ + current_type.UnattachedAttributes = (Attributes) yyVals[-1+yyTop]; + report.Error (1519, GetLocation (yyVals[-1+yyTop]), "An attribute is missing member declaration"); + lexer.putback ('}'); + } + +void case_332() +#line 2752 "cs-parser.jay" +{ + report.Error (1519, lexer.Location, "Unexpected symbol `}' in class, struct, or interface member declaration"); + + lexer.putback ('}'); + + lexer.parsing_generic_declaration = false; + FullNamedExpression type = (FullNamedExpression) yyVals[-1+yyTop]; + current_field = new Field (current_type, type, (Modifiers) yyVals[-2+yyTop], MemberName.Null, (Attributes) yyVals[-3+yyTop]); + current_type.AddField (current_field); + lbag.AddMember (current_field, GetModifierLocations ()); + yyVal = current_field; + } + +void case_333() +#line 2772 "cs-parser.jay" +{ + if (doc_support) + enumTypeComment = Lexer.consume_doc_comment (); + } + +void case_334() +#line 2777 "cs-parser.jay" +{ + if (doc_support) + Lexer.doc_state = XmlCommentState.Allowed; + + MemberName name = (MemberName) yyVals[-3+yyTop]; + if (name.IsGeneric) { + report.Error (1675, name.Location, "Enums cannot have type parameters"); + } + + push_current_container (new Enum (current_container, (FullNamedExpression) yyVals[-2+yyTop], (Modifiers) yyVals[-5+yyTop], name, (Attributes) yyVals[-6+yyTop]), null); + if (yyVals[-2+yyTop] != null) { + lbag.AddMember (current_container, GetModifierLocations (), GetLocation (yyVals[-4+yyTop]), savedLocation, GetLocation (yyVals[0+yyTop])); + } else { + lbag.AddMember (current_container, GetModifierLocations (), GetLocation (yyVals[-4+yyTop]), GetLocation (yyVals[0+yyTop])); + } + } + +void case_335() +#line 2794 "cs-parser.jay" +{ + lexer.parsing_modifiers = true; + + /* here will be evaluated after CLOSE_BLACE is consumed.*/ + if (doc_support) + Lexer.doc_state = XmlCommentState.Allowed; + } + +void case_336() +#line 2802 "cs-parser.jay" +{ + lbag.AppendToMember (current_container, GetLocation (yyVals[-1+yyTop])); + if (yyVals[0+yyTop] != null) { + lbag.AppendToMember (current_container, GetLocation (yyVals[0+yyTop])); + } + if (doc_support) + current_container.DocComment = enumTypeComment; + + --lexer.parsing_declaration; + +/* if (doc_support)*/ +/* em.DocComment = ev.DocComment;*/ + + yyVal = pop_current_class (); + } + +void case_338() +#line 2822 "cs-parser.jay" +{ + savedLocation = GetLocation (yyVals[-1+yyTop]); + yyVal = yyVals[0+yyTop]; + } + +void case_339() +#line 2827 "cs-parser.jay" +{ + Error_TypeExpected (GetLocation (yyVals[-1+yyTop])); + yyVal = null; + } + +void case_344() +#line 2845 "cs-parser.jay" +{ + lbag.AppendToMember (current_container, GetLocation (yyVals[-1+yyTop])); + yyVal = yyVals[0+yyTop]; + } + +void case_345() +#line 2853 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[0+yyTop]; + var em = new EnumMember ((Enum) current_type, new MemberName (lt.Value, lt.Location), (Attributes) yyVals[-1+yyTop]); + ((Enum) current_type).AddEnumMember (em); + + if (doc_support) { + em.DocComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + yyVal = em; + } + +void case_346() +#line 2866 "cs-parser.jay" +{ + ++lexer.parsing_block; + if (doc_support) { + tmpComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.NotAllowed; + } + } + +void case_347() +#line 2874 "cs-parser.jay" +{ + --lexer.parsing_block; + + var lt = (LocatedToken) yyVals[-3+yyTop]; + var em = new EnumMember ((Enum) current_type, new MemberName (lt.Value, lt.Location), (Attributes) yyVals[-4+yyTop]); + em.Initializer = new ConstInitializer (em, (Expression) yyVals[0+yyTop], GetLocation (yyVals[-1+yyTop])); + ((Enum) current_type).AddEnumMember (em); + + if (doc_support) + em.DocComment = ConsumeStoredComment (); + + yyVal = em; + } + +void case_348() +#line 2888 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + var lt = (LocatedToken) yyVals[-1+yyTop]; + var em = new EnumMember ((Enum) current_type, new MemberName (lt.Value, lt.Location), (Attributes) yyVals[-2+yyTop]); + ((Enum) current_type).AddEnumMember (em); + + if (doc_support) { + em.DocComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + yyVal = em; + } + +void case_351() +#line 2915 "cs-parser.jay" +{ + valid_param_mod = 0; + + ParametersCompiled p = (ParametersCompiled) yyVals[-1+yyTop]; + + Delegate del = new Delegate (current_container, (FullNamedExpression) yyVals[-5+yyTop], (Modifiers) yyVals[-7+yyTop], (MemberName) yyVals[-4+yyTop], p, (Attributes) yyVals[-8+yyTop]); + + p.CheckParameters (del); + + current_container.AddTypeContainer (del); + + current_delegate = del; + lexer.ConstraintsParsing = true; + } + +void case_353() +#line 2934 "cs-parser.jay" +{ + if (doc_support) { + current_delegate.DocComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + if (yyVals[-2+yyTop] != null) + current_delegate.SetConstraints ((List) yyVals[-2+yyTop]); + lbag.AddMember (current_delegate, GetModifierLocations (), GetLocation (yyVals[-10+yyTop]), GetLocation (yyVals[-7+yyTop]), GetLocation (yyVals[-4+yyTop]), GetLocation (yyVals[0+yyTop])); + + yyVal = current_delegate; + + current_delegate = null; + } + +void case_355() +#line 2953 "cs-parser.jay" +{ + if (lang_version < LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation (yyVals[0+yyTop]), "nullable types"); + + yyVal = ComposedTypeSpecifier.CreateNullable (GetLocation (yyVals[0+yyTop])); + } + +void case_357() +#line 2964 "cs-parser.jay" +{ + var lt1 = (LocatedToken) yyVals[-2+yyTop]; + var lt2 = (LocatedToken) yyVals[-1+yyTop]; + + yyVal = new QualifiedAliasMember (lt1.Value, lt2.Value, (TypeArguments) yyVals[0+yyTop], lt1.Location); + lbag.AddLocation (yyVal, savedLocation, GetLocation (yyVals[-1+yyTop])); + } + +void case_358() +#line 2972 "cs-parser.jay" +{ + var lt1 = (LocatedToken) yyVals[-2+yyTop]; + var lt2 = (LocatedToken) yyVals[-1+yyTop]; + var qam = new QualifiedAliasMember (lt1.Value, lt2.Value, (int) yyVals[0+yyTop], lt1.Location); + lbag.AddLocation (qam.TypeArguments, Lexer.GenericDimensionLocations); + yyVal = qam; + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_360() +#line 2985 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-1+yyTop]; + yyVal = new MemberAccess ((Expression) yyVals[-3+yyTop], lt.Value, (TypeArguments) yyVals[0+yyTop], lt.Location); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_361() +#line 2991 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-1+yyTop]; + var ma = new MemberAccess ((Expression) yyVals[-3+yyTop], lt.Value, (int) yyVals[0+yyTop], lt.Location); + lbag.AddLocation (ma.TypeArguments, Lexer.GenericDimensionLocations); + yyVal = ma; + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_362() +#line 3002 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-1+yyTop]; + yyVal = new SimpleName (lt.Value, (TypeArguments)yyVals[0+yyTop], lt.Location); + } + +void case_363() +#line 3007 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-1+yyTop]; + var sn = new SimpleName (lt.Value, (int) yyVals[0+yyTop], lt.Location); + lbag.AddLocation (sn.TypeArguments, Lexer.GenericDimensionLocations); + yyVal = sn; + } + +void case_365() +#line 3021 "cs-parser.jay" +{ + if (lang_version < LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation (yyVals[-2+yyTop]), "generics"); + var list = locationListStack.Pop (); + list.Add (GetLocation (yyVals[-2+yyTop])); + list.Add (GetLocation (yyVals[-1+yyTop])); + lbag.AddLocation (yyVals[-1+yyTop], list); + + yyVal = yyVals[-1+yyTop];; + } + +void case_366() +#line 3032 "cs-parser.jay" +{ + Error_TypeExpected (lexer.Location); + yyVal = new TypeArguments (); + } + +void case_367() +#line 3040 "cs-parser.jay" +{ + TypeArguments type_args = new TypeArguments (); + type_args.Add ((FullNamedExpression) yyVals[0+yyTop]); + yyVal = type_args; + locationListStack.Push (new List ()); + } + +void case_368() +#line 3047 "cs-parser.jay" +{ + TypeArguments type_args = (TypeArguments) yyVals[-2+yyTop]; + type_args.Add ((FullNamedExpression) yyVals[0+yyTop]); + yyVal = type_args; + locationListStack.Peek ().Add (GetLocation (yyVals[-1+yyTop])); + } + +void case_370() +#line 3064 "cs-parser.jay" +{ + lexer.parsing_generic_declaration = false; + var lt = (LocatedToken) yyVals[-2+yyTop]; + yyVal = new MemberName (lt.Value, (TypeParameters)yyVals[0+yyTop], lt.Location); + } + +void case_371() +#line 3073 "cs-parser.jay" +{ + MemberName mn = (MemberName)yyVals[0+yyTop]; + if (mn.TypeParameters != null) + syntax_error (mn.Location, string.Format ("Member `{0}' cannot declare type arguments", + mn.GetSignatureForError ())); + } + +void case_373() +#line 3084 "cs-parser.jay" +{ + lexer.parsing_generic_declaration = false; + var lt = (LocatedToken) yyVals[-1+yyTop]; + yyVal = new MemberName (lt.Value, (TypeParameters) yyVals[0+yyTop], (ATypeNameExpression) yyVals[-2+yyTop], lt.Location); + } + +void case_374() +#line 3093 "cs-parser.jay" +{ + lexer.parsing_generic_declaration = false; + yyVal = new MemberName (TypeDefinition.DefaultIndexerName, GetLocation (yyVals[0+yyTop])); + } + +void case_375() +#line 3098 "cs-parser.jay" +{ + lexer.parsing_generic_declaration = false; + yyVal = new MemberName (TypeDefinition.DefaultIndexerName, null, (ATypeNameExpression) yyVals[-1+yyTop], GetLocation (yyVals[0+yyTop])); + } + +void case_376() +#line 3106 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-2+yyTop]; + yyVal = new SimpleName (lt.Value, (TypeArguments) yyVals[-1+yyTop], lt.Location); + lbag.AddLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_377() +#line 3112 "cs-parser.jay" +{ + var lt1 = (LocatedToken) yyVals[-3+yyTop]; + var lt2 = (LocatedToken) yyVals[-2+yyTop]; + + yyVal = new QualifiedAliasMember (lt1.Value, lt2.Value, (TypeArguments) yyVals[-1+yyTop], lt1.Location); + lbag.AddLocation (yyVal, savedLocation, GetLocation (yyVals[0+yyTop])); + } + +void case_378() +#line 3120 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-2+yyTop]; + yyVal = new MemberAccess ((ATypeNameExpression) yyVals[-3+yyTop], lt.Value, (TypeArguments) yyVals[-1+yyTop], lt.Location); + lbag.AddLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_380() +#line 3130 "cs-parser.jay" +{ + if (lang_version < LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation (yyVals[-2+yyTop]), "generics"); + + yyVal = yyVals[-1+yyTop]; + var list = locationListStack.Pop (); + list.Add (GetLocation (yyVals[-2+yyTop])); + list.Add (GetLocation (yyVals[-1+yyTop])); + lbag.AddLocation (yyVals[-1+yyTop], list); + } + +void case_381() +#line 3144 "cs-parser.jay" +{ + var tparams = new TypeParameters (); + tparams.Add ((TypeParameter)yyVals[0+yyTop]); + yyVal = tparams; + locationListStack.Push (new List ()); + } + +void case_382() +#line 3151 "cs-parser.jay" +{ + var tparams = (TypeParameters) yyVals[-2+yyTop]; + tparams.Add ((TypeParameter)yyVals[0+yyTop]); + yyVal = tparams; + locationListStack.Peek ().Add (GetLocation (yyVals[-1+yyTop])); + } + +void case_383() +#line 3161 "cs-parser.jay" +{ + var lt = (LocatedToken)yyVals[0+yyTop]; + var variance = (VarianceDecl) yyVals[-1+yyTop]; + yyVal = new TypeParameter (new MemberName (lt.Value, lt.Location), (Attributes)yyVals[-2+yyTop], variance); + if (variance != null) + lbag.AddLocation (yyVal, savedLocation); + } + +void case_384() +#line 3169 "cs-parser.jay" +{ + if (GetTokenName (yyToken) == "type") + report.Error (81, GetLocation (yyVals[0+yyTop]), "Type parameter declaration must be an identifier not a type"); + else + Error_SyntaxError (yyToken); + + yyVal = new TypeParameter (MemberName.Null, null, null); + } + +void case_393() +#line 3213 "cs-parser.jay" +{ + report.Error (1536, GetLocation (yyVals[0+yyTop]), "Invalid parameter type `void'"); + yyVal = new TypeExpression (compiler.BuiltinTypes.Void, GetLocation (yyVals[0+yyTop])); + } + +void case_396() +#line 3229 "cs-parser.jay" +{ + if (yyVals[0+yyTop] != null) { + yyVal = new ComposedCast ((ATypeNameExpression) yyVals[-1+yyTop], (ComposedTypeSpecifier) yyVals[0+yyTop]); + } else { + var sn = yyVals[-1+yyTop] as SimpleName; + if (sn != null && sn.Name == "var") + yyVal = new VarExpr (sn.Location); + else + yyVal = yyVals[-1+yyTop]; + } + } + +void case_399() +#line 3249 "cs-parser.jay" +{ + Expression.Error_VoidInvalidInTheContext (GetLocation (yyVals[0+yyTop]), report); + yyVal = new TypeExpression (compiler.BuiltinTypes.Void, GetLocation (yyVals[0+yyTop])); + } + +void case_400() +#line 3257 "cs-parser.jay" +{ + if (yyVals[0+yyTop] != null) + yyVal = new ComposedCast ((FullNamedExpression) yyVals[-1+yyTop], (ComposedTypeSpecifier) yyVals[0+yyTop]); + } + +void case_403() +#line 3273 "cs-parser.jay" +{ + var types = new List (2); + types.Add ((FullNamedExpression) yyVals[0+yyTop]); + yyVal = types; + } + +void case_404() +#line 3279 "cs-parser.jay" +{ + var types = (List) yyVals[-2+yyTop]; + types.Add ((FullNamedExpression) yyVals[0+yyTop]); + lbag.AddLocation (types, GetLocation (yyVals[-1+yyTop])); + yyVal = types; + } + +void case_405() +#line 3289 "cs-parser.jay" +{ + if (yyVals[0+yyTop] is ComposedCast) { + report.Error (1521, GetLocation (yyVals[0+yyTop]), "Invalid base type `{0}'", ((ComposedCast)yyVals[0+yyTop]).GetSignatureForError ()); + } + yyVal = yyVals[0+yyTop]; + } + +void case_443() +#line 3353 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-1+yyTop]; + yyVal = new CompletionSimpleName (MemberName.MakeName (lt.Value, null), lt.Location); + } + +void case_454() +#line 3394 "cs-parser.jay" +{ + yyVal = new ParenthesizedExpression ((Expression) yyVals[-1+yyTop], GetLocation (yyVals[-2+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_456() +#line 3406 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-1+yyTop]; + yyVal = new MemberAccess ((Expression) yyVals[-3+yyTop], lt.Value, (TypeArguments) yyVals[0+yyTop], lt.Location); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_457() +#line 3412 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-1+yyTop]; + var ma = new MemberAccess ((Expression) yyVals[-3+yyTop], lt.Value, (int) yyVals[0+yyTop], lt.Location); + lbag.AddLocation (ma.TypeArguments, Lexer.GenericDimensionLocations); + yyVal = ma; + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_458() +#line 3420 "cs-parser.jay" +{ + if (lang_version < LanguageVersion.V_6) + FeatureIsNotAvailable (GetLocation (yyVals[-3+yyTop]), "null propagating operator"); + + var lt = (LocatedToken) yyVals[-1+yyTop]; + yyVal = new ConditionalMemberAccess ((Expression) yyVals[-4+yyTop], lt.Value, (TypeArguments) yyVals[0+yyTop], lt.Location); + lbag.AddLocation (yyVal, GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[-2+yyTop])); + } + +void case_459() +#line 3429 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-1+yyTop]; + yyVal = new MemberAccess ((Expression) yyVals[-3+yyTop], lt.Value, (TypeArguments) yyVals[0+yyTop], lt.Location); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_460() +#line 3435 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-1+yyTop]; + yyVal = new MemberAccess (new BaseThis (GetLocation (yyVals[-3+yyTop])), lt.Value, (TypeArguments) yyVals[0+yyTop], lt.Location); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_461() +#line 3441 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-1+yyTop]; + yyVal = new MemberAccess (new SimpleName ("await", ((LocatedToken) yyVals[-3+yyTop]).Location), lt.Value, (TypeArguments) yyVals[0+yyTop], lt.Location); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_462() +#line 3447 "cs-parser.jay" +{ + var lt1 = (LocatedToken) yyVals[-2+yyTop]; + var lt2 = (LocatedToken) yyVals[-1+yyTop]; + + yyVal = new QualifiedAliasMember (lt1.Value, lt2.Value, (TypeArguments) yyVals[0+yyTop], lt1.Location); + lbag.AddLocation (yyVal, savedLocation, GetLocation (yyVals[-1+yyTop])); + } + +void case_463() +#line 3455 "cs-parser.jay" +{ + var lt1 = (LocatedToken) yyVals[-2+yyTop]; + var lt2 = (LocatedToken) yyVals[-1+yyTop]; + var qam = new QualifiedAliasMember (lt1.Value, lt2.Value, (int) yyVals[0+yyTop], lt1.Location); + lbag.AddLocation (qam.TypeArguments, Lexer.GenericDimensionLocations); + yyVal = qam; + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_465() +#line 3466 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-1+yyTop]; + yyVal = new CompletionMemberAccess ((Expression) yyVals[-3+yyTop], lt.Value, lt.Location); + } + +void case_467() +#line 3474 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-1+yyTop]; + yyVal = new CompletionMemberAccess ((Expression) yyVals[-3+yyTop], lt.Value, lt.Location); + } + +void case_468() +#line 3482 "cs-parser.jay" +{ + yyVal = new Invocation ((Expression) yyVals[-3+yyTop], (Arguments) yyVals[-1+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_469() +#line 3487 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Invocation ((Expression) yyVals[-3+yyTop], (Arguments) yyVals[-1+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_470() +#line 3494 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Invocation ((Expression) yyVals[-2+yyTop], null); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_473() +#line 3509 "cs-parser.jay" +{ + if (yyVals[-1+yyTop] == null) { + yyVal = new CollectionOrObjectInitializers (GetLocation (yyVals[-2+yyTop])); + } else { + yyVal = new CollectionOrObjectInitializers ((List) yyVals[-1+yyTop], GetLocation (yyVals[-2+yyTop])); + } + lbag.AddLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_474() +#line 3518 "cs-parser.jay" +{ + yyVal = new CollectionOrObjectInitializers ((List) yyVals[-2+yyTop], GetLocation (yyVals[-3+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_477() +#line 3534 "cs-parser.jay" +{ + var a = new List (); + a.Add ((Expression) yyVals[0+yyTop]); + yyVal = a; + } + +void case_478() +#line 3540 "cs-parser.jay" +{ + var a = (List)yyVals[-2+yyTop]; + a.Add ((Expression) yyVals[0+yyTop]); + lbag.AddLocation (a, GetLocation (yyVals[-1+yyTop])); + yyVal = a; + } + +void case_479() +#line 3546 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = yyVals[-1+yyTop]; + } + +void case_480() +#line 3554 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-2+yyTop]; + yyVal = new ElementInitializer (lt.Value, (Expression)yyVals[0+yyTop], lt.Location); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_481() +#line 3560 "cs-parser.jay" +{ + var lt = (LocatedToken) Error_AwaitAsIdentifier (yyVals[-2+yyTop]); + yyVal = new ElementInitializer (lt.Value, (Expression)yyVals[0+yyTop], lt.Location); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_483() +#line 3569 "cs-parser.jay" +{ + CompletionSimpleName csn = yyVals[-1+yyTop] as CompletionSimpleName; + if (csn == null) + yyVal = new CollectionElementInitializer ((Expression)yyVals[-1+yyTop]); + else + yyVal = new CompletionElementInitializer (csn.Prefix, csn.Location); + } + +void case_484() +#line 3577 "cs-parser.jay" +{ + if (yyVals[-1+yyTop] == null) + yyVal = new CollectionElementInitializer (GetLocation (yyVals[-2+yyTop])); + else { + yyVal = new CollectionElementInitializer ((List)yyVals[-1+yyTop], GetLocation (yyVals[-2+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + lbag.AddLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_485() +#line 3587 "cs-parser.jay" +{ + if (lang_version < LanguageVersion.V_6) + FeatureIsNotAvailable (GetLocation (yyVals[-4+yyTop]), "dictionary initializer"); + + yyVal = new DictionaryElementInitializer ((List)yyVals[-3+yyTop], (Expression) yyVals[0+yyTop], GetLocation (yyVals[-4+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[-1+yyTop])); + } + +void case_486() +#line 3595 "cs-parser.jay" +{ + report.Error (1920, GetLocation (yyVals[-1+yyTop]), "An element initializer cannot be empty"); + yyVal = new CollectionElementInitializer (GetLocation (yyVals[-1+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_491() +#line 3614 "cs-parser.jay" +{ + Arguments list = new Arguments (4); + list.Add ((Argument) yyVals[0+yyTop]); + yyVal = list; + } + +void case_492() +#line 3620 "cs-parser.jay" +{ + Arguments list = (Arguments) yyVals[-2+yyTop]; + if (list [list.Count - 1] is NamedArgument) + Error_NamedArgumentExpected ((NamedArgument) list [list.Count - 1]); + + list.Add ((Argument) yyVals[0+yyTop]); + lbag.AddLocation (list, GetLocation (yyVals[-1+yyTop])); + yyVal = list; + } + +void case_493() +#line 3630 "cs-parser.jay" +{ + Arguments list = (Arguments) yyVals[-2+yyTop]; + NamedArgument a = (NamedArgument) yyVals[0+yyTop]; + for (int i = 0; i < list.Count; ++i) { + NamedArgument na = list [i] as NamedArgument; + if (na != null && na.Name == a.Name) + report.Error (1740, na.Location, "Named argument `{0}' specified multiple times", + na.Name); + } + + list.Add (a); + lbag.AddLocation (list, GetLocation (yyVals[-1+yyTop])); + yyVal = list; + } + +void case_494() +#line 3645 "cs-parser.jay" +{ + if (lexer.putback_char == -1) + lexer.putback (')'); /* TODO: Wrong but what can I do*/ + Error_SyntaxError (yyToken); + yyVal = yyVals[-2+yyTop]; + } + +void case_495() +#line 3652 "cs-parser.jay" +{ + report.Error (839, GetLocation (yyVals[-1+yyTop]), "An argument is missing"); + yyVal = null; + } + +void case_500() +#line 3673 "cs-parser.jay" +{ + yyVal = new Argument ((Expression) yyVals[0+yyTop], Argument.AType.Ref); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_501() +#line 3678 "cs-parser.jay" +{ + yyVal = new Argument ((Expression) yyVals[0+yyTop], Argument.AType.Out); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_502() +#line 3683 "cs-parser.jay" +{ + yyVal = new Argument (new Arglist ((Arguments) yyVals[-1+yyTop], GetLocation (yyVals[-3+yyTop]))); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_503() +#line 3688 "cs-parser.jay" +{ + yyVal = new Argument (new Arglist (GetLocation (yyVals[-2+yyTop]))); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_505() +#line 3700 "cs-parser.jay" +{ + yyVal = new ElementAccess ((Expression) yyVals[-3+yyTop], (Arguments) yyVals[-1+yyTop], GetLocation (yyVals[-2+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_506() +#line 3705 "cs-parser.jay" +{ + if (lang_version < LanguageVersion.V_6) + FeatureIsNotAvailable (GetLocation (yyVals[-3+yyTop]), "null propagating operator"); + + yyVal = new ElementAccess ((Expression) yyVals[-4+yyTop], (Arguments) yyVals[-1+yyTop], GetLocation (yyVals[-2+yyTop])) { + ConditionalAccess = true + }; + + lbag.AddLocation (yyVal, GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_507() +#line 3716 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = new ElementAccess ((Expression) yyVals[-3+yyTop], (Arguments) yyVals[-1+yyTop], GetLocation (yyVals[-2+yyTop])); + } + +void case_508() +#line 3721 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = new ElementAccess ((Expression) yyVals[-2+yyTop], null, GetLocation (yyVals[-1+yyTop])); + } + +void case_509() +#line 3729 "cs-parser.jay" +{ + var list = new List (4); + list.Add ((Expression) yyVals[0+yyTop]); + yyVal = list; + } + +void case_510() +#line 3735 "cs-parser.jay" +{ + var list = (List) yyVals[-2+yyTop]; + list.Add ((Expression) yyVals[0+yyTop]); + lbag.AddLocation (list, GetLocation (yyVals[-1+yyTop])); + yyVal = list; + } + +void case_511() +#line 3745 "cs-parser.jay" +{ + Arguments args = new Arguments (4); + args.Add ((Argument) yyVals[0+yyTop]); + yyVal = args; + } + +void case_512() +#line 3751 "cs-parser.jay" +{ + Arguments args = (Arguments) yyVals[-2+yyTop]; + if (args [args.Count - 1] is NamedArgument && !(yyVals[0+yyTop] is NamedArgument)) + Error_NamedArgumentExpected ((NamedArgument) args [args.Count - 1]); + + args.Add ((Argument) yyVals[0+yyTop]); + lbag.AddLocation (args, GetLocation (yyVals[-1+yyTop])); + yyVal = args; + } + +void case_516() +#line 3779 "cs-parser.jay" +{ + yyVal = new ElementAccess (new BaseThis (GetLocation (yyVals[-3+yyTop])), (Arguments) yyVals[-1+yyTop], GetLocation (yyVals[-2+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_517() +#line 3784 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = new ElementAccess (null, null, GetLocation (yyVals[-1+yyTop])); + } + +void case_520() +#line 3806 "cs-parser.jay" +{ + if (yyVals[0+yyTop] != null) { + if (lang_version <= LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation (yyVals[-5+yyTop]), "object initializers"); + + yyVal = new NewInitialize ((FullNamedExpression) yyVals[-4+yyTop], (Arguments) yyVals[-2+yyTop], (CollectionOrObjectInitializers) yyVals[0+yyTop], GetLocation (yyVals[-5+yyTop])); + } else { + yyVal = new New ((FullNamedExpression) yyVals[-4+yyTop], (Arguments) yyVals[-2+yyTop], GetLocation (yyVals[-5+yyTop])); + } + + lbag.AddLocation (yyVal, GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[-1+yyTop])); + } + +void case_521() +#line 3819 "cs-parser.jay" +{ + if (lang_version <= LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation (yyVals[-2+yyTop]), "collection initializers"); + + yyVal = new NewInitialize ((FullNamedExpression) yyVals[-1+yyTop], null, (CollectionOrObjectInitializers) yyVals[0+yyTop], GetLocation (yyVals[-2+yyTop])); + } + +void case_522() +#line 3831 "cs-parser.jay" +{ + yyVal = new ArrayCreation ((FullNamedExpression) yyVals[-5+yyTop], (List) yyVals[-3+yyTop], + new ComposedTypeSpecifier (((List) yyVals[-3+yyTop]).Count, GetLocation (yyVals[-4+yyTop])) { + Next = (ComposedTypeSpecifier) yyVals[-1+yyTop] + }, (ArrayInitializer) yyVals[0+yyTop], GetLocation (yyVals[-6+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-4+yyTop]), GetLocation (yyVals[-2+yyTop])); + } + +void case_523() +#line 3839 "cs-parser.jay" +{ + if (yyVals[0+yyTop] == null) + report.Error (1586, GetLocation (yyVals[-3+yyTop]), "Array creation must have array size or array initializer"); + + yyVal = new ArrayCreation ((FullNamedExpression) yyVals[-2+yyTop], (ComposedTypeSpecifier) yyVals[-1+yyTop], (ArrayInitializer) yyVals[0+yyTop], GetLocation (yyVals[-3+yyTop])); + } + +void case_524() +#line 3846 "cs-parser.jay" +{ + if (lang_version <= LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation (yyVals[-2+yyTop]), "implicitly typed arrays"); + + yyVal = new ImplicitlyTypedArrayCreation ((ComposedTypeSpecifier) yyVals[-1+yyTop], (ArrayInitializer) yyVals[0+yyTop], GetLocation (yyVals[-2+yyTop])); + } + +void case_525() +#line 3853 "cs-parser.jay" +{ + report.Error (178, GetLocation (yyVals[-1+yyTop]), "Invalid rank specifier, expecting `,' or `]'"); + yyVal = new ArrayCreation ((FullNamedExpression) yyVals[-5+yyTop], null, GetLocation (yyVals[-6+yyTop])); + } + +void case_526() +#line 3858 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + /* It can be any of new expression, create the most common one*/ + yyVal = new New ((FullNamedExpression) yyVals[-1+yyTop], null, GetLocation (yyVals[-2+yyTop])); + } + +void case_528() +#line 3870 "cs-parser.jay" +{ + --lexer.parsing_type; + yyVal = yyVals[0+yyTop]; + } + +void case_529() +#line 3878 "cs-parser.jay" +{ + if (lang_version <= LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation (yyVals[-3+yyTop]), "anonymous types"); + + yyVal = new NewAnonymousType ((List) yyVals[-1+yyTop], current_container, GetLocation (yyVals[-3+yyTop])); + + /* TODO: lbag comma location*/ + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_535() +#line 3905 "cs-parser.jay" +{ + var a = new List (4); + a.Add ((AnonymousTypeParameter) yyVals[0+yyTop]); + yyVal = a; + } + +void case_536() +#line 3911 "cs-parser.jay" +{ + var a = (List) yyVals[-2+yyTop]; + a.Add ((AnonymousTypeParameter) yyVals[0+yyTop]); + lbag.AddLocation (a, GetLocation (yyVals[-1+yyTop])); + + yyVal = a; + } + +void case_539() +#line 3930 "cs-parser.jay" +{ + var lt = (LocatedToken)yyVals[-2+yyTop]; + yyVal = new AnonymousTypeParameter ((Expression)yyVals[0+yyTop], lt.Value, lt.Location); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_540() +#line 3936 "cs-parser.jay" +{ + var lt = (LocatedToken)yyVals[0+yyTop]; + yyVal = new AnonymousTypeParameter (new SimpleName (lt.Value, lt.Location), + lt.Value, lt.Location); + } + +void case_541() +#line 3942 "cs-parser.jay" +{ + MemberAccess ma = (MemberAccess) yyVals[0+yyTop]; + yyVal = new AnonymousTypeParameter (ma, ma.Name, ma.Location); + } + +void case_542() +#line 3947 "cs-parser.jay" +{ + report.Error (746, lexer.Location, + "Invalid anonymous type member declarator. Anonymous type members must be a member assignment, simple name or member access expression"); + yyVal = null; + } + +void case_546() +#line 3962 "cs-parser.jay" +{ + ((ComposedTypeSpecifier) yyVals[-1+yyTop]).Next = (ComposedTypeSpecifier) yyVals[0+yyTop]; + yyVal = yyVals[-1+yyTop]; + } + +void case_547() +#line 3970 "cs-parser.jay" +{ + yyVal = ComposedTypeSpecifier.CreateArrayDimension (1, GetLocation (yyVals[-1+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_548() +#line 3975 "cs-parser.jay" +{ + yyVal = ComposedTypeSpecifier.CreateArrayDimension ((int)yyVals[-1+yyTop], GetLocation (yyVals[-2+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_553() +#line 4005 "cs-parser.jay" +{ + var ai = new ArrayInitializer (0, GetLocation (yyVals[-1+yyTop])); + ai.VariableDeclaration = current_variable; + lbag.AddLocation (ai, GetLocation (yyVals[0+yyTop])); + yyVal = ai; + } + +void case_554() +#line 4012 "cs-parser.jay" +{ + var ai = new ArrayInitializer ((List) yyVals[-2+yyTop], GetLocation (yyVals[-3+yyTop])); + ai.VariableDeclaration = current_variable; + if (yyVals[-1+yyTop] != null) { + lbag.AddLocation (ai, GetLocation (yyVals[-1+yyTop]), GetLocation (yyVals[0+yyTop])); + } else { + lbag.AddLocation (ai, GetLocation (yyVals[0+yyTop])); + } + yyVal = ai; + } + +void case_555() +#line 4026 "cs-parser.jay" +{ + var list = new List (4); + list.Add ((Expression) yyVals[0+yyTop]); + yyVal = list; + } + +void case_556() +#line 4032 "cs-parser.jay" +{ + var list = (List) yyVals[-2+yyTop]; + list.Add ((Expression) yyVals[0+yyTop]); + lbag.AddLocation (list, GetLocation (yyVals[-1+yyTop])); + yyVal = list; + } + +void case_557() +#line 4042 "cs-parser.jay" +{ + yyVal = new TypeOf ((FullNamedExpression) yyVals[-1+yyTop], GetLocation (yyVals[-3+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_559() +#line 4051 "cs-parser.jay" +{ + Error_TypeExpected (lexer.Location); + yyVal = null; + } + +void case_560() +#line 4059 "cs-parser.jay" +{ + if (lang_version < LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation (yyVals[0+yyTop]), "generics"); + + yyVal = yyVals[0+yyTop]; + } + +void case_561() +#line 4069 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-1+yyTop]; + if (lang_version == LanguageVersion.ISO_1) + FeatureIsNotAvailable (lt.Location, "namespace alias qualifier"); + savedLocation = GetLocation (yyVals[0+yyTop]); + yyVal = lt; + } + +void case_562() +#line 4080 "cs-parser.jay" +{ + yyVal = new SizeOf ((Expression) yyVals[-1+yyTop], GetLocation (yyVals[-3+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_563() +#line 4085 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new SizeOf ((Expression) yyVals[-1+yyTop], GetLocation (yyVals[-3+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_564() +#line 4095 "cs-parser.jay" +{ + yyVal = new CheckedExpr ((Expression) yyVals[-1+yyTop], GetLocation (yyVals[-3+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_565() +#line 4100 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new CheckedExpr (null, GetLocation (yyVals[-1+yyTop])); + } + +void case_566() +#line 4109 "cs-parser.jay" +{ + yyVal = new UnCheckedExpr ((Expression) yyVals[-1+yyTop], GetLocation (yyVals[-3+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_567() +#line 4114 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new UnCheckedExpr (null, GetLocation (yyVals[-1+yyTop])); + } + +void case_568() +#line 4123 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-1+yyTop]; + yyVal = new MemberAccess (new Indirection ((Expression) yyVals[-3+yyTop], GetLocation (yyVals[-2+yyTop])), lt.Value, (TypeArguments) yyVals[0+yyTop], lt.Location); + } + +void case_570() +#line 4135 "cs-parser.jay" +{ + yyVal = end_anonymous ((ParametersBlock) yyVals[0+yyTop]); + if ((ParametersCompiled) yyVals[-2+yyTop] != ParametersCompiled.Undefined) { + lbag.AddLocation (yyVal, GetLocation (yyVals[-3+yyTop]), PopLocation (), PopLocation ()); + } else { + lbag.AddLocation (yyVal, GetLocation (yyVals[-3+yyTop])); + } + } + +void case_572() +#line 4148 "cs-parser.jay" +{ + yyVal = end_anonymous ((ParametersBlock) yyVals[0+yyTop]); + + if ((ParametersCompiled) yyVals[-2+yyTop] != ParametersCompiled.Undefined) { + lbag.AddLocation (yyVal, GetLocation (yyVals[-4+yyTop]), GetLocation (yyVals[-3+yyTop]), PopLocation (), PopLocation ()); + } else { + lbag.AddLocation (yyVal, GetLocation (yyVals[-4+yyTop]), GetLocation (yyVals[-3+yyTop])); + } + } + +void case_576() +#line 4173 "cs-parser.jay" +{ + valid_param_mod = 0; + yyVal = yyVals[-1+yyTop]; + PushLocation (GetLocation (yyVals[-1+yyTop])); + PushLocation (GetLocation (yyVals[-3+yyTop])); + } + +void case_577() +#line 4183 "cs-parser.jay" +{ + if (lang_version < LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation (yyVals[-3+yyTop]), "default value expression"); + + yyVal = new DefaultValueExpression ((Expression) yyVals[-1+yyTop], GetLocation (yyVals[-3+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_581() +#line 4203 "cs-parser.jay" +{ + yyVal = new Cast ((FullNamedExpression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop], GetLocation (yyVals[-3+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_582() +#line 4208 "cs-parser.jay" +{ + if (!async_block) { + if (current_anonymous_method is LambdaExpression) { + report.Error (4034, GetLocation (yyVals[-1+yyTop]), + "The `await' operator can only be used when its containing lambda expression is marked with the `async' modifier"); + } else if (current_anonymous_method != null) { + report.Error (4035, GetLocation (yyVals[-1+yyTop]), + "The `await' operator can only be used when its containing anonymous method is marked with the `async' modifier"); + } else if (interactive_async != null) { + current_block.Explicit.RegisterAsyncAwait (); + interactive_async = true; + } else { + report.Error (4033, GetLocation (yyVals[-1+yyTop]), + "The `await' operator can only be used when its containing method is marked with the `async' modifier"); + } + } else { + current_block.Explicit.RegisterAsyncAwait (); + } + + yyVal = new Await ((Expression) yyVals[0+yyTop], GetLocation (yyVals[-1+yyTop])); + } + +void case_583() +#line 4230 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Unary (Unary.Operator.LogicalNot, null, GetLocation (yyVals[-1+yyTop])); + } + +void case_584() +#line 4236 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Unary (Unary.Operator.OnesComplement, null, GetLocation (yyVals[-1+yyTop])); + } + +void case_585() +#line 4242 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Cast ((FullNamedExpression) yyVals[-2+yyTop], null, GetLocation (yyVals[-3+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_586() +#line 4249 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Await (null, GetLocation (yyVals[-1+yyTop])); + } + +void case_594() +#line 4287 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Unary (Unary.Operator.UnaryPlus, null, GetLocation (yyVals[-1+yyTop])); + } + +void case_595() +#line 4293 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Unary (Unary.Operator.UnaryNegation, null, GetLocation (yyVals[-1+yyTop])); + } + +void case_596() +#line 4299 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new UnaryMutator (UnaryMutator.Mode.PreIncrement, null, GetLocation (yyVals[-1+yyTop])); + } + +void case_597() +#line 4305 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new UnaryMutator (UnaryMutator.Mode.PreDecrement, null, GetLocation (yyVals[-1+yyTop])); + } + +void case_598() +#line 4311 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Indirection (null, GetLocation (yyVals[-1+yyTop])); + } + +void case_599() +#line 4317 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Unary (Unary.Operator.AddressOf, null, GetLocation (yyVals[-1+yyTop])); + } + +void case_601() +#line 4327 "cs-parser.jay" +{ + yyVal = new Binary (Binary.Operator.Multiply, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_602() +#line 4332 "cs-parser.jay" +{ + yyVal = new Binary (Binary.Operator.Division, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_603() +#line 4337 "cs-parser.jay" +{ + yyVal = new Binary (Binary.Operator.Modulus, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_604() +#line 4342 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Binary (Binary.Operator.Multiply, (Expression) yyVals[-2+yyTop], null); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_605() +#line 4349 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Binary (Binary.Operator.Division, (Expression) yyVals[-2+yyTop], null); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_606() +#line 4356 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Binary (Binary.Operator.Modulus, (Expression) yyVals[-2+yyTop], null); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_608() +#line 4367 "cs-parser.jay" +{ + yyVal = new Binary (Binary.Operator.Addition, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_609() +#line 4372 "cs-parser.jay" +{ + yyVal = new Binary (Binary.Operator.Subtraction, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_610() +#line 4377 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Binary (Binary.Operator.Addition, (Expression) yyVals[-2+yyTop], null); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_611() +#line 4384 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Binary (Binary.Operator.Subtraction, (Expression) yyVals[-2+yyTop], null); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_613() +#line 4395 "cs-parser.jay" +{ + var is_expr = new Is ((Expression) yyVals[-3+yyTop], (Expression) yyVals[-1+yyTop], GetLocation (yyVals[-2+yyTop])); + if (yyVals[0+yyTop] != null) { + if (lang_version != LanguageVersion.Experimental) + FeatureIsNotAvailable (GetLocation (yyVals[0+yyTop]), "type pattern matching"); + + var lt = (LocatedToken) yyVals[0+yyTop]; + is_expr.Variable = new LocalVariable (current_block, lt.Value, lt.Location); + current_block.AddLocalName (is_expr.Variable); + } + + yyVal = is_expr; + } + +void case_614() +#line 4409 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new As ((Expression) yyVals[-2+yyTop], null, GetLocation (yyVals[-1+yyTop])); + } + +void case_615() +#line 4415 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Is ((Expression) yyVals[-2+yyTop], null, GetLocation (yyVals[-1+yyTop])); + } + +void case_616() +#line 4421 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-2+yyTop]; + yyVal = new Is (new SimpleName (lt.Value, lt.Location), (Expression) yyVals[0+yyTop], GetLocation (yyVals[-1+yyTop])); + } + +void case_617() +#line 4426 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-2+yyTop]; + yyVal = new As (new SimpleName (lt.Value, lt.Location), (Expression) yyVals[0+yyTop], GetLocation (yyVals[-1+yyTop])); + } + +void case_619() +#line 4435 "cs-parser.jay" +{ + if (yyVals[-1+yyTop] is VarExpr) + yyVals[-1+yyTop] = new SimpleName ("var", ((VarExpr) yyVals[-1+yyTop]).Location); + + yyVal = new ComposedCast ((FullNamedExpression) yyVals[-1+yyTop], (ComposedTypeSpecifier) yyVals[0+yyTop]); + } + +void case_623() +#line 4454 "cs-parser.jay" +{ + Expression expr = (Expression) yyVals[-1+yyTop]; + if (yyVals[0+yyTop] == null) { + SimpleName sn = expr as SimpleName; + if (sn != null && sn.Name == "var") + yyVal = new VarExpr (sn.Location); + else + yyVal = yyVals[-1+yyTop]; + } else if (expr is ATypeNameExpression) { + yyVal = new ComposedCast ((ATypeNameExpression)expr, (ComposedTypeSpecifier) yyVals[0+yyTop]); + } else { + Error_ExpectingTypeName (expr); + yyVal = null; + } + } + +void case_624() +#line 4470 "cs-parser.jay" +{ + ATypeNameExpression expr = yyVals[-1+yyTop] as ATypeNameExpression; + + if (expr != null) { + yyVal = new ComposedCast (expr, (ComposedTypeSpecifier) yyVals[0+yyTop]); + } else { + Error_ExpectingTypeName ((Expression)yyVals[-1+yyTop]); + yyVal = expr; + } + } + +void case_628() +#line 4487 "cs-parser.jay" +{ + yyVal = new Binary (Binary.Operator.LeftShift, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_629() +#line 4492 "cs-parser.jay" +{ + yyVal = new Binary (Binary.Operator.RightShift, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_630() +#line 4497 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Binary (Binary.Operator.LeftShift, (Expression) yyVals[-2+yyTop], null); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_631() +#line 4504 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Binary (Binary.Operator.RightShift, (Expression) yyVals[-2+yyTop], null); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_633() +#line 4515 "cs-parser.jay" +{ + yyVal = new Binary (Binary.Operator.LessThan, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_634() +#line 4520 "cs-parser.jay" +{ + yyVal = new Binary (Binary.Operator.GreaterThan, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_635() +#line 4525 "cs-parser.jay" +{ + yyVal = new Binary (Binary.Operator.LessThanOrEqual, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_636() +#line 4530 "cs-parser.jay" +{ + yyVal = new Binary (Binary.Operator.GreaterThanOrEqual, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_637() +#line 4535 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Binary (Binary.Operator.LessThan, (Expression) yyVals[-2+yyTop], null); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_638() +#line 4542 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Binary (Binary.Operator.GreaterThan, (Expression) yyVals[-2+yyTop], null); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_639() +#line 4549 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Binary (Binary.Operator.LessThanOrEqual, (Expression) yyVals[-2+yyTop], null); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_640() +#line 4556 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Binary (Binary.Operator.GreaterThanOrEqual, (Expression) yyVals[-2+yyTop], null); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_642() +#line 4567 "cs-parser.jay" +{ + yyVal = new Binary (Binary.Operator.Equality, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_643() +#line 4572 "cs-parser.jay" +{ + yyVal = new Binary (Binary.Operator.Inequality, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_644() +#line 4577 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Binary (Binary.Operator.Equality, (Expression) yyVals[-2+yyTop], null); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_645() +#line 4584 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Binary (Binary.Operator.Inequality, (Expression) yyVals[-2+yyTop], null); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_647() +#line 4595 "cs-parser.jay" +{ + yyVal = new Binary (Binary.Operator.BitwiseAnd, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_648() +#line 4600 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Binary (Binary.Operator.BitwiseAnd, (Expression) yyVals[-2+yyTop], null); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_650() +#line 4611 "cs-parser.jay" +{ + yyVal = new Binary (Binary.Operator.ExclusiveOr, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_651() +#line 4616 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Binary (Binary.Operator.ExclusiveOr, (Expression) yyVals[-2+yyTop], null); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_653() +#line 4627 "cs-parser.jay" +{ + yyVal = new Binary (Binary.Operator.BitwiseOr, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_654() +#line 4632 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Binary (Binary.Operator.BitwiseOr, (Expression) yyVals[-2+yyTop], null); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_656() +#line 4643 "cs-parser.jay" +{ + yyVal = new Binary (Binary.Operator.LogicalAnd, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_657() +#line 4648 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Binary (Binary.Operator.LogicalAnd, (Expression) yyVals[-2+yyTop], null); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_659() +#line 4659 "cs-parser.jay" +{ + yyVal = new Binary (Binary.Operator.LogicalOr, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_660() +#line 4664 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Binary (Binary.Operator.LogicalOr, (Expression) yyVals[-2+yyTop], null); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_662() +#line 4675 "cs-parser.jay" +{ + if (lang_version < LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation (yyVals[-1+yyTop]), "null coalescing operator"); + + yyVal = new Nullable.NullCoalescingOperator ((Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_664() +#line 4687 "cs-parser.jay" +{ + yyVal = new Conditional (new BooleanExpression ((Expression) yyVals[-4+yyTop]), (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop], GetLocation (yyVals[-3+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_665() +#line 4692 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Conditional (new BooleanExpression ((Expression) yyVals[-3+yyTop]), (Expression) yyVals[-1+yyTop], null, GetLocation (yyVals[-2+yyTop])); + } + +void case_666() +#line 4698 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Conditional (new BooleanExpression ((Expression) yyVals[-4+yyTop]), (Expression) yyVals[-2+yyTop], null, GetLocation (yyVals[-3+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_667() +#line 4705 "cs-parser.jay" +{ + Error_SyntaxError (Token.CLOSE_BRACE); + + yyVal = new Conditional (new BooleanExpression ((Expression) yyVals[-4+yyTop]), (Expression) yyVals[-2+yyTop], null, GetLocation (yyVals[-3+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + lexer.putback ('}'); + } + +void case_668() +#line 4716 "cs-parser.jay" +{ + yyVal = new SimpleAssign ((Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_669() +#line 4721 "cs-parser.jay" +{ + yyVal = new CompoundAssign (Binary.Operator.Multiply, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_670() +#line 4726 "cs-parser.jay" +{ + yyVal = new CompoundAssign (Binary.Operator.Division, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_671() +#line 4731 "cs-parser.jay" +{ + yyVal = new CompoundAssign (Binary.Operator.Modulus, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_672() +#line 4736 "cs-parser.jay" +{ + yyVal = new CompoundAssign (Binary.Operator.Addition, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_673() +#line 4741 "cs-parser.jay" +{ + yyVal = new CompoundAssign (Binary.Operator.Subtraction, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_674() +#line 4746 "cs-parser.jay" +{ + yyVal = new CompoundAssign (Binary.Operator.LeftShift, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_675() +#line 4751 "cs-parser.jay" +{ + yyVal = new CompoundAssign (Binary.Operator.RightShift, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_676() +#line 4756 "cs-parser.jay" +{ + yyVal = new CompoundAssign (Binary.Operator.BitwiseAnd, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_677() +#line 4761 "cs-parser.jay" +{ + yyVal = new CompoundAssign (Binary.Operator.BitwiseOr, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_678() +#line 4766 "cs-parser.jay" +{ + yyVal = new CompoundAssign (Binary.Operator.ExclusiveOr, (Expression) yyVals[-2+yyTop], (Expression) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_679() +#line 4774 "cs-parser.jay" +{ + var pars = new List (4); + pars.Add ((Parameter) yyVals[0+yyTop]); + parameterListCommas.Clear (); + yyVal = pars; + } + +void case_680() +#line 4781 "cs-parser.jay" +{ + var pars = (List) yyVals[-2+yyTop]; + Parameter p = (Parameter)yyVals[0+yyTop]; + if (pars[0].GetType () != p.GetType ()) { + report.Error (748, p.Location, "All lambda parameters must be typed either explicitly or implicitly"); + } + + pars.Add (p); + parameterListCommas.Add (GetLocation (yyVals[-1+yyTop])); + + yyVal = pars; + } + +void case_681() +#line 4797 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[0+yyTop]; + + yyVal = new Parameter ((FullNamedExpression) yyVals[-1+yyTop], lt.Value, (Parameter.Modifier) yyVals[-2+yyTop], null, lt.Location); + } + +void case_682() +#line 4803 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[0+yyTop]; + + yyVal = new Parameter ((FullNamedExpression) yyVals[-1+yyTop], lt.Value, Parameter.Modifier.NONE, null, lt.Location); + } + +void case_683() +#line 4809 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[0+yyTop]; + yyVal = new ImplicitLambdaParameter (lt.Value, lt.Location); + } + +void case_684() +#line 4814 "cs-parser.jay" +{ + var lt = (LocatedToken) Error_AwaitAsIdentifier (yyVals[0+yyTop]); + yyVal = new ImplicitLambdaParameter (lt.Value, lt.Location); + } + +void case_686() +#line 4822 "cs-parser.jay" +{ + var pars_list = (List) yyVals[0+yyTop]; + yyVal = new ParametersCompiled (pars_list.ToArray ()); + lbag.AddLocation (yyVal, parameterListCommas); + } + +void case_688() +#line 4834 "cs-parser.jay" +{ + Block b = end_block (Location.Null); + b.IsCompilerGenerated = true; + b.AddStatement (new ContextualReturn ((Expression) yyVals[0+yyTop])); + yyVal = b; + } + +void case_690() +#line 4842 "cs-parser.jay" +{ + /* Handles only cases like foo = x.FirstOrDefault (l => );*/ + /* where we must restore current_variable*/ + Block b = end_block (Location.Null); + b.IsCompilerGenerated = true; + + Error_SyntaxError (yyToken); + yyVal = null; + } + +void case_692() +#line 4856 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = null; + } + +void case_693() +#line 4864 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-1+yyTop]; + Parameter p = new ImplicitLambdaParameter (lt.Value, lt.Location); + start_anonymous (true, new ParametersCompiled (p), false, lt.Location); + } + +void case_694() +#line 4870 "cs-parser.jay" +{ + yyVal = end_anonymous ((ParametersBlock) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_695() +#line 4875 "cs-parser.jay" +{ + var lt = (LocatedToken) Error_AwaitAsIdentifier (yyVals[-1+yyTop]); + Parameter p = new ImplicitLambdaParameter (lt.Value, lt.Location); + start_anonymous (true, new ParametersCompiled (p), false, lt.Location); + } + +void case_696() +#line 4881 "cs-parser.jay" +{ + yyVal = end_anonymous ((ParametersBlock) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_697() +#line 4886 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-1+yyTop]; + Parameter p = new ImplicitLambdaParameter (lt.Value, lt.Location); + start_anonymous (true, new ParametersCompiled (p), true, lt.Location); + } + +void case_698() +#line 4892 "cs-parser.jay" +{ + yyVal = end_anonymous ((ParametersBlock) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-4+yyTop]), GetLocation (yyVals[-2+yyTop])); + } + +void case_700() +#line 4901 "cs-parser.jay" +{ + valid_param_mod = 0; + start_anonymous (true, (ParametersCompiled) yyVals[-2+yyTop], false, GetLocation (yyVals[-4+yyTop])); + } + +void case_701() +#line 4906 "cs-parser.jay" +{ + yyVal = end_anonymous ((ParametersBlock) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-6+yyTop]), GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[-2+yyTop])); + } + +void case_703() +#line 4915 "cs-parser.jay" +{ + valid_param_mod = 0; + start_anonymous (true, (ParametersCompiled) yyVals[-2+yyTop], true, GetLocation (yyVals[-5+yyTop])); + } + +void case_704() +#line 4920 "cs-parser.jay" +{ + yyVal = end_anonymous ((ParametersBlock) yyVals[0+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[-7+yyTop]), GetLocation (yyVals[-6+yyTop]), GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[-2+yyTop])); + } + +void case_711() +#line 4943 "cs-parser.jay" +{ + yyVal = new RefValueExpr ((Expression) yyVals[-3+yyTop], (FullNamedExpression) yyVals[-1+yyTop], GetLocation (yyVals[-5+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-4+yyTop]), GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_712() +#line 4948 "cs-parser.jay" +{ + yyVal = new RefTypeExpr ((Expression) yyVals[-1+yyTop], GetLocation (yyVals[-3+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_713() +#line 4953 "cs-parser.jay" +{ + yyVal = new MakeRefExpr ((Expression) yyVals[-1+yyTop], GetLocation (yyVals[-3+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_718() +#line 4980 "cs-parser.jay" +{ + yyVal = yyVals[-1+yyTop]; + + /* Cannot use opt_formal_parameter_list because it can be shared instance for empty parameters*/ + lbag.AppendToMember (current_container, GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + + if (lang_version < LanguageVersion.V_6) + FeatureIsNotAvailable (GetLocation (yyVals[-2+yyTop]), "primary constructor"); + } + +void case_723() +#line 5009 "cs-parser.jay" +{ + ++lexer.parsing_block; + current_type.PrimaryConstructorBaseArgumentsStart = GetLocation (yyVals[0+yyTop]); + } + +void case_724() +#line 5014 "cs-parser.jay" +{ + lbag.AppendToMember (current_container, GetLocation (yyVals[0+yyTop])); + current_type.PrimaryConstructorBaseArguments = (Arguments) yyVals[-1+yyTop]; + --lexer.parsing_block; + + yyVal = yyVals[-5+yyTop]; + } + +void case_726() +#line 5034 "cs-parser.jay" +{ + lexer.ConstraintsParsing = true; + + Class c = new Class (current_container, (MemberName) yyVals[0+yyTop], (Modifiers) yyVals[-4+yyTop], (Attributes) yyVals[-5+yyTop]); + if (((c.ModFlags & Modifiers.STATIC) != 0) && lang_version == LanguageVersion.ISO_1) { + FeatureIsNotAvailable (c.Location, "static classes"); + } + + push_current_container (c, yyVals[-3+yyTop]); + lbag.AddMember (current_container, GetModifierLocations (), GetLocation (yyVals[-2+yyTop])); + valid_param_mod = ParameterModifierType.PrimaryConstructor; + } + +void case_727() +#line 5048 "cs-parser.jay" +{ + valid_param_mod = 0; + lexer.ConstraintsParsing = false; + + if (yyVals[-1+yyTop] != null) + current_type.PrimaryConstructorParameters = (ParametersCompiled) yyVals[-1+yyTop]; + + if (yyVals[0+yyTop] != null) + current_container.SetConstraints ((List) yyVals[0+yyTop]); + + if (doc_support) { + current_container.PartialContainer.DocComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + lexer.parsing_modifiers = true; + } + +void case_728() +#line 5066 "cs-parser.jay" +{ + --lexer.parsing_declaration; + if (doc_support) + Lexer.doc_state = XmlCommentState.Allowed; + } + +void case_729() +#line 5072 "cs-parser.jay" +{ + if (yyVals[0+yyTop] == null) { + lbag.AppendToMember (current_container, GetLocation (yyVals[-4+yyTop]), GetLocation (yyVals[-2+yyTop])); + } else { + lbag.AppendToMember (current_container, GetLocation (yyVals[-4+yyTop]), GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + yyVal = pop_current_class (); + } + +void case_732() +#line 5091 "cs-parser.jay" +{ + mod_locations = null; + yyVal = ModifierNone; + lexer.parsing_modifiers = false; + } + +void case_735() +#line 5105 "cs-parser.jay" +{ + var m1 = (Modifiers) yyVals[-1+yyTop]; + var m2 = (Modifiers) yyVals[0+yyTop]; + + if ((m1 & m2) != 0) { + report.Error (1004, lexer.Location - ModifiersExtensions.Name (m2).Length, + "Duplicate `{0}' modifier", ModifiersExtensions.Name (m2)); + } else if ((m2 & Modifiers.AccessibilityMask) != 0 && (m1 & Modifiers.AccessibilityMask) != 0 && + ((m2 | m1 & Modifiers.AccessibilityMask) != (Modifiers.PROTECTED | Modifiers.INTERNAL))) { + report.Error (107, lexer.Location - ModifiersExtensions.Name (m2).Length, + "More than one protection modifier specified"); + } + + yyVal = m1 | m2; + } + +void case_736() +#line 5124 "cs-parser.jay" +{ + yyVal = Modifiers.NEW; + StoreModifierLocation (yyVal, GetLocation (yyVals[0+yyTop])); + + if (current_container.Kind == MemberKind.Namespace) + report.Error (1530, GetLocation (yyVals[0+yyTop]), "Keyword `new' is not allowed on namespace elements"); + } + +void case_737() +#line 5132 "cs-parser.jay" +{ + yyVal = Modifiers.PUBLIC; + StoreModifierLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_738() +#line 5137 "cs-parser.jay" +{ + yyVal = Modifiers.PROTECTED; + StoreModifierLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_739() +#line 5142 "cs-parser.jay" +{ + yyVal = Modifiers.INTERNAL; + StoreModifierLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_740() +#line 5147 "cs-parser.jay" +{ + yyVal = Modifiers.PRIVATE; + StoreModifierLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_741() +#line 5152 "cs-parser.jay" +{ + yyVal = Modifiers.ABSTRACT; + StoreModifierLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_742() +#line 5157 "cs-parser.jay" +{ + yyVal = Modifiers.SEALED; + StoreModifierLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_743() +#line 5162 "cs-parser.jay" +{ + yyVal = Modifiers.STATIC; + StoreModifierLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_744() +#line 5167 "cs-parser.jay" +{ + yyVal = Modifiers.READONLY; + StoreModifierLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_745() +#line 5172 "cs-parser.jay" +{ + yyVal = Modifiers.VIRTUAL; + StoreModifierLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_746() +#line 5177 "cs-parser.jay" +{ + yyVal = Modifiers.OVERRIDE; + StoreModifierLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_747() +#line 5182 "cs-parser.jay" +{ + yyVal = Modifiers.EXTERN; + StoreModifierLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_748() +#line 5187 "cs-parser.jay" +{ + yyVal = Modifiers.VOLATILE; + StoreModifierLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_749() +#line 5192 "cs-parser.jay" +{ + yyVal = Modifiers.UNSAFE; + StoreModifierLocation (yyVal, GetLocation (yyVals[0+yyTop])); + if (!settings.Unsafe) + Error_UnsafeCodeNotAllowed (GetLocation (yyVals[0+yyTop])); + } + +void case_750() +#line 5199 "cs-parser.jay" +{ + yyVal = Modifiers.ASYNC; + StoreModifierLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_753() +#line 5212 "cs-parser.jay" +{ + current_type.SetBaseTypes ((List) yyVals[0+yyTop]); + lbag.AppendToMember (current_type, GetLocation (yyVals[-1+yyTop])); + } + +void case_754() +#line 5217 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + current_type.SetBaseTypes ((List) yyVals[-1+yyTop]); + } + +void case_757() +#line 5234 "cs-parser.jay" +{ + var constraints = new List (1); + constraints.Add ((Constraints) yyVals[0+yyTop]); + yyVal = constraints; + } + +void case_758() +#line 5240 "cs-parser.jay" +{ + var constraints = (List) yyVals[-1+yyTop]; + Constraints new_constraint = (Constraints)yyVals[0+yyTop]; + + foreach (Constraints c in constraints) { + if (new_constraint.TypeParameter.Value == c.TypeParameter.Value) { + report.Error (409, new_constraint.Location, + "A constraint clause has already been specified for type parameter `{0}'", + new_constraint.TypeParameter.Value); + } + } + + constraints.Add (new_constraint); + yyVal = constraints; + } + +void case_759() +#line 5259 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-2+yyTop]; + yyVal = new Constraints (new SimpleMemberName (lt.Value, lt.Location), (List) yyVals[0+yyTop], GetLocation (yyVals[-3+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_760() +#line 5265 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + var lt = (LocatedToken) yyVals[-1+yyTop]; + yyVal = new Constraints (new SimpleMemberName (lt.Value, lt.Location), null, GetLocation (yyVals[-2+yyTop])); + } + +void case_761() +#line 5275 "cs-parser.jay" +{ + var constraints = new List (1); + constraints.Add ((FullNamedExpression) yyVals[0+yyTop]); + yyVal = constraints; + } + +void case_762() +#line 5281 "cs-parser.jay" +{ + var constraints = (List) yyVals[-2+yyTop]; + var prev = constraints [constraints.Count - 1] as SpecialContraintExpr; + if (prev != null && (prev.Constraint & SpecialConstraint.Constructor) != 0) { + report.Error (401, GetLocation (yyVals[-1+yyTop]), "The `new()' constraint must be the last constraint specified"); + } + + prev = yyVals[0+yyTop] as SpecialContraintExpr; + if (prev != null) { + if ((prev.Constraint & (SpecialConstraint.Class | SpecialConstraint.Struct)) != 0) { + report.Error (449, prev.Location, "The `class' or `struct' constraint must be the first constraint specified"); + } else { + prev = constraints [0] as SpecialContraintExpr; + if (prev != null && (prev.Constraint & SpecialConstraint.Struct) != 0) { + report.Error (451, GetLocation (yyVals[0+yyTop]), "The `new()' constraint cannot be used with the `struct' constraint"); + } + } + } + + constraints.Add ((FullNamedExpression) yyVals[0+yyTop]); + lbag.AddLocation (constraints, GetLocation (yyVals[-1+yyTop])); + yyVal = constraints; + } + +void case_763() +#line 5308 "cs-parser.jay" +{ + if (yyVals[0+yyTop] is ComposedCast) + report.Error (706, GetLocation (yyVals[0+yyTop]), "Invalid constraint type `{0}'", ((ComposedCast)yyVals[0+yyTop]).GetSignatureForError ()); + + yyVal = yyVals[0+yyTop]; + } + +void case_764() +#line 5315 "cs-parser.jay" +{ + yyVal = new SpecialContraintExpr (SpecialConstraint.Constructor, GetLocation (yyVals[-2+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-1+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_768() +#line 5335 "cs-parser.jay" +{ + if (lang_version <= LanguageVersion.V_3) + FeatureIsNotAvailable (lexer.Location, "generic type variance"); + + yyVal = yyVals[0+yyTop]; + } + +void case_769() +#line 5345 "cs-parser.jay" +{ + yyVal = new VarianceDecl (Variance.Covariant, GetLocation (yyVals[0+yyTop])); + savedLocation = GetLocation (yyVals[0+yyTop]); + } + +void case_770() +#line 5350 "cs-parser.jay" +{ + yyVal = new VarianceDecl (Variance.Contravariant, GetLocation (yyVals[0+yyTop])); + savedLocation = GetLocation (yyVals[0+yyTop]); + } + +void case_771() +#line 5371 "cs-parser.jay" +{ + ++lexer.parsing_block; + start_block (GetLocation (yyVals[0+yyTop])); + } + +void case_773() +#line 5383 "cs-parser.jay" +{ + --lexer.parsing_block; + yyVal = end_block (GetLocation (yyVals[0+yyTop])); + } + +void case_774() +#line 5388 "cs-parser.jay" +{ + --lexer.parsing_block; + yyVal = end_block (lexer.Location); + } + +void case_775() +#line 5397 "cs-parser.jay" +{ + ++lexer.parsing_block; + current_block.StartLocation = GetLocation (yyVals[0+yyTop]); + } + +void case_776() +#line 5402 "cs-parser.jay" +{ + --lexer.parsing_block; + yyVal = end_block (GetLocation (yyVals[0+yyTop])); + } + +void case_777() +#line 5406 "cs-parser.jay" +{ + report.Error (1525, GetLocation (yyVals[0+yyTop]), "Unexpected symbol '}', expected '{'"); + lexer.putback ('}'); + yyVal = end_block (GetLocation (yyVals[0+yyTop])); + } + +void case_778() +#line 5415 "cs-parser.jay" +{ + ++lexer.parsing_block; + current_block.StartLocation = GetLocation (yyVals[0+yyTop]); + } + +void case_779() +#line 5420 "cs-parser.jay" +{ + --lexer.parsing_block; + yyVal = end_block (GetLocation (yyVals[0+yyTop])); + } + +void case_787() +#line 5448 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + var lt =(LocatedToken) yyVals[-1+yyTop]; + var sn = new SimpleName (lt.Value, lt.Location); + current_block.AddStatement(new StatementErrorExpression (sn)); + yyVal = null; + } + +void case_788() +#line 5457 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = null; + } + +void case_821() +#line 5521 "cs-parser.jay" +{ + report.Error (1023, GetLocation (yyVals[0+yyTop]), "An embedded statement may not be a declaration or labeled statement"); + yyVal = null; + } + +void case_822() +#line 5526 "cs-parser.jay" +{ + report.Error (1023, GetLocation (yyVals[0+yyTop]), "An embedded statement may not be a declaration or labeled statement"); + yyVal = null; + } + +void case_823() +#line 5531 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = new EmptyStatement (GetLocation (yyVals[0+yyTop])); + } + +void case_824() +#line 5539 "cs-parser.jay" +{ + /* Uses lexer.Location because semicolon location is not kept in quick mode*/ + yyVal = new EmptyStatement (lexer.Location); + } + +void case_825() +#line 5547 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-1+yyTop]; + LabeledStatement labeled = new LabeledStatement (lt.Value, current_block, lt.Location); + lbag.AddLocation (labeled, GetLocation (yyVals[0+yyTop])); + current_block.AddLabel (labeled); + current_block.AddStatement (labeled); + } + +void case_828() +#line 5560 "cs-parser.jay" +{ + if (yyVals[-1+yyTop] is VarExpr) + yyVals[-1+yyTop] = new SimpleName ("var", ((VarExpr) yyVals[-1+yyTop]).Location); + + yyVal = new ComposedCast ((FullNamedExpression) yyVals[-1+yyTop], (ComposedTypeSpecifier) yyVals[0+yyTop]); + } + +void case_829() +#line 5576 "cs-parser.jay" +{ + /* Ok, the above "primary_expression" is there to get rid of*/ + /* both reduce/reduce and shift/reduces in the grammar, it should*/ + /* really just be "type_name". If you use type_name, a reduce/reduce*/ + /* creeps up. If you use namespace_or_type_name (which is all we need*/ + /* really) two shift/reduces appear.*/ + /* */ + + /* So the super-trick is that primary_expression*/ + /* can only be either a SimpleName or a MemberAccess. */ + /* The MemberAccess case arises when you have a fully qualified type-name like :*/ + /* Foo.Bar.Blah i;*/ + /* SimpleName is when you have*/ + /* Blah i;*/ + + Expression expr = (Expression) yyVals[-1+yyTop]; + if (yyVals[0+yyTop] == null) { + SimpleName sn = expr as SimpleName; + if (sn != null && sn.Name == "var") + yyVal = new VarExpr (sn.Location); + else + yyVal = yyVals[-1+yyTop]; + } else if (expr is ATypeNameExpression) { + yyVal = new ComposedCast ((ATypeNameExpression)expr, (ComposedTypeSpecifier) yyVals[0+yyTop]); + } else { + Error_ExpectingTypeName (expr); + yyVal = null; + } + } + +void case_830() +#line 5606 "cs-parser.jay" +{ + ATypeNameExpression expr = yyVals[-1+yyTop] as ATypeNameExpression; + + if (expr != null) { + yyVal = new ComposedCast (expr, (ComposedTypeSpecifier) yyVals[0+yyTop]); + } else { + Error_ExpectingTypeName ((Expression)yyVals[-1+yyTop]); + yyVal = expr; + } + } + +void case_834() +#line 5623 "cs-parser.jay" +{ + ((ComposedTypeSpecifier) yyVals[-1+yyTop]).Next = (ComposedTypeSpecifier) yyVals[0+yyTop]; + yyVal = yyVals[-1+yyTop]; + } + +void case_838() +#line 5646 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[0+yyTop]; + var li = new LocalVariable (current_block, lt.Value, lt.Location); + current_block.AddLocalName (li); + current_variable = new BlockVariable ((FullNamedExpression) yyVals[-1+yyTop], li); + } + +void case_839() +#line 5653 "cs-parser.jay" +{ + yyVal = current_variable; + current_variable = null; + if (yyVals[-2+yyTop] != null) + lbag.AddLocation (yyVal, PopLocation (), GetLocation (yyVals[0+yyTop])); + else + lbag.AddLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_840() +#line 5662 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[0+yyTop]; + var li = new LocalVariable (current_block, lt.Value, LocalVariable.Flags.Constant, lt.Location); + current_block.AddLocalName (li); + current_variable = new BlockConstant ((FullNamedExpression) yyVals[-1+yyTop], li); + } + +void case_841() +#line 5669 "cs-parser.jay" +{ + if (current_variable.Initializer != null) { + lbag.AddLocation (current_variable, GetLocation (yyVals[-6+yyTop]), savedLocation, GetLocation (yyVals[0+yyTop])); + } else { + lbag.AddLocation (current_variable, GetLocation (yyVals[-6+yyTop]), GetLocation (yyVals[0+yyTop])); + } + yyVal = current_variable;; + current_variable = null; + } + +void case_843() +#line 5682 "cs-parser.jay" +{ + /* Redundant, but wont regress*/ + report.Error (1525, lexer.Location, "Unexpected symbol }"); + lexer.putback ('}'); + yyVal = yyVals[0+yyTop]; + } + +void case_845() +#line 5693 "cs-parser.jay" +{ + current_variable.Initializer = (Expression) yyVals[0+yyTop]; + PushLocation (GetLocation (yyVals[-1+yyTop])); + yyVal = current_variable; + } + +void case_846() +#line 5699 "cs-parser.jay" +{ + if (yyToken == Token.OPEN_BRACKET_EXPR) { + report.Error (650, lexer.Location, + "Syntax error, bad array declarator. To declare a managed array the rank specifier precedes the variable's identifier. To declare a fixed size buffer field, use the fixed keyword before the field type"); + } else { + Error_SyntaxError (yyToken); + } + } + +void case_850() +#line 5717 "cs-parser.jay" +{ + foreach (var d in current_variable.Declarators) { + if (d.Initializer == null) + Error_MissingInitializer (d.Variable.Location); + } + } + +void case_853() +#line 5732 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[0+yyTop]; + var li = new LocalVariable (current_variable.Variable, lt.Value, lt.Location); + var d = new BlockVariableDeclarator (li, null); + current_variable.AddDeclarator (d); + current_block.AddLocalName (li); + lbag.AddLocation (d, GetLocation (yyVals[-1+yyTop])); + } + +void case_854() +#line 5741 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-2+yyTop]; + var li = new LocalVariable (current_variable.Variable, lt.Value, lt.Location); + var d = new BlockVariableDeclarator (li, (Expression) yyVals[0+yyTop]); + current_variable.AddDeclarator (d); + current_block.AddLocalName (li); + lbag.AddLocation (d, GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[-1+yyTop])); + } + +void case_856() +#line 5757 "cs-parser.jay" +{ + savedLocation = GetLocation (yyVals[-1+yyTop]); + current_variable.Initializer = (Expression) yyVals[0+yyTop]; + } + +void case_861() +#line 5775 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-2+yyTop]; + var li = new LocalVariable (current_block, lt.Value, LocalVariable.Flags.Constant, lt.Location); + var d = new BlockVariableDeclarator (li, (Expression) yyVals[0+yyTop]); + current_variable.AddDeclarator (d); + current_block.AddLocalName (li); + lbag.AddLocation (d, GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[-1+yyTop])); + } + +void case_863() +#line 5788 "cs-parser.jay" +{ + yyVal = new StackAlloc ((Expression) yyVals[-3+yyTop], (Expression) yyVals[-1+yyTop], GetLocation (yyVals[-4+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_864() +#line 5793 "cs-parser.jay" +{ + report.Error (1575, GetLocation (yyVals[-1+yyTop]), "A stackalloc expression requires [] after type"); + yyVal = new StackAlloc ((Expression) yyVals[0+yyTop], null, GetLocation (yyVals[-1+yyTop])); + } + +void case_865() +#line 5801 "cs-parser.jay" +{ + yyVal = yyVals[-1+yyTop]; + lbag.AddStatement (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_867() +#line 5807 "cs-parser.jay" +{ + yyVal = yyVals[-1+yyTop]; + report.Error (1002, GetLocation (yyVals[0+yyTop]), "; expected"); + lexer.putback ('}'); + } + +void case_870() +#line 5825 "cs-parser.jay" +{ + ExpressionStatement s = yyVals[0+yyTop] as ExpressionStatement; + if (s == null) { + var expr = yyVals[0+yyTop] as Expression; + yyVal = new StatementErrorExpression (expr); + } else { + yyVal = new StatementExpression (s); + } + } + +void case_871() +#line 5838 "cs-parser.jay" +{ + Expression expr = (Expression) yyVals[0+yyTop]; + yyVal = new StatementExpression (new OptionalAssign (expr, lexer.Location)); + } + +void case_872() +#line 5843 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = new EmptyStatement (GetLocation (yyVals[0+yyTop])); + } + +void case_875() +#line 5857 "cs-parser.jay" +{ + if (yyVals[0+yyTop] is EmptyStatement) + Warning_EmptyStatement (GetLocation (yyVals[0+yyTop])); + + yyVal = new If ((BooleanExpression) yyVals[-2+yyTop], (Statement) yyVals[0+yyTop], GetLocation (yyVals[-4+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[-1+yyTop])); + } + +void case_876() +#line 5866 "cs-parser.jay" +{ + yyVal = new If ((BooleanExpression) yyVals[-4+yyTop], (Statement) yyVals[-2+yyTop], (Statement) yyVals[0+yyTop], GetLocation (yyVals[-6+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[-5+yyTop]), GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[-1+yyTop])); + + if (yyVals[-2+yyTop] is EmptyStatement) + Warning_EmptyStatement (GetLocation (yyVals[-2+yyTop])); + if (yyVals[0+yyTop] is EmptyStatement) + Warning_EmptyStatement (GetLocation (yyVals[0+yyTop])); + } + +void case_877() +#line 5876 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new If ((BooleanExpression) yyVals[-1+yyTop], null, GetLocation (yyVals[-3+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_879() +#line 5890 "cs-parser.jay" +{ + yyVal = new Switch ((Expression) yyVals[-5+yyTop], (ExplicitBlock) current_block.Explicit, GetLocation (yyVals[-7+yyTop])); + end_block (GetLocation (yyVals[0+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[-6+yyTop]), GetLocation (yyVals[-4+yyTop]), GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_880() +#line 5896 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Switch ((Expression) yyVals[-1+yyTop], null, GetLocation (yyVals[-3+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_887() +#line 5927 "cs-parser.jay" +{ + var label = (SwitchLabel) yyVals[0+yyTop]; + label.SectionStart = true; + current_block.AddStatement (label); + } + +void case_889() +#line 5940 "cs-parser.jay" +{ + yyVal = new SwitchLabel ((Expression) yyVals[-1+yyTop], GetLocation (yyVals[-2+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_890() +#line 5945 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = new SwitchLabel ((Expression) yyVals[-1+yyTop], GetLocation (yyVals[-2+yyTop])); + } + +void case_896() +#line 5964 "cs-parser.jay" +{ + if (yyVals[0+yyTop] is EmptyStatement && lexer.peek_token () == Token.OPEN_BRACE) + Warning_EmptyStatement (GetLocation (yyVals[0+yyTop])); + + yyVal = new While ((BooleanExpression) yyVals[-2+yyTop], (Statement) yyVals[0+yyTop], GetLocation (yyVals[-4+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[-1+yyTop])); + } + +void case_897() +#line 5972 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new While ((BooleanExpression) yyVals[-1+yyTop], null, GetLocation (yyVals[-3+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_898() +#line 5982 "cs-parser.jay" +{ + yyVal = new Do ((Statement) yyVals[-5+yyTop], (BooleanExpression) yyVals[-2+yyTop], GetLocation (yyVals[-6+yyTop]), GetLocation (yyVals[-4+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[-4+yyTop]), GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[-1+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_899() +#line 5987 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = new Do ((Statement) yyVals[-1+yyTop], null, GetLocation (yyVals[-2+yyTop]), Location.Null); + } + +void case_900() +#line 5992 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Do ((Statement) yyVals[-4+yyTop], (BooleanExpression) yyVals[-1+yyTop], GetLocation (yyVals[-5+yyTop]), GetLocation (yyVals[-3+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[-2+yyTop])); + } + +void case_901() +#line 6002 "cs-parser.jay" +{ + start_block (GetLocation (yyVals[0+yyTop])); + current_block.IsCompilerGenerated = true; + For f = new For (GetLocation (yyVals[-1+yyTop])); + current_block.AddStatement (f); + lbag.AddStatement (f, current_block.StartLocation); + yyVal = f; + } + +void case_903() +#line 6019 "cs-parser.jay" +{ + For f = (For) yyVals[-2+yyTop]; + f.Initializer = (Statement) yyVals[-1+yyTop]; + lbag.AddLocation (f, GetLocation (yyVals[0+yyTop])); + yyVal = f; + } + +void case_905() +#line 6029 "cs-parser.jay" +{ + report.Error (1525, GetLocation (yyVals[0+yyTop]), "Unexpected symbol ')', expected ';'"); + For f = (For) yyVals[-2+yyTop]; + f.Initializer = (Statement) yyVals[-1+yyTop]; + lbag.AddLocation (f, GetLocation (yyVals[0+yyTop])); + yyVal = end_block (GetLocation (yyVals[0+yyTop])); + } + +void case_906() +#line 6040 "cs-parser.jay" +{ + For f = (For) yyVals[-2+yyTop]; + f.Condition = (BooleanExpression) yyVals[-1+yyTop]; + lbag.AddLocation (f, GetLocation (yyVals[0+yyTop])); + yyVal = f; + } + +void case_908() +#line 6051 "cs-parser.jay" +{ + report.Error (1525, GetLocation (yyVals[0+yyTop]), "Unexpected symbol ')', expected ';'"); + For f = (For) yyVals[-2+yyTop]; + f.Condition = (BooleanExpression) yyVals[-1+yyTop]; + lbag.AddLocation (f, GetLocation (yyVals[0+yyTop])); + yyVal = end_block (GetLocation (yyVals[0+yyTop])); + } + +void case_909() +#line 6063 "cs-parser.jay" +{ + For f = (For) yyVals[-3+yyTop]; + f.Iterator = (Statement) yyVals[-2+yyTop]; + + if (yyVals[0+yyTop] is EmptyStatement && lexer.peek_token () == Token.OPEN_BRACE) + Warning_EmptyStatement (GetLocation (yyVals[0+yyTop])); + + f.Statement = (Statement) yyVals[0+yyTop]; + lbag.AddLocation (f, GetLocation (yyVals[-1+yyTop])); + + yyVal = end_block (GetLocation (yyVals[-1+yyTop])); + } + +void case_910() +#line 6076 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = end_block (current_block.StartLocation); + } + +void case_913() +#line 6089 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[0+yyTop]; + var li = new LocalVariable (current_block, lt.Value, lt.Location); + current_block.AddLocalName (li); + current_variable = new BlockVariable ((FullNamedExpression) yyVals[-1+yyTop], li); + } + +void case_914() +#line 6096 "cs-parser.jay" +{ + yyVal = current_variable; + if (yyVals[-1+yyTop] != null) + lbag.AddLocation (current_variable, PopLocation ()); + + current_variable = null; + } + +void case_922() +#line 6123 "cs-parser.jay" +{ + var sl = yyVals[-2+yyTop] as StatementList; + if (sl == null) { + sl = new StatementList ((Statement) yyVals[-2+yyTop], (Statement) yyVals[0+yyTop]); + lbag.AddStatement (sl, GetLocation (yyVals[-1+yyTop])); + } else { + sl.Add ((Statement) yyVals[0+yyTop]); + lbag.AddLocation (sl, GetLocation (yyVals[-1+yyTop])); + + } + + yyVal = sl; + } + +void case_923() +#line 6140 "cs-parser.jay" +{ + report.Error (230, GetLocation (yyVals[-3+yyTop]), "Type and identifier are both required in a foreach statement"); + + start_block (GetLocation (yyVals[-2+yyTop])); + current_block.IsCompilerGenerated = true; + + Foreach f = new Foreach ((Expression) yyVals[-1+yyTop], null, null, null, null, GetLocation (yyVals[-3+yyTop])); + current_block.AddStatement (f); + + lbag.AddStatement (f, GetLocation (yyVals[-2+yyTop])); + yyVal = end_block (GetLocation (yyVals[0+yyTop])); + } + +void case_924() +#line 6153 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + start_block (GetLocation (yyVals[-3+yyTop])); + current_block.IsCompilerGenerated = true; + + var lt = (LocatedToken) yyVals[-1+yyTop]; + var li = new LocalVariable (current_block, lt.Value, LocalVariable.Flags.ForeachVariable | LocalVariable.Flags.Used, lt.Location); + current_block.AddLocalName (li); + + Foreach f = new Foreach ((Expression) yyVals[-2+yyTop], li, null, null, null, GetLocation (yyVals[-4+yyTop])); + current_block.AddStatement (f); + + lbag.AddStatement (f, GetLocation (yyVals[-3+yyTop])); + yyVal = end_block (GetLocation (yyVals[0+yyTop])); + } + +void case_925() +#line 6170 "cs-parser.jay" +{ + start_block (GetLocation (yyVals[-5+yyTop])); + current_block.IsCompilerGenerated = true; + + var lt = (LocatedToken) yyVals[-3+yyTop]; + var li = new LocalVariable (current_block, lt.Value, LocalVariable.Flags.ForeachVariable | LocalVariable.Flags.Used, lt.Location); + current_block.AddLocalName (li); + yyVal = li; + } + +void case_926() +#line 6180 "cs-parser.jay" +{ + if (yyVals[0+yyTop] is EmptyStatement && lexer.peek_token () == Token.OPEN_BRACE) + Warning_EmptyStatement (GetLocation (yyVals[0+yyTop])); + + Foreach f = new Foreach ((Expression) yyVals[-6+yyTop], (LocalVariable) yyVals[-1+yyTop], (Expression) yyVals[-3+yyTop], (Statement) yyVals[0+yyTop], current_block, GetLocation (yyVals[-8+yyTop])); + lbag.AddStatement (f, GetLocation (yyVals[-7+yyTop]), GetLocation (yyVals[-4+yyTop]), GetLocation (yyVals[-2+yyTop])); + end_block (GetLocation (yyVals[-2+yyTop])); + + yyVal = f; + } + +void case_927() +#line 6191 "cs-parser.jay" +{ + start_block (GetLocation (yyVals[-3+yyTop])); + current_block.IsCompilerGenerated = true; + var lt = yyVals[-1+yyTop] as LocatedToken; + var li = lt != null ? new LocalVariable (current_block, lt.Value, LocalVariable.Flags.ForeachVariable | LocalVariable.Flags.Used, lt.Location) : null; + + Foreach f = new Foreach ((Expression) yyVals[-2+yyTop], li, null, null, null, GetLocation (yyVals[-4+yyTop])); + current_block.AddStatement (f); + + lbag.AddStatement (f, GetLocation (yyVals[-3+yyTop])); + yyVal = end_block (GetLocation (yyVals[0+yyTop])); + } + +void case_928() +#line 6204 "cs-parser.jay" +{ + Foreach f = new Foreach ((Expression) yyVals[-1+yyTop], null, null, null, null, GetLocation (yyVals[-3+yyTop])); + current_block.AddStatement (f); + + lbag.AddStatement (f, GetLocation (yyVals[-2+yyTop])); + yyVal = f; + } + +void case_935() +#line 6224 "cs-parser.jay" +{ + yyVal = new Break (GetLocation (yyVals[-1+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_936() +#line 6232 "cs-parser.jay" +{ + yyVal = new Continue (GetLocation (yyVals[-1+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_937() +#line 6237 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = new Continue (GetLocation (yyVals[-1+yyTop])); + } + +void case_938() +#line 6245 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-1+yyTop]; + yyVal = new Goto (lt.Value, GetLocation (yyVals[-2+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[-1+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_939() +#line 6251 "cs-parser.jay" +{ + yyVal = new GotoCase ((Expression) yyVals[-1+yyTop], GetLocation (yyVals[-3+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_940() +#line 6256 "cs-parser.jay" +{ + yyVal = new GotoDefault (GetLocation (yyVals[-2+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[-1+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_941() +#line 6264 "cs-parser.jay" +{ + yyVal = new Return ((Expression) yyVals[-1+yyTop], GetLocation (yyVals[-2+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_942() +#line 6269 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = new Return ((Expression) yyVals[-1+yyTop], GetLocation (yyVals[-2+yyTop])); + } + +void case_943() +#line 6274 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = new Return (null, GetLocation (yyVals[-1+yyTop])); + } + +void case_944() +#line 6282 "cs-parser.jay" +{ + yyVal = new Throw ((Expression) yyVals[-1+yyTop], GetLocation (yyVals[-2+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_945() +#line 6287 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = new Throw ((Expression) yyVals[-1+yyTop], GetLocation (yyVals[-2+yyTop])); + } + +void case_946() +#line 6292 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = new Throw (null, GetLocation (yyVals[-1+yyTop])); + } + +void case_947() +#line 6300 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-3+yyTop]; + string s = lt.Value; + if (s != "yield"){ + report.Error (1003, lt.Location, "; expected"); + } else if (yyVals[-1+yyTop] == null) { + report.Error (1627, GetLocation (yyVals[0+yyTop]), "Expression expected after yield return"); + } else if (lang_version == LanguageVersion.ISO_1){ + FeatureIsNotAvailable (lt.Location, "iterators"); + } + + current_block.Explicit.RegisterIteratorYield (); + yyVal = new Yield ((Expression) yyVals[-1+yyTop], lt.Location); + lbag.AddStatement (yyVal, GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_948() +#line 6316 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + var lt = (LocatedToken) yyVals[-3+yyTop]; + string s = lt.Value; + if (s != "yield"){ + report.Error (1003, lt.Location, "; expected"); + } else if (yyVals[-1+yyTop] == null) { + report.Error (1627, GetLocation (yyVals[0+yyTop]), "Expression expected after yield return"); + } else if (lang_version == LanguageVersion.ISO_1){ + FeatureIsNotAvailable (lt.Location, "iterators"); + } + + current_block.Explicit.RegisterIteratorYield (); + yyVal = new Yield ((Expression) yyVals[-1+yyTop], lt.Location); + lbag.AddStatement (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_949() +#line 6334 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-2+yyTop]; + string s = lt.Value; + if (s != "yield"){ + report.Error (1003, lt.Location, "; expected"); + } else if (lang_version == LanguageVersion.ISO_1){ + FeatureIsNotAvailable (lt.Location, "iterators"); + } + + current_block.ParametersBlock.TopBlock.IsIterator = true; + yyVal = new YieldBreak (lt.Location); + lbag.AddStatement (yyVal, GetLocation (yyVals[-1+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_953() +#line 6360 "cs-parser.jay" +{ + yyVal = new TryFinally ((Statement) yyVals[-2+yyTop], (ExplicitBlock) yyVals[0+yyTop], GetLocation (yyVals[-3+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_954() +#line 6365 "cs-parser.jay" +{ + yyVal = new TryFinally (new TryCatch ((Block) yyVals[-3+yyTop], (List) yyVals[-2+yyTop], GetLocation (yyVals[-4+yyTop]), true), (ExplicitBlock) yyVals[0+yyTop], GetLocation (yyVals[-4+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[-1+yyTop])); + } + +void case_955() +#line 6370 "cs-parser.jay" +{ + Error_SyntaxError (1524, yyToken); + yyVal = new TryCatch ((Block) yyVals[-1+yyTop], null, GetLocation (yyVals[-2+yyTop]), false); + } + +void case_956() +#line 6378 "cs-parser.jay" +{ + var l = new List (2); + + l.Add ((Catch) yyVals[0+yyTop]); + yyVal = l; + } + +void case_957() +#line 6385 "cs-parser.jay" +{ + var l = (List) yyVals[-1+yyTop]; + + Catch c = (Catch) yyVals[0+yyTop]; + var prev_catch = l [l.Count - 1]; + if (prev_catch.IsGeneral && prev_catch.Filter == null) { + report.Error (1017, c.loc, "Try statement already has an empty catch block"); + } + + l.Add (c); + yyVal = l; + } + +void case_960() +#line 6406 "cs-parser.jay" +{ + var c = new Catch ((ExplicitBlock) yyVals[0+yyTop], GetLocation (yyVals[-2+yyTop])); + c.Filter = (CatchFilterExpression) yyVals[-1+yyTop]; + yyVal = c; + } + +void case_961() +#line 6412 "cs-parser.jay" +{ + start_block (GetLocation (yyVals[-3+yyTop])); + var c = new Catch ((ExplicitBlock) current_block, GetLocation (yyVals[-4+yyTop])); + c.TypeExpression = (FullNamedExpression) yyVals[-2+yyTop]; + + if (yyVals[-1+yyTop] != null) { + var lt = (LocatedToken) yyVals[-1+yyTop]; + c.Variable = new LocalVariable (current_block, lt.Value, lt.Location); + current_block.AddLocalName (c.Variable); + } + + lbag.AddLocation (c, GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[0+yyTop])); + yyVal = c; + } + +void case_962() +#line 6427 "cs-parser.jay" +{ + ((Catch) yyVals[-2+yyTop]).Filter = (CatchFilterExpression) yyVals[-1+yyTop]; + yyVal = yyVals[-2+yyTop]; + } + +void case_963() +#line 6432 "cs-parser.jay" +{ + if (yyToken == Token.CLOSE_PARENS) { + report.Error (1015, lexer.Location, + "A type that derives from `System.Exception', `object', or `string' expected"); + } else { + Error_SyntaxError (yyToken); + } + + yyVal = new Catch (null, GetLocation (yyVals[-2+yyTop])); + } + +void case_964() +#line 6443 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + /* Required otherwise missing block could not be detected because*/ + /* start_block is run early*/ + var c = new Catch (null, GetLocation (yyVals[-5+yyTop])); + c.TypeExpression = (FullNamedExpression) yyVals[-3+yyTop]; + + if (yyVals[-2+yyTop] != null) { + var lt = (LocatedToken) yyVals[-2+yyTop]; + c.Variable = new LocalVariable (current_block, lt.Value, lt.Location); + } + + if (yyVals[-2+yyTop] != null) { + var lt = (LocatedToken) yyVals[-2+yyTop]; + c.Variable = new LocalVariable (current_block, lt.Value, lt.Location); + } + + lbag.AddLocation (c, GetLocation (yyVals[-4+yyTop]), GetLocation (yyVals[-1+yyTop])); + + yyVal = c; + } + +void case_966() +#line 6470 "cs-parser.jay" +{ + if (lang_version <= LanguageVersion.V_5) + FeatureIsNotAvailable (GetLocation (yyVals[-3+yyTop]), "exception filter"); + + yyVal = new CatchFilterExpression ((Expression) yyVals[-1+yyTop], GetLocation (yyVals[-3+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop]), GetLocation (yyVals[0+yyTop])); + } + +void case_969() +#line 6495 "cs-parser.jay" +{ + if (!settings.Unsafe) + Error_UnsafeCodeNotAllowed (GetLocation (yyVals[0+yyTop])); + } + +void case_971() +#line 6505 "cs-parser.jay" +{ + if (yyVals[0+yyTop] is EmptyStatement && lexer.peek_token () == Token.OPEN_BRACE) + Warning_EmptyStatement (GetLocation (yyVals[0+yyTop])); + + yyVal = new Lock ((Expression) yyVals[-2+yyTop], (Statement) yyVals[0+yyTop], GetLocation (yyVals[-4+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[-1+yyTop])); + } + +void case_972() +#line 6513 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Lock ((Expression) yyVals[-1+yyTop], null, GetLocation (yyVals[-3+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_973() +#line 6523 "cs-parser.jay" +{ + start_block (GetLocation (yyVals[-2+yyTop])); + + current_block.IsCompilerGenerated = true; + var lt = (LocatedToken) yyVals[0+yyTop]; + var li = new LocalVariable (current_block, lt.Value, LocalVariable.Flags.FixedVariable | LocalVariable.Flags.Used, lt.Location); + current_block.AddLocalName (li); + current_variable = new Fixed.VariableDeclaration ((FullNamedExpression) yyVals[-1+yyTop], li); + } + +void case_974() +#line 6533 "cs-parser.jay" +{ + yyVal = current_variable; + current_variable = null; + } + +void case_975() +#line 6538 "cs-parser.jay" +{ + if (yyVals[0+yyTop] is EmptyStatement && lexer.peek_token () == Token.OPEN_BRACE) + Warning_EmptyStatement (GetLocation (yyVals[0+yyTop])); + + Fixed f = new Fixed ((Fixed.VariableDeclaration) yyVals[-1+yyTop], (Statement) yyVals[0+yyTop], GetLocation (yyVals[-9+yyTop])); + current_block.AddStatement (f); + lbag.AddStatement (f, GetLocation (yyVals[-8+yyTop]), GetLocation (yyVals[-2+yyTop])); + yyVal = end_block (GetLocation (yyVals[-2+yyTop])); + } + +void case_976() +#line 6551 "cs-parser.jay" +{ + start_block (GetLocation (yyVals[-2+yyTop])); + + current_block.IsCompilerGenerated = true; + var lt = (LocatedToken) yyVals[0+yyTop]; + var li = new LocalVariable (current_block, lt.Value, LocalVariable.Flags.UsingVariable | LocalVariable.Flags.Used, lt.Location); + current_block.AddLocalName (li); + current_variable = new Using.VariableDeclaration ((FullNamedExpression) yyVals[-1+yyTop], li); + } + +void case_977() +#line 6561 "cs-parser.jay" +{ + yyVal = current_variable; + current_variable = null; + } + +void case_978() +#line 6566 "cs-parser.jay" +{ + if (yyVals[0+yyTop] is EmptyStatement && lexer.peek_token () == Token.OPEN_BRACE) + Warning_EmptyStatement (GetLocation (yyVals[0+yyTop])); + + Using u = new Using ((Using.VariableDeclaration) yyVals[-1+yyTop], (Statement) yyVals[0+yyTop], GetLocation (yyVals[-8+yyTop])); + lbag.AddStatement (u, GetLocation (yyVals[-7+yyTop]), GetLocation (yyVals[-2+yyTop])); + current_block.AddStatement (u); + yyVal = end_block (GetLocation (yyVals[-2+yyTop])); + } + +void case_979() +#line 6576 "cs-parser.jay" +{ + if (yyVals[0+yyTop] is EmptyStatement && lexer.peek_token () == Token.OPEN_BRACE) + Warning_EmptyStatement (GetLocation (yyVals[0+yyTop])); + + yyVal = new Using ((Expression) yyVals[-2+yyTop], (Statement) yyVals[0+yyTop], GetLocation (yyVals[-4+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[-3+yyTop]), GetLocation (yyVals[-1+yyTop])); + } + +void case_980() +#line 6584 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + + yyVal = new Using ((Expression) yyVals[-1+yyTop], null, GetLocation (yyVals[-3+yyTop])); + lbag.AddStatement (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_982() +#line 6595 "cs-parser.jay" +{ + /* It has to be here for the parent to safely restore artificial block*/ + Error_SyntaxError (yyToken); + } + +void case_984() +#line 6607 "cs-parser.jay" +{ + current_variable.Initializer = (Expression) yyVals[0+yyTop]; + lbag.AddLocation (current_variable, GetLocation (yyVals[-1+yyTop])); + yyVal = current_variable; + } + +void case_985() +#line 6619 "cs-parser.jay" +{ + lexer.query_parsing = false; + + Linq.AQueryClause from = yyVals[-1+yyTop] as Linq.AQueryClause; + + from.Tail.Next = (Linq.AQueryClause)yyVals[0+yyTop]; + yyVal = from; + + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + } + +void case_986() +#line 6631 "cs-parser.jay" +{ + Linq.AQueryClause from = yyVals[-1+yyTop] as Linq.AQueryClause; + + from.Tail.Next = (Linq.AQueryClause)yyVals[0+yyTop]; + yyVal = from; + + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + } + +void case_987() +#line 6642 "cs-parser.jay" +{ + lexer.query_parsing = false; + yyVal = yyVals[-1+yyTop]; + + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + } + +void case_988() +#line 6649 "cs-parser.jay" +{ + yyVal = yyVals[-1+yyTop]; + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + } + +void case_989() +#line 6658 "cs-parser.jay" +{ + current_block = new Linq.QueryBlock (current_block, lexer.Location); + + var lt = (LocatedToken) yyVals[-2+yyTop]; + var rv = new Linq.RangeVariable (lt.Value, lt.Location); + var clause = new Linq.QueryStartClause ((Linq.QueryBlock)current_block, (Expression)yyVals[0+yyTop], rv, GetLocation (yyVals[-3+yyTop])); + lbag.AddLocation (clause, GetLocation (yyVals[-1+yyTop])); + yyVal = new Linq.QueryExpression (clause); + } + +void case_990() +#line 6668 "cs-parser.jay" +{ + current_block = new Linq.QueryBlock (current_block, lexer.Location); + + var lt = (LocatedToken) yyVals[-2+yyTop]; + var rv = new Linq.RangeVariable (lt.Value, lt.Location); + var clause = new Linq.QueryStartClause ((Linq.QueryBlock)current_block, (Expression)yyVals[0+yyTop], rv, GetLocation (yyVals[-4+yyTop])) { + IdentifierType = (FullNamedExpression)yyVals[-3+yyTop] + }; + lbag.AddLocation (clause, GetLocation (yyVals[-1+yyTop])); + yyVal = new Linq.QueryExpression (clause); + } + +void case_991() +#line 6683 "cs-parser.jay" +{ + current_block = new Linq.QueryBlock (current_block, lexer.Location); + + var lt = (LocatedToken) yyVals[-2+yyTop]; + var rv = new Linq.RangeVariable (lt.Value, lt.Location); + var clause = new Linq.QueryStartClause ((Linq.QueryBlock)current_block, (Expression)yyVals[0+yyTop], rv, GetLocation (yyVals[-3+yyTop])); + lbag.AddLocation (clause, GetLocation (yyVals[-1+yyTop])); + yyVal = new Linq.QueryExpression (clause); + } + +void case_992() +#line 6693 "cs-parser.jay" +{ + current_block = new Linq.QueryBlock (current_block, lexer.Location); + + var lt = (LocatedToken) yyVals[-2+yyTop]; + var rv = new Linq.RangeVariable (lt.Value, lt.Location); + var clause = new Linq.QueryStartClause ((Linq.QueryBlock)current_block, (Expression)yyVals[0+yyTop], rv, GetLocation (yyVals[-4+yyTop])) { + IdentifierType = (FullNamedExpression)yyVals[-3+yyTop] + }; + lbag.AddLocation (clause, GetLocation (yyVals[-1+yyTop])); + yyVal = new Linq.QueryExpression (clause); + } + +void case_994() +#line 6712 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-3+yyTop]; + var sn = new Linq.RangeVariable (lt.Value, lt.Location); + yyVal = new Linq.SelectMany ((Linq.QueryBlock)current_block, sn, (Expression)yyVals[0+yyTop], GetLocation (yyVals[-4+yyTop])); + + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + ((Linq.QueryBlock)current_block).AddRangeVariable (sn); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_996() +#line 6727 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-3+yyTop]; + var sn = new Linq.RangeVariable (lt.Value, lt.Location); + + yyVal = new Linq.SelectMany ((Linq.QueryBlock)current_block, sn, (Expression)yyVals[0+yyTop], GetLocation (yyVals[-5+yyTop])) { + IdentifierType = (FullNamedExpression)yyVals[-4+yyTop] + }; + + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + + ((Linq.QueryBlock)current_block).AddRangeVariable (sn); + + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop])); + } + +void case_997() +#line 6746 "cs-parser.jay" +{ + Linq.AQueryClause head = (Linq.AQueryClause)yyVals[-1+yyTop]; + + if (yyVals[0+yyTop] != null) + head.Next = (Linq.AQueryClause)yyVals[0+yyTop]; + + if (yyVals[-2+yyTop] != null) { + Linq.AQueryClause clause = (Linq.AQueryClause)yyVals[-2+yyTop]; + clause.Tail.Next = head; + head = clause; + } + + yyVal = head; + } + +void case_998() +#line 6761 "cs-parser.jay" +{ + Linq.AQueryClause head = (Linq.AQueryClause)yyVals[0+yyTop]; + + if (yyVals[-1+yyTop] != null) { + Linq.AQueryClause clause = (Linq.AQueryClause)yyVals[-1+yyTop]; + clause.Tail.Next = head; + head = clause; + } + + yyVal = head; + } + +void case_1000() +#line 6774 "cs-parser.jay" +{ + report.Error (742, GetLocation (yyVals[0+yyTop]), "Unexpected symbol `{0}'. A query body must end with select or group clause", GetSymbolName (yyToken)); + yyVal = yyVals[-1+yyTop]; + } + +void case_1001() +#line 6779 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = null; + } + +void case_1003() +#line 6791 "cs-parser.jay" +{ + yyVal = new Linq.Select ((Linq.QueryBlock)current_block, (Expression)yyVals[0+yyTop], GetLocation (yyVals[-2+yyTop])); + + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + } + +void case_1004() +#line 6798 "cs-parser.jay" +{ + if (linq_clause_blocks == null) + linq_clause_blocks = new Stack (); + + current_block = new Linq.QueryBlock (current_block, lexer.Location); + linq_clause_blocks.Push ((Linq.QueryBlock)current_block); + } + +void case_1005() +#line 6806 "cs-parser.jay" +{ + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + + current_block = new Linq.QueryBlock (current_block, lexer.Location); + } + +void case_1006() +#line 6813 "cs-parser.jay" +{ + var obj = (object[]) yyVals[0+yyTop]; + + yyVal = new Linq.GroupBy ((Linq.QueryBlock)current_block, (Expression)yyVals[-2+yyTop], linq_clause_blocks.Pop (), (Expression)obj[0], GetLocation (yyVals[-4+yyTop])); + lbag.AddLocation (yyVal, (Location) obj[1]); + + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + } + +void case_1008() +#line 6830 "cs-parser.jay" +{ + Error_SyntaxError (yyToken); + yyVal = new object[2] { null, Location.Null }; + } + +void case_1010() +#line 6839 "cs-parser.jay" +{ + ((Linq.AQueryClause)yyVals[-1+yyTop]).Tail.Next = (Linq.AQueryClause)yyVals[0+yyTop]; + yyVal = yyVals[-1+yyTop]; + } + +void case_1017() +#line 6859 "cs-parser.jay" +{ + var lt = (LocatedToken) yyVals[-3+yyTop]; + var sn = new Linq.RangeVariable (lt.Value, lt.Location); + yyVal = new Linq.Let ((Linq.QueryBlock) current_block, sn, (Expression)yyVals[0+yyTop], GetLocation (yyVals[-4+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-2+yyTop])); + + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + + ((Linq.QueryBlock)current_block).AddRangeVariable (sn); + } + +void case_1019() +#line 6878 "cs-parser.jay" +{ + yyVal = new Linq.Where ((Linq.QueryBlock)current_block, (Expression)yyVals[0+yyTop], GetLocation (yyVals[-2+yyTop])); + + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + } + +void case_1020() +#line 6888 "cs-parser.jay" +{ + if (linq_clause_blocks == null) + linq_clause_blocks = new Stack (); + + current_block = new Linq.QueryBlock (current_block, lexer.Location); + linq_clause_blocks.Push ((Linq.QueryBlock) current_block); + } + +void case_1021() +#line 6896 "cs-parser.jay" +{ + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + + current_block = new Linq.QueryBlock (current_block, lexer.Location); + linq_clause_blocks.Push ((Linq.QueryBlock) current_block); + } + +void case_1022() +#line 6904 "cs-parser.jay" +{ + current_block.AddStatement (new ContextualReturn ((Expression) yyVals[-1+yyTop])); + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + + current_block = new Linq.QueryBlock (current_block, lexer.Location); + } + +void case_1023() +#line 6912 "cs-parser.jay" +{ + current_block.AddStatement (new ContextualReturn ((Expression) yyVals[-1+yyTop])); + current_block.SetEndLocation (lexer.Location); + + var outer_selector = linq_clause_blocks.Pop (); + var block = linq_clause_blocks.Pop (); + + var lt = (LocatedToken) yyVals[-10+yyTop]; + var sn = new Linq.RangeVariable (lt.Value, lt.Location); + Linq.RangeVariable into; + + if (yyVals[0+yyTop] == null) { + into = sn; + yyVal = new Linq.Join (block, sn, (Expression)yyVals[-7+yyTop], outer_selector, (Linq.QueryBlock) current_block, GetLocation (yyVals[-11+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-9+yyTop]), GetLocation (yyVals[-6+yyTop]), GetLocation (yyVals[-3+yyTop])); + } else { + /**/ + /* Set equals right side parent to beginning of linq query, it is not accessible therefore cannot cause name collisions*/ + /**/ + var parent = block.Parent; + while (parent is Linq.QueryBlock) { + parent = parent.Parent; + } + current_block.Parent = parent; + + ((Linq.QueryBlock)current_block).AddRangeVariable (sn); + + lt = (LocatedToken) yyVals[0+yyTop]; + into = new Linq.RangeVariable (lt.Value, lt.Location); + + yyVal = new Linq.GroupJoin (block, sn, (Expression)yyVals[-7+yyTop], outer_selector, (Linq.QueryBlock) current_block, into, GetLocation (yyVals[-11+yyTop])); + lbag.AddLocation (yyVal, GetLocation (yyVals[-9+yyTop]), GetLocation (yyVals[-6+yyTop]), GetLocation (yyVals[-3+yyTop]), opt_intoStack.Pop ()); + } + + current_block = block.Parent; + ((Linq.QueryBlock)current_block).AddRangeVariable (into); + } + +void case_1024() +#line 6950 "cs-parser.jay" +{ + if (linq_clause_blocks == null) + linq_clause_blocks = new Stack (); + + current_block = new Linq.QueryBlock (current_block, lexer.Location); + linq_clause_blocks.Push ((Linq.QueryBlock) current_block); + } + +void case_1025() +#line 6958 "cs-parser.jay" +{ + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + + current_block = new Linq.QueryBlock (current_block, lexer.Location); + linq_clause_blocks.Push ((Linq.QueryBlock) current_block); + } + +void case_1026() +#line 6966 "cs-parser.jay" +{ + current_block.AddStatement (new ContextualReturn ((Expression) yyVals[-1+yyTop])); + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + + current_block = new Linq.QueryBlock (current_block, lexer.Location); + } + +void case_1027() +#line 6974 "cs-parser.jay" +{ + current_block.AddStatement (new ContextualReturn ((Expression) yyVals[-1+yyTop])); + current_block.SetEndLocation (lexer.Location); + + var outer_selector = linq_clause_blocks.Pop (); + var block = linq_clause_blocks.Pop (); + + var lt = (LocatedToken) yyVals[-10+yyTop]; + var sn = new Linq.RangeVariable (lt.Value, lt.Location); + Linq.RangeVariable into; + + if (yyVals[0+yyTop] == null) { + into = sn; + yyVal = new Linq.Join (block, sn, (Expression)yyVals[-7+yyTop], outer_selector, (Linq.QueryBlock) current_block, GetLocation (yyVals[-12+yyTop])) { + IdentifierType = (FullNamedExpression)yyVals[-11+yyTop] + }; + lbag.AddLocation (yyVal, GetLocation (yyVals[-10+yyTop]), GetLocation (yyVals[-7+yyTop]), GetLocation (yyVals[-4+yyTop])); + } else { + /**/ + /* Set equals right side parent to beginning of linq query, it is not accessible therefore cannot cause name collisions*/ + /**/ + var parent = block.Parent; + while (parent is Linq.QueryBlock) { + parent = parent.Parent; + } + current_block.Parent = parent; + + ((Linq.QueryBlock)current_block).AddRangeVariable (sn); + + lt = (LocatedToken) yyVals[0+yyTop]; + into = new Linq.RangeVariable (lt.Value, lt.Location); /* TODO:*/ + + yyVal = new Linq.GroupJoin (block, sn, (Expression)yyVals[-7+yyTop], outer_selector, (Linq.QueryBlock) current_block, into, GetLocation (yyVals[-12+yyTop])) { + IdentifierType = (FullNamedExpression)yyVals[-11+yyTop] + }; + lbag.AddLocation (yyVal, GetLocation (yyVals[-10+yyTop]), GetLocation (yyVals[-7+yyTop]), GetLocation (yyVals[-4+yyTop]), opt_intoStack.Pop ()); + } + + current_block = block.Parent; + ((Linq.QueryBlock)current_block).AddRangeVariable (into); + } + +void case_1029() +#line 7020 "cs-parser.jay" +{ + opt_intoStack.Push (GetLocation (yyVals[-1+yyTop])); + yyVal = yyVals[0+yyTop]; + } + +void case_1030() +#line 7028 "cs-parser.jay" +{ + current_block = new Linq.QueryBlock (current_block, lexer.Location); + lbag.AddLocation (current_block, GetLocation (yyVals[0+yyTop])); + } + +void case_1031() +#line 7033 "cs-parser.jay" +{ + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + + yyVal = yyVals[0+yyTop]; + } + +void case_1033() +#line 7044 "cs-parser.jay" +{ + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + + current_block = new Linq.QueryBlock (current_block, lexer.Location); + } + +void case_1034() +#line 7051 "cs-parser.jay" +{ + ((Linq.AQueryClause)yyVals[-3+yyTop]).Next = (Linq.AQueryClause)yyVals[0+yyTop]; + yyVal = yyVals[-3+yyTop]; + } + +void case_1036() +#line 7060 "cs-parser.jay" +{ + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + + current_block = new Linq.QueryBlock ((Linq.QueryBlock) current_block, lexer.Location); + } + +void case_1037() +#line 7067 "cs-parser.jay" +{ + ((Linq.AQueryClause)yyVals[-3+yyTop]).Tail.Next = (Linq.AQueryClause)yyVals[0+yyTop]; + yyVal = yyVals[-3+yyTop]; + } + +void case_1039() +#line 7079 "cs-parser.jay" +{ + yyVal = new Linq.OrderByAscending ((Linq.QueryBlock) current_block, (Expression)yyVals[-1+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_1040() +#line 7084 "cs-parser.jay" +{ + yyVal = new Linq.OrderByDescending ((Linq.QueryBlock) current_block, (Expression)yyVals[-1+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_1042() +#line 7096 "cs-parser.jay" +{ + yyVal = new Linq.ThenByAscending ((Linq.QueryBlock) current_block, (Expression)yyVals[-1+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_1043() +#line 7101 "cs-parser.jay" +{ + yyVal = new Linq.ThenByDescending ((Linq.QueryBlock) current_block, (Expression)yyVals[-1+yyTop]); + lbag.AddLocation (yyVal, GetLocation (yyVals[0+yyTop])); + } + +void case_1045() +#line 7111 "cs-parser.jay" +{ + /* query continuation block is not linked with query block but with block*/ + /* before. This means each query can use same range variable names for*/ + /* different identifiers.*/ + + current_block.SetEndLocation (GetLocation (yyVals[-1+yyTop])); + current_block = current_block.Parent; + + current_block = new Linq.QueryBlock (current_block, lexer.Location); + + if (linq_clause_blocks == null) + linq_clause_blocks = new Stack (); + + linq_clause_blocks.Push ((Linq.QueryBlock) current_block); + } + +void case_1046() +#line 7127 "cs-parser.jay" +{ + var current_block = linq_clause_blocks.Pop (); + var lt = (LocatedToken) yyVals[-2+yyTop]; + var rv = new Linq.RangeVariable (lt.Value, lt.Location); + yyVal = new Linq.QueryStartClause ((Linq.QueryBlock)current_block, null, rv, GetLocation (yyVals[-3+yyTop])) { + next = (Linq.AQueryClause)yyVals[0+yyTop] + }; + } + +void case_1049() +#line 7154 "cs-parser.jay" +{ + current_container = current_type = new Class (current_container, new MemberName (""), Modifiers.PUBLIC, null); + + /* (ref object retval)*/ + Parameter [] mpar = new Parameter [1]; + mpar [0] = new Parameter (new TypeExpression (compiler.BuiltinTypes.Object, Location.Null), "$retval", Parameter.Modifier.REF, null, Location.Null); + + ParametersCompiled pars = new ParametersCompiled (mpar); + var mods = Modifiers.PUBLIC | Modifiers.STATIC; + if (settings.Unsafe) + mods |= Modifiers.UNSAFE; + + current_local_parameters = pars; + var method = new InteractiveMethod ( + current_type, + new TypeExpression (compiler.BuiltinTypes.Void, Location.Null), + mods, + pars); + + current_type.AddMember (method); + oob_stack.Push (method); + + interactive_async = false; + + ++lexer.parsing_block; + start_block (lexer.Location); + } + +void case_1050() +#line 7182 "cs-parser.jay" +{ + --lexer.parsing_block; + var method = (InteractiveMethod) oob_stack.Pop (); + method.Block = (ToplevelBlock) end_block(lexer.Location); + + if (interactive_async == true) { + method.ChangeToAsync (); + } + + InteractiveResult = (Class) pop_current_class (); + current_local_parameters = null; + } + +void case_1060() +#line 7228 "cs-parser.jay" +{ + module.DocumentationBuilder.ParsedBuiltinType = (TypeExpression)yyVals[-1+yyTop]; + module.DocumentationBuilder.ParsedParameters = (List)yyVals[0+yyTop]; + yyVal = null; + } + +void case_1061() +#line 7234 "cs-parser.jay" +{ + module.DocumentationBuilder.ParsedBuiltinType = new TypeExpression (compiler.BuiltinTypes.Void, GetLocation (yyVals[-1+yyTop])); + module.DocumentationBuilder.ParsedParameters = (List)yyVals[0+yyTop]; + yyVal = null; + } + +void case_1062() +#line 7240 "cs-parser.jay" +{ + module.DocumentationBuilder.ParsedBuiltinType = (TypeExpression)yyVals[-3+yyTop]; + module.DocumentationBuilder.ParsedParameters = (List)yyVals[0+yyTop]; + var lt = (LocatedToken) yyVals[-1+yyTop]; + yyVal = new MemberName (lt.Value); + } + +void case_1065() +#line 7255 "cs-parser.jay" +{ + module.DocumentationBuilder.ParsedParameters = (List)yyVals[-1+yyTop]; + yyVal = new MemberName ((MemberName) yyVals[-6+yyTop], MemberCache.IndexerNameAlias, Location.Null); + } + +void case_1066() +#line 7260 "cs-parser.jay" +{ + var p = (List)yyVals[0+yyTop] ?? new List (1); + p.Add (new DocumentationParameter ((FullNamedExpression) yyVals[-1+yyTop])); + module.DocumentationBuilder.ParsedParameters = p; + module.DocumentationBuilder.ParsedOperator = Operator.OpType.Explicit; + yyVal = null; + } + +void case_1067() +#line 7268 "cs-parser.jay" +{ + var p = (List)yyVals[0+yyTop] ?? new List (1); + p.Add (new DocumentationParameter ((FullNamedExpression) yyVals[-1+yyTop])); + module.DocumentationBuilder.ParsedParameters = p; + module.DocumentationBuilder.ParsedOperator = Operator.OpType.Implicit; + yyVal = null; + } + +void case_1068() +#line 7276 "cs-parser.jay" +{ + var p = (List)yyVals[0+yyTop]; + module.DocumentationBuilder.ParsedParameters = p; + module.DocumentationBuilder.ParsedOperator = (Operator.OpType) yyVals[-1+yyTop]; + yyVal = null; + } + +void case_1076() +#line 7314 "cs-parser.jay" +{ + var parameters = new List (); + parameters.Add ((DocumentationParameter) yyVals[0+yyTop]); + yyVal = parameters; + } + +void case_1077() +#line 7320 "cs-parser.jay" +{ + var parameters = yyVals[-2+yyTop] as List; + parameters.Add ((DocumentationParameter) yyVals[0+yyTop]); + yyVal = parameters; + } + +void case_1078() +#line 7329 "cs-parser.jay" +{ + if (yyVals[-1+yyTop] != null) + yyVal = new DocumentationParameter ((Parameter.Modifier) yyVals[-1+yyTop], (FullNamedExpression) yyVals[0+yyTop]); + else + yyVal = new DocumentationParameter ((FullNamedExpression) yyVals[0+yyTop]); + } + +#line default + static readonly short [] yyLhs = { -1, + 0, 4, 0, 0, 1, 1, 1, 1, 2, 2, + 11, 11, 12, 12, 13, 13, 14, 15, 15, 15, + 19, 20, 17, 17, 22, 22, 22, 18, 18, 18, + 23, 23, 24, 24, 7, 7, 6, 6, 21, 21, + 8, 8, 25, 25, 25, 26, 26, 26, 26, 26, + 9, 9, 10, 10, 34, 32, 37, 33, 33, 33, + 33, 35, 35, 35, 36, 36, 41, 38, 39, 40, + 40, 42, 42, 42, 42, 42, 43, 43, 43, 47, + 44, 46, 49, 49, 49, 51, 51, 52, 52, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 67, 62, 72, 74, 77, 78, 79, + 28, 28, 82, 54, 54, 83, 83, 84, 84, 85, + 87, 81, 81, 86, 86, 92, 55, 96, 55, 55, + 91, 99, 91, 93, 93, 100, 100, 101, 102, 101, + 97, 97, 103, 103, 104, 105, 95, 95, 98, 98, + 98, 108, 56, 111, 112, 106, 113, 114, 115, 106, + 106, 106, 107, 107, 117, 117, 120, 118, 110, 110, + 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, + 122, 122, 125, 125, 125, 125, 128, 125, 126, 126, + 129, 129, 130, 130, 130, 123, 123, 123, 131, 131, + 131, 124, 133, 135, 136, 138, 57, 139, 57, 137, + 141, 137, 140, 140, 143, 145, 59, 144, 144, 134, + 134, 134, 134, 134, 149, 146, 150, 147, 148, 148, + 148, 151, 152, 153, 155, 29, 29, 154, 154, 156, + 156, 157, 157, 157, 157, 157, 157, 157, 157, 157, + 159, 60, 160, 160, 163, 158, 158, 162, 162, 162, + 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, + 162, 162, 162, 162, 162, 162, 162, 162, 162, 165, + 164, 166, 164, 164, 164, 61, 169, 171, 167, 168, + 168, 170, 170, 175, 173, 176, 173, 173, 173, 177, + 63, 179, 58, 182, 183, 58, 58, 178, 185, 178, + 180, 180, 186, 186, 187, 188, 187, 189, 184, 181, + 181, 181, 181, 181, 193, 190, 194, 191, 192, 192, + 64, 65, 196, 198, 199, 30, 195, 195, 195, 197, + 197, 197, 200, 200, 201, 202, 201, 201, 201, 203, + 204, 205, 31, 206, 206, 16, 16, 16, 207, 207, + 207, 211, 211, 209, 209, 209, 212, 212, 214, 71, + 132, 109, 109, 142, 142, 215, 215, 215, 213, 213, + 216, 216, 217, 217, 219, 219, 90, 80, 80, 94, + 94, 127, 127, 161, 161, 221, 221, 221, 220, 224, + 224, 224, 226, 226, 227, 225, 225, 225, 225, 225, + 225, 225, 228, 228, 228, 228, 228, 228, 228, 228, + 228, 229, 229, 229, 229, 229, 229, 229, 229, 229, + 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, + 229, 230, 230, 230, 231, 231, 231, 251, 251, 252, + 252, 253, 253, 233, 233, 250, 250, 250, 250, 250, + 250, 250, 250, 250, 250, 250, 250, 235, 235, 235, + 255, 255, 256, 256, 257, 257, 259, 259, 259, 260, + 260, 260, 260, 260, 260, 260, 261, 261, 174, 174, + 254, 254, 254, 254, 254, 266, 266, 265, 265, 267, + 267, 267, 267, 268, 236, 236, 236, 236, 264, 264, + 269, 269, 270, 270, 237, 238, 238, 239, 240, 241, + 241, 232, 232, 232, 232, 232, 275, 271, 242, 242, + 276, 276, 277, 277, 278, 278, 278, 278, 279, 279, + 279, 279, 272, 272, 222, 222, 274, 274, 280, 280, + 273, 273, 89, 89, 281, 281, 243, 282, 282, 210, + 208, 244, 244, 245, 245, 246, 246, 247, 284, 248, + 285, 248, 283, 283, 287, 286, 234, 288, 288, 288, + 288, 288, 288, 288, 288, 288, 289, 289, 289, 289, + 289, 289, 289, 289, 289, 289, 289, 289, 289, 290, + 290, 290, 290, 290, 290, 290, 291, 291, 291, 291, + 291, 291, 291, 291, 291, 291, 291, 292, 292, 292, + 292, 292, 294, 294, 294, 294, 295, 295, 295, 295, + 295, 296, 296, 296, 296, 296, 296, 296, 296, 296, + 297, 297, 297, 297, 297, 298, 298, 298, 299, 299, + 299, 300, 300, 300, 301, 301, 301, 302, 302, 302, + 303, 303, 304, 304, 304, 304, 304, 305, 305, 305, + 305, 305, 305, 305, 305, 305, 305, 305, 306, 306, + 307, 307, 307, 307, 308, 308, 310, 309, 309, 309, + 50, 50, 312, 311, 313, 311, 314, 311, 315, 316, + 311, 317, 318, 311, 45, 45, 262, 262, 262, 262, + 249, 249, 249, 88, 320, 73, 73, 321, 322, 322, + 322, 322, 324, 322, 325, 326, 327, 328, 27, 70, + 70, 69, 69, 116, 116, 329, 329, 329, 329, 329, + 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, + 75, 75, 323, 323, 76, 76, 330, 330, 331, 331, + 332, 332, 333, 333, 333, 333, 218, 218, 334, 334, + 335, 119, 68, 68, 336, 172, 172, 338, 337, 66, + 66, 339, 339, 340, 340, 340, 340, 340, 344, 344, + 345, 345, 345, 342, 342, 342, 342, 342, 342, 342, + 342, 342, 342, 342, 342, 342, 346, 346, 346, 346, + 346, 346, 346, 346, 346, 346, 346, 346, 346, 360, + 360, 360, 360, 347, 361, 343, 362, 362, 363, 363, + 363, 363, 223, 223, 364, 48, 48, 366, 341, 370, + 341, 368, 368, 365, 365, 365, 367, 367, 374, 374, + 373, 373, 375, 375, 369, 369, 371, 371, 376, 376, + 377, 372, 372, 372, 348, 348, 348, 359, 359, 378, + 379, 379, 349, 349, 380, 380, 380, 383, 381, 381, + 382, 382, 384, 384, 384, 385, 386, 386, 387, 387, + 387, 350, 350, 350, 350, 388, 388, 389, 389, 389, + 393, 390, 396, 392, 392, 399, 395, 395, 398, 398, + 394, 394, 402, 401, 401, 397, 397, 400, 400, 404, + 403, 403, 391, 391, 405, 391, 391, 391, 351, 351, + 351, 351, 351, 351, 406, 407, 407, 408, 408, 408, + 409, 409, 409, 410, 410, 410, 411, 411, 411, 412, + 412, 352, 352, 352, 352, 413, 413, 293, 293, 414, + 416, 414, 414, 414, 415, 415, 353, 354, 417, 357, + 355, 355, 419, 420, 358, 422, 423, 356, 356, 356, + 421, 421, 418, 418, 319, 319, 319, 319, 424, 424, + 426, 426, 428, 427, 429, 427, 425, 425, 425, 425, + 425, 433, 431, 434, 436, 431, 435, 435, 430, 430, + 437, 437, 437, 437, 437, 442, 438, 443, 439, 444, + 445, 446, 440, 448, 449, 450, 440, 447, 447, 452, + 441, 451, 455, 451, 454, 457, 454, 453, 453, 453, + 456, 456, 456, 432, 458, 432, 3, 3, 459, 3, + 3, 460, 460, 263, 263, 258, 258, 5, 461, 461, + 461, 461, 461, 465, 461, 461, 461, 461, 462, 462, + 463, 466, 463, 464, 464, 467, 467, 468, + }; + static readonly short [] yyLen = { 2, + 2, 0, 3, 1, 2, 4, 3, 1, 0, 1, + 1, 2, 4, 2, 1, 2, 1, 3, 5, 2, + 0, 0, 11, 3, 0, 1, 1, 1, 3, 1, + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, + 1, 2, 1, 1, 2, 1, 1, 1, 1, 1, + 0, 1, 1, 2, 0, 3, 0, 6, 3, 2, + 1, 1, 1, 1, 1, 3, 0, 3, 1, 0, + 3, 0, 1, 1, 3, 3, 1, 1, 1, 0, + 4, 4, 0, 1, 1, 0, 1, 1, 2, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 0, 4, 0, 0, 0, 0, 0, + 17, 5, 0, 9, 5, 0, 1, 1, 2, 3, + 0, 3, 1, 1, 1, 0, 8, 0, 9, 6, + 0, 0, 3, 0, 1, 1, 2, 2, 0, 5, + 0, 1, 1, 2, 3, 0, 4, 2, 1, 1, + 1, 0, 3, 0, 0, 10, 0, 0, 0, 12, + 8, 5, 1, 1, 1, 1, 0, 4, 0, 1, + 1, 3, 3, 3, 5, 3, 5, 1, 1, 1, + 1, 3, 4, 6, 2, 4, 0, 7, 0, 1, + 1, 2, 1, 1, 1, 4, 6, 4, 1, 2, + 2, 1, 0, 0, 0, 0, 12, 0, 6, 0, + 0, 4, 1, 1, 0, 0, 10, 3, 1, 1, + 2, 1, 2, 1, 0, 5, 0, 5, 1, 1, + 1, 0, 0, 0, 0, 15, 5, 0, 1, 1, + 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 5, 1, 1, 0, 7, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 7, 0, 7, 2, 2, 2, 0, 0, 9, 1, + 1, 0, 1, 0, 6, 0, 6, 2, 1, 0, + 8, 0, 9, 0, 0, 10, 5, 0, 0, 3, + 0, 1, 1, 2, 2, 0, 5, 0, 2, 2, + 2, 1, 1, 1, 0, 5, 0, 5, 1, 1, + 2, 4, 0, 0, 0, 12, 0, 2, 2, 0, + 1, 2, 1, 3, 2, 0, 5, 3, 1, 0, + 0, 0, 13, 0, 1, 1, 3, 3, 1, 4, + 4, 2, 2, 0, 3, 2, 1, 3, 0, 3, + 1, 1, 3, 1, 2, 3, 4, 4, 0, 3, + 1, 3, 3, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 2, 2, 2, 1, 1, 2, + 2, 2, 1, 3, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 3, 3, 4, 4, 5, 4, 4, + 4, 3, 3, 3, 4, 3, 4, 4, 4, 3, + 0, 1, 3, 4, 0, 1, 1, 3, 2, 3, + 3, 1, 2, 3, 5, 2, 1, 1, 0, 1, + 1, 3, 3, 3, 2, 1, 1, 1, 1, 2, + 2, 4, 3, 1, 4, 5, 4, 3, 1, 3, + 1, 3, 1, 1, 1, 4, 3, 2, 2, 6, + 3, 7, 4, 3, 7, 3, 0, 2, 4, 3, + 1, 2, 0, 1, 1, 3, 1, 2, 3, 1, + 1, 1, 0, 1, 1, 2, 2, 3, 1, 2, + 0, 1, 2, 4, 1, 3, 4, 1, 1, 1, + 2, 4, 4, 4, 2, 4, 2, 4, 0, 4, + 0, 5, 0, 1, 0, 4, 4, 1, 2, 2, + 4, 2, 2, 2, 4, 2, 1, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, + 3, 3, 3, 3, 3, 3, 1, 3, 3, 3, + 3, 3, 4, 3, 3, 3, 3, 1, 2, 1, + 2, 2, 2, 2, 1, 1, 1, 3, 3, 3, + 3, 1, 3, 3, 3, 3, 3, 3, 3, 3, + 1, 3, 3, 3, 3, 1, 3, 3, 1, 3, + 3, 1, 3, 3, 1, 3, 3, 1, 3, 3, + 1, 3, 1, 5, 4, 5, 5, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 1, 3, + 3, 2, 1, 1, 0, 1, 0, 2, 1, 1, + 1, 1, 0, 4, 0, 4, 0, 5, 0, 0, + 7, 0, 0, 8, 1, 1, 1, 1, 1, 1, + 6, 4, 4, 1, 1, 0, 1, 3, 0, 1, + 1, 2, 0, 6, 0, 0, 0, 0, 15, 0, + 1, 0, 1, 1, 2, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 2, 3, 0, 1, 1, 2, 4, 3, + 1, 3, 1, 3, 1, 1, 0, 1, 1, 1, + 0, 4, 1, 1, 0, 4, 1, 0, 4, 0, + 1, 1, 2, 1, 1, 1, 2, 1, 1, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 0, 4, 1, 2, 2, 2, + 1, 1, 1, 2, 1, 1, 1, 0, 6, 0, + 7, 1, 1, 0, 2, 1, 0, 1, 0, 1, + 1, 2, 2, 4, 0, 2, 0, 1, 1, 2, + 4, 1, 5, 2, 2, 2, 2, 2, 2, 1, + 1, 1, 1, 1, 5, 7, 4, 0, 8, 4, + 0, 1, 1, 2, 1, 2, 1, 2, 3, 3, + 1, 1, 1, 1, 1, 5, 4, 7, 3, 6, + 0, 4, 0, 4, 2, 0, 4, 2, 3, 1, + 0, 1, 0, 5, 1, 0, 1, 0, 1, 1, + 1, 3, 4, 5, 0, 9, 5, 4, 1, 1, + 1, 1, 1, 1, 2, 2, 2, 3, 4, 3, + 3, 3, 2, 3, 3, 2, 4, 4, 3, 0, + 1, 3, 4, 5, 3, 1, 2, 0, 1, 3, + 0, 8, 3, 6, 0, 4, 2, 2, 0, 3, + 5, 4, 0, 0, 10, 0, 0, 9, 5, 4, + 2, 1, 0, 2, 2, 2, 2, 2, 4, 5, + 4, 5, 0, 5, 0, 6, 3, 2, 2, 2, + 1, 0, 3, 0, 0, 5, 2, 1, 1, 2, + 1, 1, 1, 1, 1, 0, 5, 0, 3, 0, + 0, 0, 12, 0, 0, 0, 13, 0, 2, 0, + 3, 1, 0, 4, 1, 0, 4, 1, 2, 2, + 1, 2, 2, 0, 0, 4, 2, 3, 0, 4, + 2, 2, 3, 0, 1, 1, 1, 2, 2, 2, + 2, 4, 3, 0, 7, 4, 4, 3, 1, 3, + 0, 0, 4, 0, 1, 1, 3, 2, + }; + static readonly short [] yyDefRed = { 0, + 8, 0, 0, 0, 0, 0, 0, 0, 2, 4, + 0, 0, 11, 14, 0, 1047, 0, 0, 1051, 0, + 0, 15, 17, 408, 414, 421, 409, 411, 0, 410, + 0, 417, 419, 406, 0, 413, 415, 407, 418, 420, + 416, 0, 369, 1069, 0, 412, 1058, 0, 10, 1, + 0, 0, 0, 12, 0, 872, 0, 0, 0, 0, + 0, 0, 0, 0, 449, 0, 0, 0, 0, 0, + 0, 0, 447, 0, 0, 0, 515, 0, 448, 0, + 0, 0, 969, 0, 0, 0, 710, 0, 0, 0, + 0, 0, 0, 0, 771, 0, 824, 0, 0, 0, + 0, 0, 0, 0, 0, 446, 0, 699, 0, 871, + 0, 807, 0, 442, 832, 831, 0, 0, 0, 423, + 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, + 434, 435, 436, 437, 438, 439, 440, 441, 444, 445, + 706, 587, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 707, 705, 708, 709, 791, 793, + 0, 789, 792, 808, 810, 811, 812, 813, 814, 815, + 816, 817, 818, 819, 809, 0, 0, 0, 873, 874, + 892, 893, 894, 895, 929, 930, 931, 932, 933, 934, + 0, 0, 0, 20, 0, 0, 356, 0, 359, 1055, + 16, 1048, 0, 0, 263, 262, 259, 264, 265, 258, + 277, 276, 269, 270, 266, 268, 267, 271, 260, 261, + 272, 273, 279, 278, 274, 275, 0, 1072, 1061, 0, + 0, 1060, 0, 1059, 3, 55, 0, 0, 0, 44, + 41, 43, 46, 47, 48, 49, 50, 53, 13, 0, + 0, 0, 935, 565, 450, 451, 967, 0, 0, 0, + 0, 0, 0, 0, 0, 937, 936, 0, 575, 569, + 574, 823, 870, 794, 821, 820, 822, 795, 796, 797, + 798, 799, 800, 801, 802, 803, 804, 805, 806, 0, + 0, 0, 901, 0, 0, 0, 837, 836, 0, 0, + 0, 0, 0, 0, 0, 0, 943, 0, 0, 0, + 0, 422, 0, 0, 0, 946, 0, 0, 0, 0, + 567, 968, 0, 0, 0, 835, 402, 0, 0, 0, + 0, 0, 0, 388, 389, 0, 398, 0, 0, 0, + 0, 0, 0, 0, 702, 0, 586, 0, 0, 695, + 0, 0, 582, 0, 0, 584, 580, 594, 588, 595, + 589, 583, 579, 599, 593, 598, 592, 596, 590, 597, + 591, 693, 561, 0, 560, 443, 362, 363, 0, 0, + 0, 0, 0, 825, 0, 355, 0, 400, 401, 0, + 0, 518, 519, 0, 0, 0, 829, 830, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1050, 790, 838, 828, 0, 868, 869, 1001, 1018, + 0, 0, 1002, 1004, 0, 1030, 987, 985, 1011, 0, + 0, 1009, 1012, 1013, 1014, 1015, 988, 986, 0, 0, + 0, 0, 18, 0, 0, 0, 1068, 0, 0, 370, + 0, 0, 1070, 0, 0, 42, 741, 747, 739, 0, + 736, 746, 740, 738, 737, 744, 742, 743, 749, 745, + 748, 750, 0, 0, 734, 45, 54, 517, 0, 513, + 514, 0, 0, 511, 0, 840, 0, 0, 0, 899, + 0, 867, 865, 866, 0, 0, 0, 714, 0, 940, + 938, 715, 0, 0, 542, 0, 0, 530, 537, 0, + 0, 0, 531, 0, 0, 547, 549, 0, 526, 0, + 0, 0, 0, 0, 521, 0, 524, 528, 391, 390, + 942, 941, 0, 0, 945, 944, 955, 0, 0, 0, + 956, 559, 0, 385, 558, 0, 0, 970, 0, 0, + 834, 0, 396, 397, 0, 0, 395, 0, 0, 0, + 600, 0, 0, 571, 0, 697, 617, 616, 0, 0, + 788, 0, 0, 0, 782, 784, 785, 786, 454, 455, + 0, 366, 367, 0, 194, 193, 195, 0, 684, 0, + 0, 0, 392, 0, 679, 0, 0, 949, 0, 0, + 0, 462, 463, 0, 466, 0, 0, 0, 0, 464, + 0, 0, 508, 0, 470, 0, 0, 0, 0, 496, + 499, 0, 0, 491, 498, 497, 668, 669, 670, 671, + 672, 673, 674, 675, 676, 678, 677, 604, 601, 606, + 603, 605, 602, 614, 612, 615, 0, 0, 626, 625, + 0, 0, 0, 0, 610, 0, 611, 0, 630, 0, + 0, 631, 0, 637, 0, 638, 0, 639, 0, 640, + 0, 644, 0, 645, 0, 648, 0, 651, 0, 654, + 0, 657, 0, 660, 0, 662, 0, 0, 546, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1000, 999, + 0, 1010, 0, 998, 0, 0, 357, 358, 1066, 1067, + 0, 0, 191, 0, 0, 1076, 384, 0, 0, 0, + 381, 1062, 1064, 61, 63, 64, 0, 0, 56, 0, + 0, 65, 67, 30, 28, 0, 0, 0, 731, 0, + 735, 460, 0, 516, 0, 564, 0, 577, 180, 202, + 0, 0, 0, 170, 0, 0, 0, 181, 570, 0, + 973, 0, 921, 902, 0, 912, 0, 923, 0, 939, + 877, 0, 972, 0, 0, 529, 0, 538, 548, 550, + 0, 0, 0, 0, 482, 0, 0, 477, 0, 0, + 692, 691, 509, 0, 552, 523, 0, 0, 151, 553, + 149, 150, 555, 0, 563, 562, 880, 0, 0, 0, + 0, 953, 0, 957, 557, 566, 980, 0, 976, 897, + 0, 991, 0, 989, 0, 0, 712, 713, 0, 0, + 0, 690, 689, 696, 0, 461, 787, 773, 774, 772, + 783, 694, 0, 365, 682, 0, 0, 0, 585, 581, + 948, 947, 826, 467, 459, 0, 0, 465, 456, 457, + 568, 507, 505, 504, 501, 500, 0, 495, 452, 453, + 468, 469, 0, 621, 622, 623, 624, 959, 613, 619, + 665, 0, 846, 0, 0, 1019, 993, 0, 1020, 0, + 1003, 1005, 1016, 0, 1031, 0, 997, 1045, 19, 360, + 361, 1078, 192, 1073, 0, 770, 769, 0, 768, 0, + 380, 0, 60, 57, 0, 0, 0, 0, 0, 0, + 387, 0, 725, 0, 0, 85, 84, 0, 512, 0, + 0, 0, 0, 0, 185, 576, 0, 0, 0, 0, + 0, 913, 905, 903, 0, 924, 0, 0, 971, 539, + 536, 0, 486, 0, 0, 0, 1056, 1057, 473, 479, + 0, 483, 0, 0, 0, 0, 0, 0, 878, 0, + 963, 0, 960, 954, 979, 0, 896, 992, 990, 0, + 572, 0, 698, 688, 368, 681, 680, 700, 458, 506, + 503, 0, 494, 493, 492, 666, 667, 664, 0, 862, + 845, 0, 0, 0, 851, 0, 995, 0, 1024, 0, + 0, 1039, 1040, 1033, 0, 1077, 383, 382, 0, 0, + 66, 59, 0, 68, 29, 22, 0, 0, 333, 0, + 237, 0, 112, 0, 82, 856, 124, 125, 0, 0, + 0, 859, 200, 201, 0, 0, 0, 0, 173, 182, + 174, 176, 900, 0, 0, 0, 0, 0, 922, 0, + 0, 487, 488, 481, 484, 480, 0, 474, 478, 0, + 544, 0, 510, 520, 472, 556, 554, 0, 0, 0, + 982, 0, 0, 711, 703, 0, 502, 0, 0, 843, + 842, 839, 852, 994, 0, 0, 0, 1008, 0, 1006, + 1017, 0, 1046, 1065, 0, 79, 0, 0, 73, 74, + 77, 78, 0, 350, 339, 338, 0, 726, 233, 107, + 0, 841, 860, 186, 0, 198, 0, 0, 0, 898, + 984, 0, 0, 0, 0, 904, 0, 925, 876, 0, + 525, 522, 885, 0, 891, 0, 0, 883, 0, 887, + 966, 0, 981, 977, 0, 701, 0, 0, 996, 1021, + 0, 1007, 0, 0, 1035, 0, 80, 71, 0, 0, + 0, 334, 0, 0, 0, 0, 0, 187, 0, 177, + 175, 974, 914, 908, 906, 0, 485, 0, 879, 884, + 0, 888, 964, 0, 0, 704, 0, 854, 0, 1025, + 1042, 1043, 1036, 58, 0, 75, 76, 0, 0, 0, + 0, 0, 0, 0, 720, 0, 752, 0, 717, 861, + 184, 0, 197, 0, 0, 926, 890, 889, 0, 978, + 863, 0, 0, 0, 81, 0, 0, 351, 0, 0, + 349, 335, 0, 343, 0, 405, 0, 403, 0, 0, + 727, 0, 757, 234, 0, 188, 975, 910, 907, 0, + 0, 919, 775, 777, 962, 1022, 0, 1037, 0, 0, + 0, 331, 0, 0, 718, 754, 0, 723, 0, 0, + 758, 0, 108, 0, 0, 0, 1026, 27, 26, 23, + 352, 348, 0, 0, 344, 404, 0, 760, 0, 0, + 0, 0, 909, 0, 0, 0, 0, 0, 32, 336, + 0, 765, 0, 766, 763, 0, 761, 103, 104, 0, + 100, 0, 0, 88, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 101, 102, 152, 0, 0, 250, + 242, 243, 244, 245, 246, 247, 248, 249, 0, 0, + 240, 109, 776, 0, 1023, 0, 353, 347, 724, 0, + 0, 0, 0, 728, 89, 0, 291, 286, 290, 0, + 235, 241, 0, 1029, 1027, 764, 762, 0, 0, 0, + 0, 0, 0, 0, 0, 300, 0, 0, 251, 0, + 0, 257, 0, 167, 166, 153, 163, 164, 165, 0, + 0, 0, 105, 0, 0, 285, 0, 0, 284, 0, + 157, 0, 0, 374, 332, 0, 372, 0, 0, 0, + 0, 0, 0, 0, 0, 729, 0, 236, 110, 115, + 113, 307, 0, 371, 0, 0, 0, 0, 128, 0, + 0, 0, 0, 0, 0, 162, 154, 0, 0, 0, + 215, 0, 375, 0, 252, 0, 0, 0, 0, 304, + 0, 282, 130, 0, 280, 0, 0, 0, 132, 0, + 376, 0, 0, 204, 209, 0, 0, 0, 373, 255, + 168, 111, 123, 121, 0, 0, 309, 0, 0, 0, + 0, 0, 158, 0, 288, 0, 0, 0, 0, 136, + 0, 0, 0, 0, 377, 378, 0, 0, 0, 0, + 0, 118, 324, 0, 305, 0, 0, 318, 0, 0, + 0, 313, 0, 148, 0, 0, 0, 0, 143, 0, + 0, 301, 0, 133, 0, 127, 137, 155, 161, 224, + 0, 205, 0, 0, 216, 0, 122, 0, 114, 119, + 0, 0, 0, 320, 0, 321, 310, 0, 0, 303, + 314, 283, 0, 0, 129, 144, 281, 0, 299, 0, + 289, 293, 139, 0, 0, 0, 221, 223, 0, 256, + 120, 325, 327, 306, 0, 0, 319, 316, 147, 145, + 159, 298, 0, 0, 0, 156, 225, 227, 206, 0, + 219, 217, 0, 0, 318, 0, 294, 296, 140, 0, + 0, 0, 0, 329, 330, 326, 328, 317, 160, 0, + 0, 231, 230, 229, 226, 228, 211, 207, 218, 0, + 0, 0, 295, 297, 213, 214, 0, 212, + }; + protected static readonly short [] yyDgoto = { 7, + 8, 50, 9, 51, 10, 11, 52, 237, 771, 772, + 12, 13, 53, 22, 23, 331, 240, 756, 939, 1133, + 1257, 1310, 1634, 936, 241, 242, 243, 244, 245, 246, + 247, 248, 749, 474, 750, 751, 1040, 752, 753, 1044, + 937, 1128, 1129, 1130, 273, 641, 1225, 111, 948, 813, + 1342, 1343, 1344, 1345, 1346, 1347, 1348, 1349, 1350, 1351, + 1352, 1353, 1354, 1355, 1356, 593, 1382, 860, 493, 760, + 1437, 1054, 1238, 1195, 1236, 1271, 1322, 1393, 1478, 1266, + 1505, 1479, 1530, 1531, 1532, 1056, 1528, 1057, 822, 940, + 1490, 1464, 1518, 548, 1511, 1484, 1547, 1020, 1516, 1519, + 1520, 1615, 1548, 1549, 1545, 1357, 1416, 1386, 1438, 773, + 1492, 1594, 1461, 1551, 1626, 494, 1417, 1418, 274, 1447, + 774, 775, 776, 777, 778, 731, 611, 1242, 732, 733, + 954, 1440, 1469, 1562, 1523, 1596, 1648, 1632, 1470, 1657, + 1652, 1441, 1496, 1622, 1599, 1563, 1564, 1645, 1630, 1631, + 1052, 1194, 1302, 1369, 1421, 1370, 1371, 1409, 1444, 1410, + 334, 227, 1527, 1412, 1512, 1509, 1358, 1388, 1433, 1591, + 1553, 1285, 1592, 642, 1640, 1641, 1432, 1508, 1481, 1540, + 1535, 1506, 1572, 1577, 1538, 1541, 1542, 1625, 1578, 1536, + 1537, 1636, 1623, 1624, 1049, 1137, 1262, 1230, 1293, 1263, + 1264, 1313, 1191, 1290, 1327, 388, 197, 113, 377, 378, + 114, 604, 470, 230, 1456, 740, 741, 928, 941, 335, + 336, 435, 327, 337, 311, 1267, 1268, 46, 118, 312, + 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, + 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, + 140, 258, 891, 818, 1094, 1083, 806, 979, 807, 808, + 1084, 141, 202, 814, 644, 645, 646, 885, 503, 504, + 304, 1092, 816, 436, 306, 532, 533, 534, 535, 538, + 824, 566, 270, 509, 849, 271, 508, 142, 143, 144, + 145, 673, 899, 674, 146, 147, 148, 149, 150, 151, + 152, 153, 154, 155, 156, 614, 615, 616, 854, 855, + 157, 601, 589, 851, 379, 1106, 585, 1175, 158, 523, + 1233, 1234, 1237, 1317, 1050, 1193, 1300, 1413, 495, 1272, + 1273, 1336, 1337, 929, 354, 1305, 0, 0, 594, 595, + 275, 276, 277, 161, 162, 163, 278, 279, 280, 281, + 282, 283, 284, 285, 286, 287, 288, 289, 175, 290, + 621, 176, 177, 328, 905, 708, 1023, 1112, 951, 767, + 1060, 1021, 1024, 1153, 1025, 1061, 1062, 291, 178, 179, + 180, 1166, 1098, 1167, 1168, 1169, 1170, 181, 182, 183, + 184, 784, 516, 785, 1156, 1078, 1157, 1279, 1245, 1280, + 786, 1077, 787, 1282, 1206, 185, 186, 187, 188, 189, + 190, 313, 560, 561, 831, 1214, 324, 1076, 961, 1244, + 1103, 996, 1215, 191, 448, 192, 449, 1026, 1115, 450, + 451, 724, 715, 716, 1120, 1030, 452, 453, 454, 455, + 456, 1031, 710, 1028, 1219, 1306, 1375, 1117, 1253, 1326, + 915, 718, 916, 1184, 1122, 1185, 1254, 1035, 17, 19, + 47, 48, 229, 734, 932, 468, 735, 736, + }; + protected static readonly short [] yySindex = { -68, + 0, -187, 189, 161, 258,18441, 0, 251, 0, 0, + 258, 161, 0, 0, 303, 0, 8261, 258, 0, -151, + -258, 0, 0, 0, 0, 0, 0, 0, 249, 0, + 448, 0, 0, 0, 5471, 0, 0, 0, 0, 0, + 0, 297, 0, 0, 426, 0, 0, 720, 0, 0, + 251, 495, 258, 0, 534, 0, 322, 590, -183,17987, + -27, 138, 512, 8419, 0, 138, 138, 138, -90, 138, + 138, 610, 0,10410, 138, 138, 0,10568, 0, 642, + 138, -181, 0, 138, 645, 138, 0,18473,18473, 666, + 138, 138, 97,10728, 0,17096, 0,11916,12048,12180, +12312,12444,12576,12708,12840, 0, 232, 0, 9503, 0, + 184, 0, 50, 0, 0, 0, 579, 500, -234, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 853, 917, 209, 755, 636, 338, 671, 697, + 686, 694, 533, 713, 0, 0, 0, 0, 0, 0, + 3940, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 50, 745, 197, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 377, 418, 495, 0, 517, -240, 0, 704, 0, 0, + 0, 0, 9503, 9503, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 297, 0, 0, 712, + 751, 0, -200, 0, 0, 0, 495,19024, 841, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 877, + 50,17233, 0, 0, 0, 0, 0,17096, -205, -141, + 901, 811, 488, 500, 50, 0, 0, 9503, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 156, + 275,17987, 0, 9503,17096, 816, 0, 0, 826,17096, +17096, 6467, 537, -93, 874, 9503, 0,10728, 232, 1010, + 897, 0, 913, 9503,17096, 0, 1022, 918, 690, 951, + 0, 0,17096, 642,16685, 0, 0, 645,17096, 577, + 634, 989, 50, 0, 0, 745, 0, -234, 1013, 50, +17096,17096,17096, 512, 0, 980, 0, 9503, 9503, 0, +11784, 50, 0, 8577, 250, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1069, 0, 0, 0, 0,18291, 577, + 959, 958,17096, 0, 562, 0, 126, 0, 0, 316, + 259, 0, 0, 923,10860, 8893, 0, 0,17096,17096, +17096,17096,17096,17096,17096,17096,17096,17096,17096,12972, +13104,13236, 2103,15577,13368,13500,13632,13764,13896,14028, +14160,14292,14424,14556,14688,14820,14952,15084,15216,17644, +17096, 0, 0, 0, 0, 745, 0, 0, 0, 0, +18473,18473, 0, 0, 50, 0, 0, 0, 0, 552, + 981, 0, 0, 0, 0, 0, 0, 0, 495, 841, + 927, 943, 0, 562, 297, 297, 0, 486, 107, 0, + 297, 998, 0, -193,19024, 0, 0, 0, 0, -145, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255,19054, 0, 0, 0, 0, 962, 0, + 0, 1003, 788, 0, 1001, 0, 1011, 164, 642, 0, + 138, 0, 0, 0, 50,16685, -115, 0, 1021, 0, + 0, 0, 112, 168, 0, 811, 488, 0, 0, 1008, + 0, 1041, 0, 1037, 983, 0, 0, 805, 0, 9167, + 806,11018, 874,16548, 0, 9328, 0, 0, 0, 0, + 0, 0, 174, 192, 0, 0, 0, -224, 642, -147, + 0, 0, 645, 0, 0, 1042, 1044, 0, 195, 50, + 0, 205, 0, 0,17096, 1126, 0,17096, 1129, 1050, + 0, 1053, 1055, 0,18291, 0, 0, 0, 188, 962, + 0, -92, -272, 8577, 0, 0, 0, 0, 0, 0, + 188, 0, 0, -235, 0, 0, 0, 645, 0, 577, + 50, 9661, 0, 1056, 0, 1060,15348, 0, 1181, 1061, + 8577, 0, 0, 1014, 0, 962, 50,17233, 1015, 0, + 562, 962, 0, 116, 0,17096,17096, 1067, 1186, 0, + 0, 270, 146, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,17769,17769, 0, 0, + -234, 0, 50, 745, 0, 917, 0, 917, 0,11652, + 209, 0, 209, 0, 755, 0, 755, 0, 755, 0, + 755, 0, 636, 0, 636, 0, 338, 0, 671, 0, + 697, 0, 686, 0, 694, 0, -57, -180, 0,11018, + 1151, 50, 1152, 50,11018,11018, 1066,17096, 0, 0, + 981, 0, 50, 0, 244, 562, 0, 0, 0, 0, + 9661, 486, 0, 1076, 1075, 0, 0, 707, 495, -8, + 0, 0, 0, 0, 0, 0, -185, 1077, 0, 1079, + 1081, 0, 0, 0, 0, 1083,10269, 1039, 0, 435, + 0, 0, 687, 0,17233, 0, 1082, 0, 0, 0, + 729, 141, 1086, 0, 1088, 1091, 1092, 0, 0,17096, + 0, 50, 0, 0, 818, 0, 1094, 0, 256, 0, + 0, 8419, 0, 8419, 9802, 0,15688, 0, 0, 0, + 9962,10094, 422,11018, 0, 7, 149, 0, 1031, 1046, + 0, 0, 0, 834, 0, 0, 1099, 1098, 0, 0, + 0, 0, 0, 1100, 0, 0, 0, 1106, 138, 4352, + 642, 0, 642, 0, 0, 0, 0, 8419, 0, 0, + 8419, 0,17096, 0,17096, 9503, 0, 0, 642, 1103, + 188, 0, 0, 0,17096, 0, 0, 0, 0, 0, + 0, 0, 9503, 0, 0, 50,18291, 1135, 0, 0, + 0, 0, 0, 0, 0, 962, 842, 0, 0, 0, + 0, 0, 0, 0, 0, 0,16411, 0, 0, 0, + 0, 0, 9486, 0, 0, 0, 0, 0, 0, 0, + 0,10252, 0, 9644, 1108, 0, 0, 1190, 0, 1191, + 0, 0, 0, 932, 0, 1111, 0, 0, 0, 0, + 0, 0, 0, 0, 486, 0, 0, 1068, 0, 107, + 0, 486, 0, 0, 927, 1119, 1122, 1072, 1130, 1039, + 0, 1121, 0, 1245, 1246, 0, 0,11018, 0,16822, + 1128, 729, 9661, 9503, 0, 0, 371, 1249, 1250, 219, + 1127, 0, 0, 0,17096, 0,17096, 1230, 0, 0, + 0,16959, 0, 342,16959, 848, 0, 0, 0, 0, + 9030, 0, 1256, 745,11018, 1145, 9802, 1146, 0,17096, + 0, 50, 0, 0, 0, -87, 0, 0, 0, 1141, + 0, 1174, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 706, 0, 0, 0, 0, 0, 0, 9503, 0, + 0, 50, -242, 1108, 0,11018, 0,11018, 0, 191, +11018, 0, 0, 0, 503, 0, 0, 0, 1150, 927, + 0, 0,11176, 0, 0, 0, 1155, 4512, 0, 1039, + 0, 1039, 0, 1039, 0, 0, 0, 0, 50, 1156, + 1128, 0, 0, 0, -109, -125, 1154, 1157, 0, 0, + 0, 0, 0, 1159, 9802, 1108, -180,17096, 0, 1166, + 8419, 0, 0, 0, 0, 0, 1158, 0, 0, 1170, + 0, 874, 0, 0, 0, 0, 0, -201, 1169, 1171, + 0, 1108, 1175, 0, 0, 188, 0, 1123, 1167, 0, + 0, 0, 0, 0,11018, 1204,11018, 0,11018, 0, + 0,17096, 0, 0, 1081, 0, 515, 739, 0, 0, + 0, 0, 161, 0, 0, 0, 1185, 0, 0, 0, + 1172, 0, 0, 0, 682, 0, 1176, 1300, 1303, 0, + 0, 1108, 1193, 1108, 1195, 0, 1187, 0, 0,16959, + 0, 0, 0,17096, 0, 1194, -209, 0, 8101, 0, + 0, 1313, 0, 0, 188, 0,17096, 9644, 0, 0, + 1223, 0, 964, 1197, 0, 1202, 0, 0,11176, 258, + 164, 0, 852, 1199, 1205,16822, 1207, 0,17096, 0, + 0, 0, 0, 0, 0, 8419, 0, 77, 0, 0, + 8577, 0, 0, 1290, 8419, 0, 1213, 0,11018, 0, + 0, 0, 0, 0,17096, 0, 0, 495, 1214, 495, + 164, 9503, 1199, 1253, 0, 1253, 0, 1199, 0, 0, + 0,17096, 0, 8419,11334, 0, 0, 0, 906, 0, + 0, 1240,11018,17096, 0, 495, 1220, 0, 1180, 991, + 0, 0, 1225, 0, 1222, 0, 165, 0, 1229, 1183, + 0, 1253, 0, 0, 1253, 0, 0, 0, 0, 1231, + 1094, 0, 0, 0, 0, 0, 1255, 0, 68, 1253, + 1349, 0, 1238, 495, 0, 0, 9503, 0, 106, 1241, + 0, 1242, 0, 8419, 8577,11018, 0, 0, 0, 0, + 0, 0, 1226, 1235, 0, 0,16548, 0, 2289, 66, + 495, 1247, 0, 1248, 1260,11018, 1251,17096, 0, 0, + 1254, 0, 1257, 0, 0, 1243, 0, 0, 0,19054, + 0, 1261, 66, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 714,19054, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1262, 495, + 0, 0, 0, 50, 0, 1260, 0, 0, 0, 1259, + 2289, 8577,18159, 0, 0, 595, 0, 0, 0, 5312, + 0, 0, 66, 0, 0, 0, 0, -272, 9503, 9503, + 176, 9503, 477, 645, 1282, 0, 577, 1663, 0, 1317, + 0, 0, 1235, 0, 0, 0, 0, 0, 0, 4029, + 1235, 1264, 0, -123, -120, 0, 9503, -118, 0, 9503, + 0, 1215, 1265, 0, 0, 12, 0, 143, 4675, 0, + 1266, 1219, 153, 595, 5471, 0,17096, 0, 0, 0, + 0, 0, 12, 0, 1271, 1221, 1269, 1267, 0, 1270, + 1224, 1275, 164, 1272, 1274, 0, 0, 1280, 1281, 1310, + 0, 962, 0, 965, 0, 1296, 1292, 1235, -62, 0, + 1288, 0, 0, 1301, 0, 1304, 1299, 1302, 0, 1306, + 0, 164, 164, 0, 0, 164, 1305, 1309, 0, 0, + 0, 0, 0, 0, 1314, 150, 0, 1316, 164, 1421, + 1318, 164, 0, 501, 0, 9802, 1273, 1308, 1306, 0, + 1321, 1322, 152, 1325, 0, 0, 164,16822, 1278, 1323, + 1314, 0, 0,19054, 0, 495, 495, 0, 1279, 1324, + 1316, 0, 1326, 0,17096, 1284, 1327, 1318, 0, 1332, + 164, 0, 114, 0, 1328, 0, 0, 0, 0, 0, +19054, 0, 152, 152, 0, 1333, 0, -62, 0, 0, + 409, 1338,19054, 0,19054, 0, 0, 9802, 1329, 0, + 0, 0, 1337, 1301, 0, 0, 0, 1336, 0, 208, + 0, 0, 0, 1253, 1017, 1343, 0, 0, 538, 0, + 0, 0, 0, 0, 1398, 1451, 0, 0, 0, 0, + 0, 0, 1344, 1345, 9802, 0, 0, 0, 0, 152, + 0, 0, 580, 580, 0, 1253, 0, 0, 0, -143, + -143, 1340, 1354, 0, 0, 0, 0, 0, 0,16548, +16548, 0, 0, 0, 0, 0, 0, 0, 0, 1360, + 1361,16822, 0, 0, 0, 0, 1358, 0, + }; + protected static readonly short [] yyRindex = { 1936, + 0, 0, 8735, 1936, 0, 0, 0, 1736, 0, 0, + 3522, 1489, 0, 0, 0, 0, 0, 3522, 0, 0, + 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1737, 0, 0, 1737, 0, 0, 1737, 0, 0, + 1736, 3602, 2027, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1371, 0, 0, 0, 0, 0, 0, 0, + 0,10427, 0, 1363, 0, 0, 0, 1363, 0, 0, + 0, 0, 0, 0, 3192, 0, 0, 0, 0, 0, + 0, 0, 0, 190, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 5375, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 5788, 5311, 5699, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5893, 6070, 4119, 6623, 7048, 7255, 7393, 7531, + 7669, 7807, 1311, 5470, 0, 0, 0, 0, 0, 0, + 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 238, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3649, 0, 688, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1737, 0, 0, 307, + 0, 0, 0, 0, 0, 0, 3712, 432, 3755, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4351, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1373, 0, 0, 0, 0, 0, 0, 4351, 1366, + 0, 0, 0, 0, 0, 0, 1366, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 2148, 0, 403, + 2851, 0, 0, 0, 0, 2982, 0, 2851, 0, 0, + 0, 0, 0, 1371, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 198, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1372, 3087, + 0, 0, 1363, 0, 4351, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 278, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2384, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1852, 0, 0, 0, 0, 0, 0, 0, 3798, 3861, + 0, 0, 0, 1531, 1737, 1737, 0, 8910, -206, 0, + 1737, 1744, 0, 0, 84, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 479,17919, 0, 0, 0, 0, 4351, 0, + 0, 0, 0, 0, 0, 0, 0,18191, 0, 0, + 0, 0, 0, 0, 0, 868, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 233, 1047, 0, 0, 230, + 1928, 0, 0, 1378, 513, 0, 0, 0, 0, 215, + 0, 0, 4831, 1376, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1384, 0, 2553, + 0, 0, 222, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1372, 0, 0, 0,17370, 4351, + 0, 7940, 0, 225, 0, 0, 0, 0, 0, 0, +17370, 0, 0, 0, 0, 0, 0, 60, 0, 703, + 0, 0, 0, 1381, 0, 0, 0, 0, 1366, 0, + 0, 0, 0, 4191, 0, 4351, 0, 0, 4030, 0, + 4351, 4991, 0, 0, 0, 0, 0, -202, 0, 0, + 0, 0, 313, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5535, 5640, 6142, 5965, 0, 6247, 0, 6319, 0, 0, + 6469, 0, 6554, 0, 6692, 0, 6761, 0, 6830, 0, + 6899, 0, 7117, 0, 7186, 0, 7324, 0, 7462, 0, + 7600, 0, 7738, 0, 7876, 0, 0, 749, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1852, 0, 0, 0, 0, 1531, 0, 0, 0, 0, + 0,11035, 0, 0, 817, 0, 0, 1339,15851, 0, + 0, 0, 0, 0, 0, 0, 781, 774, 0, 0, + 1385, 0, 0, 0, 0, 1820, 0, 0, 0, 0, + 0, 0,11492, 0, 0, 0, 845, 0, 0, 0, +11193,18364, 0, 0, 881, 920, 937, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 870, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1386, 0, 0, 0, + 0, 0, 6961, 0, 0, 0, 261, 0, 117, 4511, + 0, 0, 0, 0, 0, 0, 0, 1387, 0, 0, + 0, 0, 0, 1388, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +17370, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 4351, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 674, 0, 0, 0, 0, 0, + 0, 0, 0, -169, 0, 576, 0, 0, 0, 0, + 0, 0, 0, 0,11193, 0, 0, 0, 0, -206, + 0, 9345, 0, 0, 1391, 0, 873, 0, 0, 0, + 0, 1395, 0, 1346, 1348, 0, 0, 0, 0, 0, + 1382,11351, 0, 0, 0, 0,18396, 0, 0, 0, + 939, 0, 0, 0, 0, 0, 0, 2721, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 4671, 0, 5151, 1399, 0, 0, 0, + 0, 1396, 0, 0, 0, 939, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 771, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 940, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1394, 0, 0, 0, 0, 0, 957, 960, 0, 0, + 0, 0, 0, 0, 0, 1401, 761, 1400, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 4831, 0, 0, 0, 0, 0, 1403, 0, 0, + 0, 1401, 0, 0, 0,17370, 0, 752, 762, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1385, 0,15689, 0, 0, 0, + 0, 0,18521, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 793, 0, 798, 0, 0, 0, + 0, 1402, 0, 895, 1404, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1407, 0, 0, 0, + 0, 202, 0, 0,17370, 0, 0, 0, 0, 0, + 0, 0, 113, 678, 0, 0, 0, 0, 0,18592, +18191, 0, 386, 511, 449, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -208, 0, 0, 1020, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,18748, 0, -253, +18191, 0, 522, 1410, 0, 1410, 0, 511, 0, 0, + 0, 0, 0, 0, 1406, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,18791, 0, 0, 0,16125, + 0, 0, 1411, 0, 0, 0, 364, 0, 532, 0, + 0, 606, 0, 0, 1410, 0, 0, 0, 0, 0, + 1409, 0, 0, 0, 0, 0, 0, 0, 3479, 1412, + 493, 0, 0, 333, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1414, 0, 0, 0, 0, 0, + 0, 0, 0, 3363, 0, 0, 1376, 0, 0,15957, +16209, 0, 0, 0, 767, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 617, 0, 0, 0,18091, + 0, 0,16041, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 5152, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,16293, + 0, 0, 0, 0, 0, 767, 0, 0, 0, 0, + 0, 198, 479, 0, 0, 0, 0, 0, 0, 479, + 0, 0,15957, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 4830, 507, 0,16335, 0, 0, 0, + 4990, 0, 3363, 0, 0, 0, 0, 0, 0, 0, + 3363, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 257, 0, 584, 0, 647, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 824, 0, 692, 0, 0, 0, 0, 0, + 0, 0,18191, 947, 0, 0, 0, 0, 0, 0, + 0, 1390, 0, 438, 0, 0, 0, 3363, 0, 0, + 963, 0, 0, 0, 0, 0, 0, 0, 0, 1417, + 0,18191,18191, 0, 0,18259, 0, 0, 0, 0, + 0, 0, 0, 0, 1418,15719, 0, 1420,18191,17507, + 1425,18191, 0, 0, 0, 0, 0, 0, 1426, 0, + 0, 0,18984, 0, 0, 0,18191, 0, 0, 0, + 1427, 0, 0, 424, 0,18914,18954, 0, 0, 0, + 1428, 0, 0, 0, 0, 0, 0, 1430, 0, 0, +18191, 0, 716, 0, 968, 0, 0, 0, 0, 0, + 1029, 0,18834,18874, 0, 0, 0, 0, 0, 0, + 0, 0, 1469, 0, 1525, 0, 0, 0, 978, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 611, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,18984, + 0, 0,18634,18672, 0, 611, 0, 0, 0, 0, + 0,15498, 0, 0, 0, 0, 0, 0, 0, 1376, + 1376, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + }; + protected static readonly short [] yyGindex = { 0, + 0, 1757, 0, 0, 0, -1, -16, -178, -44, -41, + 0, 1797, 1806, 103, 0, 6, 0, 0, 0, 0, + 0, 0,-1157, -774, -216, -708, 0, 0, 0, 0, + 0, -221, 0, 0, 0, 776, 0, 883, 0, 0, + 0, 0, 630, 632, -17, -227, 0, -47, 0, -112, + 430, 0, 487, -587, -577, -576, -541, -537, -525, -516, + -503, 0, 0,-1152, 0,-1187, 0, 434,-1258, 0, + 3, 0, 0, 0, 596,-1183, 0, 0, 0, -3, + 267, 0, 0, 0, 306,-1151, 0, -281, -301, -354, + 0, 0, 0, -985, 262, 0, 0, -529, 0, 0, + 320, 0, 0, 299, 0, 0, 401, 0, -630, -856, + 0, 0, 0, 0, 0, -404, 335,-1396, -10, 0, + 0, 0, 893, 898, 900, 1089, -572, 0, 0, -336, + 902, 436, 0, -905, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 234, 0, 0, + 0, 0, 0, 0, 0, 0, 496, 0, 0, 0, + -315, 423, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 505, 0, -531, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 246, 0, 0, 331, 0, 0, 336, + 340, 266, 0, 0, 0, 0, 0, 0, 0, 0, + 598, 0, 0, 0, 0, -86, 0, 135, -373, -304, + 1277, 0, 421, 0, -349, 0, 966, 0, 1577, 148, + -305, -274, -81, 203, 1232, 0, 601, 0, -21, 509, + 1495, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, -267, + 0, 28, 0, -352, 0, -280, 0, 0, 0, 931, + -927, -313, -132, 554, 0, 1026, 0, 1276, -343, 1160, + 0, 0, 828, 1851, 0, 0, 0, 0, 1137, 0, + 0, 0, 1593, 0, 0, 0, 0, 0, 1627, 986, + 987, 0, 946, 0, 812, 984, 1514, 1515, 1513, 1516, + 1517, 0, 1512, 0, 0, 0, 1084, 1364, -569, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, -306, + 748, 0, -444, 0, 0, 0, 0, 0, -475, 0, + 681, 0, 573, 0, 0, 0, 0, 0, 787, -563, + -11, -334, -7, 0, 1796, 0, 48, 0, 85, 91, + 111, 128, 157, 173, 175, 181, 193, 194, 0, -662, + 0, -23, 0, 0, 882, 0, 804, 0, 0, 0, + 0, 784, -959, 862, -931, 0, 905, -489, 0, 0, + 0, 0, 0, 0, 800, 0, 802, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 724, 0, 0, 0, 0, 0, 0, 0, + 0, -31, 0, 1416, 763, 0, 0, 985, 0, 0, + 0, 0, 0, 0, -176, 0, 0, 0, 0, 0, + 1529, 1283, 0, 0, 0, 0, 1532, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 607, 0, 0, 0, + 0, 0, 0, 0, 0, 731, 0, 0, 0, 0, + 0, 0, 35, 1054, 0, 0, 0, 1062, + }; + protected static readonly short [] yyTable = { 110, + 550, 193, 18, 547, 564, 159, 112, 238, 44, 160, + 239, 622, 817, 519, 459, 458, 823, 497, 761, 597, + 476, 299, 572, 545, 501, 196, 783, 580, 432, 543, + 861, 862, 397, 1108, 531, 389, 265, 398, 264, 866, + 332, 339, 612, 643, 1240, 346, 318, 1086, 257, 988, + 254, 634, 1274, 710, 1163, 1054, 310, 873, 1164, 886, + 317, 577, 744, 613, 164, 385, 1164, 829, 14, 319, + 933, 322, 254, 1495, 321, 903, 20, 1261, 355, 232, + 623, 1383, 234, 6, 333, 340, 1038, 51, 745, 268, + 727, 1303, 1113, 292, 293, 294, 858, 300, 301, 51, + 1390, 165, 314, 315, 194, 381, 1311, 166, 320, 323, + 754, 325, 1642, 329, 321, 340, 1152, 1324, 342, 343, + 746, 558, 386, 201, 472, 762, 1110, 167, 434, 968, + 1146, 969, 1450, 462, 1111, 1452, 463, 1458, 833, 863, + 788, 1261, 1152, 110, 168, 396, 1144, 255, 238, 159, + 112, 460, 326, 160, 198, 201, 1330, 859, 922, 728, + 886, 709, 539, 857, 115, 51, 255, 501, 1101, 1038, + 710, 200, 710, 169, 1038, 995, 1038, 295, 997, 1038, + 1038, 864, 1038, 1038, 95, 296, 95, 1, 255, 170, + 255, 171, 475, 1503, 1398, 460, 256, 172, 901, 465, + 466, 904, 1621, 499, 502, 1038, 373, 115, 164, 173, + 174, 115, 51, 1165, 886, 256, 856, 506, 43, 116, + 1113, 1165, 198, 198, 95, 747, 809, 710, 266, 374, + 255, 15, 1207, 1643, 500, 473, 375, 256, 497, 256, + 505, 815, 476, 198, 573, 165, 571, 297, 612, 574, + 372, 166, 875, 297, 530, 1446, 389, 879, 881, 597, + 1038, 467, 116, 1448, 507, 970, 116, 195, 515, 613, + 264, 167, 297, 755, 540, 1571, 541, 518, 255, 256, + 264, 1003, 522, 524, 877, 576, 597, 323, 168, 2, + 517, 396, 579, 1147, 1075, 1451, 613, 554, 1453, 373, + 1459, 570, 1595, 298, 590, 567, 379, 569, 115, 298, + 553, 522, 1054, 568, 1605, 393, 1606, 169, 902, 1504, + 1502, 1338, 374, 1308, 582, 583, 880, 256, 298, 375, + 542, 544, 1247, 170, 1229, 171, 376, 198, 198, 626, + 6, 172, 596, 631, 587, 588, 598, 502, 502, 267, + 1186, 620, 920, 173, 174, 732, 3, 4, 5, 6, + 732, 1318, 737, 116, 732, 619, 930, 791, 1041, 1589, + 603, 882, 1054, 344, 1265, 977, 1567, 500, 640, 732, + 1065, 647, 648, 649, 650, 651, 652, 653, 654, 655, + 656, 657, 264, 711, 713, 923, 955, 717, 1466, 900, + 501, 892, 198, 373, 980, 1533, 732, 1560, 931, 665, + 1616, 510, 297, 707, 238, 613, 1428, 460, 1159, 769, + 1296, 921, 393, 793, 738, 732, 374, 739, 198, 825, + 393, 1426, 393, 1339, 393, 236, 978, 712, 714, 115, + 198, 564, 1639, 852, 1309, 16, 1118, 827, 198, 382, + 837, 1041, 1248, 549, 198, 837, 1041, 1096, 1041, 297, + 840, 1041, 1041, 1612, 1041, 1041, 725, 781, 298, 789, + 413, 1613, 115, 960, 1073, 1079, 236, 1473, 393, 748, + 1427, 1319, 198, 198, 792, 1054, 883, 1041, 297, 1590, + 765, 1054, 782, 961, 116, 511, 502, 383, 779, 729, + 730, 115, 1009, 837, 770, 742, 414, 49, 198, 255, + 236, 966, 369, 198, 1467, 298, 345, 497, 2, 236, + 893, 236, 839, 981, 812, 119, 640, 116, 821, 531, + 612, 757, 1614, 236, 1012, 758, 1176, 501, 780, 1297, + 794, 1119, 1041, 1246, 624, 1151, 826, 198, 832, 967, + 497, 613, 1250, 203, 625, 95, 116, 842, 256, 384, + 844, 669, 379, 865, 828, 837, 780, 838, 119, 961, + 961, 1474, 119, 437, 372, 198, 198, 841, 853, 876, + 502, 1277, 596, 475, 896, 830, 598, 415, 416, 897, + 853, 1074, 20, 781, 386, 198, 759, 906, 540, 369, + 827, 837, 911, 912, 540, 1216, 1488, 837, 198, 596, + 500, 1341, 1360, 598, 837, 1063, 670, 462, 884, 884, + 919, 297, 599, 373, 369, 898, 438, 780, 369, 476, + 364, 126, 439, 126, 1341, 1521, 1522, 613, 126, 1524, + 386, 1323, 889, 512, 475, 379, 374, 861, 1058, 379, + 489, 513, 1543, 375, 781, 1550, 827, 1597, 1598, 540, + 376, 1360, 837, 115, 908, 1014, 910, 809, 228, 119, + 1566, 1602, 369, 439, 379, 918, 982, 629, 379, 600, + 379, 379, 379, 379, 1341, 490, 732, 630, 379, 627, + 476, 250, 812, 379, 1588, 251, 836, 812, 812, 890, + 914, 342, 753, 732, 514, 1095, 943, 489, 732, 1091, + 1085, 1067, 732, 550, 1633, 440, 985, 502, 116, 198, + 441, 55, 442, 1603, 719, 443, 444, 732, 445, 446, + 944, 753, 1429, 1361, 962, 753, 423, 424, 732, 628, + 236, 115, 490, 1362, 1363, 252, 198, 500, 1235, 530, + 730, 51, 204, 719, 732, 783, 440, 945, 439, 364, + 942, 441, 522, 442, 372, 364, 443, 444, 115, 445, + 446, 1155, 364, 732, 730, 264, 364, 821, 731, 1364, + 379, 1430, 1361, 1365, 812, 1331, 812, 716, 1269, 364, + 815, 605, 1362, 1363, 1454, 1366, 116, 228, 606, 231, + 119, 730, 731, 975, 1367, 379, 447, 719, 1468, 379, + 607, 364, 379, 373, 379, 1132, 716, 1368, 1006, 379, + 993, 364, 994, 116, 716, 998, 992, 999, 1364, 731, + 1486, 1032, 1365, 119, 597, 1055, 374, 1004, 1001, 502, + 853, 440, 1000, 375, 1366, 502, 441, 457, 442, 751, + 376, 443, 444, 1367, 445, 446, 990, 372, 1123, 1005, + 721, 345, 119, 390, 236, 198, 1368, 345, 95, 640, + 722, 255, 1093, 391, 346, 640, 597, 1415, 751, 373, + 1414, 535, 1208, 269, 1018, 738, 821, 535, 739, 721, + 440, 198, 392, 393, 1058, 441, 1187, 442, 461, 722, + 443, 444, 374, 445, 446, 1620, 373, 536, 373, 375, + 249, 537, 394, 1114, 1032, 1116, 376, 1243, 1121, 1032, + 256, 1032, 671, 395, 1032, 1032, 371, 1032, 1032, 374, + 812, 374, 518, 1034, 429, 386, 375, 1414, 375, 115, + 748, 115, 1047, 376, 898, 557, 430, 95, 756, 1080, + 1066, 371, 387, 755, 1082, 759, 1329, 1082, 558, 759, + 1276, 1132, 95, 761, 198, 326, 253, 812, 373, 821, + 597, 1415, 1099, 756, 1109, 559, 374, 302, 755, 303, + 198, 720, 756, 375, 759, 115, 1554, 755, 115, 208, + 386, 374, 946, 759, 116, 502, 116, 198, 375, 947, + 926, 198, 1179, 1439, 1181, 1032, 1182, 462, 812, 95, + 812, 1141, 927, 812, 203, 1439, 1034, 1145, 419, 420, + 326, 1034, 1028, 1034, 119, 1131, 1034, 1034, 1408, 1034, + 1034, 326, 421, 422, 605, 1420, 952, 341, 497, 476, + 116, 606, 847, 116, 1136, 748, 1378, 597, 1607, 1256, + 847, 1197, 1138, 607, 1139, 425, 1140, 821, 1443, 364, + 522, 364, 1465, 1198, 364, 364, 302, 1411, 302, 198, + 1443, 427, 364, 302, 1411, 683, 364, 683, 1107, 1465, + 893, 1283, 1284, 292, 292, 1629, 426, 198, 198, 364, + 1387, 228, 292, 233, 428, 853, 550, 812, 1497, 812, + 1498, 812, 119, 431, 1183, 1028, 1252, 1034, 1650, 1651, + 1028, 1188, 1028, 1189, 303, 1028, 1028, 844, 1028, 1028, + 864, 364, 464, 844, 864, 844, 864, 469, 864, 119, + 853, 1190, 498, 844, 853, 844, 853, 844, 853, 848, + 1287, 502, 1082, 848, 69, 69, 518, 848, 69, 417, + 418, 364, 364, 198, 364, 364, 62, 596, 764, 1217, + 821, 598, 765, 183, 853, 183, 549, 183, 196, 471, + 196, 1131, 196, 1228, 198, 799, 810, 344, 518, 800, + 537, 518, 198, 238, 352, 1259, 460, 1075, 1260, 1075, + 963, 369, 520, 1325, 964, 369, 1028, 364, 369, 596, + 369, 812, 521, 598, 984, 369, 562, 1255, 985, 496, + 236, 238, 1010, 1376, 460, 24, 765, 25, 1087, 855, + 26, 855, 985, 1231, 518, 27, 1058, 1232, 115, 28, + 685, 687, 689, 691, 399, 812, 1183, 45, 30, 369, + 911, 546, 915, 70, 911, 32, 915, 70, 117, 1259, + 33, 171, 1260, 171, 34, 400, 401, 402, 403, 404, + 405, 406, 407, 408, 409, 551, 36, 847, 37, 502, + 387, 847, 38, 1283, 1284, 1340, 1359, 555, 1260, 739, + 39, 40, 575, 116, 41, 1032, 1033, 563, 812, 552, + 178, 117, 178, 596, 556, 117, 199, 598, 1340, 640, + 119, 1260, 119, 410, 411, 412, 578, 179, 812, 179, + 518, 983, 72, 983, 72, 1335, 115, 1221, 1222, 338, + 338, 131, 586, 131, 602, 1359, 1394, 202, 739, 202, + 172, 617, 172, 24, 618, 25, 723, 308, 26, 308, + 338, 632, 138, 27, 138, 380, 119, 28, 1340, 119, + 1656, 1260, 315, 115, 315, 974, 30, 976, 115, 1292, + 236, 726, 115, 32, 199, 199, 198, 743, 33, 380, + 596, 116, 34, 766, 598, 1419, 374, 1335, 763, 374, + 469, 1617, 1618, 768, 36, 199, 37, 965, 965, 795, + 38, 115, 117, 732, 732, 1424, 1425, 790, 39, 40, + 676, 678, 41, 681, 683, 85, 693, 695, 116, 796, + 364, 797, 798, 116, 835, 836, 836, 116, 364, 843, + 364, 836, 845, 1457, 846, 847, 1460, 848, 836, 1477, + 867, 198, 868, 1419, 338, 338, 871, 872, 887, 364, + 364, 888, 874, 878, 907, 909, 116, 913, 924, 925, + 462, 115, 115, 198, 934, 935, 938, 43, 956, 364, + 200, 1534, 957, 950, 739, 958, 959, 364, 965, 983, + 364, 986, 893, 989, 987, 1002, 836, 1008, 1561, 199, + 199, 739, 1022, 1027, 1029, 1034, 1037, 380, 38, 1042, + 1045, 1573, 1575, 1043, 739, 739, 1048, 1046, 821, 338, + 1051, 1053, 1059, 1419, 1071, 1072, 116, 116, 1075, 1081, + 518, 1090, 540, 1104, 1097, 198, 1105, 198, 1561, 1561, + 1124, 739, 739, 117, 198, 338, 1134, 1583, 1148, 115, + 364, 1149, 1142, 198, 198, 1150, 198, 338, 1158, 1160, + 1161, 1171, 1442, 1172, 199, 338, 1177, 1174, 1178, 549, + 1180, 338, 1192, 1196, 1442, 1200, 117, 1199, 1201, 1442, + 821, 198, 1209, 1205, 198, 1202, 661, 1204, 1213, 1220, + 199, 1223, 1224, 1442, 1232, 1561, 1231, 1241, 739, 338, + 338, 829, 199, 1251, 116, 117, 1258, 1286, 1289, 119, + 199, 1270, 502, 502, 1295, 1442, 199, 821, 1291, 1294, + 1298, 1299, 1307, 1304, 1312, 338, 1314, 1328, 1320, 1321, + 338, 1329, 1635, 1635, 1372, 1374, 1373, 1381, 1431, 1644, + 1644, 1445, 640, 640, 199, 199, 1379, 1377, 1380, 1384, + 1391, 1396, 1449, 1462, 1655, 1471, 1463, 1472, 1480, 1474, + 1482, 1485, 1453, 1483, 338, 117, 1487, 1491, 1494, 661, + 199, 1493, 1414, 1489, 661, 199, 661, 661, 661, 661, + 661, 661, 661, 661, 661, 661, 661, 1500, 1501, 1507, + 1510, 1514, 338, 338, 1515, 1513, 1544, 119, 1525, 661, + 1517, 661, 1526, 661, 1556, 661, 661, 661, 1529, 199, + 1539, 1555, 1546, 1558, 1559, 1565, 1568, 1579, 1582, 1569, + 1580, 661, 1584, 1585, 1587, 1600, 1604, 1609, 1611, 1593, + 1608, 1619, 1603, 1602, 119, 1627, 1628, 199, 199, 119, + 353, 1647, 1649, 119, 357, 359, 361, 363, 365, 367, + 369, 371, 1653, 1654, 1658, 9, 1071, 199, 573, 950, + 661, 533, 951, 1063, 685, 38, 534, 117, 489, 38, + 199, 965, 119, 686, 532, 33, 33, 767, 857, 490, + 38, 34, 337, 364, 232, 38, 106, 34, 958, 38, + 858, 881, 38, 849, 850, 882, 916, 755, 918, 341, + 917, 920, 780, 732, 38, 38, 364, 732, 755, 38, + 38, 364, 364, 134, 116, 38, 311, 38, 38, 38, + 38, 141, 135, 117, 312, 38, 142, 235, 54, 38, + 21, 38, 119, 119, 364, 1125, 338, 1041, 1226, 24, + 1227, 38, 1422, 38, 38, 117, 38, 364, 364, 1385, + 38, 1423, 364, 1275, 1601, 364, 1570, 364, 1557, 364, + 364, 364, 364, 338, 1475, 1610, 1586, 364, 1552, 1068, + 38, 364, 117, 1064, 1069, 364, 1070, 38, 38, 953, + 1455, 199, 1389, 364, 1646, 1392, 364, 1476, 364, 364, + 1638, 1581, 1576, 364, 364, 1574, 364, 364, 364, 364, + 364, 364, 364, 364, 364, 364, 364, 364, 199, 1637, + 119, 1315, 364, 364, 1499, 1038, 565, 1316, 364, 364, + 364, 364, 364, 364, 364, 364, 364, 364, 672, 364, + 364, 1089, 886, 364, 364, 364, 364, 364, 1015, 1162, + 364, 364, 305, 477, 949, 364, 364, 364, 364, 364, + 364, 364, 364, 971, 353, 37, 584, 1100, 697, 701, + 699, 706, 1239, 703, 364, 705, 478, 364, 850, 364, + 1007, 364, 1301, 1397, 364, 1211, 433, 1203, 1154, 479, + 364, 1218, 338, 1173, 481, 1143, 1210, 581, 1281, 482, + 1212, 483, 484, 485, 486, 834, 1249, 353, 721, 487, + 1102, 722, 1395, 488, 1288, 1039, 1036, 1434, 338, 0, + 0, 0, 0, 0, 0, 489, 0, 0, 490, 0, + 491, 0, 0, 917, 0, 0, 0, 199, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 117, 492, 117, 36, 0, 0, 0, + 0, 1435, 0, 199, 0, 0, 659, 661, 663, 0, + 0, 581, 581, 581, 581, 581, 581, 581, 581, 581, + 581, 581, 581, 581, 581, 581, 581, 0, 0, 0, + 0, 338, 0, 0, 0, 0, 0, 0, 0, 117, + 0, 0, 117, 0, 0, 0, 24, 338, 0, 0, + 24, 1436, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 24, 0, 0, 338, 0, 24, 0, 338, 0, + 24, 0, 0, 24, 0, 0, 199, 1044, 0, 0, + 0, 0, 0, 0, 0, 24, 24, 0, 0, 0, + 24, 24, 199, 0, 0, 0, 24, 0, 24, 24, + 24, 24, 0, 0, 0, 0, 24, 0, 0, 199, + 24, 0, 24, 199, 0, 0, 0, 833, 0, 0, + 0, 0, 24, 0, 0, 24, 0, 24, 0, 0, + 0, 24, 0, 0, 0, 0, 581, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 24, 0, 0, 338, 338, 0, 21, 24, 24, + 1044, 0, 37, 0, 0, 1044, 37, 1044, 1044, 1044, + 1044, 1044, 1044, 1044, 1044, 1044, 1044, 37, 0, 0, + 0, 199, 37, 0, 0, 0, 37, 0, 0, 37, + 1044, 0, 1044, 0, 1044, 0, 1044, 1044, 1044, 199, + 199, 37, 37, 0, 0, 0, 37, 37, 0, 0, + 0, 0, 37, 870, 37, 37, 37, 37, 0, 0, + 338, 0, 37, 0, 0, 0, 37, 0, 37, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 37, 0, + 37, 37, 0, 37, 0, 0, 0, 37, 0, 338, + 0, 1044, 0, 36, 0, 0, 0, 36, 0, 0, + 0, 444, 0, 894, 895, 199, 541, 37, 36, 444, + 0, 444, 541, 36, 0, 37, 353, 36, 0, 0, + 36, 0, 117, 0, 0, 0, 199, 0, 0, 0, + 444, 444, 36, 36, 199, 0, 0, 36, 36, 0, + 0, 0, 0, 36, 0, 36, 36, 36, 36, 0, + 444, 0, 0, 36, 0, 0, 0, 36, 444, 36, + 0, 444, 0, 0, 0, 0, 0, 541, 664, 36, + 0, 0, 36, 0, 36, 0, 0, 24, 36, 25, + 0, 0, 26, 0, 0, 0, 0, 27, 0, 0, + 0, 28, 0, 545, 0, 0, 0, 0, 36, 0, + 30, 0, 0, 0, 0, 36, 36, 32, 0, 0, + 117, 0, 33, 833, 0, 0, 34, 0, 833, 833, + 0, 0, 0, 0, 0, 0, 0, 0, 36, 0, + 37, 0, 0, 0, 38, 0, 0, 353, 0, 0, + 0, 833, 39, 40, 0, 0, 41, 117, 0, 85, + 0, 0, 117, 0, 833, 833, 117, 0, 0, 833, + 0, 0, 833, 0, 833, 0, 833, 833, 833, 833, + 0, 0, 0, 338, 833, 0, 0, 0, 833, 0, + 0, 0, 833, 0, 0, 117, 0, 0, 0, 0, + 833, 0, 0, 833, 0, 833, 833, 0, 0, 0, + 833, 833, 0, 833, 833, 833, 833, 833, 833, 833, + 833, 833, 833, 833, 0, 0, 0, 0, 199, 833, + 833, 0, 0, 0, 0, 833, 833, 833, 833, 833, + 833, 380, 833, 833, 833, 0, 833, 833, 338, 0, + 833, 833, 833, 833, 0, 117, 117, 833, 833, 0, + 0, 0, 833, 833, 833, 833, 833, 833, 833, 833, + 338, 0, 952, 24, 0, 25, 0, 0, 26, 0, + 1332, 833, 0, 27, 833, 0, 833, 28, 833, 0, + 0, 833, 0, 199, 0, 0, 30, 833, 0, 0, + 0, 0, 0, 32, 0, 0, 0, 0, 33, 0, + 1333, 0, 34, 0, 0, 199, 0, 0, 0, 0, + 0, 0, 0, 0, 36, 0, 37, 581, 0, 0, + 38, 1334, 338, 117, 338, 0, 0, 0, 39, 40, + 0, 338, 41, 0, 0, 85, 0, 0, 0, 0, + 338, 338, 0, 338, 0, 0, 0, 0, 0, 545, + 0, 0, 0, 0, 545, 545, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 199, 338, 199, + 0, 338, 0, 0, 0, 0, 199, 545, 0, 0, + 0, 0, 0, 0, 0, 199, 199, 0, 199, 0, + 545, 545, 0, 0, 0, 545, 0, 0, 545, 0, + 545, 0, 545, 545, 545, 545, 0, 0, 0, 0, + 545, 0, 0, 199, 545, 0, 199, 380, 545, 0, + 0, 0, 0, 0, 0, 0, 545, 0, 0, 545, + 875, 545, 545, 0, 0, 0, 545, 545, 0, 545, + 545, 545, 545, 545, 545, 545, 545, 545, 545, 545, + 0, 0, 0, 0, 0, 545, 545, 545, 0, 0, + 0, 545, 545, 0, 545, 545, 545, 545, 545, 545, + 545, 0, 545, 545, 0, 545, 545, 545, 545, 545, + 545, 545, 545, 545, 545, 0, 545, 545, 545, 545, + 545, 545, 545, 545, 545, 545, 545, 545, 545, 545, + 545, 545, 545, 545, 545, 545, 545, 545, 0, 0, + 545, 0, 545, 0, 545, 0, 0, 545, 952, 952, + 0, 0, 0, 545, 0, 0, 952, 952, 952, 952, + 952, 0, 952, 952, 0, 952, 952, 952, 952, 952, + 952, 952, 952, 0, 0, 0, 0, 952, 0, 952, + 952, 952, 952, 952, 952, 0, 0, 952, 0, 0, + 354, 952, 952, 0, 952, 952, 952, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 952, 0, 952, 0, + 952, 952, 0, 0, 952, 0, 952, 952, 952, 952, + 952, 952, 952, 952, 952, 952, 952, 952, 0, 952, + 0, 0, 952, 952, 0, 0, 952, 952, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 952, 952, 952, 952, 952, 0, 0, 0, 0, + 952, 952, 0, 0, 952, 0, 0, 0, 0, 952, + 952, 952, 952, 952, 0, 0, 0, 952, 0, 952, + 0, 0, 0, 0, 0, 952, 952, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 952, 952, 952, 952, 0, 952, 875, 875, 0, 0, + 0, 394, 952, 0, 875, 875, 875, 875, 875, 0, + 875, 875, 0, 875, 875, 875, 875, 875, 875, 875, + 0, 0, 0, 0, 0, 875, 0, 875, 875, 875, + 875, 875, 875, 0, 0, 875, 0, 0, 0, 875, + 875, 0, 875, 875, 875, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 875, 0, 875, 0, 875, 875, + 0, 0, 875, 0, 875, 875, 875, 875, 875, 875, + 875, 875, 875, 875, 875, 875, 0, 875, 0, 0, + 875, 875, 0, 0, 875, 875, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 875, + 875, 875, 875, 875, 0, 0, 364, 0, 875, 875, + 0, 0, 875, 0, 0, 0, 0, 875, 875, 875, + 875, 875, 0, 0, 0, 875, 354, 875, 0, 0, + 0, 354, 354, 875, 875, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 354, 0, 0, 0, 875, 875, + 875, 875, 0, 875, 0, 0, 0, 354, 354, 0, + 875, 0, 354, 0, 0, 354, 0, 354, 0, 354, + 354, 354, 354, 0, 0, 0, 0, 354, 0, 0, + 0, 354, 0, 0, 0, 354, 0, 0, 0, 0, + 0, 0, 0, 354, 0, 0, 354, 0, 354, 354, + 0, 399, 0, 354, 354, 0, 354, 354, 354, 354, + 354, 354, 354, 354, 354, 354, 354, 0, 0, 0, + 0, 0, 354, 354, 0, 0, 0, 0, 354, 354, + 354, 354, 354, 354, 0, 354, 354, 354, 0, 354, + 354, 0, 0, 354, 354, 354, 354, 394, 0, 0, + 354, 354, 394, 394, 0, 354, 354, 354, 354, 354, + 354, 354, 354, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 354, 394, 0, 354, 0, 354, + 0, 354, 0, 0, 354, 0, 0, 0, 394, 394, + 354, 0, 0, 394, 0, 0, 394, 0, 394, 0, + 394, 394, 394, 394, 0, 0, 0, 0, 394, 0, + 0, 0, 394, 0, 0, 0, 394, 0, 0, 0, + 0, 0, 0, 0, 394, 0, 0, 394, 0, 394, + 394, 0, 0, 0, 394, 394, 0, 394, 394, 394, + 394, 394, 394, 394, 394, 394, 394, 394, 0, 0, + 0, 0, 364, 394, 394, 0, 0, 0, 364, 394, + 394, 0, 394, 394, 394, 0, 394, 394, 394, 0, + 394, 394, 31, 0, 394, 394, 394, 394, 0, 0, + 0, 394, 394, 0, 0, 0, 394, 394, 394, 394, + 394, 394, 394, 394, 364, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 394, 0, 0, 394, 0, + 394, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 394, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 364, 0, 0, 0, 364, + 364, 0, 364, 364, 364, 364, 364, 364, 364, 364, + 364, 364, 364, 364, 0, 0, 0, 399, 0, 364, + 0, 0, 0, 399, 364, 364, 364, 364, 364, 364, + 364, 364, 364, 364, 0, 364, 364, 0, 0, 364, + 364, 364, 364, 364, 0, 0, 364, 364, 25, 0, + 0, 364, 364, 364, 364, 364, 364, 364, 364, 399, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 364, 0, 0, 364, 0, 364, 0, 364, 0, 0, + 364, 0, 0, 0, 0, 0, 364, 0, 0, 0, + 0, 35, 0, 0, 0, 0, 0, 0, 0, 0, + 399, 0, 0, 0, 399, 399, 0, 399, 399, 399, + 399, 399, 399, 399, 399, 399, 399, 399, 0, 0, + 0, 0, 0, 0, 399, 0, 0, 0, 0, 399, + 399, 399, 399, 399, 399, 0, 399, 399, 399, 0, + 399, 399, 0, 0, 399, 399, 399, 399, 0, 0, + 0, 399, 399, 0, 0, 0, 399, 399, 399, 399, + 399, 399, 399, 399, 0, 0, 0, 0, 0, 0, + 0, 5, 0, 0, 0, 399, 0, 0, 399, 0, + 399, 0, 399, 0, 0, 399, 0, 0, 31, 31, + 0, 399, 0, 31, 0, 0, 0, 31, 0, 31, + 0, 0, 31, 0, 31, 31, 0, 31, 0, 31, + 0, 31, 0, 31, 31, 31, 31, 0, 1052, 31, + 31, 0, 0, 0, 0, 31, 0, 31, 31, 31, + 0, 0, 31, 31, 31, 0, 31, 0, 0, 31, + 0, 31, 31, 31, 31, 0, 0, 0, 31, 31, + 31, 0, 0, 31, 31, 31, 0, 0, 0, 0, + 0, 0, 31, 31, 0, 31, 31, 0, 31, 31, + 31, 0, 0, 0, 31, 0, 0, 0, 0, 0, + 0, 51, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 31, 0, 0, 0, 0, 0, + 31, 31, 31, 0, 0, 25, 0, 0, 0, 25, + 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 25, 0, 0, 0, 7, 25, 0, 0, 0, 25, + 0, 0, 25, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 25, 25, 0, 0, 35, 25, + 25, 31, 35, 0, 0, 25, 0, 25, 25, 25, + 25, 0, 0, 35, 0, 25, 0, 1053, 35, 25, + 0, 25, 35, 0, 0, 35, 0, 0, 0, 0, + 0, 25, 0, 0, 25, 0, 25, 35, 35, 0, + 25, 0, 35, 35, 0, 0, 0, 0, 35, 0, + 35, 35, 35, 35, 0, 0, 0, 0, 35, 0, + 25, 0, 35, 0, 35, 0, 0, 25, 25, 0, + 0, 0, 0, 0, 35, 0, 0, 35, 5, 35, + 52, 0, 51, 35, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 51, 0, 0, 0, 0, 51, 0, + 0, 0, 51, 35, 0, 51, 0, 0, 0, 0, + 0, 35, 0, 0, 0, 0, 0, 51, 51, 0, + 0, 0, 51, 51, 0, 1052, 0, 0, 51, 51, + 51, 51, 51, 51, 0, 0, 0, 0, 51, 0, + 51, 0, 51, 0, 51, 51, 0, 0, 0, 51, + 0, 0, 51, 0, 51, 0, 0, 51, 0, 51, + 0, 0, 0, 51, 51, 51, 0, 0, 0, 51, + 51, 0, 0, 0, 0, 51, 0, 51, 51, 51, + 51, 0, 0, 51, 0, 51, 0, 0, 51, 51, + 0, 51, 51, 0, 0, 0, 0, 0, 0, 0, + 0, 51, 0, 51, 51, 0, 51, 0, 51, 0, + 51, 0, 51, 0, 0, 51, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 51, 51, 0, + 51, 7, 51, 51, 0, 52, 0, 0, 51, 0, + 51, 51, 51, 51, 0, 0, 52, 0, 51, 0, + 0, 52, 51, 0, 51, 52, 0, 0, 52, 0, + 0, 0, 0, 0, 51, 0, 0, 51, 0, 51, + 52, 52, 0, 51, 1053, 52, 52, 0, 51, 0, + 0, 52, 0, 52, 52, 52, 52, 0, 0, 51, + 0, 52, 0, 51, 51, 52, 0, 52, 51, 0, + 0, 51, 0, 0, 0, 0, 0, 52, 0, 0, + 52, 0, 52, 51, 51, 0, 52, 0, 51, 51, + 0, 0, 0, 0, 51, 0, 51, 51, 51, 51, + 0, 0, 0, 0, 51, 0, 52, 52, 51, 0, + 51, 52, 0, 0, 0, 0, 0, 0, 0, 0, + 51, 0, 52, 51, 0, 51, 0, 52, 0, 51, + 0, 52, 0, 0, 52, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 52, 52, 0, 51, + 0, 52, 52, 0, 0, 0, 0, 52, 0, 52, + 52, 52, 52, 0, 0, 0, 0, 52, 0, 0, + 0, 52, 0, 52, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 52, 0, 56, 52, 0, 52, 0, + 0, 0, 52, 57, 24, 58, 25, 0, 0, 26, + 59, 0, 60, 61, 27, 62, 63, 64, 28, 0, + 0, 0, 52, 0, 65, 0, 66, 30, 67, 68, + 69, 70, 0, 0, 32, 0, 0, 0, 71, 33, + 0, 72, 73, 34, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 74, 0, 36, 0, 37, 75, 0, + 0, 38, 0, 76, 77, 78, 79, 80, 81, 39, + 40, 82, 83, 41, 84, 0, 85, 0, 0, 86, + 87, 0, 0, 88, 89, 836, 0, 0, 0, 477, + 0, 836, 0, 0, 0, 0, 0, 0, 90, 91, + 92, 93, 94, 0, 0, 0, 0, 95, 0, 0, + 0, 96, 478, 0, 0, 0, 97, 98, 99, 100, + 101, 0, 0, 0, 102, 479, 103, 836, 0, 0, + 481, 0, 104, 105, 0, 482, 0, 483, 484, 485, + 486, 0, 0, 0, 0, 487, 0, 0, 0, 488, + 0, 0, 0, 1434, 0, 0, 0, 106, 107, 108, + 109, 489, 0, 0, 490, 0, 491, 0, 836, 200, + 0, 0, 0, 836, 627, 836, 836, 836, 836, 836, + 836, 836, 836, 836, 836, 836, 836, 0, 0, 0, + 492, 0, 836, 836, 0, 0, 0, 0, 836, 836, + 836, 836, 836, 836, 836, 836, 836, 0, 836, 836, + 0, 836, 836, 836, 836, 836, 836, 836, 836, 836, + 836, 0, 836, 836, 836, 836, 836, 836, 836, 836, + 836, 836, 836, 836, 836, 836, 836, 836, 836, 836, + 836, 836, 836, 836, 836, 0, 836, 1436, 836, 0, + 836, 836, 836, 836, 0, 0, 0, 627, 0, 836, + 0, 0, 627, 0, 627, 627, 627, 627, 627, 627, + 627, 627, 627, 627, 627, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 627, 836, 627, + 0, 627, 0, 627, 627, 627, 0, 0, 0, 0, + 0, 627, 627, 627, 627, 0, 0, 0, 627, 627, + 0, 0, 0, 627, 627, 627, 627, 627, 627, 627, + 627, 0, 0, 0, 0, 0, 0, 0, 0, 836, + 0, 0, 627, 0, 836, 0, 836, 836, 836, 836, + 836, 836, 836, 836, 836, 836, 836, 836, 627, 0, + 0, 0, 0, 836, 836, 0, 0, 0, 0, 836, + 836, 836, 836, 836, 836, 836, 836, 836, 0, 836, + 836, 0, 836, 836, 836, 836, 836, 836, 836, 836, + 836, 836, 0, 836, 836, 836, 836, 836, 836, 836, + 836, 836, 836, 836, 836, 836, 836, 836, 836, 836, + 836, 836, 836, 836, 836, 836, 364, 991, 0, 836, + 0, 836, 364, 0, 836, 0, 24, 0, 25, 0, + 836, 26, 0, 0, 0, 0, 27, 0, 0, 0, + 28, 0, 0, 0, 0, 0, 0, 0, 0, 30, + 0, 0, 0, 0, 0, 0, 32, 0, 364, 0, + 0, 33, 0, 0, 0, 34, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 36, 0, 37, + 0, 0, 0, 38, 0, 0, 0, 0, 0, 0, + 0, 39, 40, 0, 0, 41, 0, 0, 85, 364, + 0, 0, 0, 0, 364, 0, 364, 364, 364, 364, + 364, 364, 364, 364, 364, 364, 364, 364, 0, 0, + 0, 0, 0, 364, 364, 0, 0, 0, 0, 364, + 364, 364, 364, 364, 364, 364, 364, 364, 0, 364, + 364, 0, 364, 364, 364, 364, 364, 364, 364, 364, + 364, 364, 0, 364, 364, 364, 364, 364, 364, 364, + 364, 364, 364, 364, 364, 364, 364, 364, 364, 364, + 364, 364, 364, 364, 364, 0, 547, 1135, 0, 364, + 380, 364, 547, 0, 364, 0, 24, 0, 25, 0, + 364, 26, 0, 0, 0, 0, 27, 0, 0, 0, + 28, 0, 0, 0, 0, 0, 0, 0, 0, 30, + 0, 0, 0, 0, 0, 0, 32, 0, 547, 0, + 0, 33, 0, 0, 0, 34, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 36, 0, 37, + 0, 0, 0, 38, 0, 0, 0, 0, 0, 0, + 0, 39, 40, 0, 0, 41, 0, 0, 85, 547, + 0, 0, 0, 0, 547, 0, 547, 547, 547, 547, + 547, 547, 547, 547, 547, 547, 547, 0, 0, 0, + 0, 0, 0, 547, 547, 0, 0, 0, 547, 547, + 547, 547, 547, 547, 547, 547, 547, 547, 0, 547, + 547, 0, 547, 547, 547, 547, 547, 547, 547, 547, + 547, 547, 0, 547, 547, 547, 547, 547, 547, 547, + 547, 547, 547, 547, 547, 547, 547, 547, 547, 547, + 547, 547, 547, 547, 547, 0, 543, 0, 0, 547, + 380, 547, 543, 0, 0, 477, 0, 0, 0, 0, + 547, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 478, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 543, 0, + 0, 479, 0, 0, 0, 0, 481, 0, 0, 0, + 0, 482, 0, 483, 484, 485, 486, 0, 0, 0, + 0, 487, 0, 0, 0, 488, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 489, 0, 543, + 490, 0, 491, 0, 543, 0, 543, 543, 543, 543, + 543, 543, 543, 543, 543, 543, 543, 0, 0, 0, + 0, 0, 0, 543, 543, 0, 492, 0, 543, 543, + 0, 543, 543, 543, 543, 543, 543, 543, 0, 543, + 543, 0, 543, 543, 543, 543, 543, 543, 543, 543, + 543, 543, 0, 543, 543, 543, 543, 543, 543, 543, + 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, + 543, 543, 543, 543, 543, 0, 551, 0, 0, 543, + 386, 543, 551, 1453, 543, 0, 0, 0, 0, 0, + 543, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 386, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 386, 0, 551, 0, + 0, 386, 0, 0, 254, 0, 386, 0, 386, 386, + 386, 386, 0, 0, 0, 0, 386, 0, 0, 0, + 386, 0, 0, 0, 386, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 386, 0, 386, 0, 551, + 0, 0, 0, 0, 551, 0, 551, 551, 551, 551, + 551, 551, 551, 551, 551, 551, 551, 0, 0, 0, + 0, 386, 0, 551, 551, 0, 0, 0, 386, 551, + 0, 551, 551, 551, 551, 551, 551, 551, 0, 551, + 551, 0, 551, 551, 551, 551, 551, 551, 551, 551, + 551, 551, 0, 551, 551, 551, 551, 551, 551, 551, + 551, 551, 551, 551, 551, 551, 551, 551, 551, 551, + 551, 551, 551, 551, 551, 0, 364, 0, 386, 551, + 385, 551, 364, 0, 551, 0, 0, 0, 0, 0, + 551, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 385, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 385, 0, 364, 0, + 0, 385, 0, 0, 253, 0, 385, 0, 385, 385, + 385, 385, 0, 0, 0, 0, 385, 0, 0, 0, + 385, 0, 0, 0, 385, 0, 0, 0, 0, 0, + 0, 0, 385, 0, 0, 385, 0, 385, 0, 364, + 0, 0, 0, 0, 364, 0, 364, 364, 364, 364, + 364, 364, 364, 364, 364, 364, 364, 0, 0, 0, + 0, 385, 0, 364, 364, 0, 0, 0, 385, 364, + 0, 364, 364, 364, 364, 364, 364, 364, 0, 364, + 364, 0, 364, 364, 364, 364, 364, 364, 364, 364, + 364, 364, 0, 364, 364, 364, 364, 364, 364, 364, + 364, 364, 364, 364, 364, 364, 364, 364, 364, 364, + 364, 364, 364, 364, 364, 0, 471, 0, 385, 364, + 0, 364, 471, 0, 364, 0, 732, 0, 732, 0, + 364, 732, 0, 732, 732, 0, 732, 0, 732, 0, + 732, 0, 732, 732, 732, 0, 0, 0, 732, 732, + 0, 0, 0, 0, 732, 0, 732, 732, 471, 0, + 0, 732, 0, 0, 0, 732, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 732, 0, 732, + 0, 0, 0, 732, 732, 0, 0, 0, 0, 0, + 0, 732, 732, 0, 0, 732, 0, 0, 732, 471, + 0, 0, 0, 732, 471, 0, 471, 471, 471, 471, + 471, 471, 471, 471, 471, 471, 471, 0, 0, 0, + 0, 0, 0, 471, 471, 0, 0, 0, 0, 471, + 0, 471, 471, 471, 471, 471, 471, 471, 0, 471, + 471, 0, 471, 471, 471, 471, 471, 471, 471, 471, + 471, 471, 0, 471, 471, 471, 471, 471, 471, 471, + 471, 471, 471, 471, 471, 471, 471, 471, 471, 471, + 471, 471, 471, 471, 471, 0, 578, 0, 0, 471, + 732, 471, 578, 0, 471, 0, 24, 0, 25, 0, + 471, 26, 0, 0, 1399, 0, 27, 0, 757, 0, + 28, 0, 758, 1400, 1401, 0, 0, 0, 1402, 30, + 0, 0, 0, 0, 1403, 0, 32, 0, 578, 0, + 0, 33, 0, 0, 0, 34, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 36, 0, 37, + 364, 0, 0, 38, 0, 0, 364, 0, 0, 0, + 836, 39, 40, 0, 0, 41, 0, 0, 1404, 578, + 0, 0, 0, 1405, 578, 0, 578, 578, 578, 578, + 578, 578, 578, 578, 578, 578, 578, 0, 0, 0, + 0, 0, 364, 578, 0, 0, 0, 0, 0, 578, + 0, 578, 0, 578, 0, 578, 578, 578, 836, 578, + 578, 0, 578, 578, 578, 578, 578, 578, 578, 578, + 578, 578, 0, 0, 0, 578, 578, 578, 578, 578, + 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, + 578, 578, 578, 0, 578, 663, 0, 0, 0, 578, + 1407, 364, 0, 0, 0, 0, 0, 364, 364, 0, + 578, 0, 0, 364, 364, 364, 364, 364, 364, 364, + 836, 364, 0, 364, 364, 205, 364, 364, 364, 364, + 364, 364, 364, 364, 364, 364, 0, 364, 364, 364, + 364, 364, 364, 364, 364, 364, 364, 364, 364, 364, + 364, 364, 364, 364, 364, 364, 364, 364, 364, 0, + 354, 0, 0, 364, 0, 364, 354, 206, 364, 0, + 0, 0, 0, 0, 364, 0, 0, 0, 663, 0, + 0, 0, 0, 663, 0, 663, 663, 663, 663, 663, + 663, 663, 663, 663, 663, 663, 0, 0, 0, 0, + 0, 0, 354, 0, 0, 0, 0, 0, 663, 0, + 663, 0, 663, 0, 663, 663, 663, 0, 207, 208, + 209, 210, 0, 211, 212, 213, 214, 215, 216, 217, + 218, 0, 0, 219, 220, 221, 222, 223, 224, 225, + 226, 0, 0, 354, 0, 0, 0, 0, 354, 0, + 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, + 354, 0, 0, 0, 0, 620, 0, 354, 422, 663, + 0, 620, 0, 354, 354, 354, 422, 354, 422, 354, + 354, 354, 0, 354, 354, 0, 0, 354, 354, 354, + 354, 0, 0, 0, 354, 354, 0, 422, 422, 354, + 354, 354, 354, 354, 354, 354, 354, 620, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 422, 354, 0, + 0, 0, 0, 354, 422, 422, 0, 0, 422, 0, + 422, 0, 0, 0, 354, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 620, 0, + 0, 0, 0, 620, 0, 620, 620, 620, 620, 620, + 620, 620, 620, 620, 620, 620, 422, 0, 0, 0, + 0, 0, 620, 423, 0, 0, 0, 0, 620, 0, + 620, 423, 620, 423, 620, 620, 620, 0, 620, 620, + 0, 0, 620, 620, 620, 620, 0, 0, 0, 620, + 620, 0, 423, 423, 620, 620, 620, 620, 620, 620, + 620, 620, 0, 354, 0, 0, 0, 0, 0, 354, + 0, 0, 423, 620, 0, 0, 0, 0, 620, 0, + 423, 354, 422, 423, 0, 0, 0, 422, 354, 620, + 422, 422, 422, 422, 0, 422, 0, 422, 422, 0, + 422, 422, 422, 422, 422, 354, 422, 422, 422, 422, + 0, 422, 422, 422, 422, 422, 422, 422, 422, 422, + 422, 422, 422, 422, 422, 422, 422, 422, 422, 422, + 422, 422, 422, 0, 0, 0, 0, 354, 0, 422, + 0, 0, 422, 0, 0, 0, 354, 0, 422, 0, + 0, 354, 0, 354, 354, 354, 354, 354, 354, 354, + 354, 354, 354, 354, 0, 0, 0, 0, 600, 0, + 354, 0, 0, 0, 600, 0, 354, 354, 354, 0, + 354, 0, 354, 354, 354, 0, 354, 354, 0, 0, + 354, 354, 354, 354, 0, 0, 0, 354, 354, 0, + 0, 0, 354, 354, 354, 354, 354, 354, 354, 354, + 600, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 354, 0, 0, 0, 0, 354, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 354, 0, 0, + 618, 0, 0, 0, 0, 0, 618, 0, 0, 0, + 0, 600, 0, 0, 0, 0, 600, 0, 600, 600, + 600, 600, 600, 600, 600, 600, 600, 600, 600, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 600, 618, 600, 0, 600, 0, 600, 600, 600, + 0, 600, 600, 0, 0, 600, 600, 600, 600, 600, + 600, 600, 600, 600, 0, 0, 0, 600, 600, 600, + 600, 600, 600, 600, 600, 0, 0, 0, 0, 0, + 0, 0, 0, 618, 0, 0, 600, 0, 618, 0, + 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, + 618, 0, 600, 0, 0, 607, 0, 618, 0, 0, + 0, 607, 0, 618, 0, 618, 0, 618, 0, 618, + 618, 618, 0, 618, 618, 0, 0, 618, 618, 618, + 618, 0, 0, 0, 618, 618, 0, 0, 0, 618, + 618, 618, 618, 618, 618, 618, 618, 607, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 618, 0, + 0, 0, 0, 618, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 618, 0, 0, 958, 0, 0, + 0, 0, 0, 958, 0, 0, 0, 0, 607, 0, + 0, 0, 0, 607, 0, 607, 607, 607, 607, 607, + 607, 607, 607, 607, 607, 607, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 607, 958, + 607, 0, 607, 0, 607, 607, 607, 0, 607, 607, + 0, 0, 607, 607, 607, 607, 0, 0, 0, 607, + 607, 0, 0, 0, 607, 607, 607, 607, 607, 607, + 607, 607, 0, 0, 0, 0, 0, 0, 0, 0, + 958, 0, 0, 607, 0, 958, 0, 958, 958, 958, + 958, 958, 958, 958, 958, 958, 958, 958, 0, 607, + 0, 0, 608, 0, 0, 0, 0, 0, 608, 0, + 958, 0, 958, 0, 958, 0, 958, 958, 958, 0, + 958, 958, 0, 0, 958, 958, 958, 958, 0, 0, + 0, 958, 958, 0, 0, 0, 958, 958, 958, 958, + 958, 958, 958, 958, 608, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 958, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 958, 0, 0, 609, 0, 0, 0, 0, 0, + 609, 0, 0, 0, 0, 608, 0, 0, 0, 0, + 608, 0, 608, 608, 608, 608, 608, 608, 608, 608, + 608, 608, 608, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 608, 609, 608, 0, 608, + 0, 608, 608, 608, 0, 608, 608, 0, 0, 608, + 608, 608, 608, 0, 0, 0, 608, 608, 0, 0, + 0, 608, 608, 608, 608, 608, 608, 608, 608, 0, + 0, 0, 0, 0, 0, 0, 0, 609, 0, 0, + 608, 0, 609, 0, 609, 609, 609, 609, 609, 609, + 609, 609, 609, 609, 609, 0, 608, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 609, 0, 609, + 0, 609, 0, 609, 609, 609, 0, 609, 609, 0, + 0, 609, 609, 609, 609, 0, 0, 0, 609, 609, + 0, 0, 0, 609, 609, 609, 609, 609, 609, 609, + 609, 0, 525, 0, 628, 0, 0, 0, 0, 0, + 57, 24, 609, 25, 0, 0, 26, 259, 0, 0, + 0, 27, 62, 63, 0, 28, 0, 0, 609, 0, + 0, 65, 0, 0, 30, 0, 0, 0, 0, 0, + 0, 32, 0, 0, 0, 0, 33, 0, 72, 73, + 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 36, 0, 37, 75, 0, 0, 38, 0, + 0, 77, 0, 79, 0, 81, 39, 40, 260, 0, + 41, 0, 0, 0, 0, 0, 0, 628, 0, 629, + 0, 0, 628, 0, 628, 628, 628, 628, 628, 628, + 628, 628, 628, 628, 628, 90, 91, 92, 261, 526, + 0, 0, 0, 0, 0, 0, 0, 628, 96, 628, + 0, 628, 0, 628, 628, 628, 0, 0, 0, 0, + 0, 628, 628, 628, 628, 0, 0, 0, 628, 628, + 0, 0, 0, 628, 628, 628, 628, 628, 628, 628, + 628, 0, 0, 0, 0, 0, 0, 0, 632, 0, + 0, 0, 628, 0, 106, 527, 0, 0, 0, 0, + 0, 0, 629, 0, 0, 528, 529, 629, 628, 629, + 629, 629, 629, 629, 629, 629, 629, 629, 629, 629, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 629, 0, 629, 0, 629, 0, 629, 629, + 629, 0, 0, 0, 0, 0, 629, 629, 629, 629, + 0, 0, 0, 629, 629, 0, 0, 633, 629, 629, + 629, 629, 629, 629, 629, 629, 0, 0, 0, 0, + 0, 632, 0, 0, 0, 0, 632, 629, 632, 632, + 632, 632, 632, 632, 632, 632, 632, 632, 632, 0, + 0, 0, 0, 629, 0, 0, 0, 0, 0, 0, + 0, 632, 0, 632, 0, 632, 0, 632, 632, 632, + 0, 0, 0, 0, 0, 632, 632, 632, 632, 0, + 0, 0, 632, 632, 0, 0, 634, 0, 0, 632, + 632, 632, 632, 632, 632, 0, 0, 0, 0, 0, + 633, 0, 0, 0, 0, 633, 632, 633, 633, 633, + 633, 633, 633, 633, 633, 633, 633, 633, 0, 0, + 0, 0, 632, 0, 0, 0, 0, 0, 0, 0, + 633, 0, 633, 0, 633, 0, 633, 633, 633, 0, + 0, 0, 0, 0, 633, 633, 633, 633, 0, 0, + 0, 633, 633, 0, 0, 635, 0, 0, 633, 633, + 633, 633, 633, 633, 0, 0, 0, 0, 0, 634, + 0, 0, 0, 0, 634, 633, 634, 634, 634, 634, + 634, 634, 634, 634, 634, 634, 634, 0, 0, 0, + 0, 633, 0, 0, 0, 0, 0, 0, 0, 634, + 0, 634, 0, 634, 0, 634, 634, 634, 0, 0, + 0, 0, 0, 634, 634, 634, 634, 0, 0, 0, + 634, 634, 0, 0, 636, 0, 0, 634, 634, 634, + 634, 634, 634, 0, 0, 0, 0, 0, 635, 0, + 0, 0, 0, 635, 634, 635, 635, 635, 635, 635, + 635, 635, 635, 635, 635, 635, 0, 0, 0, 0, + 634, 0, 0, 0, 0, 0, 0, 0, 635, 0, + 635, 0, 635, 0, 635, 635, 635, 0, 0, 0, + 0, 0, 635, 635, 635, 635, 364, 0, 0, 635, + 635, 0, 364, 0, 0, 0, 635, 635, 635, 635, + 635, 635, 0, 0, 0, 0, 0, 636, 0, 0, + 0, 0, 636, 635, 636, 636, 636, 636, 636, 636, + 636, 636, 636, 636, 636, 0, 0, 0, 364, 635, + 0, 0, 0, 0, 0, 0, 0, 636, 0, 636, + 0, 636, 0, 636, 636, 636, 0, 0, 0, 0, + 0, 636, 636, 636, 636, 0, 0, 0, 636, 636, + 0, 0, 0, 0, 0, 636, 636, 636, 636, 636, + 636, 0, 0, 641, 0, 0, 0, 0, 0, 0, + 0, 0, 636, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 364, 0, 0, 0, 636, 364, + 0, 0, 364, 0, 364, 364, 0, 0, 0, 364, + 364, 0, 0, 364, 364, 364, 364, 364, 364, 364, + 364, 364, 0, 364, 364, 364, 364, 364, 364, 364, + 364, 364, 364, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 642, 364, 364, 0, 0, 0, 0, 0, + 0, 364, 0, 0, 364, 0, 641, 0, 0, 0, + 364, 641, 0, 641, 641, 641, 641, 641, 641, 641, + 641, 641, 641, 641, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 641, 0, 641, 0, + 641, 0, 641, 641, 641, 0, 0, 0, 0, 0, + 0, 0, 641, 641, 0, 0, 0, 641, 641, 0, + 0, 643, 0, 0, 0, 0, 641, 641, 641, 641, + 0, 0, 0, 0, 0, 642, 0, 0, 0, 0, + 642, 641, 642, 642, 642, 642, 642, 642, 642, 642, + 642, 642, 642, 0, 0, 0, 0, 641, 0, 0, + 0, 0, 0, 0, 0, 642, 0, 642, 0, 642, + 0, 642, 642, 642, 0, 0, 0, 0, 0, 0, + 0, 642, 642, 0, 0, 0, 642, 642, 0, 0, + 646, 0, 0, 0, 0, 642, 642, 642, 642, 0, + 0, 0, 0, 0, 643, 0, 0, 0, 0, 643, + 642, 643, 643, 643, 643, 643, 643, 643, 643, 643, + 643, 643, 0, 0, 0, 0, 642, 0, 0, 0, + 0, 0, 0, 0, 643, 0, 643, 0, 643, 0, + 643, 643, 643, 0, 0, 0, 0, 0, 0, 0, + 643, 643, 0, 0, 0, 643, 643, 0, 0, 647, + 0, 0, 0, 0, 643, 643, 643, 643, 0, 0, + 0, 0, 0, 646, 0, 0, 0, 0, 646, 643, + 646, 646, 646, 646, 646, 646, 646, 646, 646, 646, + 646, 0, 0, 0, 0, 643, 0, 0, 0, 0, + 0, 0, 0, 646, 0, 646, 0, 646, 0, 646, + 646, 646, 0, 0, 0, 0, 0, 0, 0, 646, + 646, 0, 0, 0, 646, 646, 0, 0, 649, 0, + 0, 0, 0, 0, 0, 646, 646, 0, 0, 0, + 0, 0, 647, 0, 0, 0, 0, 647, 646, 647, + 647, 647, 647, 647, 647, 647, 647, 647, 647, 647, + 0, 0, 0, 0, 646, 0, 0, 0, 0, 0, + 0, 0, 647, 0, 647, 0, 647, 0, 647, 647, + 647, 0, 0, 0, 0, 0, 0, 0, 647, 647, + 0, 0, 0, 647, 647, 0, 0, 650, 0, 0, + 0, 0, 0, 0, 647, 647, 0, 0, 0, 0, + 0, 649, 0, 0, 0, 0, 649, 647, 649, 649, + 649, 649, 649, 649, 649, 649, 649, 649, 649, 0, + 0, 0, 0, 647, 0, 0, 0, 0, 0, 0, + 0, 649, 0, 649, 0, 649, 0, 649, 649, 649, + 0, 0, 0, 0, 0, 0, 0, 0, 649, 0, + 0, 0, 649, 649, 0, 0, 652, 0, 0, 0, + 0, 0, 0, 649, 649, 0, 0, 0, 0, 0, + 650, 0, 0, 0, 0, 650, 649, 650, 650, 650, + 650, 650, 650, 650, 650, 650, 650, 650, 0, 0, + 0, 0, 649, 0, 0, 0, 0, 0, 0, 0, + 650, 0, 650, 0, 650, 0, 650, 650, 650, 0, + 0, 0, 0, 0, 0, 0, 0, 650, 0, 0, + 0, 650, 650, 0, 0, 653, 0, 0, 0, 0, + 0, 0, 650, 650, 0, 0, 0, 0, 0, 652, + 0, 0, 0, 0, 652, 650, 652, 652, 652, 652, + 652, 652, 652, 652, 652, 652, 652, 0, 0, 0, + 0, 650, 0, 0, 0, 0, 0, 0, 0, 652, + 0, 652, 0, 652, 0, 652, 652, 652, 0, 0, + 0, 0, 0, 0, 0, 0, 652, 0, 0, 0, + 0, 652, 0, 0, 655, 0, 0, 0, 0, 0, + 0, 652, 652, 0, 0, 0, 0, 0, 653, 0, + 0, 0, 0, 653, 652, 653, 653, 653, 653, 653, + 653, 653, 653, 653, 653, 653, 0, 0, 0, 0, + 652, 0, 0, 0, 0, 0, 0, 0, 653, 0, + 653, 0, 653, 0, 653, 653, 653, 0, 0, 0, + 0, 0, 0, 0, 0, 653, 0, 0, 0, 0, + 653, 0, 0, 656, 0, 0, 0, 0, 0, 0, + 653, 653, 0, 0, 0, 0, 0, 655, 0, 0, + 0, 0, 655, 653, 655, 655, 655, 655, 655, 655, + 655, 655, 655, 655, 655, 0, 0, 0, 0, 653, + 0, 0, 0, 0, 0, 0, 0, 655, 0, 655, + 0, 655, 0, 655, 655, 655, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 655, + 0, 0, 658, 0, 0, 0, 0, 0, 0, 655, + 655, 0, 0, 0, 0, 0, 656, 0, 0, 0, + 0, 656, 655, 656, 656, 656, 656, 656, 656, 656, + 656, 656, 656, 656, 0, 0, 0, 0, 655, 0, + 0, 0, 0, 0, 0, 0, 656, 0, 656, 0, + 656, 0, 656, 656, 656, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 656, 0, + 0, 659, 0, 0, 0, 0, 0, 0, 656, 656, + 0, 0, 0, 0, 0, 658, 0, 0, 0, 0, + 658, 656, 658, 658, 658, 658, 658, 658, 658, 658, + 658, 658, 658, 0, 0, 0, 0, 656, 0, 0, + 0, 0, 0, 0, 0, 658, 0, 658, 0, 658, + 0, 658, 658, 658, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 658, 0, 0, + 0, 364, 0, 0, 0, 836, 0, 0, 658, 0, + 0, 0, 0, 0, 659, 0, 0, 0, 0, 659, + 658, 659, 659, 659, 659, 659, 659, 659, 659, 659, + 659, 659, 0, 0, 0, 0, 658, 364, 0, 0, + 0, 0, 0, 0, 659, 0, 659, 0, 659, 0, + 659, 659, 659, 836, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 659, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 659, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 659, + 0, 0, 0, 0, 0, 0, 364, 0, 0, 0, + 0, 0, 364, 364, 0, 659, 0, 0, 364, 364, + 0, 364, 0, 364, 0, 836, 364, 0, 364, 364, + 0, 364, 364, 364, 364, 364, 364, 364, 364, 364, + 364, 0, 364, 364, 364, 364, 364, 364, 364, 364, + 364, 364, 364, 364, 364, 364, 364, 364, 364, 364, + 364, 364, 364, 364, 0, 0, 591, 0, 364, 0, + 364, 0, 0, 364, 57, 24, 58, 25, 1164, 364, + 26, 59, 0, 60, 61, 27, 62, 63, 64, 28, + 0, 0, 0, 0, 0, 65, 0, 66, 30, 67, + 68, 69, 70, 0, 0, 32, 0, 0, 0, 71, + 33, 0, 72, 73, 34, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 74, 0, 36, 0, 37, 75, + 0, 0, 38, 0, 76, 77, 78, 79, 80, 81, + 39, 40, 82, 83, 41, 84, 0, 85, 0, 0, + 86, 87, 0, 0, 88, 89, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, + 91, 92, 93, 94, 0, 0, 0, 0, 95, 0, + 0, 0, 96, 0, 0, 0, 0, 97, 98, 99, + 100, 101, 0, 0, 0, 102, 0, 103, 0, 0, + 0, 0, 0, 104, 105, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 56, 0, 106, 592, + 108, 109, 0, 1165, 57, 24, 58, 25, 0, 0, + 26, 59, 0, 60, 61, 27, 62, 63, 64, 28, + 0, 0, 0, 0, 0, 65, 0, 66, 30, 67, + 68, 69, 70, 0, 0, 32, 0, 0, 0, 71, + 33, 0, 72, 73, 34, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 74, 0, 36, 0, 37, 75, + 0, 0, 38, 0, 76, 77, 78, 79, 80, 81, + 39, 40, 82, 83, 41, 84, 0, 85, 0, 0, + 86, 87, 0, 0, 88, 89, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, + 91, 92, 93, 94, 0, 0, 0, 0, 95, 0, + 0, 0, 96, 0, 0, 0, 0, 97, 98, 99, + 100, 101, 0, 0, 0, 102, 0, 103, 0, 0, + 0, 0, 0, 104, 105, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 272, 0, 0, 0, 106, 107, + 108, 109, 57, 24, 58, 25, 0, 0, 26, 59, + 0, 60, 61, 27, 62, 63, 64, 28, 0, 0, + 0, 0, 0, 65, 0, 66, 30, 67, 68, 69, + 70, 0, 0, 32, 0, 0, 0, 71, 33, 0, + 72, 73, 34, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 74, 0, 36, 0, 37, 75, 0, 0, + 38, 0, 76, 77, 78, 79, 80, 81, 39, 40, + 82, 83, 41, 84, 0, 85, 0, 0, 86, 87, + 0, 0, 88, 89, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 90, 91, 92, + 93, 94, 0, 0, 0, 0, 95, 0, 0, 0, + 96, 0, 0, 0, 0, 97, 98, 99, 100, 101, + 0, 0, 0, 102, 0, 103, 0, 0, 0, 0, + 0, 104, 105, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 591, 0, 0, 0, 106, 107, 108, 109, + 57, 24, 58, 25, 0, 0, 26, 59, 0, 60, + 61, 27, 62, 63, 64, 28, 0, 0, 0, 0, + 0, 65, 0, 66, 30, 67, 68, 69, 70, 0, + 0, 32, 0, 0, 0, 71, 33, 0, 72, 73, + 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 74, 0, 36, 0, 37, 75, 0, 0, 38, 0, + 76, 77, 78, 79, 80, 81, 39, 40, 82, 83, + 41, 84, 0, 85, 0, 0, 86, 87, 0, 0, + 88, 89, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 90, 91, 92, 93, 94, + 0, 0, 0, 0, 95, 0, 0, 0, 96, 0, + 0, 0, 0, 97, 98, 99, 100, 101, 0, 0, + 0, 102, 0, 103, 0, 0, 0, 0, 0, 104, + 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1049, 0, 0, 0, 106, 592, 108, 109, 1049, 1049, + 1049, 1049, 0, 0, 1049, 1049, 0, 1049, 1049, 1049, + 1049, 1049, 1049, 1049, 0, 0, 0, 0, 0, 1049, + 0, 1049, 1049, 1049, 1049, 1049, 1049, 0, 0, 1049, + 0, 0, 0, 1049, 1049, 0, 1049, 1049, 1049, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1049, 0, + 1049, 0, 1049, 1049, 0, 0, 1049, 0, 1049, 1049, + 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, + 0, 1049, 0, 0, 1049, 1049, 0, 0, 1049, 1049, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1049, 1049, 1049, 1049, 1049, 0, 0, + 0, 0, 1049, 0, 0, 0, 1049, 0, 0, 0, + 0, 1049, 1049, 1049, 1049, 1049, 0, 0, 0, 1049, + 0, 1049, 0, 0, 0, 0, 0, 1049, 1049, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 635, 0, + 0, 0, 1049, 1049, 1049, 1049, 57, 24, 0, 25, + 0, 0, 26, 259, 0, 0, 0, 27, 62, 63, + 0, 28, 0, 0, 189, 0, 189, 65, 0, 189, + 30, 0, 0, 0, 189, 0, 0, 32, 189, 0, + 0, 0, 33, 0, 72, 73, 34, 189, 636, 0, + 0, 0, 0, 0, 189, 637, 0, 0, 36, 189, + 37, 75, 0, 189, 38, 0, 0, 77, 0, 79, + 0, 81, 39, 40, 260, 189, 41, 189, 0, 0, + 0, 189, 0, 638, 0, 0, 88, 89, 0, 189, + 189, 0, 0, 189, 0, 0, 189, 0, 0, 0, + 0, 90, 91, 92, 93, 94, 0, 0, 0, 0, + 0, 0, 0, 0, 96, 0, 0, 639, 0, 0, + 98, 99, 100, 101, 0, 0, 0, 102, 0, 103, + 0, 0, 1074, 0, 0, 104, 105, 0, 0, 0, + 0, 0, 0, 57, 24, 0, 25, 0, 0, 26, + 259, 0, 0, 0, 27, 62, 63, 0, 28, 0, + 106, 107, 108, 109, 65, 0, 0, 30, 0, 0, + 0, 0, 0, 0, 32, 0, 0, 0, 189, 33, + 0, 72, 73, 34, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 36, 0, 37, 75, 0, + 0, 38, 0, 0, 77, 0, 79, 0, 81, 39, + 40, 260, 0, 41, 0, 0, 0, 0, 0, 0, + 87, 0, 0, 88, 89, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 90, 91, + 92, 93, 801, 0, 0, 0, 0, 802, 1088, 0, + 0, 96, 0, 0, 0, 0, 0, 98, 99, 100, + 101, 0, 0, 0, 102, 0, 103, 0, 0, 0, + 0, 0, 104, 105, 0, 0, 0, 0, 0, 0, + 57, 24, 0, 25, 0, 0, 26, 259, 0, 0, + 0, 27, 62, 63, 0, 28, 0, 106, 803, 108, + 109, 65, 0, 804, 30, 0, 0, 0, 805, 0, + 0, 32, 0, 0, 0, 0, 33, 0, 72, 73, + 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 36, 0, 37, 75, 0, 0, 38, 0, + 0, 77, 0, 79, 0, 81, 39, 40, 260, 0, + 41, 0, 0, 0, 0, 0, 0, 87, 0, 0, + 88, 89, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 90, 91, 92, 93, 801, + 0, 0, 0, 0, 802, 0, 0, 0, 96, 0, + 0, 0, 0, 0, 98, 99, 100, 101, 0, 0, + 0, 102, 0, 103, 0, 0, 0, 0, 0, 104, + 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 819, 106, 803, 108, 109, 0, 0, + 804, 57, 24, 0, 25, 805, 0, 26, 259, 0, + 0, 0, 27, 62, 63, 0, 28, 0, 0, 189, + 0, 189, 65, 0, 189, 30, 0, 0, 0, 189, + 0, 0, 32, 189, 0, 0, 0, 33, 0, 72, + 73, 34, 189, 0, 0, 0, 0, 0, 0, 189, + 0, 0, 0, 36, 189, 37, 75, 0, 189, 38, + 0, 0, 77, 0, 79, 0, 81, 39, 40, 260, + 189, 41, 189, 0, 0, 0, 189, 0, 87, 0, + 0, 88, 89, 0, 189, 189, 0, 0, 189, 0, + 0, 189, 0, 0, 0, 0, 90, 91, 92, 93, + 308, 0, 0, 0, 0, 546, 820, 0, 0, 96, + 0, 0, 0, 0, 0, 98, 99, 100, 101, 0, + 0, 0, 102, 0, 103, 1074, 0, 0, 0, 0, + 104, 105, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1013, 0, 0, 0, 106, 309, 108, 109, 57, + 24, 0, 25, 0, 0, 26, 259, 0, 0, 0, + 27, 62, 63, 189, 28, 0, 0, 24, 0, 25, + 65, 0, 26, 30, 0, 0, 0, 27, 0, 0, + 32, 28, 0, 0, 0, 33, 0, 72, 73, 34, + 30, 636, 0, 0, 0, 0, 0, 32, 637, 0, + 0, 36, 33, 37, 75, 0, 34, 38, 0, 0, + 77, 0, 79, 0, 81, 39, 40, 260, 36, 41, + 37, 0, 0, 0, 38, 0, 638, 0, 0, 88, + 89, 0, 39, 40, 0, 0, 41, 0, 0, 85, + 0, 0, 0, 0, 90, 91, 92, 93, 94, 0, + 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, + 0, 0, 0, 98, 99, 100, 101, 0, 0, 0, + 102, 0, 103, 0, 0, 0, 0, 0, 104, 105, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 819, + 0, 0, 0, 106, 107, 108, 109, 57, 24, 0, + 25, 0, 0, 26, 259, 0, 0, 0, 27, 62, + 63, 380, 28, 0, 0, 24, 0, 25, 65, 0, + 26, 30, 0, 0, 0, 27, 0, 0, 32, 28, + 0, 0, 0, 33, 0, 72, 73, 34, 30, 0, + 0, 0, 0, 0, 0, 32, 0, 0, 0, 36, + 33, 37, 75, 1019, 34, 38, 0, 0, 77, 0, + 79, 0, 81, 39, 40, 260, 36, 41, 37, 0, + 0, 0, 38, 0, 87, 0, 0, 88, 89, 0, + 39, 40, 0, 0, 41, 0, 0, 608, 0, 0, + 0, 0, 90, 91, 92, 93, 308, 0, 0, 0, + 0, 546, 0, 0, 0, 96, 0, 0, 0, 0, + 0, 98, 99, 100, 101, 0, 0, 0, 102, 0, + 103, 0, 0, 0, 0, 0, 104, 105, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 819, 0, 0, + 0, 106, 309, 108, 109, 57, 24, 0, 25, 0, + 0, 26, 259, 0, 0, 0, 27, 62, 63, 380, + 28, 0, 0, 0, 0, 0, 65, 0, 0, 30, + 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, + 0, 33, 0, 72, 73, 34, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 36, 0, 37, + 75, 0, 0, 38, 0, 0, 77, 0, 79, 0, + 81, 39, 40, 260, 0, 41, 0, 0, 0, 0, + 0, 0, 87, 0, 0, 88, 89, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 90, 91, 92, 93, 308, 0, 0, 0, 0, 546, + 0, 0, 0, 96, 0, 0, 0, 0, 0, 98, + 99, 100, 101, 0, 0, 0, 102, 0, 103, 0, + 0, 0, 0, 0, 104, 105, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 347, 0, 106, + 309, 108, 109, 348, 0, 57, 24, 0, 25, 0, + 0, 26, 259, 0, 0, 0, 27, 62, 63, 0, + 28, 0, 0, 0, 0, 0, 65, 0, 0, 30, + 0, 0, 0, 0, 0, 0, 32, 0, 0, 349, + 0, 33, 0, 72, 73, 34, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 36, 0, 37, + 75, 0, 0, 38, 0, 0, 77, 0, 79, 0, + 81, 39, 40, 260, 0, 41, 0, 0, 0, 0, + 0, 0, 0, 0, 350, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 90, 91, 92, 261, 351, 0, 0, 0, 0, 0, + 0, 0, 0, 96, 0, 352, 0, 0, 0, 98, + 99, 100, 101, 972, 0, 0, 102, 0, 103, 811, + 0, 0, 0, 0, 104, 105, 0, 57, 24, 0, + 25, 0, 0, 26, 259, 0, 0, 0, 27, 62, + 63, 0, 28, 0, 0, 0, 0, 0, 65, 106, + 263, 30, 109, 0, 0, 0, 0, 0, 32, 0, + 0, 0, 0, 33, 0, 72, 73, 34, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, + 0, 37, 75, 0, 0, 38, 0, 0, 77, 0, + 79, 0, 81, 39, 40, 260, 0, 41, 0, 0, + 0, 0, 0, 0, 87, 0, 0, 88, 89, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 90, 91, 92, 93, 308, 0, 0, 0, + 0, 0, 973, 0, 0, 96, 0, 0, 0, 0, + 0, 98, 99, 100, 101, 0, 0, 0, 102, 0, + 103, 0, 0, 0, 0, 0, 104, 105, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1016, 0, 0, + 0, 106, 309, 108, 109, 57, 24, 0, 25, 0, + 0, 26, 259, 0, 0, 0, 27, 62, 63, 0, + 28, 0, 0, 24, 0, 25, 65, 0, 26, 30, + 0, 0, 0, 27, 0, 0, 32, 28, 0, 0, + 0, 33, 0, 72, 73, 34, 30, 0, 0, 0, + 0, 0, 0, 32, 0, 0, 0, 36, 33, 37, + 75, 0, 34, 38, 0, 0, 77, 0, 79, 0, + 81, 39, 40, 260, 36, 41, 37, 0, 0, 0, + 38, 0, 87, 0, 0, 88, 89, 0, 39, 40, + 0, 0, 41, 0, 0, 563, 0, 0, 0, 0, + 90, 91, 92, 93, 308, 0, 0, 0, 0, 0, + 1017, 0, 0, 96, 0, 0, 0, 0, 0, 98, + 99, 100, 101, 0, 0, 0, 102, 0, 103, 0, + 0, 0, 0, 0, 104, 105, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 307, 0, 0, 0, 106, + 309, 108, 109, 57, 24, 0, 25, 0, 0, 26, + 259, 0, 0, 0, 27, 62, 63, 380, 28, 0, + 0, 527, 0, 527, 65, 0, 527, 30, 0, 0, + 0, 527, 0, 0, 32, 527, 0, 0, 0, 33, + 0, 72, 73, 34, 527, 0, 0, 0, 0, 0, + 0, 527, 0, 0, 0, 36, 527, 37, 75, 0, + 527, 38, 0, 0, 77, 0, 79, 0, 81, 39, + 40, 260, 527, 41, 527, 0, 0, 0, 527, 0, + 87, 0, 0, 88, 89, 0, 527, 527, 0, 0, + 527, 0, 0, 527, 0, 0, 0, 0, 90, 91, + 92, 93, 308, 0, 0, 0, 0, 0, 0, 0, + 0, 96, 0, 0, 0, 0, 0, 98, 99, 100, + 101, 0, 0, 0, 102, 0, 103, 0, 0, 0, + 0, 0, 104, 105, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 316, 0, 0, 0, 106, 309, 108, + 109, 57, 24, 0, 25, 0, 0, 26, 259, 0, + 0, 0, 27, 62, 63, 527, 28, 0, 0, 0, + 0, 0, 65, 0, 0, 30, 0, 0, 0, 0, + 0, 0, 32, 0, 0, 0, 0, 33, 0, 72, + 73, 34, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 36, 0, 37, 75, 0, 0, 38, + 0, 0, 77, 0, 79, 0, 81, 39, 40, 260, + 0, 41, 0, 0, 0, 0, 0, 0, 87, 0, + 0, 88, 89, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 90, 91, 92, 93, + 308, 0, 0, 0, 0, 0, 0, 0, 0, 96, + 0, 0, 0, 0, 0, 98, 99, 100, 101, 0, + 0, 0, 102, 0, 103, 0, 0, 0, 0, 0, + 104, 105, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 347, 0, 106, 309, 108, 109, 348, + 0, 57, 24, 0, 25, 0, 0, 26, 259, 0, + 0, 0, 27, 62, 63, 0, 28, 0, 0, 0, + 0, 0, 65, 0, 0, 30, 0, 0, 0, 0, + 0, 0, 32, 0, 0, 349, 0, 33, 0, 72, + 73, 34, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 36, 0, 37, 75, 0, 0, 38, + 0, 0, 77, 0, 79, 0, 81, 39, 40, 260, + 0, 41, 0, 0, 0, 0, 0, 0, 0, 0, + 350, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 90, 91, 92, 261, + 351, 0, 0, 0, 0, 0, 0, 0, 0, 96, + 0, 352, 0, 0, 0, 98, 99, 100, 101, 0, + 0, 0, 102, 0, 103, 633, 0, 0, 0, 0, + 104, 105, 0, 57, 24, 0, 25, 0, 0, 26, + 259, 0, 0, 0, 27, 62, 63, 0, 28, 0, + 0, 0, 0, 0, 65, 106, 263, 30, 109, 0, + 0, 0, 0, 0, 32, 0, 0, 0, 0, 33, + 0, 72, 73, 34, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 36, 0, 37, 75, 0, + 0, 38, 0, 0, 77, 0, 79, 0, 81, 39, + 40, 260, 0, 41, 0, 0, 0, 0, 0, 0, + 87, 0, 0, 88, 89, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 90, 91, + 92, 93, 94, 0, 0, 0, 0, 0, 0, 0, + 0, 96, 0, 0, 0, 0, 0, 98, 99, 100, + 101, 0, 0, 0, 102, 0, 103, 0, 0, 0, + 0, 0, 104, 105, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 811, 0, 0, 0, 106, 107, 108, + 109, 57, 24, 0, 25, 0, 0, 26, 259, 0, + 0, 0, 27, 62, 63, 0, 28, 0, 0, 190, + 0, 190, 65, 0, 190, 30, 0, 0, 0, 190, + 0, 0, 32, 190, 0, 0, 0, 33, 0, 72, + 73, 34, 190, 0, 0, 0, 0, 0, 0, 190, + 0, 0, 0, 36, 190, 37, 75, 0, 190, 38, + 0, 0, 77, 0, 79, 0, 81, 39, 40, 260, + 190, 41, 190, 0, 0, 0, 190, 0, 87, 0, + 0, 88, 89, 0, 190, 190, 0, 0, 190, 0, + 0, 190, 0, 0, 0, 0, 90, 91, 92, 93, + 308, 0, 0, 0, 0, 0, 0, 0, 0, 96, + 0, 0, 0, 0, 0, 98, 99, 100, 101, 0, + 0, 0, 102, 0, 103, 0, 0, 0, 0, 0, + 104, 105, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1126, 0, 0, 0, 106, 309, 108, 109, 57, + 24, 0, 25, 0, 0, 26, 259, 0, 0, 0, + 27, 62, 63, 190, 28, 0, 0, 189, 0, 189, + 65, 0, 189, 30, 0, 0, 0, 189, 0, 0, + 32, 189, 0, 0, 0, 33, 0, 72, 73, 34, + 189, 0, 0, 0, 0, 0, 0, 189, 0, 0, + 0, 36, 189, 37, 75, 0, 189, 38, 0, 0, + 77, 0, 79, 0, 81, 39, 40, 260, 189, 41, + 189, 0, 0, 0, 189, 0, 87, 0, 0, 88, + 89, 0, 189, 189, 0, 0, 189, 0, 0, 189, + 0, 0, 0, 0, 90, 91, 92, 93, 94, 0, + 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, + 0, 0, 0, 98, 99, 100, 101, 0, 0, 0, + 102, 0, 103, 0, 0, 0, 0, 0, 104, 105, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1278, + 0, 0, 0, 106, 1127, 108, 109, 57, 24, 0, + 25, 0, 0, 26, 259, 0, 0, 0, 27, 62, + 63, 189, 28, 0, 0, 199, 0, 199, 65, 0, + 199, 30, 0, 0, 0, 199, 0, 0, 32, 199, + 0, 0, 0, 33, 0, 72, 73, 34, 199, 0, + 0, 0, 0, 0, 0, 199, 0, 0, 0, 36, + 199, 37, 75, 0, 199, 38, 0, 0, 77, 0, + 79, 0, 81, 39, 40, 260, 199, 41, 199, 0, + 0, 0, 199, 0, 87, 0, 0, 88, 89, 0, + 199, 199, 0, 0, 199, 0, 0, 199, 0, 0, + 0, 0, 90, 91, 92, 93, 308, 0, 0, 0, + 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, + 0, 98, 99, 100, 101, 0, 0, 0, 102, 0, + 103, 0, 0, 0, 0, 0, 104, 105, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 83, 0, 0, + 0, 106, 309, 108, 109, 83, 83, 0, 83, 0, + 0, 83, 83, 0, 0, 0, 83, 83, 83, 199, + 83, 0, 0, 0, 0, 0, 83, 0, 0, 83, + 0, 0, 0, 0, 0, 0, 83, 0, 0, 0, + 0, 83, 0, 83, 83, 83, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 83, 0, 83, + 83, 0, 0, 83, 0, 0, 83, 0, 83, 0, + 83, 83, 83, 83, 0, 83, 0, 0, 0, 0, + 0, 0, 83, 0, 0, 83, 83, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 83, 83, 83, 83, 83, 0, 0, 0, 0, 0, + 0, 0, 0, 83, 0, 0, 0, 0, 0, 83, + 83, 83, 83, 0, 0, 0, 83, 0, 83, 0, + 0, 0, 0, 0, 83, 83, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 347, 0, 83, + 83, 83, 83, 348, 0, 57, 24, 0, 25, 0, + 0, 26, 259, 0, 0, 0, 27, 62, 63, 0, + 28, 0, 0, 0, 0, 0, 65, 0, 0, 30, + 0, 0, 0, 0, 0, 0, 32, 0, 0, 349, + 0, 33, 0, 72, 73, 34, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 36, 0, 37, + 75, 0, 0, 38, 0, 0, 77, 0, 79, 0, + 81, 39, 40, 260, 0, 41, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 90, 91, 92, 261, 351, 0, 0, 0, 0, 0, + 0, 0, 0, 96, 0, 352, 0, 0, 0, 98, + 99, 100, 101, 0, 0, 0, 102, 0, 103, 347, + 0, 0, 0, 0, 104, 105, 0, 57, 24, 0, + 25, 0, 0, 26, 259, 0, 0, 0, 27, 62, + 63, 0, 28, 0, 0, 0, 0, 0, 65, 106, + 263, 30, 109, 0, 0, 0, 0, 0, 32, 0, + 0, 0, 0, 33, 0, 72, 73, 34, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, + 0, 37, 75, 0, 0, 38, 0, 0, 77, 0, + 79, 0, 81, 39, 40, 260, 0, 41, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 90, 91, 92, 261, 351, 0, 0, 0, + 0, 0, 0, 0, 0, 96, 0, 352, 0, 0, + 0, 98, 99, 100, 101, 0, 0, 0, 102, 0, + 103, 356, 0, 0, 0, 0, 104, 105, 0, 57, + 24, 0, 25, 0, 0, 26, 259, 0, 0, 0, + 27, 62, 63, 0, 28, 0, 0, 0, 0, 0, + 65, 106, 263, 30, 109, 0, 0, 0, 0, 0, + 32, 0, 0, 0, 0, 33, 0, 72, 73, 34, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 36, 0, 37, 75, 0, 0, 38, 0, 0, + 77, 0, 79, 0, 81, 39, 40, 260, 0, 41, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 90, 91, 92, 261, 351, 0, + 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, + 0, 0, 0, 98, 99, 100, 101, 0, 0, 0, + 102, 0, 103, 358, 0, 0, 0, 0, 104, 105, + 0, 57, 24, 0, 25, 0, 0, 26, 259, 0, + 0, 0, 27, 62, 63, 0, 28, 0, 0, 0, + 0, 0, 65, 106, 263, 30, 109, 0, 0, 0, + 0, 0, 32, 0, 0, 0, 0, 33, 0, 72, + 73, 34, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 36, 0, 37, 75, 0, 0, 38, + 0, 0, 77, 0, 79, 0, 81, 39, 40, 260, + 0, 41, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 90, 91, 92, 261, + 351, 0, 0, 0, 0, 0, 0, 0, 0, 96, + 0, 0, 0, 0, 0, 98, 99, 100, 101, 0, + 0, 0, 102, 0, 103, 360, 0, 0, 0, 0, + 104, 105, 0, 57, 24, 0, 25, 0, 0, 26, + 259, 0, 0, 0, 27, 62, 63, 0, 28, 0, + 0, 0, 0, 0, 65, 106, 263, 30, 109, 0, + 0, 0, 0, 0, 32, 0, 0, 0, 0, 33, + 0, 72, 73, 34, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 36, 0, 37, 75, 0, + 0, 38, 0, 0, 77, 0, 79, 0, 81, 39, + 40, 260, 0, 41, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 90, 91, + 92, 261, 351, 0, 0, 0, 0, 0, 0, 0, + 0, 96, 0, 0, 0, 0, 0, 98, 99, 100, + 101, 0, 0, 0, 102, 0, 103, 362, 0, 0, + 0, 0, 104, 105, 0, 57, 24, 0, 25, 0, + 0, 26, 259, 0, 0, 0, 27, 62, 63, 0, + 28, 0, 0, 0, 0, 0, 65, 106, 263, 30, + 109, 0, 0, 0, 0, 0, 32, 0, 0, 0, + 0, 33, 0, 72, 73, 34, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 36, 0, 37, + 75, 0, 0, 38, 0, 0, 77, 0, 79, 0, + 81, 39, 40, 260, 0, 41, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 90, 91, 92, 261, 351, 0, 0, 0, 0, 0, + 0, 0, 0, 96, 0, 0, 0, 0, 0, 98, + 99, 100, 101, 0, 0, 0, 102, 0, 103, 364, + 0, 0, 0, 0, 104, 105, 0, 57, 24, 0, + 25, 0, 0, 26, 259, 0, 0, 0, 27, 62, + 63, 0, 28, 0, 0, 0, 0, 0, 65, 106, + 263, 30, 109, 0, 0, 0, 0, 0, 32, 0, + 0, 0, 0, 33, 0, 72, 73, 34, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, + 0, 37, 75, 0, 0, 38, 0, 0, 77, 0, + 79, 0, 81, 39, 40, 260, 0, 41, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 90, 91, 92, 261, 351, 0, 0, 0, + 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, + 0, 98, 99, 100, 101, 0, 0, 0, 102, 0, + 103, 366, 0, 0, 0, 0, 104, 105, 0, 57, + 24, 0, 25, 0, 0, 26, 259, 0, 0, 0, + 27, 62, 63, 0, 28, 0, 0, 0, 0, 0, + 65, 106, 263, 30, 109, 0, 0, 0, 0, 0, + 32, 0, 0, 0, 0, 33, 0, 72, 73, 34, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 36, 0, 37, 75, 0, 0, 38, 0, 0, + 77, 0, 79, 0, 81, 39, 40, 260, 0, 41, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 90, 91, 92, 261, 351, 0, + 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, + 0, 0, 0, 98, 99, 100, 101, 0, 0, 0, + 102, 0, 103, 368, 0, 0, 0, 0, 104, 105, + 0, 57, 24, 0, 25, 0, 0, 26, 259, 0, + 0, 0, 27, 62, 63, 0, 28, 0, 0, 0, + 0, 0, 65, 106, 263, 30, 109, 0, 0, 0, + 0, 0, 32, 0, 0, 0, 0, 33, 0, 72, + 73, 34, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 36, 0, 37, 75, 0, 0, 38, + 0, 0, 77, 0, 79, 0, 81, 39, 40, 260, + 0, 41, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 90, 91, 92, 261, + 351, 0, 0, 0, 0, 0, 0, 0, 0, 96, + 0, 0, 0, 0, 0, 98, 99, 100, 101, 0, + 0, 0, 102, 0, 103, 370, 0, 0, 0, 0, + 104, 105, 0, 57, 24, 0, 25, 0, 0, 26, + 259, 0, 0, 0, 27, 62, 63, 0, 28, 0, + 0, 0, 0, 0, 65, 106, 263, 30, 109, 0, + 0, 0, 0, 0, 32, 0, 0, 0, 0, 33, + 0, 72, 73, 34, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 36, 0, 37, 75, 0, + 0, 38, 0, 0, 77, 0, 79, 0, 81, 39, + 40, 260, 0, 41, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 90, 91, + 92, 261, 351, 0, 0, 0, 0, 0, 0, 0, + 0, 96, 0, 0, 0, 0, 0, 98, 99, 100, + 101, 0, 0, 0, 102, 0, 103, 658, 0, 0, + 0, 0, 104, 105, 0, 57, 24, 0, 25, 0, + 0, 26, 259, 0, 0, 0, 27, 62, 63, 0, + 28, 0, 0, 0, 0, 0, 65, 106, 263, 30, + 109, 0, 0, 0, 0, 0, 32, 0, 0, 0, + 0, 33, 0, 72, 73, 34, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 36, 0, 37, + 75, 0, 0, 38, 0, 0, 77, 0, 79, 0, + 81, 39, 40, 260, 0, 41, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 90, 91, 92, 261, 351, 0, 0, 0, 0, 0, + 0, 0, 0, 96, 0, 0, 0, 0, 0, 98, + 99, 100, 101, 0, 0, 0, 102, 0, 103, 660, + 0, 0, 0, 0, 104, 105, 0, 57, 24, 0, + 25, 0, 0, 26, 259, 0, 0, 0, 27, 62, + 63, 0, 28, 0, 0, 0, 0, 0, 65, 106, + 263, 30, 109, 0, 0, 0, 0, 0, 32, 0, + 0, 0, 0, 33, 0, 72, 73, 34, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, + 0, 37, 75, 0, 0, 38, 0, 0, 77, 0, + 79, 0, 81, 39, 40, 260, 0, 41, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 90, 91, 92, 261, 351, 0, 0, 0, + 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, + 0, 98, 99, 100, 101, 0, 0, 0, 102, 0, + 103, 662, 0, 0, 0, 0, 104, 105, 0, 57, + 24, 0, 25, 0, 0, 26, 259, 0, 0, 0, + 27, 62, 63, 0, 28, 0, 0, 0, 0, 0, + 65, 106, 263, 30, 109, 0, 0, 0, 0, 0, + 32, 0, 0, 0, 0, 33, 0, 72, 73, 34, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 36, 0, 37, 75, 0, 0, 38, 0, 0, + 77, 0, 79, 0, 81, 39, 40, 260, 0, 41, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 90, 91, 92, 261, 351, 0, + 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, + 0, 0, 0, 98, 99, 100, 101, 0, 0, 0, + 102, 0, 103, 675, 0, 0, 0, 0, 104, 105, + 0, 57, 24, 0, 25, 0, 0, 26, 259, 0, + 0, 0, 27, 62, 63, 0, 28, 0, 0, 0, + 0, 0, 65, 106, 263, 30, 109, 0, 0, 0, + 0, 0, 32, 0, 0, 0, 0, 33, 0, 72, + 73, 34, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 36, 0, 37, 75, 0, 0, 38, + 0, 0, 77, 0, 79, 0, 81, 39, 40, 260, + 0, 41, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 90, 91, 92, 261, + 351, 0, 0, 0, 0, 0, 0, 0, 0, 96, + 0, 0, 0, 0, 0, 98, 99, 100, 101, 0, + 0, 0, 102, 0, 103, 677, 0, 0, 0, 0, + 104, 105, 0, 57, 24, 0, 25, 0, 0, 26, + 259, 0, 0, 0, 27, 62, 63, 0, 28, 0, + 0, 0, 0, 0, 65, 106, 263, 30, 109, 0, + 0, 0, 0, 0, 32, 0, 0, 0, 0, 33, + 0, 72, 73, 34, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 36, 0, 37, 75, 0, + 0, 38, 0, 0, 77, 0, 79, 0, 81, 39, + 40, 260, 0, 41, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 90, 91, + 92, 261, 351, 0, 0, 0, 0, 0, 0, 0, + 0, 96, 0, 0, 0, 0, 0, 98, 99, 100, + 101, 0, 0, 0, 102, 0, 103, 679, 0, 0, + 0, 0, 104, 105, 0, 57, 24, 0, 25, 0, + 0, 26, 259, 0, 0, 0, 27, 62, 63, 0, + 28, 0, 0, 0, 0, 0, 65, 106, 263, 30, + 109, 0, 0, 0, 0, 0, 32, 0, 0, 0, + 0, 33, 0, 72, 73, 34, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 36, 0, 37, + 75, 0, 0, 38, 0, 0, 77, 0, 79, 0, + 81, 39, 40, 260, 0, 41, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 90, 91, 92, 261, 680, 0, 0, 0, 0, 0, + 0, 0, 0, 96, 0, 0, 0, 0, 0, 98, + 99, 100, 101, 0, 0, 0, 102, 0, 103, 682, + 0, 0, 0, 0, 104, 105, 0, 57, 24, 0, + 25, 0, 0, 26, 259, 0, 0, 0, 27, 62, + 63, 0, 28, 0, 0, 0, 0, 0, 65, 106, + 263, 30, 109, 0, 0, 0, 0, 0, 32, 0, + 0, 0, 0, 33, 0, 72, 73, 34, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, + 0, 37, 75, 0, 0, 38, 0, 0, 77, 0, + 79, 0, 81, 39, 40, 260, 0, 41, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 90, 91, 92, 261, 680, 0, 0, 0, + 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, + 0, 98, 99, 100, 101, 0, 0, 0, 102, 0, + 103, 684, 0, 0, 0, 0, 104, 105, 0, 57, + 24, 0, 25, 0, 0, 26, 259, 0, 0, 0, + 27, 62, 63, 0, 28, 0, 0, 0, 0, 0, + 65, 106, 263, 30, 109, 0, 0, 0, 0, 0, + 32, 0, 0, 0, 0, 33, 0, 72, 73, 34, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 36, 0, 37, 75, 0, 0, 38, 0, 0, + 77, 0, 79, 0, 81, 39, 40, 260, 0, 41, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 90, 91, 92, 261, 680, 0, + 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, + 0, 0, 0, 98, 99, 100, 101, 0, 0, 0, + 102, 0, 103, 686, 0, 0, 0, 0, 104, 105, + 0, 57, 24, 0, 25, 0, 0, 26, 259, 0, + 0, 0, 27, 62, 63, 0, 28, 0, 0, 0, + 0, 0, 65, 106, 263, 30, 109, 0, 0, 0, + 0, 0, 32, 0, 0, 0, 0, 33, 0, 72, + 73, 34, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 36, 0, 37, 75, 0, 0, 38, + 0, 0, 77, 0, 79, 0, 81, 39, 40, 260, + 0, 41, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 90, 91, 92, 261, + 680, 0, 0, 0, 0, 0, 0, 0, 0, 96, + 0, 0, 0, 0, 0, 98, 99, 100, 101, 0, + 0, 0, 102, 0, 103, 688, 0, 0, 0, 0, + 104, 105, 0, 57, 24, 0, 25, 0, 0, 26, + 259, 0, 0, 0, 27, 62, 63, 0, 28, 0, + 0, 0, 0, 0, 65, 106, 263, 30, 109, 0, + 0, 0, 0, 0, 32, 0, 0, 0, 0, 33, + 0, 72, 73, 34, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 36, 0, 37, 75, 0, + 0, 38, 0, 0, 77, 0, 79, 0, 81, 39, + 40, 260, 0, 41, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 90, 91, + 92, 261, 680, 0, 0, 0, 0, 0, 0, 0, + 0, 96, 0, 0, 0, 0, 0, 98, 99, 100, + 101, 0, 0, 0, 102, 0, 103, 690, 0, 0, + 0, 0, 104, 105, 0, 57, 24, 0, 25, 0, + 0, 26, 259, 0, 0, 0, 27, 62, 63, 0, + 28, 0, 0, 0, 0, 0, 65, 106, 263, 30, + 109, 0, 0, 0, 0, 0, 32, 0, 0, 0, + 0, 33, 0, 72, 73, 34, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 36, 0, 37, + 75, 0, 0, 38, 0, 0, 77, 0, 79, 0, + 81, 39, 40, 260, 0, 41, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 90, 91, 92, 261, 680, 0, 0, 0, 0, 0, + 0, 0, 0, 96, 0, 0, 0, 0, 0, 98, + 99, 100, 101, 0, 0, 0, 102, 0, 103, 692, + 0, 0, 0, 0, 104, 105, 0, 57, 24, 0, + 25, 0, 0, 26, 259, 0, 0, 0, 27, 62, + 63, 0, 28, 0, 0, 0, 0, 0, 65, 106, + 263, 30, 109, 0, 0, 0, 0, 0, 32, 0, + 0, 0, 0, 33, 0, 72, 73, 34, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, + 0, 37, 75, 0, 0, 38, 0, 0, 77, 0, + 79, 0, 81, 39, 40, 260, 0, 41, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 90, 91, 92, 261, 680, 0, 0, 0, + 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, + 0, 98, 99, 100, 101, 0, 0, 0, 102, 0, + 103, 694, 0, 0, 0, 0, 104, 105, 0, 57, + 24, 0, 25, 0, 0, 26, 259, 0, 0, 0, + 27, 62, 63, 0, 28, 0, 0, 0, 0, 0, + 65, 106, 263, 30, 109, 0, 0, 0, 0, 0, + 32, 0, 0, 0, 0, 33, 0, 72, 73, 34, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 36, 0, 37, 75, 0, 0, 38, 0, 0, + 77, 0, 79, 0, 81, 39, 40, 260, 0, 41, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 90, 91, 92, 261, 680, 0, + 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, + 0, 0, 0, 98, 99, 100, 101, 0, 0, 0, + 102, 0, 103, 696, 0, 0, 0, 0, 104, 105, + 0, 57, 24, 0, 25, 0, 0, 26, 259, 0, + 0, 0, 27, 62, 63, 0, 28, 0, 0, 0, + 0, 0, 65, 106, 263, 30, 109, 0, 0, 0, + 0, 0, 32, 0, 0, 0, 0, 33, 0, 72, + 73, 34, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 36, 0, 37, 75, 0, 0, 38, + 0, 0, 77, 0, 79, 0, 81, 39, 40, 260, + 0, 41, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 90, 91, 92, 261, + 680, 0, 0, 0, 0, 0, 0, 0, 0, 96, + 0, 0, 0, 0, 0, 98, 99, 100, 101, 0, + 0, 0, 102, 0, 103, 698, 0, 0, 0, 0, + 104, 105, 0, 57, 24, 0, 25, 0, 0, 26, + 259, 0, 0, 0, 27, 62, 63, 0, 28, 0, + 0, 0, 0, 0, 65, 106, 263, 30, 109, 0, + 0, 0, 0, 0, 32, 0, 0, 0, 0, 33, + 0, 72, 73, 34, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 36, 0, 37, 75, 0, + 0, 38, 0, 0, 77, 0, 79, 0, 81, 39, + 40, 260, 0, 41, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 90, 91, + 92, 261, 680, 0, 0, 0, 0, 0, 0, 0, + 0, 96, 0, 0, 0, 0, 0, 98, 99, 100, + 101, 0, 0, 0, 102, 0, 103, 700, 0, 0, + 0, 0, 104, 105, 0, 57, 24, 0, 25, 0, + 0, 26, 259, 0, 0, 0, 27, 62, 63, 0, + 28, 0, 0, 0, 0, 0, 65, 106, 263, 30, + 109, 0, 0, 0, 0, 0, 32, 0, 0, 0, + 0, 33, 0, 72, 73, 34, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 36, 0, 37, + 75, 0, 0, 38, 0, 0, 77, 0, 79, 0, + 81, 39, 40, 260, 0, 41, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 90, 91, 92, 261, 680, 0, 0, 0, 0, 0, + 0, 0, 0, 96, 0, 0, 0, 0, 0, 98, + 99, 100, 101, 0, 0, 0, 102, 0, 103, 702, + 0, 0, 0, 0, 104, 105, 0, 57, 24, 0, + 25, 0, 0, 26, 259, 0, 0, 0, 27, 62, + 63, 0, 28, 0, 0, 0, 0, 0, 65, 106, + 263, 30, 109, 0, 0, 0, 0, 0, 32, 0, + 0, 0, 0, 33, 0, 72, 73, 34, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, + 0, 37, 75, 0, 0, 38, 0, 0, 77, 0, + 79, 0, 81, 39, 40, 260, 0, 41, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 90, 91, 92, 261, 680, 0, 0, 0, + 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, + 0, 98, 99, 100, 101, 0, 0, 0, 102, 0, + 103, 704, 0, 0, 0, 0, 104, 105, 0, 57, + 24, 0, 25, 0, 0, 26, 259, 0, 0, 0, + 27, 62, 63, 0, 28, 0, 0, 0, 0, 0, + 65, 106, 263, 30, 109, 0, 0, 0, 0, 0, + 32, 0, 0, 0, 0, 33, 0, 72, 73, 34, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 36, 0, 37, 75, 0, 0, 38, 0, 0, + 77, 0, 79, 0, 81, 39, 40, 260, 0, 41, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 90, 91, 92, 261, 680, 0, + 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, + 0, 0, 0, 98, 99, 100, 101, 0, 0, 0, + 102, 0, 103, 869, 0, 0, 0, 0, 104, 105, + 0, 57, 24, 0, 25, 0, 0, 26, 259, 0, + 0, 0, 27, 62, 63, 0, 28, 0, 0, 0, + 0, 0, 65, 106, 263, 30, 109, 0, 0, 0, + 0, 0, 32, 0, 0, 0, 0, 33, 0, 72, + 73, 34, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 36, 0, 37, 75, 0, 0, 38, + 0, 0, 77, 0, 79, 0, 81, 39, 40, 260, + 0, 41, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 90, 91, 92, 261, + 351, 0, 0, 0, 0, 0, 0, 0, 0, 96, + 0, 0, 0, 0, 0, 98, 99, 100, 101, 0, + 0, 0, 102, 0, 103, 0, 0, 0, 0, 0, + 104, 105, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 210, 0, 0, 0, 0, 210, 0, + 0, 0, 210, 0, 210, 106, 263, 210, 109, 210, + 210, 0, 210, 0, 210, 0, 210, 0, 210, 210, + 210, 210, 0, 0, 210, 210, 0, 0, 0, 0, + 210, 0, 210, 210, 210, 0, 0, 210, 0, 210, + 0, 210, 0, 0, 210, 0, 210, 210, 210, 210, + 0, 0, 0, 210, 210, 210, 0, 0, 210, 210, + 210, 0, 0, 0, 0, 0, 0, 210, 210, 0, + 210, 210, 666, 210, 210, 210, 0, 0, 0, 210, + 57, 24, 0, 25, 0, 0, 26, 259, 0, 0, + 0, 27, 62, 63, 0, 28, 0, 0, 0, 210, + 0, 65, 0, 0, 30, 210, 210, 210, 0, 0, + 0, 32, 0, 0, 0, 210, 33, 0, 72, 73, + 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 36, 0, 37, 75, 0, 0, 38, 0, + 0, 77, 0, 79, 0, 81, 39, 40, 260, 0, + 41, 0, 0, 85, 0, 0, 210, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 90, 91, 92, 261, 262, + 0, 0, 0, 525, 0, 0, 0, 0, 96, 0, + 364, 57, 24, 0, 25, 667, 668, 26, 259, 0, + 0, 0, 27, 62, 63, 0, 28, 0, 0, 0, + 0, 0, 65, 0, 0, 30, 0, 0, 0, 51, + 0, 51, 32, 0, 0, 0, 364, 33, 0, 72, + 73, 34, 0, 0, 106, 263, 0, 0, 0, 0, + 0, 0, 51, 36, 0, 37, 75, 0, 0, 38, + 0, 0, 77, 0, 79, 51, 81, 39, 40, 260, + 51, 41, 0, 0, 0, 51, 0, 51, 51, 51, + 51, 0, 0, 51, 0, 51, 0, 0, 0, 51, + 0, 0, 0, 0, 0, 0, 90, 91, 92, 261, + 526, 51, 364, 0, 51, 0, 51, 0, 0, 96, + 364, 364, 364, 364, 836, 0, 0, 364, 364, 0, + 0, 364, 364, 364, 364, 364, 364, 364, 364, 364, + 51, 364, 364, 364, 364, 364, 364, 364, 364, 364, + 364, 364, 364, 364, 364, 364, 364, 364, 364, 364, + 364, 364, 364, 0, 0, 106, 527, 0, 0, 364, + 0, 52, 364, 52, 0, 52, 0, 52, 0, 0, + 52, 0, 52, 52, 0, 52, 0, 52, 0, 52, + 0, 52, 52, 52, 52, 0, 0, 52, 52, 0, + 0, 0, 0, 52, 52, 52, 52, 52, 0, 0, + 52, 0, 52, 0, 52, 0, 52, 52, 0, 52, + 52, 52, 52, 0, 0, 52, 52, 52, 52, 0, + 0, 52, 52, 52, 0, 0, 0, 0, 0, 0, + 52, 52, 0, 52, 52, 0, 52, 52, 52, 0, + 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 52, 0, 0, 52, 52, 51, 0, 0, + 0, 51, 0, 51, 0, 0, 51, 0, 51, 51, + 0, 51, 0, 51, 0, 51, 0, 51, 51, 51, + 51, 0, 0, 51, 51, 0, 0, 0, 0, 51, + 0, 51, 51, 51, 0, 0, 51, 0, 51, 0, + 51, 0, 0, 51, 0, 51, 51, 51, 51, 52, + 0, 0, 51, 51, 51, 0, 0, 51, 51, 51, + 0, 0, 0, 0, 0, 0, 51, 51, 0, 51, + 51, 0, 51, 51, 51, 0, 0, 0, 51, 0, + 0, 51, 0, 0, 0, 51, 0, 51, 0, 0, + 51, 0, 51, 51, 0, 51, 0, 51, 51, 51, + 0, 51, 51, 51, 51, 86, 0, 51, 51, 0, + 0, 0, 0, 51, 51, 51, 51, 51, 0, 0, + 51, 0, 51, 0, 51, 0, 0, 51, 0, 51, + 51, 51, 51, 0, 0, 0, 51, 51, 51, 0, + 0, 51, 51, 51, 0, 0, 0, 0, 0, 0, + 51, 51, 0, 51, 51, 51, 51, 51, 51, 0, + 0, 0, 51, 0, 0, 52, 0, 0, 0, 52, + 0, 52, 0, 0, 52, 0, 52, 52, 0, 52, + 0, 52, 51, 52, 0, 52, 52, 52, 52, 87, + 0, 52, 52, 0, 0, 0, 0, 52, 51, 52, + 52, 52, 0, 0, 52, 0, 52, 0, 52, 0, + 0, 52, 0, 52, 52, 52, 52, 0, 0, 0, + 52, 52, 52, 0, 0, 52, 52, 52, 0, 0, + 0, 0, 0, 0, 52, 52, 0, 52, 52, 51, + 52, 52, 52, 0, 0, 0, 52, 0, 0, 51, + 0, 0, 0, 51, 0, 51, 0, 0, 51, 0, + 51, 51, 0, 51, 0, 51, 52, 51, 0, 51, + 51, 51, 51, 0, 0, 51, 51, 0, 0, 0, + 0, 51, 52, 51, 51, 51, 0, 0, 51, 0, + 51, 0, 51, 0, 0, 51, 0, 51, 51, 51, + 51, 0, 0, 0, 51, 51, 51, 0, 0, 51, + 51, 51, 0, 0, 0, 0, 0, 0, 51, 51, + 0, 51, 51, 52, 51, 51, 51, 0, 0, 0, + 51, 0, 0, 51, 0, 0, 0, 51, 0, 51, + 0, 0, 51, 0, 51, 51, 0, 51, 0, 51, + 51, 51, 0, 51, 51, 51, 51, 238, 0, 51, + 51, 0, 0, 0, 0, 51, 0, 51, 51, 51, + 0, 0, 51, 0, 51, 364, 51, 0, 0, 51, + 0, 51, 51, 51, 51, 0, 0, 0, 51, 51, + 51, 0, 0, 51, 51, 51, 0, 0, 364, 0, + 0, 0, 51, 51, 0, 51, 51, 51, 51, 51, + 51, 364, 0, 0, 51, 0, 364, 0, 0, 364, + 0, 364, 0, 364, 364, 364, 364, 0, 0, 0, + 0, 364, 0, 0, 51, 364, 0, 0, 0, 364, + 0, 239, 0, 0, 0, 0, 0, 364, 0, 0, + 364, 0, 364, 0, 57, 24, 0, 25, 0, 0, + 26, 259, 0, 0, 0, 27, 62, 63, 0, 28, + 0, 364, 0, 0, 0, 65, 364, 0, 30, 0, + 0, 0, 0, 364, 364, 32, 287, 0, 364, 0, + 33, 51, 72, 73, 34, 0, 636, 0, 0, 0, + 0, 364, 0, 637, 0, 0, 36, 0, 37, 75, + 0, 0, 38, 0, 0, 77, 0, 79, 0, 81, + 39, 40, 260, 0, 41, 0, 0, 0, 0, 0, + 0, 638, 0, 364, 88, 89, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, + 91, 92, 93, 94, 0, 0, 0, 0, 0, 0, + 0, 0, 96, 1011, 0, 639, 0, 0, 98, 99, + 100, 101, 0, 0, 0, 102, 0, 103, 0, 0, + 0, 0, 0, 104, 105, 0, 0, 0, 0, 0, + 0, 57, 24, 0, 25, 0, 0, 26, 259, 0, + 0, 0, 27, 62, 63, 0, 28, 0, 106, 107, + 108, 109, 65, 0, 0, 30, 0, 0, 0, 0, + 0, 0, 32, 0, 0, 0, 0, 33, 0, 72, + 73, 34, 0, 636, 0, 0, 0, 0, 0, 0, + 637, 0, 0, 36, 0, 37, 75, 0, 0, 38, + 0, 0, 77, 0, 79, 0, 81, 39, 40, 260, + 0, 41, 0, 0, 0, 0, 0, 0, 638, 0, + 0, 88, 89, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 90, 91, 92, 93, + 94, 0, 0, 0, 0, 0, 0, 0, 0, 96, + 0, 0, 639, 0, 0, 98, 99, 100, 101, 0, + 0, 0, 102, 0, 103, 0, 0, 0, 0, 0, + 104, 105, 0, 0, 0, 0, 0, 0, 57, 24, + 0, 25, 0, 0, 26, 259, 0, 0, 0, 27, + 62, 63, 0, 28, 0, 106, 107, 108, 109, 65, + 0, 0, 30, 0, 0, 0, 0, 0, 0, 32, + 0, 0, 0, 0, 33, 0, 72, 73, 34, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 36, 0, 37, 75, 0, 0, 38, 0, 0, 77, + 0, 79, 0, 81, 39, 40, 260, 0, 41, 0, + 0, 85, 0, 0, 0, 87, 0, 0, 88, 89, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 90, 91, 92, 93, 308, 0, 0, + 0, 0, 0, 0, 0, 0, 96, 0, 0, 0, + 0, 0, 98, 99, 100, 101, 0, 0, 0, 102, + 0, 103, 0, 0, 0, 0, 0, 104, 105, 0, + 0, 0, 0, 0, 0, 57, 24, 0, 25, 0, + 0, 26, 259, 0, 0, 0, 27, 62, 63, 0, + 28, 0, 106, 309, 108, 109, 65, 0, 0, 30, + 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, + 0, 33, 0, 72, 73, 34, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 36, 0, 37, + 75, 0, 0, 38, 0, 0, 77, 0, 79, 0, + 81, 39, 40, 260, 0, 41, 0, 0, 0, 0, + 0, 0, 87, 0, 0, 88, 89, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 90, 91, 92, 93, 308, 0, 0, 0, 0, 546, + 0, 0, 0, 96, 0, 0, 0, 0, 0, 98, + 99, 100, 101, 0, 0, 0, 102, 0, 103, 0, + 0, 0, 0, 0, 104, 105, 0, 0, 0, 0, + 0, 0, 57, 24, 0, 25, 0, 0, 26, 259, + 0, 0, 0, 27, 62, 63, 0, 28, 0, 106, + 309, 108, 109, 65, 0, 0, 30, 0, 0, 0, + 0, 0, 0, 32, 0, 0, 0, 0, 33, 0, + 72, 73, 34, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 36, 0, 37, 75, 0, 0, + 38, 0, 0, 77, 0, 79, 0, 81, 39, 40, + 260, 0, 41, 0, 0, 0, 0, 0, 0, 87, + 0, 0, 88, 89, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 90, 91, 92, + 93, 308, 0, 0, 0, 0, 540, 0, 0, 0, + 96, 0, 0, 0, 0, 0, 98, 99, 100, 101, + 0, 0, 0, 102, 0, 103, 0, 0, 0, 0, + 0, 104, 105, 0, 0, 0, 0, 0, 0, 57, + 24, 0, 25, 0, 0, 26, 259, 0, 0, 0, + 27, 62, 63, 0, 28, 0, 106, 309, 108, 109, + 65, 0, 0, 30, 0, 0, 0, 0, 0, 0, + 32, 0, 0, 0, 0, 33, 0, 72, 73, 34, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 36, 0, 37, 75, 0, 0, 38, 0, 0, + 77, 0, 79, 0, 81, 39, 40, 260, 0, 41, + 0, 0, 0, 0, 0, 0, 87, 0, 0, 88, + 89, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 90, 91, 92, 93, 308, 0, + 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, + 0, 0, 0, 98, 99, 100, 101, 0, 0, 0, + 102, 0, 103, 0, 0, 0, 0, 0, 104, 105, + 0, 0, 0, 0, 0, 0, 57, 24, 0, 25, + 0, 0, 26, 259, 0, 0, 0, 27, 62, 63, + 0, 28, 0, 106, 309, 108, 109, 65, 0, 0, + 30, 0, 0, 0, 0, 0, 0, 32, 0, 0, + 0, 0, 33, 0, 72, 73, 34, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 36, 0, + 37, 75, 0, 0, 38, 0, 0, 77, 0, 79, + 0, 81, 39, 40, 260, 0, 41, 0, 0, 0, + 0, 0, 0, 87, 0, 0, 88, 89, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 90, 91, 92, 93, 94, 0, 0, 0, 0, + 0, 0, 0, 0, 96, 0, 0, 0, 0, 0, + 98, 99, 100, 101, 0, 0, 0, 102, 0, 103, + 0, 0, 0, 0, 0, 104, 105, 0, 0, 0, + 0, 0, 0, 687, 687, 0, 687, 0, 0, 687, + 687, 0, 0, 0, 687, 687, 687, 0, 687, 0, + 106, 107, 108, 109, 687, 0, 0, 687, 0, 0, + 0, 0, 0, 0, 687, 0, 0, 0, 0, 687, + 0, 687, 687, 687, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 687, 0, 687, 687, 0, + 0, 687, 0, 0, 687, 0, 687, 0, 687, 687, + 687, 687, 0, 687, 0, 0, 0, 0, 0, 0, + 687, 0, 0, 687, 687, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 687, 687, + 687, 687, 687, 0, 0, 0, 0, 0, 0, 0, + 0, 687, 0, 0, 0, 0, 0, 687, 687, 687, + 687, 0, 0, 0, 687, 0, 687, 0, 0, 0, + 0, 0, 687, 687, 0, 0, 0, 0, 0, 0, + 146, 146, 0, 146, 0, 0, 146, 146, 0, 0, + 0, 146, 146, 146, 0, 146, 0, 687, 687, 687, + 687, 146, 0, 0, 146, 0, 0, 0, 0, 0, + 0, 146, 0, 0, 0, 0, 146, 0, 146, 146, + 146, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 146, 0, 146, 146, 0, 0, 146, 0, + 0, 146, 0, 146, 0, 146, 146, 146, 146, 0, + 146, 0, 0, 0, 0, 0, 0, 146, 0, 0, + 146, 146, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 146, 146, 146, 146, 146, + 0, 0, 0, 0, 0, 0, 0, 0, 146, 0, + 0, 0, 0, 0, 146, 146, 146, 146, 0, 0, + 0, 146, 0, 146, 0, 0, 0, 0, 0, 146, + 146, 0, 0, 0, 0, 0, 0, 57, 24, 0, + 25, 0, 0, 26, 259, 0, 0, 0, 27, 62, + 63, 0, 28, 0, 146, 146, 146, 146, 65, 0, + 0, 30, 0, 0, 0, 0, 0, 0, 32, 0, + 0, 0, 0, 33, 0, 72, 73, 34, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, + 0, 37, 75, 0, 0, 38, 0, 0, 77, 0, + 79, 0, 81, 39, 40, 260, 0, 41, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 90, 91, 92, 261, 680, 0, 0, 0, + 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, + 0, 98, 99, 100, 101, 0, 0, 0, 102, 0, + 103, 0, 57, 24, 0, 25, 104, 105, 26, 259, + 0, 0, 0, 27, 62, 63, 0, 28, 0, 0, + 0, 0, 0, 65, 0, 0, 30, 0, 0, 0, + 0, 106, 263, 32, 109, 0, 0, 0, 33, 0, + 72, 73, 34, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 36, 0, 37, 75, 0, 0, + 38, 0, 0, 77, 0, 79, 0, 81, 39, 40, + 260, 0, 41, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 90, 91, 92, + 261, 351, 0, 0, 0, 0, 0, 0, 0, 0, + 96, 0, 0, 0, 0, 0, 98, 99, 100, 101, + 0, 0, 0, 102, 0, 103, 0, 0, 0, 0, + 0, 104, 105, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 733, 0, 733, 0, 733, 106, 263, 733, 109, + 733, 733, 0, 733, 0, 733, 0, 733, 0, 733, + 733, 733, 0, 0, 0, 733, 733, 0, 0, 0, + 0, 733, 0, 733, 733, 0, 0, 0, 733, 0, + 0, 0, 733, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 733, 733, 0, 733, 0, 0, 0, + 733, 733, 0, 0, 0, 0, 0, 0, 733, 733, + 57, 24, 733, 25, 0, 733, 26, 259, 0, 0, + 733, 27, 62, 63, 0, 28, 0, 0, 0, 0, + 0, 65, 0, 0, 30, 0, 0, 0, 0, 0, + 0, 32, 0, 733, 733, 0, 33, 0, 72, 73, + 34, 0, 0, 0, 0, 0, 733, 0, 0, 0, + 0, 0, 36, 0, 37, 75, 0, 0, 38, 0, + 0, 77, 0, 79, 0, 81, 39, 40, 260, 0, + 41, 0, 0, 85, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 733, 0, 0, + 0, 0, 0, 0, 0, 90, 91, 92, 261, 262, + 0, 0, 0, 0, 0, 732, 0, 732, 96, 0, + 732, 0, 732, 732, 0, 732, 0, 732, 0, 732, + 0, 732, 732, 732, 0, 0, 0, 732, 732, 0, + 0, 0, 0, 732, 0, 732, 732, 0, 0, 0, + 732, 0, 0, 0, 732, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 106, 263, 732, 0, 732, 0, + 0, 0, 732, 732, 0, 0, 0, 0, 0, 0, + 732, 732, 0, 24, 732, 25, 0, 732, 26, 0, + 0, 1399, 732, 27, 0, 757, 0, 28, 0, 758, + 1400, 1401, 0, 0, 0, 1402, 30, 0, 0, 0, + 0, 1403, 0, 32, 0, 51, 0, 51, 33, 0, + 51, 0, 34, 0, 0, 51, 0, 0, 732, 51, + 0, 0, 0, 0, 36, 0, 37, 0, 51, 0, + 38, 0, 0, 0, 0, 51, 0, 0, 39, 40, + 51, 0, 41, 0, 51, 1404, 51, 0, 51, 0, + 1405, 0, 0, 51, 0, 0, 51, 0, 51, 732, + 0, 0, 51, 0, 0, 51, 0, 0, 0, 0, + 51, 51, 0, 51, 51, 51, 0, 51, 51, 0, + 0, 0, 0, 51, 0, 0, 1406, 51, 0, 0, + 0, 0, 0, 0, 0, 0, 51, 0, 0, 0, + 0, 0, 0, 51, 0, 24, 0, 25, 51, 0, + 26, 0, 51, 169, 51, 27, 51, 0, 0, 28, + 0, 51, 0, 0, 51, 0, 51, 1407, 30, 0, + 51, 0, 0, 51, 0, 32, 0, 0, 51, 51, + 33, 0, 51, 0, 34, 51, 605, 0, 0, 0, + 0, 0, 0, 606, 0, 0, 36, 0, 37, 51, + 0, 0, 38, 0, 0, 607, 0, 0, 0, 0, + 39, 40, 0, 0, 41, 0, 0, 608, 52, 169, + 52, 0, 0, 52, 0, 0, 0, 0, 52, 0, + 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, + 0, 52, 0, 609, 0, 0, 0, 0, 52, 0, + 51, 0, 51, 52, 0, 51, 0, 52, 0, 52, + 51, 52, 0, 0, 51, 0, 52, 51, 0, 52, + 0, 52, 0, 51, 0, 52, 0, 0, 52, 0, + 51, 0, 0, 52, 52, 51, 0, 52, 0, 51, + 52, 51, 0, 51, 0, 24, 0, 25, 51, 610, + 26, 51, 0, 51, 0, 27, 0, 51, 0, 28, + 51, 0, 0, 29, 0, 51, 51, 0, 30, 51, + 0, 0, 51, 31, 0, 32, 0, 24, 0, 25, + 33, 0, 26, 0, 34, 35, 0, 27, 0, 0, + 0, 28, 0, 0, 0, 0, 36, 0, 37, 0, + 30, 0, 38, 0, 0, 0, 0, 32, 0, 0, + 39, 40, 33, 0, 41, 0, 34, 42, 0, 0, + 0, 37, 52, 0, 0, 0, 0, 0, 36, 0, + 37, 0, 37, 0, 38, 0, 0, 37, 0, 0, + 0, 37, 39, 40, 37, 0, 41, 0, 0, 85, + 0, 0, 0, 0, 51, 0, 37, 37, 0, 0, + 0, 37, 37, 0, 0, 0, 0, 37, 0, 37, + 37, 37, 37, 0, 0, 297, 0, 37, 0, 0, + 0, 37, 0, 37, 0, 0, 0, 0, 0, 0, + 0, 0, 35, 37, 0, 37, 37, 0, 37, 43, + 0, 0, 37, 35, 0, 0, 0, 0, 35, 0, + 0, 0, 35, 0, 0, 35, 0, 0, 0, 0, + 0, 0, 37, 0, 0, 0, 0, 35, 35, 37, + 37, 330, 35, 35, 31, 0, 0, 0, 35, 0, + 35, 35, 35, 35, 0, 0, 0, 0, 35, 0, + 0, 0, 35, 0, 35, 0, 0, 31, 0, 0, + 0, 0, 0, 0, 35, 0, 0, 35, 0, 35, + 31, 0, 31, 35, 31, 31, 0, 0, 0, 0, + 31, 0, 31, 31, 31, 31, 0, 0, 31, 0, + 31, 0, 0, 35, 31, 31, 0, 0, 0, 0, + 35, 35, 0, 0, 0, 0, 31, 0, 31, 31, + 0, 31, 0, 31, 0, 0, 0, 0, 31, 0, + 31, 31, 31, 31, 0, 0, 0, 0, 31, 0, + 0, 0, 31, 0, 0, 31, 0, 0, 0, 0, + 0, 0, 31, 31, 31, 0, 0, 31, 51, 31, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, + 0, 0, 0, 0, 51, 0, 0, 0, 51, 0, + 0, 51, 0, 31, 0, 0, 0, 0, 0, 0, + 31, 31, 0, 51, 51, 0, 0, 0, 51, 51, + 0, 51, 0, 0, 51, 0, 51, 51, 51, 51, + 0, 0, 51, 0, 51, 0, 0, 51, 51, 0, + 51, 51, 0, 0, 51, 0, 0, 0, 0, 0, + 51, 0, 0, 51, 0, 51, 51, 51, 0, 51, + 0, 51, 51, 0, 51, 0, 0, 51, 0, 51, + 51, 51, 51, 0, 0, 0, 0, 51, 0, 51, + 0, 51, 0, 51, 0, 0, 39, 51, 0, 0, + 0, 0, 0, 51, 0, 0, 51, 0, 51, 0, + 51, 0, 51, 0, 51, 51, 0, 0, 0, 0, + 51, 0, 51, 51, 51, 51, 0, 0, 0, 0, + 51, 0, 51, 0, 51, 0, 0, 51, 0, 40, + 0, 0, 0, 0, 0, 0, 51, 0, 0, 51, + 51, 51, 0, 0, 51, 51, 0, 0, 0, 0, + 51, 0, 51, 51, 51, 51, 0, 0, 0, 0, + 51, 0, 0, 0, 51, 51, 0, 51, 51, 51, + 0, 0, 220, 0, 0, 0, 51, 0, 0, 51, + 51, 51, 0, 0, 51, 51, 51, 0, 0, 0, + 51, 0, 51, 51, 51, 51, 0, 0, 51, 0, + 51, 0, 0, 0, 51, 51, 0, 51, 51, 51, + 0, 0, 222, 0, 51, 0, 51, 0, 0, 51, + 51, 51, 0, 0, 0, 51, 0, 0, 0, 0, + 51, 0, 51, 51, 51, 51, 0, 51, 0, 0, + 51, 0, 0, 0, 51, 51, 0, 0, 0, 0, + 51, 0, 322, 0, 477, 51, 51, 0, 0, 51, + 51, 51, 51, 51, 51, 51, 0, 0, 0, 0, + 51, 0, 0, 0, 51, 0, 0, 478, 0, 0, + 0, 0, 0, 0, 477, 51, 51, 0, 0, 51, + 479, 51, 323, 0, 480, 481, 0, 0, 0, 0, + 482, 0, 483, 484, 485, 486, 0, 478, 0, 0, + 487, 0, 0, 0, 488, 51, 0, 0, 51, 51, + 479, 0, 0, 0, 0, 481, 489, 0, 0, 490, + 482, 491, 483, 484, 485, 486, 0, 0, 0, 0, + 487, 0, 0, 0, 488, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 492, 489, 0, 0, 490, + 0, 491, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 492, + }; + protected static readonly short [] yyCheck = { 17, + 306, 18, 4, 305, 320, 17, 17, 52, 6, 17, + 52, 385, 544, 295, 193, 192, 546, 239, 494, 354, + 237, 69, 329, 304, 252, 20, 516, 341, 161, 304, + 594, 601, 119, 1019, 302, 117, 60, 119, 60, 612, + 88, 89, 379, 396, 1196, 93, 78, 975, 59, 824, + 256, 395, 1236, 256, 256, 0, 74, 621, 268, 268, + 78, 336, 256, 379, 17, 113, 268, 292, 256, 80, + 256, 82, 256, 1470, 256, 256, 335, 1230, 96, 45, + 385, 1340, 48, 0, 88, 89, 256, 294, 282, 62, + 464, 1275, 1024, 66, 67, 68, 369, 70, 71, 306, + 1359, 17, 75, 76, 256, 109, 1290, 17, 81, 82, + 256, 84, 256, 86, 256, 369, 1076, 1305, 91, 92, + 314, 269, 357, 21, 325, 499, 369, 17, 176, 792, + 256, 794, 256, 374, 377, 256, 377, 256, 286, 375, + 256, 1294, 1102, 161, 17, 118, 256, 372, 193, 161, + 161, 193, 387, 161, 20, 53, 1314, 430, 731, 464, + 369, 436, 256, 256, 17, 419, 372, 395, 256, 339, + 373, 430, 375, 17, 344, 838, 346, 268, 841, 349, + 350, 417, 352, 353, 368, 276, 368, 256, 372, 17, + 372, 17, 237, 256, 1382, 237, 421, 17, 256, 203, + 204, 382, 1599, 251, 252, 375, 392, 60, 161, 17, + 17, 64, 419, 423, 423, 421, 590, 265, 419, 17, + 1152, 423, 88, 89, 368, 419, 540, 430, 256, 415, + 372, 419, 1160, 377, 252, 233, 422, 421, 460, 421, + 258, 543, 459, 109, 331, 161, 328, 363, 585, 331, + 343, 161, 626, 363, 302, 1413, 338, 631, 632, 594, + 430, 227, 60, 1421, 268, 795, 64, 419, 292, 585, + 292, 161, 363, 419, 368, 1534, 370, 295, 372, 421, + 302, 851, 300, 301, 628, 333, 621, 260, 161, 358, + 294, 264, 340, 419, 382, 419, 612, 315, 419, 392, + 419, 325, 1561, 419, 352, 323, 0, 325, 161, 419, + 314, 329, 257, 324, 1573, 256, 1575, 161, 376, 382, + 1478, 256, 415, 256, 342, 343, 631, 421, 419, 422, + 424, 304, 256, 161, 1191, 161, 429, 203, 204, 387, + 257, 161, 354, 391, 348, 349, 354, 395, 396, 377, + 1125, 383, 726, 161, 161, 272, 425, 426, 427, 428, + 277, 256, 256, 161, 281, 383, 375, 256, 256, 256, + 374, 256, 256, 277, 1231, 369, 1528, 395, 396, 296, + 953, 399, 400, 401, 402, 403, 404, 405, 406, 407, + 408, 409, 414, 441, 442, 732, 256, 445, 256, 674, + 628, 256, 268, 392, 256, 256, 323, 256, 417, 413, + 1594, 256, 363, 431, 459, 731, 1402, 459, 1081, 256, + 256, 726, 363, 256, 469, 342, 415, 469, 294, 256, + 371, 256, 373, 368, 375, 370, 430, 441, 442, 292, + 306, 757, 1626, 256, 377, 257, 256, 256, 314, 266, + 256, 339, 376, 306, 320, 266, 344, 987, 346, 363, + 256, 349, 350, 256, 352, 353, 461, 515, 419, 517, + 262, 264, 325, 780, 256, 965, 370, 325, 419, 474, + 305, 376, 348, 349, 373, 369, 371, 375, 363, 376, + 375, 375, 516, 292, 292, 340, 544, 314, 509, 465, + 466, 354, 876, 314, 341, 471, 298, 257, 374, 372, + 370, 256, 256, 379, 372, 419, 420, 739, 358, 370, + 375, 370, 570, 375, 542, 17, 544, 325, 546, 797, + 867, 277, 325, 370, 887, 281, 1106, 765, 511, 375, + 373, 351, 430, 1206, 419, 1075, 373, 413, 559, 294, + 772, 867, 1215, 305, 429, 368, 354, 575, 421, 376, + 578, 414, 256, 611, 373, 376, 369, 373, 60, 368, + 369, 419, 64, 377, 343, 441, 442, 373, 589, 627, + 628, 1244, 594, 369, 671, 558, 594, 379, 380, 671, + 601, 373, 335, 369, 373, 461, 342, 710, 369, 343, + 363, 369, 715, 716, 375, 1175, 1463, 375, 474, 621, + 628, 1320, 1321, 621, 382, 952, 414, 374, 636, 637, + 377, 363, 373, 392, 368, 673, 430, 430, 372, 369, + 374, 375, 256, 377, 1343, 1492, 1493, 953, 382, 1496, + 419, 1304, 373, 369, 430, 339, 415, 1211, 950, 343, + 373, 377, 1509, 422, 430, 1512, 419, 1563, 1564, 430, + 429, 1370, 430, 516, 712, 893, 714, 981, 372, 161, + 1527, 263, 416, 256, 368, 723, 809, 419, 372, 430, + 374, 375, 376, 377, 1393, 373, 263, 429, 382, 374, + 430, 370, 710, 256, 1551, 374, 294, 715, 716, 430, + 718, 369, 339, 272, 430, 986, 272, 430, 277, 984, + 369, 341, 281, 1019, 1620, 339, 375, 765, 516, 585, + 344, 419, 346, 315, 339, 349, 350, 296, 352, 353, + 296, 368, 256, 1321, 782, 372, 399, 400, 315, 424, + 370, 594, 430, 1321, 1321, 424, 612, 765, 1193, 797, + 272, 419, 305, 368, 323, 1245, 339, 323, 256, 357, + 758, 344, 780, 346, 343, 363, 349, 350, 621, 352, + 353, 1078, 370, 342, 296, 797, 374, 795, 272, 1321, + 343, 305, 1370, 1321, 802, 1317, 804, 339, 1233, 387, + 1092, 306, 1370, 1370, 1425, 1321, 594, 372, 313, 374, + 292, 323, 296, 382, 1321, 368, 430, 256, 1439, 372, + 325, 374, 375, 392, 377, 1043, 368, 1321, 866, 382, + 831, 419, 833, 621, 376, 843, 830, 845, 1370, 323, + 1461, 256, 1370, 325, 1169, 948, 415, 855, 849, 887, + 851, 339, 846, 422, 1370, 893, 344, 430, 346, 339, + 429, 349, 350, 1370, 352, 353, 829, 343, 1035, 863, + 339, 369, 354, 364, 370, 731, 1370, 375, 368, 887, + 339, 372, 985, 374, 382, 893, 1211, 377, 368, 392, + 343, 369, 1164, 372, 902, 930, 904, 375, 930, 368, + 339, 757, 393, 394, 1196, 344, 382, 346, 382, 368, + 349, 350, 415, 352, 353, 368, 392, 371, 392, 422, + 377, 375, 413, 1026, 339, 1028, 429, 1199, 1031, 344, + 421, 346, 414, 424, 349, 350, 343, 352, 353, 415, + 948, 415, 950, 256, 402, 357, 422, 343, 422, 792, + 935, 794, 940, 429, 992, 256, 414, 368, 343, 967, + 954, 368, 374, 343, 972, 339, 377, 975, 269, 343, + 1242, 1189, 368, 1439, 830, 387, 377, 985, 392, 987, + 1305, 377, 990, 368, 1022, 286, 415, 368, 368, 370, + 846, 430, 377, 422, 368, 838, 1516, 377, 841, 343, + 357, 415, 306, 377, 792, 1043, 794, 863, 422, 313, + 294, 867, 1115, 1408, 1117, 430, 1119, 374, 1026, 368, + 1028, 1059, 306, 1031, 368, 1420, 339, 1065, 383, 384, + 387, 344, 256, 346, 516, 1043, 349, 350, 1383, 352, + 353, 387, 397, 398, 306, 1390, 308, 372, 1260, 1256, + 838, 313, 369, 841, 1048, 1040, 1328, 1382, 1578, 1228, + 377, 370, 1050, 325, 1052, 385, 1054, 1075, 1408, 357, + 1078, 374, 1436, 382, 377, 363, 375, 1383, 377, 935, + 1420, 386, 370, 382, 1390, 373, 374, 375, 373, 1453, + 375, 368, 369, 368, 369, 1615, 390, 953, 954, 387, + 377, 372, 377, 374, 401, 1106, 1402, 1115, 1472, 1117, + 1474, 1119, 594, 391, 1122, 339, 1219, 430, 1640, 1641, + 344, 373, 346, 375, 370, 349, 350, 369, 352, 353, + 369, 419, 419, 375, 373, 377, 375, 416, 377, 621, + 369, 1133, 256, 373, 373, 375, 375, 377, 377, 369, + 1253, 1189, 1160, 373, 371, 372, 1164, 377, 375, 395, + 396, 371, 372, 1019, 374, 375, 376, 1169, 371, 1177, + 1178, 1169, 375, 371, 1175, 373, 1019, 375, 371, 419, + 373, 1189, 375, 1190, 1040, 371, 371, 277, 1196, 375, + 375, 1199, 1048, 1228, 374, 1230, 1228, 371, 1230, 373, + 373, 368, 377, 1306, 377, 372, 430, 374, 375, 1211, + 377, 1219, 377, 1211, 371, 382, 256, 1225, 375, 369, + 370, 1256, 371, 1326, 1256, 265, 375, 267, 371, 375, + 270, 377, 375, 372, 1242, 275, 1528, 376, 1081, 279, + 419, 420, 421, 422, 382, 1253, 1254, 6, 288, 416, + 373, 368, 373, 371, 377, 295, 377, 375, 17, 1294, + 300, 371, 1294, 373, 304, 403, 404, 405, 406, 407, + 408, 409, 410, 411, 412, 256, 316, 373, 318, 1317, + 374, 377, 322, 368, 369, 1320, 1321, 256, 1320, 1321, + 330, 331, 294, 1081, 334, 354, 355, 337, 1306, 377, + 371, 60, 373, 1305, 377, 64, 20, 1305, 1343, 1317, + 792, 1343, 794, 387, 388, 389, 294, 371, 1326, 373, + 1328, 373, 373, 375, 375, 1319, 1169, 354, 355, 88, + 89, 375, 343, 377, 256, 1370, 1374, 371, 1370, 373, + 371, 373, 373, 265, 377, 267, 356, 375, 270, 377, + 109, 419, 375, 275, 377, 419, 838, 279, 1393, 841, + 1652, 1393, 375, 1206, 377, 802, 288, 804, 1211, 369, + 370, 419, 1215, 295, 88, 89, 1232, 370, 300, 419, + 1382, 1169, 304, 373, 1382, 1386, 415, 1381, 376, 415, + 416, 365, 366, 373, 316, 109, 318, 368, 369, 382, + 322, 1244, 161, 365, 366, 1399, 1400, 377, 330, 331, + 415, 416, 334, 417, 418, 337, 423, 424, 1206, 369, + 364, 375, 430, 1211, 373, 369, 373, 1215, 372, 294, + 374, 375, 294, 1427, 375, 373, 1430, 373, 382, 1447, + 375, 1297, 373, 1444, 203, 204, 256, 377, 372, 393, + 394, 256, 429, 429, 294, 294, 1244, 382, 373, 375, + 374, 1304, 1305, 1319, 376, 375, 374, 419, 373, 413, + 430, 1506, 375, 382, 1506, 375, 375, 421, 375, 424, + 424, 373, 375, 368, 375, 373, 430, 343, 1523, 203, + 204, 1523, 375, 294, 294, 375, 419, 419, 0, 371, + 419, 1536, 1537, 372, 1536, 1537, 376, 368, 1516, 268, + 256, 256, 375, 1514, 256, 256, 1304, 1305, 382, 280, + 1528, 256, 368, 373, 369, 1381, 343, 1383, 1563, 1564, + 371, 1563, 1564, 292, 1390, 294, 372, 1545, 375, 1382, + 0, 375, 377, 1399, 1400, 377, 1402, 306, 373, 382, + 371, 373, 1408, 373, 268, 314, 424, 373, 382, 1402, + 347, 320, 368, 382, 1420, 256, 325, 382, 256, 1425, + 1578, 1427, 369, 377, 1430, 373, 256, 373, 256, 347, + 294, 375, 371, 1439, 376, 1620, 372, 371, 1620, 348, + 349, 292, 306, 371, 1382, 354, 373, 348, 369, 1081, + 314, 339, 1640, 1641, 373, 1461, 320, 1615, 419, 375, + 372, 419, 348, 373, 256, 374, 369, 382, 368, 368, + 379, 377, 1623, 1624, 368, 356, 369, 375, 337, 1630, + 1631, 305, 1640, 1641, 348, 349, 373, 377, 372, 369, + 369, 373, 369, 419, 1652, 370, 372, 419, 368, 419, + 372, 372, 419, 377, 413, 414, 372, 374, 368, 339, + 374, 372, 343, 382, 344, 379, 346, 347, 348, 349, + 350, 351, 352, 353, 354, 355, 356, 372, 377, 382, + 370, 373, 441, 442, 373, 372, 256, 1169, 374, 369, + 375, 371, 374, 373, 377, 375, 376, 377, 375, 413, + 375, 419, 375, 373, 373, 371, 419, 419, 373, 377, + 377, 391, 419, 377, 373, 373, 369, 371, 373, 382, + 382, 369, 315, 263, 1206, 372, 372, 441, 442, 1211, + 94, 382, 369, 1215, 98, 99, 100, 101, 102, 103, + 104, 105, 373, 373, 377, 0, 0, 461, 368, 377, + 430, 369, 377, 0, 373, 257, 369, 516, 373, 261, + 474, 368, 1244, 373, 369, 371, 369, 419, 377, 373, + 272, 371, 368, 374, 419, 277, 419, 369, 373, 281, + 377, 369, 284, 373, 373, 369, 377, 368, 373, 369, + 377, 373, 369, 315, 296, 297, 256, 263, 377, 301, + 302, 261, 262, 377, 377, 307, 377, 309, 310, 311, + 312, 377, 377, 377, 377, 317, 377, 51, 12, 321, + 5, 323, 1304, 1305, 284, 1040, 585, 935, 1189, 0, + 1189, 333, 1393, 335, 336, 594, 338, 297, 298, 1343, + 342, 1398, 302, 1238, 1568, 305, 1531, 307, 1519, 309, + 310, 311, 312, 612, 1444, 1584, 1548, 317, 1514, 957, + 362, 321, 621, 952, 957, 325, 957, 369, 370, 771, + 1425, 585, 1358, 333, 1631, 1370, 336, 1445, 338, 339, + 1625, 1541, 1537, 343, 344, 1536, 346, 347, 348, 349, + 350, 351, 352, 353, 354, 355, 356, 357, 612, 1624, + 1382, 1294, 362, 363, 1474, 930, 320, 1297, 368, 369, + 370, 371, 372, 373, 374, 375, 376, 377, 414, 379, + 380, 981, 637, 383, 384, 385, 386, 387, 893, 1092, + 390, 391, 72, 261, 765, 395, 396, 397, 398, 399, + 400, 401, 402, 797, 308, 0, 344, 992, 425, 427, + 426, 430, 1195, 428, 414, 429, 284, 417, 585, 419, + 867, 421, 1272, 1381, 424, 1169, 161, 1154, 1077, 297, + 430, 1178, 731, 1102, 302, 1061, 1167, 341, 1245, 307, + 1169, 309, 310, 311, 312, 560, 1214, 351, 450, 317, + 996, 450, 1376, 321, 1254, 932, 925, 325, 757, -1, + -1, -1, -1, -1, -1, 333, -1, -1, 336, -1, + 338, -1, -1, 721, -1, -1, -1, 731, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 792, 362, 794, 0, -1, -1, -1, + -1, 369, -1, 757, -1, -1, 410, 411, 412, -1, + -1, 415, 416, 417, 418, 419, 420, 421, 422, 423, + 424, 425, 426, 427, 428, 429, 430, -1, -1, -1, + -1, 830, -1, -1, -1, -1, -1, -1, -1, 838, + -1, -1, 841, -1, -1, -1, 257, 846, -1, -1, + 261, 419, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 272, -1, -1, 863, -1, 277, -1, 867, -1, + 281, -1, -1, 284, -1, -1, 830, 256, -1, -1, + -1, -1, -1, -1, -1, 296, 297, -1, -1, -1, + 301, 302, 846, -1, -1, -1, 307, -1, 309, 310, + 311, 312, -1, -1, -1, -1, 317, -1, -1, 863, + 321, -1, 323, 867, -1, -1, -1, 0, -1, -1, + -1, -1, 333, -1, -1, 336, -1, 338, -1, -1, + -1, 342, -1, -1, -1, -1, 540, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 362, -1, -1, 953, 954, -1, 368, 369, 370, + 339, -1, 257, -1, -1, 344, 261, 346, 347, 348, + 349, 350, 351, 352, 353, 354, 355, 272, -1, -1, + -1, 935, 277, -1, -1, -1, 281, -1, -1, 284, + 369, -1, 371, -1, 373, -1, 375, 376, 377, 953, + 954, 296, 297, -1, -1, -1, 301, 302, -1, -1, + -1, -1, 307, 617, 309, 310, 311, 312, -1, -1, + 1019, -1, 317, -1, -1, -1, 321, -1, 323, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 333, -1, + 335, 336, -1, 338, -1, -1, -1, 342, -1, 1048, + -1, 430, -1, 257, -1, -1, -1, 261, -1, -1, + -1, 364, -1, 667, 668, 1019, 369, 362, 272, 372, + -1, 374, 375, 277, -1, 370, 680, 281, -1, -1, + 284, -1, 1081, -1, -1, -1, 1040, -1, -1, -1, + 393, 394, 296, 297, 1048, -1, -1, 301, 302, -1, + -1, -1, -1, 307, -1, 309, 310, 311, 312, -1, + 413, -1, -1, 317, -1, -1, -1, 321, 421, 323, + -1, 424, -1, -1, -1, -1, -1, 430, 256, 333, + -1, -1, 336, -1, 338, -1, -1, 265, 342, 267, + -1, -1, 270, -1, -1, -1, -1, 275, -1, -1, + -1, 279, -1, 0, -1, -1, -1, -1, 362, -1, + 288, -1, -1, -1, -1, 369, 370, 295, -1, -1, + 1169, -1, 300, 256, -1, -1, 304, -1, 261, 262, + -1, -1, -1, -1, -1, -1, -1, -1, 316, -1, + 318, -1, -1, -1, 322, -1, -1, 801, -1, -1, + -1, 284, 330, 331, -1, -1, 334, 1206, -1, 337, + -1, -1, 1211, -1, 297, 298, 1215, -1, -1, 302, + -1, -1, 305, -1, 307, -1, 309, 310, 311, 312, + -1, -1, -1, 1232, 317, -1, -1, -1, 321, -1, + -1, -1, 325, -1, -1, 1244, -1, -1, -1, -1, + 333, -1, -1, 336, -1, 338, 339, -1, -1, -1, + 343, 344, -1, 346, 347, 348, 349, 350, 351, 352, + 353, 354, 355, 356, -1, -1, -1, -1, 1232, 362, + 363, -1, -1, -1, -1, 368, 369, 370, 371, 372, + 373, 419, 375, 376, 377, -1, 379, 380, 1297, -1, + 383, 384, 385, 386, -1, 1304, 1305, 390, 391, -1, + -1, -1, 395, 396, 397, 398, 399, 400, 401, 402, + 1319, -1, 0, 265, -1, 267, -1, -1, 270, -1, + 272, 414, -1, 275, 417, -1, 419, 279, 421, -1, + -1, 424, -1, 1297, -1, -1, 288, 430, -1, -1, + -1, -1, -1, 295, -1, -1, -1, -1, 300, -1, + 302, -1, 304, -1, -1, 1319, -1, -1, -1, -1, + -1, -1, -1, -1, 316, -1, 318, 981, -1, -1, + 322, 323, 1381, 1382, 1383, -1, -1, -1, 330, 331, + -1, 1390, 334, -1, -1, 337, -1, -1, -1, -1, + 1399, 1400, -1, 1402, -1, -1, -1, -1, -1, 256, + -1, -1, -1, -1, 261, 262, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 1381, 1427, 1383, + -1, 1430, -1, -1, -1, -1, 1390, 284, -1, -1, + -1, -1, -1, -1, -1, 1399, 1400, -1, 1402, -1, + 297, 298, -1, -1, -1, 302, -1, -1, 305, -1, + 307, -1, 309, 310, 311, 312, -1, -1, -1, -1, + 317, -1, -1, 1427, 321, -1, 1430, 419, 325, -1, + -1, -1, -1, -1, -1, -1, 333, -1, -1, 336, + 0, 338, 339, -1, -1, -1, 343, 344, -1, 346, + 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, + -1, -1, -1, -1, -1, 362, 363, 364, -1, -1, + -1, 368, 369, -1, 371, 372, 373, 374, 375, 376, + 377, -1, 379, 380, -1, 382, 383, 384, 385, 386, + 387, 388, 389, 390, 391, -1, 393, 394, 395, 396, + 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, + 407, 408, 409, 410, 411, 412, 413, 414, -1, -1, + 417, -1, 419, -1, 421, -1, -1, 424, 256, 257, + -1, -1, -1, 430, -1, -1, 264, 265, 266, 267, + 268, -1, 270, 271, -1, 273, 274, 275, 276, 277, + 278, 279, 280, -1, -1, -1, -1, 285, -1, 287, + 288, 289, 290, 291, 292, -1, -1, 295, -1, -1, + 0, 299, 300, -1, 302, 303, 304, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 314, -1, 316, -1, + 318, 319, -1, -1, 322, -1, 324, 325, 326, 327, + 328, 329, 330, 331, 332, 333, 334, 335, -1, 337, + -1, -1, 340, 341, -1, -1, 344, 345, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 359, 360, 361, 362, 363, -1, -1, -1, -1, + 368, 369, -1, -1, 372, -1, -1, -1, -1, 377, + 378, 379, 380, 381, -1, -1, -1, 385, -1, 387, + -1, -1, -1, -1, -1, 393, 394, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 418, 419, 420, 421, -1, 423, 256, 257, -1, -1, + -1, 0, 430, -1, 264, 265, 266, 267, 268, -1, + 270, 271, -1, 273, 274, 275, 276, 277, 278, 279, + -1, -1, -1, -1, -1, 285, -1, 287, 288, 289, + 290, 291, 292, -1, -1, 295, -1, -1, -1, 299, + 300, -1, 302, 303, 304, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 314, -1, 316, -1, 318, 319, + -1, -1, 322, -1, 324, 325, 326, 327, 328, 329, + 330, 331, 332, 333, 334, 335, -1, 337, -1, -1, + 340, 341, -1, -1, 344, 345, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 359, + 360, 361, 362, 363, -1, -1, 0, -1, 368, 369, + -1, -1, 372, -1, -1, -1, -1, 377, 378, 379, + 380, 381, -1, -1, -1, 385, 256, 387, -1, -1, + -1, 261, 262, 393, 394, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 284, -1, -1, -1, 418, 419, + 420, 421, -1, 423, -1, -1, -1, 297, 298, -1, + 430, -1, 302, -1, -1, 305, -1, 307, -1, 309, + 310, 311, 312, -1, -1, -1, -1, 317, -1, -1, + -1, 321, -1, -1, -1, 325, -1, -1, -1, -1, + -1, -1, -1, 333, -1, -1, 336, -1, 338, 339, + -1, 0, -1, 343, 344, -1, 346, 347, 348, 349, + 350, 351, 352, 353, 354, 355, 356, -1, -1, -1, + -1, -1, 362, 363, -1, -1, -1, -1, 368, 369, + 370, 371, 372, 373, -1, 375, 376, 377, -1, 379, + 380, -1, -1, 383, 384, 385, 386, 256, -1, -1, + 390, 391, 261, 262, -1, 395, 396, 397, 398, 399, + 400, 401, 402, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 414, 284, -1, 417, -1, 419, + -1, 421, -1, -1, 424, -1, -1, -1, 297, 298, + 430, -1, -1, 302, -1, -1, 305, -1, 307, -1, + 309, 310, 311, 312, -1, -1, -1, -1, 317, -1, + -1, -1, 321, -1, -1, -1, 325, -1, -1, -1, + -1, -1, -1, -1, 333, -1, -1, 336, -1, 338, + 339, -1, -1, -1, 343, 344, -1, 346, 347, 348, + 349, 350, 351, 352, 353, 354, 355, 356, -1, -1, + -1, -1, 256, 362, 363, -1, -1, -1, 262, 368, + 369, -1, 371, 372, 373, -1, 375, 376, 377, -1, + 379, 380, 0, -1, 383, 384, 385, 386, -1, -1, + -1, 390, 391, -1, -1, -1, 395, 396, 397, 398, + 399, 400, 401, 402, 298, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 414, -1, -1, 417, -1, + 419, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 430, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 339, -1, -1, -1, 343, + 344, -1, 346, 347, 348, 349, 350, 351, 352, 353, + 354, 355, 356, 357, -1, -1, -1, 256, -1, 363, + -1, -1, -1, 262, 368, 369, 370, 371, 372, 373, + 374, 375, 376, 377, -1, 379, 380, -1, -1, 383, + 384, 385, 386, 387, -1, -1, 390, 391, 0, -1, + -1, 395, 396, 397, 398, 399, 400, 401, 402, 298, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 414, -1, -1, 417, -1, 419, -1, 421, -1, -1, + 424, -1, -1, -1, -1, -1, 430, -1, -1, -1, + -1, 0, -1, -1, -1, -1, -1, -1, -1, -1, + 339, -1, -1, -1, 343, 344, -1, 346, 347, 348, + 349, 350, 351, 352, 353, 354, 355, 356, -1, -1, + -1, -1, -1, -1, 363, -1, -1, -1, -1, 368, + 369, 370, 371, 372, 373, -1, 375, 376, 377, -1, + 379, 380, -1, -1, 383, 384, 385, 386, -1, -1, + -1, 390, 391, -1, -1, -1, 395, 396, 397, 398, + 399, 400, 401, 402, -1, -1, -1, -1, -1, -1, + -1, 0, -1, -1, -1, 414, -1, -1, 417, -1, + 419, -1, 421, -1, -1, 424, -1, -1, 256, 257, + -1, 430, -1, 261, -1, -1, -1, 265, -1, 267, + -1, -1, 270, -1, 272, 273, -1, 275, -1, 277, + -1, 279, -1, 281, 282, 283, 284, -1, 0, 287, + 288, -1, -1, -1, -1, 293, -1, 295, 296, 297, + -1, -1, 300, 301, 302, -1, 304, -1, -1, 307, + -1, 309, 310, 311, 312, -1, -1, -1, 316, 317, + 318, -1, -1, 321, 322, 323, -1, -1, -1, -1, + -1, -1, 330, 331, -1, 333, 334, -1, 336, 337, + 338, -1, -1, -1, 342, -1, -1, -1, -1, -1, + -1, 0, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 362, -1, -1, -1, -1, -1, + 368, 369, 370, -1, -1, 257, -1, -1, -1, 261, + 378, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 272, -1, -1, -1, 0, 277, -1, -1, -1, 281, + -1, -1, 284, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 296, 297, -1, -1, 257, 301, + 302, 419, 261, -1, -1, 307, -1, 309, 310, 311, + 312, -1, -1, 272, -1, 317, -1, 0, 277, 321, + -1, 323, 281, -1, -1, 284, -1, -1, -1, -1, + -1, 333, -1, -1, 336, -1, 338, 296, 297, -1, + 342, -1, 301, 302, -1, -1, -1, -1, 307, -1, + 309, 310, 311, 312, -1, -1, -1, -1, 317, -1, + 362, -1, 321, -1, 323, -1, -1, 369, 370, -1, + -1, -1, -1, -1, 333, -1, -1, 336, 257, 338, + 0, -1, 261, 342, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 272, -1, -1, -1, -1, 277, -1, + -1, -1, 281, 362, -1, 284, -1, -1, -1, -1, + -1, 370, -1, -1, -1, -1, -1, 296, 297, -1, + -1, -1, 301, 302, -1, 257, -1, -1, 307, 261, + 309, 310, 311, 312, -1, -1, -1, -1, 317, -1, + 272, -1, 321, -1, 323, 277, -1, -1, -1, 281, + -1, -1, 284, -1, 333, -1, -1, 336, -1, 338, + -1, -1, -1, 342, 296, 297, -1, -1, -1, 301, + 302, -1, -1, -1, -1, 307, -1, 309, 310, 311, + 312, -1, -1, 362, -1, 317, -1, -1, 257, 321, + -1, 323, 261, -1, -1, -1, -1, -1, -1, -1, + -1, 333, -1, 272, 336, -1, 338, -1, 277, -1, + 342, -1, 281, -1, -1, 284, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 296, 297, -1, + 362, 257, 301, 302, -1, 261, -1, -1, 307, -1, + 309, 310, 311, 312, -1, -1, 272, -1, 317, -1, + -1, 277, 321, -1, 323, 281, -1, -1, 284, -1, + -1, -1, -1, -1, 333, -1, -1, 336, -1, 338, + 296, 297, -1, 342, 257, 301, 302, -1, 261, -1, + -1, 307, -1, 309, 310, 311, 312, -1, -1, 272, + -1, 317, -1, 362, 277, 321, -1, 323, 281, -1, + -1, 284, -1, -1, -1, -1, -1, 333, -1, -1, + 336, -1, 338, 296, 297, -1, 342, -1, 301, 302, + -1, -1, -1, -1, 307, -1, 309, 310, 311, 312, + -1, -1, -1, -1, 317, -1, 362, 257, 321, -1, + 323, 261, -1, -1, -1, -1, -1, -1, -1, -1, + 333, -1, 272, 336, -1, 338, -1, 277, -1, 342, + -1, 281, -1, -1, 284, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 296, 297, -1, 362, + -1, 301, 302, -1, -1, -1, -1, 307, -1, 309, + 310, 311, 312, -1, -1, -1, -1, 317, -1, -1, + -1, 321, -1, 323, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 333, -1, 256, 336, -1, 338, -1, + -1, -1, 342, 264, 265, 266, 267, -1, -1, 270, + 271, -1, 273, 274, 275, 276, 277, 278, 279, -1, + -1, -1, 362, -1, 285, -1, 287, 288, 289, 290, + 291, 292, -1, -1, 295, -1, -1, -1, 299, 300, + -1, 302, 303, 304, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 314, -1, 316, -1, 318, 319, -1, + -1, 322, -1, 324, 325, 326, 327, 328, 329, 330, + 331, 332, 333, 334, 335, -1, 337, -1, -1, 340, + 341, -1, -1, 344, 345, 256, -1, -1, -1, 261, + -1, 262, -1, -1, -1, -1, -1, -1, 359, 360, + 361, 362, 363, -1, -1, -1, -1, 368, -1, -1, + -1, 372, 284, -1, -1, -1, 377, 378, 379, 380, + 381, -1, -1, -1, 385, 297, 387, 298, -1, -1, + 302, -1, 393, 394, -1, 307, -1, 309, 310, 311, + 312, -1, -1, -1, -1, 317, -1, -1, -1, 321, + -1, -1, -1, 325, -1, -1, -1, 418, 419, 420, + 421, 333, -1, -1, 336, -1, 338, -1, 339, 430, + -1, -1, -1, 344, 256, 346, 347, 348, 349, 350, + 351, 352, 353, 354, 355, 356, 357, -1, -1, -1, + 362, -1, 363, 364, -1, -1, -1, -1, 369, 370, + 371, 372, 373, 374, 375, 376, 377, -1, 379, 380, + -1, 382, 383, 384, 385, 386, 387, 388, 389, 390, + 391, -1, 393, 394, 395, 396, 397, 398, 399, 400, + 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, + 411, 412, 413, 414, 415, -1, 256, 419, 419, -1, + 421, 422, 262, 424, -1, -1, -1, 339, -1, 430, + -1, -1, 344, -1, 346, 347, 348, 349, 350, 351, + 352, 353, 354, 355, 356, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 369, 298, 371, + -1, 373, -1, 375, 376, 377, -1, -1, -1, -1, + -1, 383, 384, 385, 386, -1, -1, -1, 390, 391, + -1, -1, -1, 395, 396, 397, 398, 399, 400, 401, + 402, -1, -1, -1, -1, -1, -1, -1, -1, 339, + -1, -1, 414, -1, 344, -1, 346, 347, 348, 349, + 350, 351, 352, 353, 354, 355, 356, 357, 430, -1, + -1, -1, -1, 363, 364, -1, -1, -1, -1, 369, + 370, 371, 372, 373, 374, 375, 376, 377, -1, 379, + 380, -1, 382, 383, 384, 385, 386, 387, 388, 389, + 390, 391, -1, 393, 394, 395, 396, 397, 398, 399, + 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, + 410, 411, 412, 413, 414, 415, 256, 256, -1, 419, + -1, 421, 262, -1, 424, -1, 265, -1, 267, -1, + 430, 270, -1, -1, -1, -1, 275, -1, -1, -1, + 279, -1, -1, -1, -1, -1, -1, -1, -1, 288, + -1, -1, -1, -1, -1, -1, 295, -1, 298, -1, + -1, 300, -1, -1, -1, 304, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 316, -1, 318, + -1, -1, -1, 322, -1, -1, -1, -1, -1, -1, + -1, 330, 331, -1, -1, 334, -1, -1, 337, 339, + -1, -1, -1, -1, 344, -1, 346, 347, 348, 349, + 350, 351, 352, 353, 354, 355, 356, 357, -1, -1, + -1, -1, -1, 363, 364, -1, -1, -1, -1, 369, + 370, 371, 372, 373, 374, 375, 376, 377, -1, 379, + 380, -1, 382, 383, 384, 385, 386, 387, 388, 389, + 390, 391, -1, 393, 394, 395, 396, 397, 398, 399, + 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, + 410, 411, 412, 413, 414, -1, 256, 256, -1, 419, + 419, 421, 262, -1, 424, -1, 265, -1, 267, -1, + 430, 270, -1, -1, -1, -1, 275, -1, -1, -1, + 279, -1, -1, -1, -1, -1, -1, -1, -1, 288, + -1, -1, -1, -1, -1, -1, 295, -1, 298, -1, + -1, 300, -1, -1, -1, 304, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 316, -1, 318, + -1, -1, -1, 322, -1, -1, -1, -1, -1, -1, + -1, 330, 331, -1, -1, 334, -1, -1, 337, 339, + -1, -1, -1, -1, 344, -1, 346, 347, 348, 349, + 350, 351, 352, 353, 354, 355, 356, -1, -1, -1, + -1, -1, -1, 363, 364, -1, -1, -1, 368, 369, + 370, 371, 372, 373, 374, 375, 376, 377, -1, 379, + 380, -1, 382, 383, 384, 385, 386, 387, 388, 389, + 390, 391, -1, 393, 394, 395, 396, 397, 398, 399, + 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, + 410, 411, 412, 413, 414, -1, 256, -1, -1, 419, + 419, 421, 262, -1, -1, 261, -1, -1, -1, -1, + 430, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 284, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 298, -1, + -1, 297, -1, -1, -1, -1, 302, -1, -1, -1, + -1, 307, -1, 309, 310, 311, 312, -1, -1, -1, + -1, 317, -1, -1, -1, 321, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 333, -1, 339, + 336, -1, 338, -1, 344, -1, 346, 347, 348, 349, + 350, 351, 352, 353, 354, 355, 356, -1, -1, -1, + -1, -1, -1, 363, 364, -1, 362, -1, 368, 369, + -1, 371, 372, 373, 374, 375, 376, 377, -1, 379, + 380, -1, 382, 383, 384, 385, 386, 387, 388, 389, + 390, 391, -1, 393, 394, 395, 396, 397, 398, 399, + 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, + 410, 411, 412, 413, 414, -1, 256, -1, -1, 419, + 261, 421, 262, 419, 424, -1, -1, -1, -1, -1, + 430, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 284, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 297, -1, 298, -1, + -1, 302, -1, -1, 305, -1, 307, -1, 309, 310, + 311, 312, -1, -1, -1, -1, 317, -1, -1, -1, + 321, -1, -1, -1, 325, -1, -1, -1, -1, -1, + -1, -1, 333, -1, -1, 336, -1, 338, -1, 339, + -1, -1, -1, -1, 344, -1, 346, 347, 348, 349, + 350, 351, 352, 353, 354, 355, 356, -1, -1, -1, + -1, 362, -1, 363, 364, -1, -1, -1, 369, 369, + -1, 371, 372, 373, 374, 375, 376, 377, -1, 379, + 380, -1, 382, 383, 384, 385, 386, 387, 388, 389, + 390, 391, -1, 393, 394, 395, 396, 397, 398, 399, + 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, + 410, 411, 412, 413, 414, -1, 256, -1, 419, 419, + 261, 421, 262, -1, 424, -1, -1, -1, -1, -1, + 430, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 284, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 297, -1, 298, -1, + -1, 302, -1, -1, 305, -1, 307, -1, 309, 310, + 311, 312, -1, -1, -1, -1, 317, -1, -1, -1, + 321, -1, -1, -1, 325, -1, -1, -1, -1, -1, + -1, -1, 333, -1, -1, 336, -1, 338, -1, 339, + -1, -1, -1, -1, 344, -1, 346, 347, 348, 349, + 350, 351, 352, 353, 354, 355, 356, -1, -1, -1, + -1, 362, -1, 363, 364, -1, -1, -1, 369, 369, + -1, 371, 372, 373, 374, 375, 376, 377, -1, 379, + 380, -1, 382, 383, 384, 385, 386, 387, 388, 389, + 390, 391, -1, 393, 394, 395, 396, 397, 398, 399, + 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, + 410, 411, 412, 413, 414, -1, 256, -1, 419, 419, + -1, 421, 262, -1, 424, -1, 265, -1, 267, -1, + 430, 270, -1, 272, 273, -1, 275, -1, 277, -1, + 279, -1, 281, 282, 283, -1, -1, -1, 287, 288, + -1, -1, -1, -1, 293, -1, 295, 296, 298, -1, + -1, 300, -1, -1, -1, 304, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 316, -1, 318, + -1, -1, -1, 322, 323, -1, -1, -1, -1, -1, + -1, 330, 331, -1, -1, 334, -1, -1, 337, 339, + -1, -1, -1, 342, 344, -1, 346, 347, 348, 349, + 350, 351, 352, 353, 354, 355, 356, -1, -1, -1, + -1, -1, -1, 363, 364, -1, -1, -1, -1, 369, + -1, 371, 372, 373, 374, 375, 376, 377, -1, 379, + 380, -1, 382, 383, 384, 385, 386, 387, 388, 389, + 390, 391, -1, 393, 394, 395, 396, 397, 398, 399, + 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, + 410, 411, 412, 413, 414, -1, 256, -1, -1, 419, + 419, 421, 262, -1, 424, -1, 265, -1, 267, -1, + 430, 270, -1, -1, 273, -1, 275, -1, 277, -1, + 279, -1, 281, 282, 283, -1, -1, -1, 287, 288, + -1, -1, -1, -1, 293, -1, 295, -1, 298, -1, + -1, 300, -1, -1, -1, 304, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 316, -1, 318, + 256, -1, -1, 322, -1, -1, 262, -1, -1, -1, + 266, 330, 331, -1, -1, 334, -1, -1, 337, 339, + -1, -1, -1, 342, 344, -1, 346, 347, 348, 349, + 350, 351, 352, 353, 354, 355, 356, -1, -1, -1, + -1, -1, 298, 363, -1, -1, -1, -1, -1, 369, + -1, 371, -1, 373, -1, 375, 376, 377, 314, 379, + 380, -1, 382, 383, 384, 385, 386, 387, 388, 389, + 390, 391, -1, -1, -1, 395, 396, 397, 398, 399, + 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, + 410, 411, 412, -1, 414, 256, -1, -1, -1, 419, + 419, 357, -1, -1, -1, -1, -1, 363, 364, -1, + 430, -1, -1, 369, 370, 371, 372, 373, 374, 375, + 376, 377, -1, 379, 380, 285, 382, 383, 384, 385, + 386, 387, 388, 389, 390, 391, -1, 393, 394, 395, + 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, + 406, 407, 408, 409, 410, 411, 412, 413, 414, -1, + 256, -1, -1, 419, -1, 421, 262, 327, 424, -1, + -1, -1, -1, -1, 430, -1, -1, -1, 339, -1, + -1, -1, -1, 344, -1, 346, 347, 348, 349, 350, + 351, 352, 353, 354, 355, 356, -1, -1, -1, -1, + -1, -1, 298, -1, -1, -1, -1, -1, 369, -1, + 371, -1, 373, -1, 375, 376, 377, -1, 378, 379, + 380, 381, -1, 383, 384, 385, 386, 387, 388, 389, + 390, -1, -1, 393, 394, 395, 396, 397, 398, 399, + 400, -1, -1, 339, -1, -1, -1, -1, 344, -1, + 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, + 356, -1, -1, -1, -1, 256, -1, 363, 364, 430, + -1, 262, -1, 369, 370, 371, 372, 373, 374, 375, + 376, 377, -1, 379, 380, -1, -1, 383, 384, 385, + 386, -1, -1, -1, 390, 391, -1, 393, 394, 395, + 396, 397, 398, 399, 400, 401, 402, 298, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 413, 414, -1, + -1, -1, -1, 419, 256, 421, -1, -1, 424, -1, + 262, -1, -1, -1, 430, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 339, -1, + -1, -1, -1, 344, -1, 346, 347, 348, 349, 350, + 351, 352, 353, 354, 355, 356, 298, -1, -1, -1, + -1, -1, 363, 364, -1, -1, -1, -1, 369, -1, + 371, 372, 373, 374, 375, 376, 377, -1, 379, 380, + -1, -1, 383, 384, 385, 386, -1, -1, -1, 390, + 391, -1, 393, 394, 395, 396, 397, 398, 399, 400, + 401, 402, -1, 256, -1, -1, -1, -1, -1, 262, + -1, -1, 413, 414, -1, -1, -1, -1, 419, -1, + 421, 363, 364, 424, -1, -1, -1, 369, 370, 430, + 372, 373, 374, 375, -1, 377, -1, 379, 380, -1, + 382, 383, 384, 385, 386, 298, 388, 389, 390, 391, + -1, 393, 394, 395, 396, 397, 398, 399, 400, 401, + 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, + 412, 413, 414, -1, -1, -1, -1, 419, -1, 421, + -1, -1, 424, -1, -1, -1, 339, -1, 430, -1, + -1, 344, -1, 346, 347, 348, 349, 350, 351, 352, + 353, 354, 355, 356, -1, -1, -1, -1, 256, -1, + 363, -1, -1, -1, 262, -1, 369, 370, 371, -1, + 373, -1, 375, 376, 377, -1, 379, 380, -1, -1, + 383, 384, 385, 386, -1, -1, -1, 390, 391, -1, + -1, -1, 395, 396, 397, 398, 399, 400, 401, 402, + 298, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 414, -1, -1, -1, -1, 419, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 430, -1, -1, + 256, -1, -1, -1, -1, -1, 262, -1, -1, -1, + -1, 339, -1, -1, -1, -1, 344, -1, 346, 347, + 348, 349, 350, 351, 352, 353, 354, 355, 356, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 369, 298, 371, -1, 373, -1, 375, 376, 377, + -1, 379, 380, -1, -1, 383, 384, 385, 386, 387, + 388, 389, 390, 391, -1, -1, -1, 395, 396, 397, + 398, 399, 400, 401, 402, -1, -1, -1, -1, -1, + -1, -1, -1, 339, -1, -1, 414, -1, 344, -1, + 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, + 356, -1, 430, -1, -1, 256, -1, 363, -1, -1, + -1, 262, -1, 369, -1, 371, -1, 373, -1, 375, + 376, 377, -1, 379, 380, -1, -1, 383, 384, 385, + 386, -1, -1, -1, 390, 391, -1, -1, -1, 395, + 396, 397, 398, 399, 400, 401, 402, 298, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 414, -1, + -1, -1, -1, 419, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 430, -1, -1, 256, -1, -1, + -1, -1, -1, 262, -1, -1, -1, -1, 339, -1, + -1, -1, -1, 344, -1, 346, 347, 348, 349, 350, + 351, 352, 353, 354, 355, 356, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 369, 298, + 371, -1, 373, -1, 375, 376, 377, -1, 379, 380, + -1, -1, 383, 384, 385, 386, -1, -1, -1, 390, + 391, -1, -1, -1, 395, 396, 397, 398, 399, 400, + 401, 402, -1, -1, -1, -1, -1, -1, -1, -1, + 339, -1, -1, 414, -1, 344, -1, 346, 347, 348, + 349, 350, 351, 352, 353, 354, 355, 356, -1, 430, + -1, -1, 256, -1, -1, -1, -1, -1, 262, -1, + 369, -1, 371, -1, 373, -1, 375, 376, 377, -1, + 379, 380, -1, -1, 383, 384, 385, 386, -1, -1, + -1, 390, 391, -1, -1, -1, 395, 396, 397, 398, + 399, 400, 401, 402, 298, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 414, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 430, -1, -1, 256, -1, -1, -1, -1, -1, + 262, -1, -1, -1, -1, 339, -1, -1, -1, -1, + 344, -1, 346, 347, 348, 349, 350, 351, 352, 353, + 354, 355, 356, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 369, 298, 371, -1, 373, + -1, 375, 376, 377, -1, 379, 380, -1, -1, 383, + 384, 385, 386, -1, -1, -1, 390, 391, -1, -1, + -1, 395, 396, 397, 398, 399, 400, 401, 402, -1, + -1, -1, -1, -1, -1, -1, -1, 339, -1, -1, + 414, -1, 344, -1, 346, 347, 348, 349, 350, 351, + 352, 353, 354, 355, 356, -1, 430, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 369, -1, 371, + -1, 373, -1, 375, 376, 377, -1, 379, 380, -1, + -1, 383, 384, 385, 386, -1, -1, -1, 390, 391, + -1, -1, -1, 395, 396, 397, 398, 399, 400, 401, + 402, -1, 256, -1, 256, -1, -1, -1, -1, -1, + 264, 265, 414, 267, -1, -1, 270, 271, -1, -1, + -1, 275, 276, 277, -1, 279, -1, -1, 430, -1, + -1, 285, -1, -1, 288, -1, -1, -1, -1, -1, + -1, 295, -1, -1, -1, -1, 300, -1, 302, 303, + 304, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 316, -1, 318, 319, -1, -1, 322, -1, + -1, 325, -1, 327, -1, 329, 330, 331, 332, -1, + 334, -1, -1, -1, -1, -1, -1, 339, -1, 256, + -1, -1, 344, -1, 346, 347, 348, 349, 350, 351, + 352, 353, 354, 355, 356, 359, 360, 361, 362, 363, + -1, -1, -1, -1, -1, -1, -1, 369, 372, 371, + -1, 373, -1, 375, 376, 377, -1, -1, -1, -1, + -1, 383, 384, 385, 386, -1, -1, -1, 390, 391, + -1, -1, -1, 395, 396, 397, 398, 399, 400, 401, + 402, -1, -1, -1, -1, -1, -1, -1, 256, -1, + -1, -1, 414, -1, 418, 419, -1, -1, -1, -1, + -1, -1, 339, -1, -1, 429, 430, 344, 430, 346, + 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 369, -1, 371, -1, 373, -1, 375, 376, + 377, -1, -1, -1, -1, -1, 383, 384, 385, 386, + -1, -1, -1, 390, 391, -1, -1, 256, 395, 396, + 397, 398, 399, 400, 401, 402, -1, -1, -1, -1, + -1, 339, -1, -1, -1, -1, 344, 414, 346, 347, + 348, 349, 350, 351, 352, 353, 354, 355, 356, -1, + -1, -1, -1, 430, -1, -1, -1, -1, -1, -1, + -1, 369, -1, 371, -1, 373, -1, 375, 376, 377, + -1, -1, -1, -1, -1, 383, 384, 385, 386, -1, + -1, -1, 390, 391, -1, -1, 256, -1, -1, 397, + 398, 399, 400, 401, 402, -1, -1, -1, -1, -1, + 339, -1, -1, -1, -1, 344, 414, 346, 347, 348, + 349, 350, 351, 352, 353, 354, 355, 356, -1, -1, + -1, -1, 430, -1, -1, -1, -1, -1, -1, -1, + 369, -1, 371, -1, 373, -1, 375, 376, 377, -1, + -1, -1, -1, -1, 383, 384, 385, 386, -1, -1, + -1, 390, 391, -1, -1, 256, -1, -1, 397, 398, + 399, 400, 401, 402, -1, -1, -1, -1, -1, 339, + -1, -1, -1, -1, 344, 414, 346, 347, 348, 349, + 350, 351, 352, 353, 354, 355, 356, -1, -1, -1, + -1, 430, -1, -1, -1, -1, -1, -1, -1, 369, + -1, 371, -1, 373, -1, 375, 376, 377, -1, -1, + -1, -1, -1, 383, 384, 385, 386, -1, -1, -1, + 390, 391, -1, -1, 256, -1, -1, 397, 398, 399, + 400, 401, 402, -1, -1, -1, -1, -1, 339, -1, + -1, -1, -1, 344, 414, 346, 347, 348, 349, 350, + 351, 352, 353, 354, 355, 356, -1, -1, -1, -1, + 430, -1, -1, -1, -1, -1, -1, -1, 369, -1, + 371, -1, 373, -1, 375, 376, 377, -1, -1, -1, + -1, -1, 383, 384, 385, 386, 256, -1, -1, 390, + 391, -1, 262, -1, -1, -1, 397, 398, 399, 400, + 401, 402, -1, -1, -1, -1, -1, 339, -1, -1, + -1, -1, 344, 414, 346, 347, 348, 349, 350, 351, + 352, 353, 354, 355, 356, -1, -1, -1, 298, 430, + -1, -1, -1, -1, -1, -1, -1, 369, -1, 371, + -1, 373, -1, 375, 376, 377, -1, -1, -1, -1, + -1, 383, 384, 385, 386, -1, -1, -1, 390, 391, + -1, -1, -1, -1, -1, 397, 398, 399, 400, 401, + 402, -1, -1, 256, -1, -1, -1, -1, -1, -1, + -1, -1, 414, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 364, -1, -1, -1, 430, 369, + -1, -1, 372, -1, 374, 375, -1, -1, -1, 379, + 380, -1, -1, 383, 384, 385, 386, 387, 388, 389, + 390, 391, -1, 393, 394, 395, 396, 397, 398, 399, + 400, 401, 402, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 256, 413, 414, -1, -1, -1, -1, -1, + -1, 421, -1, -1, 424, -1, 339, -1, -1, -1, + 430, 344, -1, 346, 347, 348, 349, 350, 351, 352, + 353, 354, 355, 356, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 369, -1, 371, -1, + 373, -1, 375, 376, 377, -1, -1, -1, -1, -1, + -1, -1, 385, 386, -1, -1, -1, 390, 391, -1, + -1, 256, -1, -1, -1, -1, 399, 400, 401, 402, + -1, -1, -1, -1, -1, 339, -1, -1, -1, -1, + 344, 414, 346, 347, 348, 349, 350, 351, 352, 353, + 354, 355, 356, -1, -1, -1, -1, 430, -1, -1, + -1, -1, -1, -1, -1, 369, -1, 371, -1, 373, + -1, 375, 376, 377, -1, -1, -1, -1, -1, -1, + -1, 385, 386, -1, -1, -1, 390, 391, -1, -1, + 256, -1, -1, -1, -1, 399, 400, 401, 402, -1, + -1, -1, -1, -1, 339, -1, -1, -1, -1, 344, + 414, 346, 347, 348, 349, 350, 351, 352, 353, 354, + 355, 356, -1, -1, -1, -1, 430, -1, -1, -1, + -1, -1, -1, -1, 369, -1, 371, -1, 373, -1, + 375, 376, 377, -1, -1, -1, -1, -1, -1, -1, + 385, 386, -1, -1, -1, 390, 391, -1, -1, 256, + -1, -1, -1, -1, 399, 400, 401, 402, -1, -1, + -1, -1, -1, 339, -1, -1, -1, -1, 344, 414, + 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, + 356, -1, -1, -1, -1, 430, -1, -1, -1, -1, + -1, -1, -1, 369, -1, 371, -1, 373, -1, 375, + 376, 377, -1, -1, -1, -1, -1, -1, -1, 385, + 386, -1, -1, -1, 390, 391, -1, -1, 256, -1, + -1, -1, -1, -1, -1, 401, 402, -1, -1, -1, + -1, -1, 339, -1, -1, -1, -1, 344, 414, 346, + 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, + -1, -1, -1, -1, 430, -1, -1, -1, -1, -1, + -1, -1, 369, -1, 371, -1, 373, -1, 375, 376, + 377, -1, -1, -1, -1, -1, -1, -1, 385, 386, + -1, -1, -1, 390, 391, -1, -1, 256, -1, -1, + -1, -1, -1, -1, 401, 402, -1, -1, -1, -1, + -1, 339, -1, -1, -1, -1, 344, 414, 346, 347, + 348, 349, 350, 351, 352, 353, 354, 355, 356, -1, + -1, -1, -1, 430, -1, -1, -1, -1, -1, -1, + -1, 369, -1, 371, -1, 373, -1, 375, 376, 377, + -1, -1, -1, -1, -1, -1, -1, -1, 386, -1, + -1, -1, 390, 391, -1, -1, 256, -1, -1, -1, + -1, -1, -1, 401, 402, -1, -1, -1, -1, -1, + 339, -1, -1, -1, -1, 344, 414, 346, 347, 348, + 349, 350, 351, 352, 353, 354, 355, 356, -1, -1, + -1, -1, 430, -1, -1, -1, -1, -1, -1, -1, + 369, -1, 371, -1, 373, -1, 375, 376, 377, -1, + -1, -1, -1, -1, -1, -1, -1, 386, -1, -1, + -1, 390, 391, -1, -1, 256, -1, -1, -1, -1, + -1, -1, 401, 402, -1, -1, -1, -1, -1, 339, + -1, -1, -1, -1, 344, 414, 346, 347, 348, 349, + 350, 351, 352, 353, 354, 355, 356, -1, -1, -1, + -1, 430, -1, -1, -1, -1, -1, -1, -1, 369, + -1, 371, -1, 373, -1, 375, 376, 377, -1, -1, + -1, -1, -1, -1, -1, -1, 386, -1, -1, -1, + -1, 391, -1, -1, 256, -1, -1, -1, -1, -1, + -1, 401, 402, -1, -1, -1, -1, -1, 339, -1, + -1, -1, -1, 344, 414, 346, 347, 348, 349, 350, + 351, 352, 353, 354, 355, 356, -1, -1, -1, -1, + 430, -1, -1, -1, -1, -1, -1, -1, 369, -1, + 371, -1, 373, -1, 375, 376, 377, -1, -1, -1, + -1, -1, -1, -1, -1, 386, -1, -1, -1, -1, + 391, -1, -1, 256, -1, -1, -1, -1, -1, -1, + 401, 402, -1, -1, -1, -1, -1, 339, -1, -1, + -1, -1, 344, 414, 346, 347, 348, 349, 350, 351, + 352, 353, 354, 355, 356, -1, -1, -1, -1, 430, + -1, -1, -1, -1, -1, -1, -1, 369, -1, 371, + -1, 373, -1, 375, 376, 377, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 391, + -1, -1, 256, -1, -1, -1, -1, -1, -1, 401, + 402, -1, -1, -1, -1, -1, 339, -1, -1, -1, + -1, 344, 414, 346, 347, 348, 349, 350, 351, 352, + 353, 354, 355, 356, -1, -1, -1, -1, 430, -1, + -1, -1, -1, -1, -1, -1, 369, -1, 371, -1, + 373, -1, 375, 376, 377, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 391, -1, + -1, 256, -1, -1, -1, -1, -1, -1, 401, 402, + -1, -1, -1, -1, -1, 339, -1, -1, -1, -1, + 344, 414, 346, 347, 348, 349, 350, 351, 352, 353, + 354, 355, 356, -1, -1, -1, -1, 430, -1, -1, + -1, -1, -1, -1, -1, 369, -1, 371, -1, 373, + -1, 375, 376, 377, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 391, -1, -1, + -1, 262, -1, -1, -1, 266, -1, -1, 402, -1, + -1, -1, -1, -1, 339, -1, -1, -1, -1, 344, + 414, 346, 347, 348, 349, 350, 351, 352, 353, 354, + 355, 356, -1, -1, -1, -1, 430, 298, -1, -1, + -1, -1, -1, -1, 369, -1, 371, -1, 373, -1, + 375, 376, 377, 314, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 391, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 402, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 414, + -1, -1, -1, -1, -1, -1, 357, -1, -1, -1, + -1, -1, 363, 364, -1, 430, -1, -1, 369, 370, + -1, 372, -1, 374, -1, 376, 377, -1, 379, 380, + -1, 382, 383, 384, 385, 386, 387, 388, 389, 390, + 391, -1, 393, 394, 395, 396, 397, 398, 399, 400, + 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, + 411, 412, 413, 414, -1, -1, 256, -1, 419, -1, + 421, -1, -1, 424, 264, 265, 266, 267, 268, 430, + 270, 271, -1, 273, 274, 275, 276, 277, 278, 279, + -1, -1, -1, -1, -1, 285, -1, 287, 288, 289, + 290, 291, 292, -1, -1, 295, -1, -1, -1, 299, + 300, -1, 302, 303, 304, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 314, -1, 316, -1, 318, 319, + -1, -1, 322, -1, 324, 325, 326, 327, 328, 329, + 330, 331, 332, 333, 334, 335, -1, 337, -1, -1, + 340, 341, -1, -1, 344, 345, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 359, + 360, 361, 362, 363, -1, -1, -1, -1, 368, -1, + -1, -1, 372, -1, -1, -1, -1, 377, 378, 379, + 380, 381, -1, -1, -1, 385, -1, 387, -1, -1, + -1, -1, -1, 393, 394, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 256, -1, 418, 419, + 420, 421, -1, 423, 264, 265, 266, 267, -1, -1, + 270, 271, -1, 273, 274, 275, 276, 277, 278, 279, + -1, -1, -1, -1, -1, 285, -1, 287, 288, 289, + 290, 291, 292, -1, -1, 295, -1, -1, -1, 299, + 300, -1, 302, 303, 304, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 314, -1, 316, -1, 318, 319, + -1, -1, 322, -1, 324, 325, 326, 327, 328, 329, + 330, 331, 332, 333, 334, 335, -1, 337, -1, -1, + 340, 341, -1, -1, 344, 345, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 359, + 360, 361, 362, 363, -1, -1, -1, -1, 368, -1, + -1, -1, 372, -1, -1, -1, -1, 377, 378, 379, + 380, 381, -1, -1, -1, 385, -1, 387, -1, -1, + -1, -1, -1, 393, 394, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 256, -1, -1, -1, 418, 419, + 420, 421, 264, 265, 266, 267, -1, -1, 270, 271, + -1, 273, 274, 275, 276, 277, 278, 279, -1, -1, + -1, -1, -1, 285, -1, 287, 288, 289, 290, 291, + 292, -1, -1, 295, -1, -1, -1, 299, 300, -1, + 302, 303, 304, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 314, -1, 316, -1, 318, 319, -1, -1, + 322, -1, 324, 325, 326, 327, 328, 329, 330, 331, + 332, 333, 334, 335, -1, 337, -1, -1, 340, 341, + -1, -1, 344, 345, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 359, 360, 361, + 362, 363, -1, -1, -1, -1, 368, -1, -1, -1, + 372, -1, -1, -1, -1, 377, 378, 379, 380, 381, + -1, -1, -1, 385, -1, 387, -1, -1, -1, -1, + -1, 393, 394, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 256, -1, -1, -1, 418, 419, 420, 421, + 264, 265, 266, 267, -1, -1, 270, 271, -1, 273, + 274, 275, 276, 277, 278, 279, -1, -1, -1, -1, + -1, 285, -1, 287, 288, 289, 290, 291, 292, -1, + -1, 295, -1, -1, -1, 299, 300, -1, 302, 303, + 304, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 314, -1, 316, -1, 318, 319, -1, -1, 322, -1, + 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, + 334, 335, -1, 337, -1, -1, 340, 341, -1, -1, + 344, 345, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 359, 360, 361, 362, 363, + -1, -1, -1, -1, 368, -1, -1, -1, 372, -1, + -1, -1, -1, 377, 378, 379, 380, 381, -1, -1, + -1, 385, -1, 387, -1, -1, -1, -1, -1, 393, + 394, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 256, -1, -1, -1, 418, 419, 420, 421, 264, 265, + 266, 267, -1, -1, 270, 271, -1, 273, 274, 275, + 276, 277, 278, 279, -1, -1, -1, -1, -1, 285, + -1, 287, 288, 289, 290, 291, 292, -1, -1, 295, + -1, -1, -1, 299, 300, -1, 302, 303, 304, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 314, -1, + 316, -1, 318, 319, -1, -1, 322, -1, 324, 325, + 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, + -1, 337, -1, -1, 340, 341, -1, -1, 344, 345, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 359, 360, 361, 362, 363, -1, -1, + -1, -1, 368, -1, -1, -1, 372, -1, -1, -1, + -1, 377, 378, 379, 380, 381, -1, -1, -1, 385, + -1, 387, -1, -1, -1, -1, -1, 393, 394, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 256, -1, + -1, -1, 418, 419, 420, 421, 264, 265, -1, 267, + -1, -1, 270, 271, -1, -1, -1, 275, 276, 277, + -1, 279, -1, -1, 265, -1, 267, 285, -1, 270, + 288, -1, -1, -1, 275, -1, -1, 295, 279, -1, + -1, -1, 300, -1, 302, 303, 304, 288, 306, -1, + -1, -1, -1, -1, 295, 313, -1, -1, 316, 300, + 318, 319, -1, 304, 322, -1, -1, 325, -1, 327, + -1, 329, 330, 331, 332, 316, 334, 318, -1, -1, + -1, 322, -1, 341, -1, -1, 344, 345, -1, 330, + 331, -1, -1, 334, -1, -1, 337, -1, -1, -1, + -1, 359, 360, 361, 362, 363, -1, -1, -1, -1, + -1, -1, -1, -1, 372, -1, -1, 375, -1, -1, + 378, 379, 380, 381, -1, -1, -1, 385, -1, 387, + -1, -1, 373, -1, -1, 393, 394, -1, -1, -1, + -1, -1, -1, 264, 265, -1, 267, -1, -1, 270, + 271, -1, -1, -1, 275, 276, 277, -1, 279, -1, + 418, 419, 420, 421, 285, -1, -1, 288, -1, -1, + -1, -1, -1, -1, 295, -1, -1, -1, 419, 300, + -1, 302, 303, 304, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 316, -1, 318, 319, -1, + -1, 322, -1, -1, 325, -1, 327, -1, 329, 330, + 331, 332, -1, 334, -1, -1, -1, -1, -1, -1, + 341, -1, -1, 344, 345, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 359, 360, + 361, 362, 363, -1, -1, -1, -1, 368, 369, -1, + -1, 372, -1, -1, -1, -1, -1, 378, 379, 380, + 381, -1, -1, -1, 385, -1, 387, -1, -1, -1, + -1, -1, 393, 394, -1, -1, -1, -1, -1, -1, + 264, 265, -1, 267, -1, -1, 270, 271, -1, -1, + -1, 275, 276, 277, -1, 279, -1, 418, 419, 420, + 421, 285, -1, 424, 288, -1, -1, -1, 429, -1, + -1, 295, -1, -1, -1, -1, 300, -1, 302, 303, + 304, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 316, -1, 318, 319, -1, -1, 322, -1, + -1, 325, -1, 327, -1, 329, 330, 331, 332, -1, + 334, -1, -1, -1, -1, -1, -1, 341, -1, -1, + 344, 345, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 359, 360, 361, 362, 363, + -1, -1, -1, -1, 368, -1, -1, -1, 372, -1, + -1, -1, -1, -1, 378, 379, 380, 381, -1, -1, + -1, 385, -1, 387, -1, -1, -1, -1, -1, 393, + 394, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 256, 418, 419, 420, 421, -1, -1, + 424, 264, 265, -1, 267, 429, -1, 270, 271, -1, + -1, -1, 275, 276, 277, -1, 279, -1, -1, 265, + -1, 267, 285, -1, 270, 288, -1, -1, -1, 275, + -1, -1, 295, 279, -1, -1, -1, 300, -1, 302, + 303, 304, 288, -1, -1, -1, -1, -1, -1, 295, + -1, -1, -1, 316, 300, 318, 319, -1, 304, 322, + -1, -1, 325, -1, 327, -1, 329, 330, 331, 332, + 316, 334, 318, -1, -1, -1, 322, -1, 341, -1, + -1, 344, 345, -1, 330, 331, -1, -1, 334, -1, + -1, 337, -1, -1, -1, -1, 359, 360, 361, 362, + 363, -1, -1, -1, -1, 368, 369, -1, -1, 372, + -1, -1, -1, -1, -1, 378, 379, 380, 381, -1, + -1, -1, 385, -1, 387, 371, -1, -1, -1, -1, + 393, 394, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 256, -1, -1, -1, 418, 419, 420, 421, 264, + 265, -1, 267, -1, -1, 270, 271, -1, -1, -1, + 275, 276, 277, 419, 279, -1, -1, 265, -1, 267, + 285, -1, 270, 288, -1, -1, -1, 275, -1, -1, + 295, 279, -1, -1, -1, 300, -1, 302, 303, 304, + 288, 306, -1, -1, -1, -1, -1, 295, 313, -1, + -1, 316, 300, 318, 319, -1, 304, 322, -1, -1, + 325, -1, 327, -1, 329, 330, 331, 332, 316, 334, + 318, -1, -1, -1, 322, -1, 341, -1, -1, 344, + 345, -1, 330, 331, -1, -1, 334, -1, -1, 337, + -1, -1, -1, -1, 359, 360, 361, 362, 363, -1, + -1, -1, -1, -1, -1, -1, -1, 372, -1, -1, + -1, -1, -1, 378, 379, 380, 381, -1, -1, -1, + 385, -1, 387, -1, -1, -1, -1, -1, 393, 394, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 256, + -1, -1, -1, 418, 419, 420, 421, 264, 265, -1, + 267, -1, -1, 270, 271, -1, -1, -1, 275, 276, + 277, 419, 279, -1, -1, 265, -1, 267, 285, -1, + 270, 288, -1, -1, -1, 275, -1, -1, 295, 279, + -1, -1, -1, 300, -1, 302, 303, 304, 288, -1, + -1, -1, -1, -1, -1, 295, -1, -1, -1, 316, + 300, 318, 319, 320, 304, 322, -1, -1, 325, -1, + 327, -1, 329, 330, 331, 332, 316, 334, 318, -1, + -1, -1, 322, -1, 341, -1, -1, 344, 345, -1, + 330, 331, -1, -1, 334, -1, -1, 337, -1, -1, + -1, -1, 359, 360, 361, 362, 363, -1, -1, -1, + -1, 368, -1, -1, -1, 372, -1, -1, -1, -1, + -1, 378, 379, 380, 381, -1, -1, -1, 385, -1, + 387, -1, -1, -1, -1, -1, 393, 394, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 256, -1, -1, + -1, 418, 419, 420, 421, 264, 265, -1, 267, -1, + -1, 270, 271, -1, -1, -1, 275, 276, 277, 419, + 279, -1, -1, -1, -1, -1, 285, -1, -1, 288, + -1, -1, -1, -1, -1, -1, 295, -1, -1, -1, + -1, 300, -1, 302, 303, 304, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 316, -1, 318, + 319, -1, -1, 322, -1, -1, 325, -1, 327, -1, + 329, 330, 331, 332, -1, 334, -1, -1, -1, -1, + -1, -1, 341, -1, -1, 344, 345, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 359, 360, 361, 362, 363, -1, -1, -1, -1, 368, + -1, -1, -1, 372, -1, -1, -1, -1, -1, 378, + 379, 380, 381, -1, -1, -1, 385, -1, 387, -1, + -1, -1, -1, -1, 393, 394, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 256, -1, 418, + 419, 420, 421, 262, -1, 264, 265, -1, 267, -1, + -1, 270, 271, -1, -1, -1, 275, 276, 277, -1, + 279, -1, -1, -1, -1, -1, 285, -1, -1, 288, + -1, -1, -1, -1, -1, -1, 295, -1, -1, 298, + -1, 300, -1, 302, 303, 304, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 316, -1, 318, + 319, -1, -1, 322, -1, -1, 325, -1, 327, -1, + 329, 330, 331, 332, -1, 334, -1, -1, -1, -1, + -1, -1, -1, -1, 343, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 359, 360, 361, 362, 363, -1, -1, -1, -1, -1, + -1, -1, -1, 372, -1, 374, -1, -1, -1, 378, + 379, 380, 381, 382, -1, -1, 385, -1, 387, 256, + -1, -1, -1, -1, 393, 394, -1, 264, 265, -1, + 267, -1, -1, 270, 271, -1, -1, -1, 275, 276, + 277, -1, 279, -1, -1, -1, -1, -1, 285, 418, + 419, 288, 421, -1, -1, -1, -1, -1, 295, -1, + -1, -1, -1, 300, -1, 302, 303, 304, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 316, + -1, 318, 319, -1, -1, 322, -1, -1, 325, -1, + 327, -1, 329, 330, 331, 332, -1, 334, -1, -1, + -1, -1, -1, -1, 341, -1, -1, 344, 345, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 359, 360, 361, 362, 363, -1, -1, -1, + -1, -1, 369, -1, -1, 372, -1, -1, -1, -1, + -1, 378, 379, 380, 381, -1, -1, -1, 385, -1, + 387, -1, -1, -1, -1, -1, 393, 394, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 256, -1, -1, + -1, 418, 419, 420, 421, 264, 265, -1, 267, -1, + -1, 270, 271, -1, -1, -1, 275, 276, 277, -1, + 279, -1, -1, 265, -1, 267, 285, -1, 270, 288, + -1, -1, -1, 275, -1, -1, 295, 279, -1, -1, + -1, 300, -1, 302, 303, 304, 288, -1, -1, -1, + -1, -1, -1, 295, -1, -1, -1, 316, 300, 318, + 319, -1, 304, 322, -1, -1, 325, -1, 327, -1, + 329, 330, 331, 332, 316, 334, 318, -1, -1, -1, + 322, -1, 341, -1, -1, 344, 345, -1, 330, 331, + -1, -1, 334, -1, -1, 337, -1, -1, -1, -1, + 359, 360, 361, 362, 363, -1, -1, -1, -1, -1, + 369, -1, -1, 372, -1, -1, -1, -1, -1, 378, + 379, 380, 381, -1, -1, -1, 385, -1, 387, -1, + -1, -1, -1, -1, 393, 394, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 256, -1, -1, -1, 418, + 419, 420, 421, 264, 265, -1, 267, -1, -1, 270, + 271, -1, -1, -1, 275, 276, 277, 419, 279, -1, + -1, 265, -1, 267, 285, -1, 270, 288, -1, -1, + -1, 275, -1, -1, 295, 279, -1, -1, -1, 300, + -1, 302, 303, 304, 288, -1, -1, -1, -1, -1, + -1, 295, -1, -1, -1, 316, 300, 318, 319, -1, + 304, 322, -1, -1, 325, -1, 327, -1, 329, 330, + 331, 332, 316, 334, 318, -1, -1, -1, 322, -1, + 341, -1, -1, 344, 345, -1, 330, 331, -1, -1, + 334, -1, -1, 337, -1, -1, -1, -1, 359, 360, + 361, 362, 363, -1, -1, -1, -1, -1, -1, -1, + -1, 372, -1, -1, -1, -1, -1, 378, 379, 380, + 381, -1, -1, -1, 385, -1, 387, -1, -1, -1, + -1, -1, 393, 394, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 256, -1, -1, -1, 418, 419, 420, + 421, 264, 265, -1, 267, -1, -1, 270, 271, -1, + -1, -1, 275, 276, 277, 419, 279, -1, -1, -1, + -1, -1, 285, -1, -1, 288, -1, -1, -1, -1, + -1, -1, 295, -1, -1, -1, -1, 300, -1, 302, + 303, 304, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 316, -1, 318, 319, -1, -1, 322, + -1, -1, 325, -1, 327, -1, 329, 330, 331, 332, + -1, 334, -1, -1, -1, -1, -1, -1, 341, -1, + -1, 344, 345, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 359, 360, 361, 362, + 363, -1, -1, -1, -1, -1, -1, -1, -1, 372, + -1, -1, -1, -1, -1, 378, 379, 380, 381, -1, + -1, -1, 385, -1, 387, -1, -1, -1, -1, -1, + 393, 394, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 256, -1, 418, 419, 420, 421, 262, + -1, 264, 265, -1, 267, -1, -1, 270, 271, -1, + -1, -1, 275, 276, 277, -1, 279, -1, -1, -1, + -1, -1, 285, -1, -1, 288, -1, -1, -1, -1, + -1, -1, 295, -1, -1, 298, -1, 300, -1, 302, + 303, 304, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 316, -1, 318, 319, -1, -1, 322, + -1, -1, 325, -1, 327, -1, 329, 330, 331, 332, + -1, 334, -1, -1, -1, -1, -1, -1, -1, -1, + 343, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 359, 360, 361, 362, + 363, -1, -1, -1, -1, -1, -1, -1, -1, 372, + -1, 374, -1, -1, -1, 378, 379, 380, 381, -1, + -1, -1, 385, -1, 387, 256, -1, -1, -1, -1, + 393, 394, -1, 264, 265, -1, 267, -1, -1, 270, + 271, -1, -1, -1, 275, 276, 277, -1, 279, -1, + -1, -1, -1, -1, 285, 418, 419, 288, 421, -1, + -1, -1, -1, -1, 295, -1, -1, -1, -1, 300, + -1, 302, 303, 304, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 316, -1, 318, 319, -1, + -1, 322, -1, -1, 325, -1, 327, -1, 329, 330, + 331, 332, -1, 334, -1, -1, -1, -1, -1, -1, + 341, -1, -1, 344, 345, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 359, 360, + 361, 362, 363, -1, -1, -1, -1, -1, -1, -1, + -1, 372, -1, -1, -1, -1, -1, 378, 379, 380, + 381, -1, -1, -1, 385, -1, 387, -1, -1, -1, + -1, -1, 393, 394, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 256, -1, -1, -1, 418, 419, 420, + 421, 264, 265, -1, 267, -1, -1, 270, 271, -1, + -1, -1, 275, 276, 277, -1, 279, -1, -1, 265, + -1, 267, 285, -1, 270, 288, -1, -1, -1, 275, + -1, -1, 295, 279, -1, -1, -1, 300, -1, 302, + 303, 304, 288, -1, -1, -1, -1, -1, -1, 295, + -1, -1, -1, 316, 300, 318, 319, -1, 304, 322, + -1, -1, 325, -1, 327, -1, 329, 330, 331, 332, + 316, 334, 318, -1, -1, -1, 322, -1, 341, -1, + -1, 344, 345, -1, 330, 331, -1, -1, 334, -1, + -1, 337, -1, -1, -1, -1, 359, 360, 361, 362, + 363, -1, -1, -1, -1, -1, -1, -1, -1, 372, + -1, -1, -1, -1, -1, 378, 379, 380, 381, -1, + -1, -1, 385, -1, 387, -1, -1, -1, -1, -1, + 393, 394, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 256, -1, -1, -1, 418, 419, 420, 421, 264, + 265, -1, 267, -1, -1, 270, 271, -1, -1, -1, + 275, 276, 277, 419, 279, -1, -1, 265, -1, 267, + 285, -1, 270, 288, -1, -1, -1, 275, -1, -1, + 295, 279, -1, -1, -1, 300, -1, 302, 303, 304, + 288, -1, -1, -1, -1, -1, -1, 295, -1, -1, + -1, 316, 300, 318, 319, -1, 304, 322, -1, -1, + 325, -1, 327, -1, 329, 330, 331, 332, 316, 334, + 318, -1, -1, -1, 322, -1, 341, -1, -1, 344, + 345, -1, 330, 331, -1, -1, 334, -1, -1, 337, + -1, -1, -1, -1, 359, 360, 361, 362, 363, -1, + -1, -1, -1, -1, -1, -1, -1, 372, -1, -1, + -1, -1, -1, 378, 379, 380, 381, -1, -1, -1, + 385, -1, 387, -1, -1, -1, -1, -1, 393, 394, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 256, + -1, -1, -1, 418, 419, 420, 421, 264, 265, -1, + 267, -1, -1, 270, 271, -1, -1, -1, 275, 276, + 277, 419, 279, -1, -1, 265, -1, 267, 285, -1, + 270, 288, -1, -1, -1, 275, -1, -1, 295, 279, + -1, -1, -1, 300, -1, 302, 303, 304, 288, -1, + -1, -1, -1, -1, -1, 295, -1, -1, -1, 316, + 300, 318, 319, -1, 304, 322, -1, -1, 325, -1, + 327, -1, 329, 330, 331, 332, 316, 334, 318, -1, + -1, -1, 322, -1, 341, -1, -1, 344, 345, -1, + 330, 331, -1, -1, 334, -1, -1, 337, -1, -1, + -1, -1, 359, 360, 361, 362, 363, -1, -1, -1, + -1, -1, -1, -1, -1, 372, -1, -1, -1, -1, + -1, 378, 379, 380, 381, -1, -1, -1, 385, -1, + 387, -1, -1, -1, -1, -1, 393, 394, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 256, -1, -1, + -1, 418, 419, 420, 421, 264, 265, -1, 267, -1, + -1, 270, 271, -1, -1, -1, 275, 276, 277, 419, + 279, -1, -1, -1, -1, -1, 285, -1, -1, 288, + -1, -1, -1, -1, -1, -1, 295, -1, -1, -1, + -1, 300, -1, 302, 303, 304, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 316, -1, 318, + 319, -1, -1, 322, -1, -1, 325, -1, 327, -1, + 329, 330, 331, 332, -1, 334, -1, -1, -1, -1, + -1, -1, 341, -1, -1, 344, 345, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 359, 360, 361, 362, 363, -1, -1, -1, -1, -1, + -1, -1, -1, 372, -1, -1, -1, -1, -1, 378, + 379, 380, 381, -1, -1, -1, 385, -1, 387, -1, + -1, -1, -1, -1, 393, 394, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 256, -1, 418, + 419, 420, 421, 262, -1, 264, 265, -1, 267, -1, + -1, 270, 271, -1, -1, -1, 275, 276, 277, -1, + 279, -1, -1, -1, -1, -1, 285, -1, -1, 288, + -1, -1, -1, -1, -1, -1, 295, -1, -1, 298, + -1, 300, -1, 302, 303, 304, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 316, -1, 318, + 319, -1, -1, 322, -1, -1, 325, -1, 327, -1, + 329, 330, 331, 332, -1, 334, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 359, 360, 361, 362, 363, -1, -1, -1, -1, -1, + -1, -1, -1, 372, -1, 374, -1, -1, -1, 378, + 379, 380, 381, -1, -1, -1, 385, -1, 387, 256, + -1, -1, -1, -1, 393, 394, -1, 264, 265, -1, + 267, -1, -1, 270, 271, -1, -1, -1, 275, 276, + 277, -1, 279, -1, -1, -1, -1, -1, 285, 418, + 419, 288, 421, -1, -1, -1, -1, -1, 295, -1, + -1, -1, -1, 300, -1, 302, 303, 304, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 316, + -1, 318, 319, -1, -1, 322, -1, -1, 325, -1, + 327, -1, 329, 330, 331, 332, -1, 334, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 359, 360, 361, 362, 363, -1, -1, -1, + -1, -1, -1, -1, -1, 372, -1, 374, -1, -1, + -1, 378, 379, 380, 381, -1, -1, -1, 385, -1, + 387, 256, -1, -1, -1, -1, 393, 394, -1, 264, + 265, -1, 267, -1, -1, 270, 271, -1, -1, -1, + 275, 276, 277, -1, 279, -1, -1, -1, -1, -1, + 285, 418, 419, 288, 421, -1, -1, -1, -1, -1, + 295, -1, -1, -1, -1, 300, -1, 302, 303, 304, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 316, -1, 318, 319, -1, -1, 322, -1, -1, + 325, -1, 327, -1, 329, 330, 331, 332, -1, 334, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 359, 360, 361, 362, 363, -1, + -1, -1, -1, -1, -1, -1, -1, 372, -1, -1, + -1, -1, -1, 378, 379, 380, 381, -1, -1, -1, + 385, -1, 387, 256, -1, -1, -1, -1, 393, 394, + -1, 264, 265, -1, 267, -1, -1, 270, 271, -1, + -1, -1, 275, 276, 277, -1, 279, -1, -1, -1, + -1, -1, 285, 418, 419, 288, 421, -1, -1, -1, + -1, -1, 295, -1, -1, -1, -1, 300, -1, 302, + 303, 304, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 316, -1, 318, 319, -1, -1, 322, + -1, -1, 325, -1, 327, -1, 329, 330, 331, 332, + -1, 334, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 359, 360, 361, 362, + 363, -1, -1, -1, -1, -1, -1, -1, -1, 372, + -1, -1, -1, -1, -1, 378, 379, 380, 381, -1, + -1, -1, 385, -1, 387, 256, -1, -1, -1, -1, + 393, 394, -1, 264, 265, -1, 267, -1, -1, 270, + 271, -1, -1, -1, 275, 276, 277, -1, 279, -1, + -1, -1, -1, -1, 285, 418, 419, 288, 421, -1, + -1, -1, -1, -1, 295, -1, -1, -1, -1, 300, + -1, 302, 303, 304, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 316, -1, 318, 319, -1, + -1, 322, -1, -1, 325, -1, 327, -1, 329, 330, + 331, 332, -1, 334, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 359, 360, + 361, 362, 363, -1, -1, -1, -1, -1, -1, -1, + -1, 372, -1, -1, -1, -1, -1, 378, 379, 380, + 381, -1, -1, -1, 385, -1, 387, 256, -1, -1, + -1, -1, 393, 394, -1, 264, 265, -1, 267, -1, + -1, 270, 271, -1, -1, -1, 275, 276, 277, -1, + 279, -1, -1, -1, -1, -1, 285, 418, 419, 288, + 421, -1, -1, -1, -1, -1, 295, -1, -1, -1, + -1, 300, -1, 302, 303, 304, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 316, -1, 318, + 319, -1, -1, 322, -1, -1, 325, -1, 327, -1, + 329, 330, 331, 332, -1, 334, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 359, 360, 361, 362, 363, -1, -1, -1, -1, -1, + -1, -1, -1, 372, -1, -1, -1, -1, -1, 378, + 379, 380, 381, -1, -1, -1, 385, -1, 387, 256, + -1, -1, -1, -1, 393, 394, -1, 264, 265, -1, + 267, -1, -1, 270, 271, -1, -1, -1, 275, 276, + 277, -1, 279, -1, -1, -1, -1, -1, 285, 418, + 419, 288, 421, -1, -1, -1, -1, -1, 295, -1, + -1, -1, -1, 300, -1, 302, 303, 304, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 316, + -1, 318, 319, -1, -1, 322, -1, -1, 325, -1, + 327, -1, 329, 330, 331, 332, -1, 334, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 359, 360, 361, 362, 363, -1, -1, -1, + -1, -1, -1, -1, -1, 372, -1, -1, -1, -1, + -1, 378, 379, 380, 381, -1, -1, -1, 385, -1, + 387, 256, -1, -1, -1, -1, 393, 394, -1, 264, + 265, -1, 267, -1, -1, 270, 271, -1, -1, -1, + 275, 276, 277, -1, 279, -1, -1, -1, -1, -1, + 285, 418, 419, 288, 421, -1, -1, -1, -1, -1, + 295, -1, -1, -1, -1, 300, -1, 302, 303, 304, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 316, -1, 318, 319, -1, -1, 322, -1, -1, + 325, -1, 327, -1, 329, 330, 331, 332, -1, 334, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 359, 360, 361, 362, 363, -1, + -1, -1, -1, -1, -1, -1, -1, 372, -1, -1, + -1, -1, -1, 378, 379, 380, 381, -1, -1, -1, + 385, -1, 387, 256, -1, -1, -1, -1, 393, 394, + -1, 264, 265, -1, 267, -1, -1, 270, 271, -1, + -1, -1, 275, 276, 277, -1, 279, -1, -1, -1, + -1, -1, 285, 418, 419, 288, 421, -1, -1, -1, + -1, -1, 295, -1, -1, -1, -1, 300, -1, 302, + 303, 304, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 316, -1, 318, 319, -1, -1, 322, + -1, -1, 325, -1, 327, -1, 329, 330, 331, 332, + -1, 334, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 359, 360, 361, 362, + 363, -1, -1, -1, -1, -1, -1, -1, -1, 372, + -1, -1, -1, -1, -1, 378, 379, 380, 381, -1, + -1, -1, 385, -1, 387, 256, -1, -1, -1, -1, + 393, 394, -1, 264, 265, -1, 267, -1, -1, 270, + 271, -1, -1, -1, 275, 276, 277, -1, 279, -1, + -1, -1, -1, -1, 285, 418, 419, 288, 421, -1, + -1, -1, -1, -1, 295, -1, -1, -1, -1, 300, + -1, 302, 303, 304, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 316, -1, 318, 319, -1, + -1, 322, -1, -1, 325, -1, 327, -1, 329, 330, + 331, 332, -1, 334, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 359, 360, + 361, 362, 363, -1, -1, -1, -1, -1, -1, -1, + -1, 372, -1, -1, -1, -1, -1, 378, 379, 380, + 381, -1, -1, -1, 385, -1, 387, 256, -1, -1, + -1, -1, 393, 394, -1, 264, 265, -1, 267, -1, + -1, 270, 271, -1, -1, -1, 275, 276, 277, -1, + 279, -1, -1, -1, -1, -1, 285, 418, 419, 288, + 421, -1, -1, -1, -1, -1, 295, -1, -1, -1, + -1, 300, -1, 302, 303, 304, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 316, -1, 318, + 319, -1, -1, 322, -1, -1, 325, -1, 327, -1, + 329, 330, 331, 332, -1, 334, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 359, 360, 361, 362, 363, -1, -1, -1, -1, -1, + -1, -1, -1, 372, -1, -1, -1, -1, -1, 378, + 379, 380, 381, -1, -1, -1, 385, -1, 387, 256, + -1, -1, -1, -1, 393, 394, -1, 264, 265, -1, + 267, -1, -1, 270, 271, -1, -1, -1, 275, 276, + 277, -1, 279, -1, -1, -1, -1, -1, 285, 418, + 419, 288, 421, -1, -1, -1, -1, -1, 295, -1, + -1, -1, -1, 300, -1, 302, 303, 304, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 316, + -1, 318, 319, -1, -1, 322, -1, -1, 325, -1, + 327, -1, 329, 330, 331, 332, -1, 334, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 359, 360, 361, 362, 363, -1, -1, -1, + -1, -1, -1, -1, -1, 372, -1, -1, -1, -1, + -1, 378, 379, 380, 381, -1, -1, -1, 385, -1, + 387, 256, -1, -1, -1, -1, 393, 394, -1, 264, + 265, -1, 267, -1, -1, 270, 271, -1, -1, -1, + 275, 276, 277, -1, 279, -1, -1, -1, -1, -1, + 285, 418, 419, 288, 421, -1, -1, -1, -1, -1, + 295, -1, -1, -1, -1, 300, -1, 302, 303, 304, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 316, -1, 318, 319, -1, -1, 322, -1, -1, + 325, -1, 327, -1, 329, 330, 331, 332, -1, 334, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 359, 360, 361, 362, 363, -1, + -1, -1, -1, -1, -1, -1, -1, 372, -1, -1, + -1, -1, -1, 378, 379, 380, 381, -1, -1, -1, + 385, -1, 387, 256, -1, -1, -1, -1, 393, 394, + -1, 264, 265, -1, 267, -1, -1, 270, 271, -1, + -1, -1, 275, 276, 277, -1, 279, -1, -1, -1, + -1, -1, 285, 418, 419, 288, 421, -1, -1, -1, + -1, -1, 295, -1, -1, -1, -1, 300, -1, 302, + 303, 304, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 316, -1, 318, 319, -1, -1, 322, + -1, -1, 325, -1, 327, -1, 329, 330, 331, 332, + -1, 334, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 359, 360, 361, 362, + 363, -1, -1, -1, -1, -1, -1, -1, -1, 372, + -1, -1, -1, -1, -1, 378, 379, 380, 381, -1, + -1, -1, 385, -1, 387, 256, -1, -1, -1, -1, + 393, 394, -1, 264, 265, -1, 267, -1, -1, 270, + 271, -1, -1, -1, 275, 276, 277, -1, 279, -1, + -1, -1, -1, -1, 285, 418, 419, 288, 421, -1, + -1, -1, -1, -1, 295, -1, -1, -1, -1, 300, + -1, 302, 303, 304, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 316, -1, 318, 319, -1, + -1, 322, -1, -1, 325, -1, 327, -1, 329, 330, + 331, 332, -1, 334, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 359, 360, + 361, 362, 363, -1, -1, -1, -1, -1, -1, -1, + -1, 372, -1, -1, -1, -1, -1, 378, 379, 380, + 381, -1, -1, -1, 385, -1, 387, 256, -1, -1, + -1, -1, 393, 394, -1, 264, 265, -1, 267, -1, + -1, 270, 271, -1, -1, -1, 275, 276, 277, -1, + 279, -1, -1, -1, -1, -1, 285, 418, 419, 288, + 421, -1, -1, -1, -1, -1, 295, -1, -1, -1, + -1, 300, -1, 302, 303, 304, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 316, -1, 318, + 319, -1, -1, 322, -1, -1, 325, -1, 327, -1, + 329, 330, 331, 332, -1, 334, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 359, 360, 361, 362, 363, -1, -1, -1, -1, -1, + -1, -1, -1, 372, -1, -1, -1, -1, -1, 378, + 379, 380, 381, -1, -1, -1, 385, -1, 387, 256, + -1, -1, -1, -1, 393, 394, -1, 264, 265, -1, + 267, -1, -1, 270, 271, -1, -1, -1, 275, 276, + 277, -1, 279, -1, -1, -1, -1, -1, 285, 418, + 419, 288, 421, -1, -1, -1, -1, -1, 295, -1, + -1, -1, -1, 300, -1, 302, 303, 304, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 316, + -1, 318, 319, -1, -1, 322, -1, -1, 325, -1, + 327, -1, 329, 330, 331, 332, -1, 334, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 359, 360, 361, 362, 363, -1, -1, -1, + -1, -1, -1, -1, -1, 372, -1, -1, -1, -1, + -1, 378, 379, 380, 381, -1, -1, -1, 385, -1, + 387, 256, -1, -1, -1, -1, 393, 394, -1, 264, + 265, -1, 267, -1, -1, 270, 271, -1, -1, -1, + 275, 276, 277, -1, 279, -1, -1, -1, -1, -1, + 285, 418, 419, 288, 421, -1, -1, -1, -1, -1, + 295, -1, -1, -1, -1, 300, -1, 302, 303, 304, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 316, -1, 318, 319, -1, -1, 322, -1, -1, + 325, -1, 327, -1, 329, 330, 331, 332, -1, 334, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 359, 360, 361, 362, 363, -1, + -1, -1, -1, -1, -1, -1, -1, 372, -1, -1, + -1, -1, -1, 378, 379, 380, 381, -1, -1, -1, + 385, -1, 387, 256, -1, -1, -1, -1, 393, 394, + -1, 264, 265, -1, 267, -1, -1, 270, 271, -1, + -1, -1, 275, 276, 277, -1, 279, -1, -1, -1, + -1, -1, 285, 418, 419, 288, 421, -1, -1, -1, + -1, -1, 295, -1, -1, -1, -1, 300, -1, 302, + 303, 304, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 316, -1, 318, 319, -1, -1, 322, + -1, -1, 325, -1, 327, -1, 329, 330, 331, 332, + -1, 334, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 359, 360, 361, 362, + 363, -1, -1, -1, -1, -1, -1, -1, -1, 372, + -1, -1, -1, -1, -1, 378, 379, 380, 381, -1, + -1, -1, 385, -1, 387, 256, -1, -1, -1, -1, + 393, 394, -1, 264, 265, -1, 267, -1, -1, 270, + 271, -1, -1, -1, 275, 276, 277, -1, 279, -1, + -1, -1, -1, -1, 285, 418, 419, 288, 421, -1, + -1, -1, -1, -1, 295, -1, -1, -1, -1, 300, + -1, 302, 303, 304, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 316, -1, 318, 319, -1, + -1, 322, -1, -1, 325, -1, 327, -1, 329, 330, + 331, 332, -1, 334, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 359, 360, + 361, 362, 363, -1, -1, -1, -1, -1, -1, -1, + -1, 372, -1, -1, -1, -1, -1, 378, 379, 380, + 381, -1, -1, -1, 385, -1, 387, 256, -1, -1, + -1, -1, 393, 394, -1, 264, 265, -1, 267, -1, + -1, 270, 271, -1, -1, -1, 275, 276, 277, -1, + 279, -1, -1, -1, -1, -1, 285, 418, 419, 288, + 421, -1, -1, -1, -1, -1, 295, -1, -1, -1, + -1, 300, -1, 302, 303, 304, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 316, -1, 318, + 319, -1, -1, 322, -1, -1, 325, -1, 327, -1, + 329, 330, 331, 332, -1, 334, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 359, 360, 361, 362, 363, -1, -1, -1, -1, -1, + -1, -1, -1, 372, -1, -1, -1, -1, -1, 378, + 379, 380, 381, -1, -1, -1, 385, -1, 387, 256, + -1, -1, -1, -1, 393, 394, -1, 264, 265, -1, + 267, -1, -1, 270, 271, -1, -1, -1, 275, 276, + 277, -1, 279, -1, -1, -1, -1, -1, 285, 418, + 419, 288, 421, -1, -1, -1, -1, -1, 295, -1, + -1, -1, -1, 300, -1, 302, 303, 304, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 316, + -1, 318, 319, -1, -1, 322, -1, -1, 325, -1, + 327, -1, 329, 330, 331, 332, -1, 334, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 359, 360, 361, 362, 363, -1, -1, -1, + -1, -1, -1, -1, -1, 372, -1, -1, -1, -1, + -1, 378, 379, 380, 381, -1, -1, -1, 385, -1, + 387, 256, -1, -1, -1, -1, 393, 394, -1, 264, + 265, -1, 267, -1, -1, 270, 271, -1, -1, -1, + 275, 276, 277, -1, 279, -1, -1, -1, -1, -1, + 285, 418, 419, 288, 421, -1, -1, -1, -1, -1, + 295, -1, -1, -1, -1, 300, -1, 302, 303, 304, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 316, -1, 318, 319, -1, -1, 322, -1, -1, + 325, -1, 327, -1, 329, 330, 331, 332, -1, 334, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 359, 360, 361, 362, 363, -1, + -1, -1, -1, -1, -1, -1, -1, 372, -1, -1, + -1, -1, -1, 378, 379, 380, 381, -1, -1, -1, + 385, -1, 387, 256, -1, -1, -1, -1, 393, 394, + -1, 264, 265, -1, 267, -1, -1, 270, 271, -1, + -1, -1, 275, 276, 277, -1, 279, -1, -1, -1, + -1, -1, 285, 418, 419, 288, 421, -1, -1, -1, + -1, -1, 295, -1, -1, -1, -1, 300, -1, 302, + 303, 304, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 316, -1, 318, 319, -1, -1, 322, + -1, -1, 325, -1, 327, -1, 329, 330, 331, 332, + -1, 334, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 359, 360, 361, 362, + 363, -1, -1, -1, -1, -1, -1, -1, -1, 372, + -1, -1, -1, -1, -1, 378, 379, 380, 381, -1, + -1, -1, 385, -1, 387, 256, -1, -1, -1, -1, + 393, 394, -1, 264, 265, -1, 267, -1, -1, 270, + 271, -1, -1, -1, 275, 276, 277, -1, 279, -1, + -1, -1, -1, -1, 285, 418, 419, 288, 421, -1, + -1, -1, -1, -1, 295, -1, -1, -1, -1, 300, + -1, 302, 303, 304, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 316, -1, 318, 319, -1, + -1, 322, -1, -1, 325, -1, 327, -1, 329, 330, + 331, 332, -1, 334, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 359, 360, + 361, 362, 363, -1, -1, -1, -1, -1, -1, -1, + -1, 372, -1, -1, -1, -1, -1, 378, 379, 380, + 381, -1, -1, -1, 385, -1, 387, 256, -1, -1, + -1, -1, 393, 394, -1, 264, 265, -1, 267, -1, + -1, 270, 271, -1, -1, -1, 275, 276, 277, -1, + 279, -1, -1, -1, -1, -1, 285, 418, 419, 288, + 421, -1, -1, -1, -1, -1, 295, -1, -1, -1, + -1, 300, -1, 302, 303, 304, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 316, -1, 318, + 319, -1, -1, 322, -1, -1, 325, -1, 327, -1, + 329, 330, 331, 332, -1, 334, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 359, 360, 361, 362, 363, -1, -1, -1, -1, -1, + -1, -1, -1, 372, -1, -1, -1, -1, -1, 378, + 379, 380, 381, -1, -1, -1, 385, -1, 387, 256, + -1, -1, -1, -1, 393, 394, -1, 264, 265, -1, + 267, -1, -1, 270, 271, -1, -1, -1, 275, 276, + 277, -1, 279, -1, -1, -1, -1, -1, 285, 418, + 419, 288, 421, -1, -1, -1, -1, -1, 295, -1, + -1, -1, -1, 300, -1, 302, 303, 304, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 316, + -1, 318, 319, -1, -1, 322, -1, -1, 325, -1, + 327, -1, 329, 330, 331, 332, -1, 334, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 359, 360, 361, 362, 363, -1, -1, -1, + -1, -1, -1, -1, -1, 372, -1, -1, -1, -1, + -1, 378, 379, 380, 381, -1, -1, -1, 385, -1, + 387, 256, -1, -1, -1, -1, 393, 394, -1, 264, + 265, -1, 267, -1, -1, 270, 271, -1, -1, -1, + 275, 276, 277, -1, 279, -1, -1, -1, -1, -1, + 285, 418, 419, 288, 421, -1, -1, -1, -1, -1, + 295, -1, -1, -1, -1, 300, -1, 302, 303, 304, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 316, -1, 318, 319, -1, -1, 322, -1, -1, + 325, -1, 327, -1, 329, 330, 331, 332, -1, 334, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 359, 360, 361, 362, 363, -1, + -1, -1, -1, -1, -1, -1, -1, 372, -1, -1, + -1, -1, -1, 378, 379, 380, 381, -1, -1, -1, + 385, -1, 387, 256, -1, -1, -1, -1, 393, 394, + -1, 264, 265, -1, 267, -1, -1, 270, 271, -1, + -1, -1, 275, 276, 277, -1, 279, -1, -1, -1, + -1, -1, 285, 418, 419, 288, 421, -1, -1, -1, + -1, -1, 295, -1, -1, -1, -1, 300, -1, 302, + 303, 304, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 316, -1, 318, 319, -1, -1, 322, + -1, -1, 325, -1, 327, -1, 329, 330, 331, 332, + -1, 334, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 359, 360, 361, 362, + 363, -1, -1, -1, -1, -1, -1, -1, -1, 372, + -1, -1, -1, -1, -1, 378, 379, 380, 381, -1, + -1, -1, 385, -1, 387, -1, -1, -1, -1, -1, + 393, 394, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 256, -1, -1, -1, -1, 261, -1, + -1, -1, 265, -1, 267, 418, 419, 270, 421, 272, + 273, -1, 275, -1, 277, -1, 279, -1, 281, 282, + 283, 284, -1, -1, 287, 288, -1, -1, -1, -1, + 293, -1, 295, 296, 297, -1, -1, 300, -1, 302, + -1, 304, -1, -1, 307, -1, 309, 310, 311, 312, + -1, -1, -1, 316, 317, 318, -1, -1, 321, 322, + 323, -1, -1, -1, -1, -1, -1, 330, 331, -1, + 333, 334, 256, 336, 337, 338, -1, -1, -1, 342, + 264, 265, -1, 267, -1, -1, 270, 271, -1, -1, + -1, 275, 276, 277, -1, 279, -1, -1, -1, 362, + -1, 285, -1, -1, 288, 368, 369, 370, -1, -1, + -1, 295, -1, -1, -1, 378, 300, -1, 302, 303, + 304, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 316, -1, 318, 319, -1, -1, 322, -1, + -1, 325, -1, 327, -1, 329, 330, 331, 332, -1, + 334, -1, -1, 337, -1, -1, 419, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 359, 360, 361, 362, 363, + -1, -1, -1, 256, -1, -1, -1, -1, 372, -1, + 262, 264, 265, -1, 267, 379, 380, 270, 271, -1, + -1, -1, 275, 276, 277, -1, 279, -1, -1, -1, + -1, -1, 285, -1, -1, 288, -1, -1, -1, 261, + -1, 263, 295, -1, -1, -1, 298, 300, -1, 302, + 303, 304, -1, -1, 418, 419, -1, -1, -1, -1, + -1, -1, 284, 316, -1, 318, 319, -1, -1, 322, + -1, -1, 325, -1, 327, 297, 329, 330, 331, 332, + 302, 334, -1, -1, -1, 307, -1, 309, 310, 311, + 312, -1, -1, 315, -1, 317, -1, -1, -1, 321, + -1, -1, -1, -1, -1, -1, 359, 360, 361, 362, + 363, 333, 364, -1, 336, -1, 338, -1, -1, 372, + 372, 373, 374, 375, 376, -1, -1, 379, 380, -1, + -1, 383, 384, 385, 386, 387, 388, 389, 390, 391, + 362, 393, 394, 395, 396, 397, 398, 399, 400, 401, + 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, + 412, 413, 414, -1, -1, 418, 419, -1, -1, 421, + -1, 261, 424, 263, -1, 265, -1, 267, -1, -1, + 270, -1, 272, 273, -1, 275, -1, 277, -1, 279, + -1, 281, 282, 283, 284, -1, -1, 287, 288, -1, + -1, -1, -1, 293, 294, 295, 296, 297, -1, -1, + 300, -1, 302, -1, 304, -1, 306, 307, -1, 309, + 310, 311, 312, -1, -1, 315, 316, 317, 318, -1, + -1, 321, 322, 323, -1, -1, -1, -1, -1, -1, + 330, 331, -1, 333, 334, -1, 336, 337, 338, -1, + -1, -1, 342, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 362, -1, -1, 365, 366, 261, -1, -1, + -1, 265, -1, 267, -1, -1, 270, -1, 272, 273, + -1, 275, -1, 277, -1, 279, -1, 281, 282, 283, + 284, -1, -1, 287, 288, -1, -1, -1, -1, 293, + -1, 295, 296, 297, -1, -1, 300, -1, 302, -1, + 304, -1, -1, 307, -1, 309, 310, 311, 312, 419, + -1, -1, 316, 317, 318, -1, -1, 321, 322, 323, + -1, -1, -1, -1, -1, -1, 330, 331, -1, 333, + 334, -1, 336, 337, 338, -1, -1, -1, 342, -1, + -1, 261, -1, -1, -1, 265, -1, 267, -1, -1, + 270, -1, 272, 273, -1, 275, -1, 277, 362, 279, + -1, 281, 282, 283, 284, 369, -1, 287, 288, -1, + -1, -1, -1, 293, 378, 295, 296, 297, -1, -1, + 300, -1, 302, -1, 304, -1, -1, 307, -1, 309, + 310, 311, 312, -1, -1, -1, 316, 317, 318, -1, + -1, 321, 322, 323, -1, -1, -1, -1, -1, -1, + 330, 331, -1, 333, 334, 419, 336, 337, 338, -1, + -1, -1, 342, -1, -1, 261, -1, -1, -1, 265, + -1, 267, -1, -1, 270, -1, 272, 273, -1, 275, + -1, 277, 362, 279, -1, 281, 282, 283, 284, 369, + -1, 287, 288, -1, -1, -1, -1, 293, 378, 295, + 296, 297, -1, -1, 300, -1, 302, -1, 304, -1, + -1, 307, -1, 309, 310, 311, 312, -1, -1, -1, + 316, 317, 318, -1, -1, 321, 322, 323, -1, -1, + -1, -1, -1, -1, 330, 331, -1, 333, 334, 419, + 336, 337, 338, -1, -1, -1, 342, -1, -1, 261, + -1, -1, -1, 265, -1, 267, -1, -1, 270, -1, + 272, 273, -1, 275, -1, 277, 362, 279, -1, 281, + 282, 283, 284, -1, -1, 287, 288, -1, -1, -1, + -1, 293, 378, 295, 296, 297, -1, -1, 300, -1, + 302, -1, 304, -1, -1, 307, -1, 309, 310, 311, + 312, -1, -1, -1, 316, 317, 318, -1, -1, 321, + 322, 323, -1, -1, -1, -1, -1, -1, 330, 331, + -1, 333, 334, 419, 336, 337, 338, -1, -1, -1, + 342, -1, -1, 261, -1, -1, -1, 265, -1, 267, + -1, -1, 270, -1, 272, 273, -1, 275, -1, 277, + 362, 279, -1, 281, 282, 283, 284, 369, -1, 287, + 288, -1, -1, -1, -1, 293, -1, 295, 296, 297, + -1, -1, 300, -1, 302, 261, 304, -1, -1, 307, + -1, 309, 310, 311, 312, -1, -1, -1, 316, 317, + 318, -1, -1, 321, 322, 323, -1, -1, 284, -1, + -1, -1, 330, 331, -1, 333, 334, 419, 336, 337, + 338, 297, -1, -1, 342, -1, 302, -1, -1, 305, + -1, 307, -1, 309, 310, 311, 312, -1, -1, -1, + -1, 317, -1, -1, 362, 321, -1, -1, -1, 325, + -1, 369, -1, -1, -1, -1, -1, 333, -1, -1, + 336, -1, 338, -1, 264, 265, -1, 267, -1, -1, + 270, 271, -1, -1, -1, 275, 276, 277, -1, 279, + -1, 357, -1, -1, -1, 285, 362, -1, 288, -1, + -1, -1, -1, 369, 370, 295, 372, -1, 374, -1, + 300, 419, 302, 303, 304, -1, 306, -1, -1, -1, + -1, 387, -1, 313, -1, -1, 316, -1, 318, 319, + -1, -1, 322, -1, -1, 325, -1, 327, -1, 329, + 330, 331, 332, -1, 334, -1, -1, -1, -1, -1, + -1, 341, -1, 419, 344, 345, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 359, + 360, 361, 362, 363, -1, -1, -1, -1, -1, -1, + -1, -1, 372, 373, -1, 375, -1, -1, 378, 379, + 380, 381, -1, -1, -1, 385, -1, 387, -1, -1, + -1, -1, -1, 393, 394, -1, -1, -1, -1, -1, + -1, 264, 265, -1, 267, -1, -1, 270, 271, -1, + -1, -1, 275, 276, 277, -1, 279, -1, 418, 419, + 420, 421, 285, -1, -1, 288, -1, -1, -1, -1, + -1, -1, 295, -1, -1, -1, -1, 300, -1, 302, + 303, 304, -1, 306, -1, -1, -1, -1, -1, -1, + 313, -1, -1, 316, -1, 318, 319, -1, -1, 322, + -1, -1, 325, -1, 327, -1, 329, 330, 331, 332, + -1, 334, -1, -1, -1, -1, -1, -1, 341, -1, + -1, 344, 345, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 359, 360, 361, 362, + 363, -1, -1, -1, -1, -1, -1, -1, -1, 372, + -1, -1, 375, -1, -1, 378, 379, 380, 381, -1, + -1, -1, 385, -1, 387, -1, -1, -1, -1, -1, + 393, 394, -1, -1, -1, -1, -1, -1, 264, 265, + -1, 267, -1, -1, 270, 271, -1, -1, -1, 275, + 276, 277, -1, 279, -1, 418, 419, 420, 421, 285, + -1, -1, 288, -1, -1, -1, -1, -1, -1, 295, + -1, -1, -1, -1, 300, -1, 302, 303, 304, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 316, -1, 318, 319, -1, -1, 322, -1, -1, 325, + -1, 327, -1, 329, 330, 331, 332, -1, 334, -1, + -1, 337, -1, -1, -1, 341, -1, -1, 344, 345, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 359, 360, 361, 362, 363, -1, -1, + -1, -1, -1, -1, -1, -1, 372, -1, -1, -1, + -1, -1, 378, 379, 380, 381, -1, -1, -1, 385, + -1, 387, -1, -1, -1, -1, -1, 393, 394, -1, + -1, -1, -1, -1, -1, 264, 265, -1, 267, -1, + -1, 270, 271, -1, -1, -1, 275, 276, 277, -1, + 279, -1, 418, 419, 420, 421, 285, -1, -1, 288, + -1, -1, -1, -1, -1, -1, 295, -1, -1, -1, + -1, 300, -1, 302, 303, 304, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 316, -1, 318, + 319, -1, -1, 322, -1, -1, 325, -1, 327, -1, + 329, 330, 331, 332, -1, 334, -1, -1, -1, -1, + -1, -1, 341, -1, -1, 344, 345, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 359, 360, 361, 362, 363, -1, -1, -1, -1, 368, + -1, -1, -1, 372, -1, -1, -1, -1, -1, 378, + 379, 380, 381, -1, -1, -1, 385, -1, 387, -1, + -1, -1, -1, -1, 393, 394, -1, -1, -1, -1, + -1, -1, 264, 265, -1, 267, -1, -1, 270, 271, + -1, -1, -1, 275, 276, 277, -1, 279, -1, 418, + 419, 420, 421, 285, -1, -1, 288, -1, -1, -1, + -1, -1, -1, 295, -1, -1, -1, -1, 300, -1, + 302, 303, 304, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 316, -1, 318, 319, -1, -1, + 322, -1, -1, 325, -1, 327, -1, 329, 330, 331, + 332, -1, 334, -1, -1, -1, -1, -1, -1, 341, + -1, -1, 344, 345, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 359, 360, 361, + 362, 363, -1, -1, -1, -1, 368, -1, -1, -1, + 372, -1, -1, -1, -1, -1, 378, 379, 380, 381, + -1, -1, -1, 385, -1, 387, -1, -1, -1, -1, + -1, 393, 394, -1, -1, -1, -1, -1, -1, 264, + 265, -1, 267, -1, -1, 270, 271, -1, -1, -1, + 275, 276, 277, -1, 279, -1, 418, 419, 420, 421, + 285, -1, -1, 288, -1, -1, -1, -1, -1, -1, + 295, -1, -1, -1, -1, 300, -1, 302, 303, 304, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 316, -1, 318, 319, -1, -1, 322, -1, -1, + 325, -1, 327, -1, 329, 330, 331, 332, -1, 334, + -1, -1, -1, -1, -1, -1, 341, -1, -1, 344, + 345, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 359, 360, 361, 362, 363, -1, + -1, -1, -1, -1, -1, -1, -1, 372, -1, -1, + -1, -1, -1, 378, 379, 380, 381, -1, -1, -1, + 385, -1, 387, -1, -1, -1, -1, -1, 393, 394, + -1, -1, -1, -1, -1, -1, 264, 265, -1, 267, + -1, -1, 270, 271, -1, -1, -1, 275, 276, 277, + -1, 279, -1, 418, 419, 420, 421, 285, -1, -1, + 288, -1, -1, -1, -1, -1, -1, 295, -1, -1, + -1, -1, 300, -1, 302, 303, 304, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 316, -1, + 318, 319, -1, -1, 322, -1, -1, 325, -1, 327, + -1, 329, 330, 331, 332, -1, 334, -1, -1, -1, + -1, -1, -1, 341, -1, -1, 344, 345, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 359, 360, 361, 362, 363, -1, -1, -1, -1, + -1, -1, -1, -1, 372, -1, -1, -1, -1, -1, + 378, 379, 380, 381, -1, -1, -1, 385, -1, 387, + -1, -1, -1, -1, -1, 393, 394, -1, -1, -1, + -1, -1, -1, 264, 265, -1, 267, -1, -1, 270, + 271, -1, -1, -1, 275, 276, 277, -1, 279, -1, + 418, 419, 420, 421, 285, -1, -1, 288, -1, -1, + -1, -1, -1, -1, 295, -1, -1, -1, -1, 300, + -1, 302, 303, 304, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 316, -1, 318, 319, -1, + -1, 322, -1, -1, 325, -1, 327, -1, 329, 330, + 331, 332, -1, 334, -1, -1, -1, -1, -1, -1, + 341, -1, -1, 344, 345, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 359, 360, + 361, 362, 363, -1, -1, -1, -1, -1, -1, -1, + -1, 372, -1, -1, -1, -1, -1, 378, 379, 380, + 381, -1, -1, -1, 385, -1, 387, -1, -1, -1, + -1, -1, 393, 394, -1, -1, -1, -1, -1, -1, + 264, 265, -1, 267, -1, -1, 270, 271, -1, -1, + -1, 275, 276, 277, -1, 279, -1, 418, 419, 420, + 421, 285, -1, -1, 288, -1, -1, -1, -1, -1, + -1, 295, -1, -1, -1, -1, 300, -1, 302, 303, + 304, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 316, -1, 318, 319, -1, -1, 322, -1, + -1, 325, -1, 327, -1, 329, 330, 331, 332, -1, + 334, -1, -1, -1, -1, -1, -1, 341, -1, -1, + 344, 345, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 359, 360, 361, 362, 363, + -1, -1, -1, -1, -1, -1, -1, -1, 372, -1, + -1, -1, -1, -1, 378, 379, 380, 381, -1, -1, + -1, 385, -1, 387, -1, -1, -1, -1, -1, 393, + 394, -1, -1, -1, -1, -1, -1, 264, 265, -1, + 267, -1, -1, 270, 271, -1, -1, -1, 275, 276, + 277, -1, 279, -1, 418, 419, 420, 421, 285, -1, + -1, 288, -1, -1, -1, -1, -1, -1, 295, -1, + -1, -1, -1, 300, -1, 302, 303, 304, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 316, + -1, 318, 319, -1, -1, 322, -1, -1, 325, -1, + 327, -1, 329, 330, 331, 332, -1, 334, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 359, 360, 361, 362, 363, -1, -1, -1, + -1, -1, -1, -1, -1, 372, -1, -1, -1, -1, + -1, 378, 379, 380, 381, -1, -1, -1, 385, -1, + 387, -1, 264, 265, -1, 267, 393, 394, 270, 271, + -1, -1, -1, 275, 276, 277, -1, 279, -1, -1, + -1, -1, -1, 285, -1, -1, 288, -1, -1, -1, + -1, 418, 419, 295, 421, -1, -1, -1, 300, -1, + 302, 303, 304, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 316, -1, 318, 319, -1, -1, + 322, -1, -1, 325, -1, 327, -1, 329, 330, 331, + 332, -1, 334, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 359, 360, 361, + 362, 363, -1, -1, -1, -1, -1, -1, -1, -1, + 372, -1, -1, -1, -1, -1, 378, 379, 380, 381, + -1, -1, -1, 385, -1, 387, -1, -1, -1, -1, + -1, 393, 394, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 263, -1, 265, -1, 267, 418, 419, 270, 421, + 272, 273, -1, 275, -1, 277, -1, 279, -1, 281, + 282, 283, -1, -1, -1, 287, 288, -1, -1, -1, + -1, 293, -1, 295, 296, -1, -1, -1, 300, -1, + -1, -1, 304, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 315, 316, -1, 318, -1, -1, -1, + 322, 323, -1, -1, -1, -1, -1, -1, 330, 331, + 264, 265, 334, 267, -1, 337, 270, 271, -1, -1, + 342, 275, 276, 277, -1, 279, -1, -1, -1, -1, + -1, 285, -1, -1, 288, -1, -1, -1, -1, -1, + -1, 295, -1, 365, 366, -1, 300, -1, 302, 303, + 304, -1, -1, -1, -1, -1, 378, -1, -1, -1, + -1, -1, 316, -1, 318, 319, -1, -1, 322, -1, + -1, 325, -1, 327, -1, 329, 330, 331, 332, -1, + 334, -1, -1, 337, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 419, -1, -1, + -1, -1, -1, -1, -1, 359, 360, 361, 362, 363, + -1, -1, -1, -1, -1, 265, -1, 267, 372, -1, + 270, -1, 272, 273, -1, 275, -1, 277, -1, 279, + -1, 281, 282, 283, -1, -1, -1, 287, 288, -1, + -1, -1, -1, 293, -1, 295, 296, -1, -1, -1, + 300, -1, -1, -1, 304, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 418, 419, 316, -1, 318, -1, + -1, -1, 322, 323, -1, -1, -1, -1, -1, -1, + 330, 331, -1, 265, 334, 267, -1, 337, 270, -1, + -1, 273, 342, 275, -1, 277, -1, 279, -1, 281, + 282, 283, -1, -1, -1, 287, 288, -1, -1, -1, + -1, 293, -1, 295, -1, 265, -1, 267, 300, -1, + 270, -1, 304, -1, -1, 275, -1, -1, 378, 279, + -1, -1, -1, -1, 316, -1, 318, -1, 288, -1, + 322, -1, -1, -1, -1, 295, -1, -1, 330, 331, + 300, -1, 334, -1, 304, 337, 306, -1, 308, -1, + 342, -1, -1, 313, -1, -1, 316, -1, 318, 419, + -1, -1, 322, -1, -1, 325, -1, -1, -1, -1, + 330, 331, -1, 265, 334, 267, -1, 337, 270, -1, + -1, -1, -1, 275, -1, -1, 378, 279, -1, -1, + -1, -1, -1, -1, -1, -1, 288, -1, -1, -1, + -1, -1, -1, 295, -1, 265, -1, 267, 300, -1, + 270, -1, 304, 373, 306, 275, 308, -1, -1, 279, + -1, 313, -1, -1, 316, -1, 318, 419, 288, -1, + 322, -1, -1, 325, -1, 295, -1, -1, 330, 331, + 300, -1, 334, -1, 304, 337, 306, -1, -1, -1, + -1, -1, -1, 313, -1, -1, 316, -1, 318, 419, + -1, -1, 322, -1, -1, 325, -1, -1, -1, -1, + 330, 331, -1, -1, 334, -1, -1, 337, 265, 371, + 267, -1, -1, 270, -1, -1, -1, -1, 275, -1, + -1, -1, 279, -1, -1, -1, -1, -1, -1, -1, + -1, 288, -1, 363, -1, -1, -1, -1, 295, -1, + 265, -1, 267, 300, -1, 270, -1, 304, -1, 306, + 275, 308, -1, -1, 279, -1, 313, 419, -1, 316, + -1, 318, -1, 288, -1, 322, -1, -1, 325, -1, + 295, -1, -1, 330, 331, 300, -1, 334, -1, 304, + 337, 306, -1, 308, -1, 265, -1, 267, 313, 419, + 270, 316, -1, 318, -1, 275, -1, 322, -1, 279, + 325, -1, -1, 283, -1, 330, 331, -1, 288, 334, + -1, -1, 337, 293, -1, 295, -1, 265, -1, 267, + 300, -1, 270, -1, 304, 305, -1, 275, -1, -1, + -1, 279, -1, -1, -1, -1, 316, -1, 318, -1, + 288, -1, 322, -1, -1, -1, -1, 295, -1, -1, + 330, 331, 300, -1, 334, -1, 304, 337, -1, -1, + -1, 261, 419, -1, -1, -1, -1, -1, 316, -1, + 318, -1, 272, -1, 322, -1, -1, 277, -1, -1, + -1, 281, 330, 331, 284, -1, 334, -1, -1, 337, + -1, -1, -1, -1, 419, -1, 296, 297, -1, -1, + -1, 301, 302, -1, -1, -1, -1, 307, -1, 309, + 310, 311, 312, -1, -1, 363, -1, 317, -1, -1, + -1, 321, -1, 323, -1, -1, -1, -1, -1, -1, + -1, -1, 261, 333, -1, 335, 336, -1, 338, 419, + -1, -1, 342, 272, -1, -1, -1, -1, 277, -1, + -1, -1, 281, -1, -1, 284, -1, -1, -1, -1, + -1, -1, 362, -1, -1, -1, -1, 296, 297, 369, + 370, 419, 301, 302, 261, -1, -1, -1, 307, -1, + 309, 310, 311, 312, -1, -1, -1, -1, 317, -1, + -1, -1, 321, -1, 323, -1, -1, 284, -1, -1, + -1, -1, -1, -1, 333, -1, -1, 336, -1, 338, + 297, -1, 261, 342, 263, 302, -1, -1, -1, -1, + 307, -1, 309, 310, 311, 312, -1, -1, 315, -1, + 317, -1, -1, 362, 321, 284, -1, -1, -1, -1, + 369, 370, -1, -1, -1, -1, 333, -1, 297, 336, + -1, 338, -1, 302, -1, -1, -1, -1, 307, -1, + 309, 310, 311, 312, -1, -1, -1, -1, 317, -1, + -1, -1, 321, -1, -1, 362, -1, -1, -1, -1, + -1, -1, 369, 370, 333, -1, -1, 336, 261, 338, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 272, + -1, -1, -1, -1, 277, -1, -1, -1, 281, -1, + -1, 284, -1, 362, -1, -1, -1, -1, -1, -1, + 369, 370, -1, 296, 297, -1, -1, -1, 301, 302, + -1, 261, -1, -1, 307, -1, 309, 310, 311, 312, + -1, -1, 272, -1, 317, -1, -1, 277, 321, -1, + 323, 281, -1, -1, 284, -1, -1, -1, -1, -1, + 333, -1, -1, 336, -1, 338, 296, 297, -1, 342, + -1, 301, 302, -1, 261, -1, -1, 307, -1, 309, + 310, 311, 312, -1, -1, -1, -1, 317, -1, 362, + -1, 321, -1, 323, -1, -1, 369, 284, -1, -1, + -1, -1, -1, 333, -1, -1, 336, -1, 338, -1, + 297, -1, 342, -1, 261, 302, -1, -1, -1, -1, + 307, -1, 309, 310, 311, 312, -1, -1, -1, -1, + 317, -1, 362, -1, 321, -1, -1, 284, -1, 369, + -1, -1, -1, -1, -1, -1, 333, -1, -1, 336, + 297, 338, -1, -1, 261, 302, -1, -1, -1, -1, + 307, -1, 309, 310, 311, 312, -1, -1, -1, -1, + 317, -1, -1, -1, 321, 362, -1, 284, 365, 366, + -1, -1, 369, -1, -1, -1, 333, -1, -1, 336, + 297, 338, -1, -1, 261, 302, 263, -1, -1, -1, + 307, -1, 309, 310, 311, 312, -1, -1, 315, -1, + 317, -1, -1, -1, 321, 362, -1, 284, 365, 366, + -1, -1, 369, -1, 261, -1, 333, -1, -1, 336, + 297, 338, -1, -1, -1, 302, -1, -1, -1, -1, + 307, -1, 309, 310, 311, 312, -1, 284, -1, -1, + 317, -1, -1, -1, 321, 362, -1, -1, -1, -1, + 297, -1, 369, -1, 261, 302, 333, -1, -1, 336, + 307, 338, 309, 310, 311, 312, -1, -1, -1, -1, + 317, -1, -1, -1, 321, -1, -1, 284, -1, -1, + -1, -1, -1, -1, 261, 362, 333, -1, -1, 336, + 297, 338, 369, -1, 301, 302, -1, -1, -1, -1, + 307, -1, 309, 310, 311, 312, -1, 284, -1, -1, + 317, -1, -1, -1, 321, 362, -1, -1, 365, 366, + 297, -1, -1, -1, -1, 302, 333, -1, -1, 336, + 307, 338, 309, 310, 311, 312, -1, -1, -1, -1, + 317, -1, -1, -1, 321, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 362, 333, -1, -1, 336, + -1, 338, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 362, + }; + +#line 7338 "cs-parser.jay" + +// +// A class used to hold info about an operator declarator +// +class OperatorDeclaration { + public readonly Operator.OpType optype; + public readonly FullNamedExpression ret_type; + public readonly Location location; + + public OperatorDeclaration (Operator.OpType op, FullNamedExpression ret_type, Location location) + { + optype = op; + this.ret_type = ret_type; + this.location = location; + } +} + +void Error_ExpectingTypeName (Expression expr) +{ + if (expr is Invocation){ + report.Error (1002, expr.Location, "Expecting `;'"); + } else { + expr.Error_InvalidExpressionStatement (report); + } +} + +void Error_ParameterModifierNotValid (string modifier, Location loc) +{ + report.Error (631, loc, "The parameter modifier `{0}' is not valid in this context", + modifier); +} + +void Error_DuplicateParameterModifier (Location loc, Parameter.Modifier mod) +{ + report.Error (1107, loc, "Duplicate parameter modifier `{0}'", + Parameter.GetModifierSignature (mod)); +} + +void Error_TypeExpected (Location loc) +{ + report.Error (1031, loc, "Type expected"); +} + +void Error_UnsafeCodeNotAllowed (Location loc) +{ + report.Error (227, loc, "Unsafe code requires the `unsafe' command line option to be specified"); +} + +void Warning_EmptyStatement (Location loc) +{ + report.Warning (642, 3, loc, "Possible mistaken empty statement"); +} + +void Error_NamedArgumentExpected (NamedArgument a) +{ + report.Error (1738, a.Location, "Named arguments must appear after the positional arguments"); +} + +void Error_MissingInitializer (Location loc) +{ + report.Error (210, loc, "You must provide an initializer in a fixed or using statement declaration"); +} + +object Error_AwaitAsIdentifier (object token) +{ + if (async_block) { + report.Error (4003, GetLocation (token), "`await' cannot be used as an identifier within an async method or lambda expression"); + return new LocatedToken ("await", GetLocation (token)); + } + + return token; +} + +void push_current_container (TypeDefinition tc, object partial_token) +{ + if (module.Evaluator != null){ + tc.Definition.Modifiers = tc.ModFlags = (tc.ModFlags & ~Modifiers.AccessibilityMask) | Modifiers.PUBLIC; + if (undo == null) + undo = new Undo (); + + undo.AddTypeContainer (current_container, tc); + } + + if (partial_token != null) + current_container.AddPartial (tc); + else + current_container.AddTypeContainer (tc); + + ++lexer.parsing_declaration; + current_container = tc; + current_type = tc; +} + +TypeContainer pop_current_class () +{ + var retval = current_container; + + current_container = current_container.Parent; + current_type = current_type.Parent as TypeDefinition; + + return retval; +} + +[System.Diagnostics.Conditional ("FULL_AST")] +void StoreModifierLocation (object token, Location loc) +{ + if (lbag == null) + return; + + if (mod_locations == null) + mod_locations = new List> (); + + mod_locations.Add (Tuple.Create ((Modifiers) token, loc)); +} + +List> GetModifierLocations () +{ + var result = mod_locations; + mod_locations = null; + return result; +} + +[System.Diagnostics.Conditional ("FULL_AST")] +void PushLocation (Location loc) +{ + if (location_stack == null) + location_stack = new Stack (); + + location_stack.Push (loc); +} + +Location PopLocation () +{ + if (location_stack == null) + return Location.Null; + + return location_stack.Pop (); +} + +string CheckAttributeTarget (int token, string a, Location l) +{ + switch (a) { + case "assembly" : case "module" : case "field" : case "method" : case "param" : case "property" : case "type" : + return a; + } + + if (!Tokenizer.IsValidIdentifier (a)) { + Error_SyntaxError (token); + } else { + report.Warning (658, 1, l, + "`{0}' is invalid attribute target. All attributes in this attribute section will be ignored", a); + } + + return string.Empty; +} + +static bool IsUnaryOperator (Operator.OpType op) +{ + switch (op) { + + case Operator.OpType.LogicalNot: + case Operator.OpType.OnesComplement: + case Operator.OpType.Increment: + case Operator.OpType.Decrement: + case Operator.OpType.True: + case Operator.OpType.False: + case Operator.OpType.UnaryPlus: + case Operator.OpType.UnaryNegation: + return true; + } + return false; +} + +void syntax_error (Location l, string msg) +{ + report.Error (1003, l, "Syntax error, " + msg); +} + +Tokenizer lexer; + +public Tokenizer Lexer { + get { + return lexer; + } +} + +public CSharpParser (SeekableStreamReader reader, CompilationSourceFile file, ParserSession session) + : this (reader, file, file.Compiler.Report, session) +{ +} + +public CSharpParser (SeekableStreamReader reader, CompilationSourceFile file, Report report, ParserSession session) +{ + this.file = file; + current_container = current_namespace = file; + + this.module = file.Module; + this.compiler = file.Compiler; + this.settings = compiler.Settings; + this.report = report; + + lang_version = settings.Version; + yacc_verbose_flag = settings.VerboseParserFlag; + doc_support = settings.DocumentationFile != null; + lexer = new Tokenizer (reader, file, session, report); + oob_stack = new Stack (); + lbag = session.LocationsBag; + use_global_stacks = session.UseJayGlobalArrays; + parameters_bucket = session.ParametersStack; +} + +public void parse () +{ + eof_token = Token.EOF; + + try { + if (yacc_verbose_flag > 1) + yyparse (lexer, new yydebug.yyDebugSimple ()); + else + yyparse (lexer); + + Tokenizer tokenizer = lexer as Tokenizer; + tokenizer.cleanup (); + } catch (Exception e){ + if (e is yyParser.yyUnexpectedEof) { + Error_SyntaxError (yyToken); + UnexpectedEOF = true; + return; + } + + if (e is yyParser.yyException) { + if (report.Errors == 0) + report.Error (-25, lexer.Location, "Parsing error"); + } else { + // Used by compiler-tester to test internal errors + if (yacc_verbose_flag > 0 || e is FatalException) + throw; + + report.Error (589, lexer.Location, "Internal compiler error during parsing" + e); + } + } +} + +void CheckToken (int error, int yyToken, string msg, Location loc) +{ + if (yyToken >= Token.FIRST_KEYWORD && yyToken <= Token.LAST_KEYWORD) + report.Error (error, loc, "{0}: `{1}' is a keyword", msg, GetTokenName (yyToken)); + else + report.Error (error, loc, msg); +} + +string ConsumeStoredComment () +{ + string s = tmpComment; + tmpComment = null; + Lexer.doc_state = XmlCommentState.Allowed; + return s; +} + +void FeatureIsNotAvailable (Location loc, string feature) +{ + report.FeatureIsNotAvailable (compiler, loc, feature); +} + +Location GetLocation (object obj) +{ + var lt = obj as LocatedToken; + if (lt != null) + return lt.Location; + + var mn = obj as MemberName; + if (mn != null) + return mn.Location; + + var expr = obj as Expression; + if (expr != null) + return expr.Location; + + return lexer.Location; +} + +void start_block (Location loc) +{ + if (current_block == null) { + current_block = new ToplevelBlock (compiler, current_local_parameters, loc); + parsing_anonymous_method = false; + } else if (parsing_anonymous_method) { + current_block = new ParametersBlock (current_block, current_local_parameters, loc); + parsing_anonymous_method = false; + } else { + current_block = new ExplicitBlock (current_block, loc, Location.Null); + } +} + +Block +end_block (Location loc) +{ + Block retval = current_block.Explicit; + retval.SetEndLocation (loc); + current_block = retval.Parent; + return retval; +} + +void start_anonymous (bool isLambda, ParametersCompiled parameters, bool isAsync, Location loc) +{ + oob_stack.Push (current_anonymous_method); + oob_stack.Push (current_local_parameters); + oob_stack.Push (current_variable); + oob_stack.Push (async_block); + + current_local_parameters = parameters; + if (isLambda) { + if (lang_version <= LanguageVersion.ISO_2) + FeatureIsNotAvailable (loc, "lambda expressions"); + + current_anonymous_method = new LambdaExpression (loc); + } else { + if (lang_version == LanguageVersion.ISO_1) + FeatureIsNotAvailable (loc, "anonymous methods"); + + current_anonymous_method = new AnonymousMethodExpression (loc); + } + current_anonymous_method.IsAsync = isAsync; + + async_block = isAsync; + // Force the next block to be created as a ToplevelBlock + parsing_anonymous_method = true; +} + +/* + * Completes the anonymous method processing, if lambda_expr is null, this + * means that we have a Statement instead of an Expression embedded + */ +AnonymousMethodExpression end_anonymous (ParametersBlock anon_block) +{ + AnonymousMethodExpression retval; + + if (async_block) + anon_block.IsAsync = true; + + current_anonymous_method.Block = anon_block; + retval = current_anonymous_method; + + async_block = (bool) oob_stack.Pop (); + current_variable = (BlockVariable) oob_stack.Pop (); + current_local_parameters = (ParametersCompiled) oob_stack.Pop (); + current_anonymous_method = (AnonymousMethodExpression) oob_stack.Pop (); + + return retval; +} + +void Error_SyntaxError (int token) +{ + Error_SyntaxError (0, token); +} + +void Error_SyntaxError (int error_code, int token) +{ + Error_SyntaxError (error_code, token, "Unexpected symbol"); +} + +void Error_SyntaxError (int error_code, int token, string msg) +{ + Lexer.CompleteOnEOF = false; + + // An error message has been reported by tokenizer + if (token == Token.ERROR) + return; + + // Avoid duplicit error message after unterminated string literals + if (token == Token.LITERAL && lexer.Location.Column == 0) + return; + + string symbol = GetSymbolName (token); + string expecting = GetExpecting (); + var loc = lexer.Location - symbol.Length; + + if (error_code == 0) { + if (expecting == "`identifier'") { + if (token > Token.FIRST_KEYWORD && token < Token.LAST_KEYWORD) { + report.Error (1041, loc, "Identifier expected, `{0}' is a keyword", symbol); + return; + } + + error_code = 1001; + expecting = "identifier"; + } else if (expecting == "`)'") { + error_code = 1026; + } else { + error_code = 1525; + } + } + + if (string.IsNullOrEmpty (expecting)) + report.Error (error_code, loc, "{1} `{0}'", symbol, msg); + else + report.Error (error_code, loc, "{2} `{0}', expecting {1}", symbol, expecting, msg); +} + +string GetExpecting () +{ + int [] tokens = yyExpectingTokens (yyExpectingState); + var names = new List (tokens.Length); + bool has_type = false; + bool has_identifier = false; + for (int i = 0; i < tokens.Length; i++){ + int token = tokens [i]; + has_identifier |= token == Token.IDENTIFIER; + + string name = GetTokenName (token); + if (name == "") + continue; + + has_type |= name == "type"; + if (names.Contains (name)) + continue; + + names.Add (name); + } + + // + // Too many tokens to enumerate + // + if (names.Count > 8) + return null; + + if (has_type && has_identifier) + names.Remove ("identifier"); + + if (names.Count == 1) + return "`" + GetTokenName (tokens [0]) + "'"; + + StringBuilder sb = new StringBuilder (); + names.Sort (); + int count = names.Count; + for (int i = 0; i < count; i++){ + bool last = i + 1 == count; + if (last) + sb.Append ("or "); + sb.Append ('`'); + sb.Append (names [i]); + sb.Append (last ? "'" : count < 3 ? "' " : "', "); + } + return sb.ToString (); +} + + +string GetSymbolName (int token) +{ + switch (token){ + case Token.LITERAL: + return ((Constant)lexer.Value).GetValue ().ToString (); + case Token.IDENTIFIER: + return ((LocatedToken)lexer.Value).Value; + + case Token.BOOL: + return "bool"; + case Token.BYTE: + return "byte"; + case Token.CHAR: + return "char"; + case Token.VOID: + return "void"; + case Token.DECIMAL: + return "decimal"; + case Token.DOUBLE: + return "double"; + case Token.FLOAT: + return "float"; + case Token.INT: + return "int"; + case Token.LONG: + return "long"; + case Token.SBYTE: + return "sbyte"; + case Token.SHORT: + return "short"; + case Token.STRING: + return "string"; + case Token.UINT: + return "uint"; + case Token.ULONG: + return "ulong"; + case Token.USHORT: + return "ushort"; + case Token.OBJECT: + return "object"; + + case Token.PLUS: + return "+"; + case Token.UMINUS: + case Token.MINUS: + return "-"; + case Token.BANG: + return "!"; + case Token.BITWISE_AND: + return "&"; + case Token.BITWISE_OR: + return "|"; + case Token.STAR: + return "*"; + case Token.PERCENT: + return "%"; + case Token.DIV: + return "/"; + case Token.CARRET: + return "^"; + case Token.OP_INC: + return "++"; + case Token.OP_DEC: + return "--"; + case Token.OP_SHIFT_LEFT: + return "<<"; + case Token.OP_SHIFT_RIGHT: + return ">>"; + case Token.OP_LT: + return "<"; + case Token.OP_GT: + return ">"; + case Token.OP_LE: + return "<="; + case Token.OP_GE: + return ">="; + case Token.OP_EQ: + return "=="; + case Token.OP_NE: + return "!="; + case Token.OP_AND: + return "&&"; + case Token.OP_OR: + return "||"; + case Token.OP_PTR: + return "->"; + case Token.OP_COALESCING: + return "??"; + case Token.OP_MULT_ASSIGN: + return "*="; + case Token.OP_DIV_ASSIGN: + return "/="; + case Token.OP_MOD_ASSIGN: + return "%="; + case Token.OP_ADD_ASSIGN: + return "+="; + case Token.OP_SUB_ASSIGN: + return "-="; + case Token.OP_SHIFT_LEFT_ASSIGN: + return "<<="; + case Token.OP_SHIFT_RIGHT_ASSIGN: + return ">>="; + case Token.OP_AND_ASSIGN: + return "&="; + case Token.OP_XOR_ASSIGN: + return "^="; + case Token.OP_OR_ASSIGN: + return "|="; + } + + return GetTokenName (token); +} + +static string GetTokenName (int token) +{ + switch (token){ + case Token.ABSTRACT: + return "abstract"; + case Token.AS: + return "as"; + case Token.ADD: + return "add"; + case Token.ASYNC: + return "async"; + case Token.BASE: + return "base"; + case Token.BREAK: + return "break"; + case Token.CASE: + return "case"; + case Token.CATCH: + return "catch"; + case Token.CHECKED: + return "checked"; + case Token.CLASS: + return "class"; + case Token.CONST: + return "const"; + case Token.CONTINUE: + return "continue"; + case Token.DEFAULT: + return "default"; + case Token.DELEGATE: + return "delegate"; + case Token.DO: + return "do"; + case Token.ELSE: + return "else"; + case Token.ENUM: + return "enum"; + case Token.EVENT: + return "event"; + case Token.EXPLICIT: + return "explicit"; + case Token.EXTERN: + case Token.EXTERN_ALIAS: + return "extern"; + case Token.FALSE: + return "false"; + case Token.FINALLY: + return "finally"; + case Token.FIXED: + return "fixed"; + case Token.FOR: + return "for"; + case Token.FOREACH: + return "foreach"; + case Token.GOTO: + return "goto"; + case Token.IF: + return "if"; + case Token.IMPLICIT: + return "implicit"; + case Token.IN: + return "in"; + case Token.INTERFACE: + return "interface"; + case Token.INTERNAL: + return "internal"; + case Token.IS: + return "is"; + case Token.LOCK: + return "lock"; + case Token.NAMESPACE: + return "namespace"; + case Token.NEW: + return "new"; + case Token.NULL: + return "null"; + case Token.OPERATOR: + return "operator"; + case Token.OUT: + return "out"; + case Token.OVERRIDE: + return "override"; + case Token.PARAMS: + return "params"; + case Token.PRIVATE: + return "private"; + case Token.PROTECTED: + return "protected"; + case Token.PUBLIC: + return "public"; + case Token.READONLY: + return "readonly"; + case Token.REF: + return "ref"; + case Token.RETURN: + return "return"; + case Token.REMOVE: + return "remove"; + case Token.SEALED: + return "sealed"; + case Token.SIZEOF: + return "sizeof"; + case Token.STACKALLOC: + return "stackalloc"; + case Token.STATIC: + return "static"; + case Token.STRUCT: + return "struct"; + case Token.SWITCH: + return "switch"; + case Token.THIS: + return "this"; + case Token.THROW: + return "throw"; + case Token.TRUE: + return "true"; + case Token.TRY: + return "try"; + case Token.TYPEOF: + return "typeof"; + case Token.UNCHECKED: + return "unchecked"; + case Token.UNSAFE: + return "unsafe"; + case Token.USING: + return "using"; + case Token.VIRTUAL: + return "virtual"; + case Token.VOLATILE: + return "volatile"; + case Token.WHERE: + return "where"; + case Token.WHILE: + return "while"; + case Token.ARGLIST: + return "__arglist"; + case Token.REFVALUE: + return "__refvalue"; + case Token.REFTYPE: + return "__reftype"; + case Token.MAKEREF: + return "__makeref"; + case Token.PARTIAL: + return "partial"; + case Token.ARROW: + return "=>"; + case Token.FROM: + case Token.FROM_FIRST: + return "from"; + case Token.JOIN: + return "join"; + case Token.ON: + return "on"; + case Token.EQUALS: + return "equals"; + case Token.SELECT: + return "select"; + case Token.GROUP: + return "group"; + case Token.BY: + return "by"; + case Token.LET: + return "let"; + case Token.ORDERBY: + return "orderby"; + case Token.ASCENDING: + return "ascending"; + case Token.DESCENDING: + return "descending"; + case Token.INTO: + return "into"; + case Token.GET: + return "get"; + case Token.SET: + return "set"; + case Token.OPEN_BRACE: + return "{"; + case Token.CLOSE_BRACE: + return "}"; + case Token.OPEN_BRACKET: + case Token.OPEN_BRACKET_EXPR: + return "["; + case Token.CLOSE_BRACKET: + return "]"; + case Token.OPEN_PARENS_CAST: + case Token.OPEN_PARENS_LAMBDA: + case Token.OPEN_PARENS: + return "("; + case Token.CLOSE_PARENS: + return ")"; + case Token.DOT: + return "."; + case Token.COMMA: + return ","; + case Token.DEFAULT_COLON: + return "default:"; + case Token.COLON: + return ":"; + case Token.SEMICOLON: + return ";"; + case Token.TILDE: + return "~"; + + case Token.PLUS: + case Token.UMINUS: + case Token.MINUS: + case Token.BANG: + case Token.OP_LT: + case Token.OP_GT: + case Token.BITWISE_AND: + case Token.BITWISE_OR: + case Token.STAR: + case Token.PERCENT: + case Token.DIV: + case Token.CARRET: + case Token.OP_INC: + case Token.OP_DEC: + case Token.OP_SHIFT_LEFT: + case Token.OP_SHIFT_RIGHT: + case Token.OP_LE: + case Token.OP_GE: + case Token.OP_EQ: + case Token.OP_NE: + case Token.OP_AND: + case Token.OP_OR: + case Token.OP_PTR: + case Token.OP_COALESCING: + case Token.OP_MULT_ASSIGN: + case Token.OP_DIV_ASSIGN: + case Token.OP_MOD_ASSIGN: + case Token.OP_ADD_ASSIGN: + case Token.OP_SUB_ASSIGN: + case Token.OP_SHIFT_LEFT_ASSIGN: + case Token.OP_SHIFT_RIGHT_ASSIGN: + case Token.OP_AND_ASSIGN: + case Token.OP_XOR_ASSIGN: + case Token.OP_OR_ASSIGN: + return ""; + + case Token.BOOL: + case Token.BYTE: + case Token.CHAR: + case Token.VOID: + case Token.DECIMAL: + case Token.DOUBLE: + case Token.FLOAT: + case Token.INT: + case Token.LONG: + case Token.SBYTE: + case Token.SHORT: + case Token.STRING: + case Token.UINT: + case Token.ULONG: + case Token.USHORT: + case Token.OBJECT: + return "type"; + + case Token.ASSIGN: + return "="; + case Token.OP_GENERICS_LT: + case Token.GENERIC_DIMENSION: + return "<"; + case Token.OP_GENERICS_GT: + return ">"; + case Token.INTERR: + case Token.INTERR_NULLABLE: + return "?"; + case Token.DOUBLE_COLON: + return "::"; + case Token.LITERAL: + return "value"; + case Token.IDENTIFIER: + case Token.AWAIT: + return "identifier"; + + case Token.EOF: + return "end-of-file"; + + // All of these are internal. + case Token.NONE: + case Token.ERROR: + case Token.FIRST_KEYWORD: + case Token.EVAL_COMPILATION_UNIT_PARSER: + case Token.EVAL_USING_DECLARATIONS_UNIT_PARSER: + case Token.EVAL_STATEMENT_PARSER: + case Token.LAST_KEYWORD: + case Token.GENERATE_COMPLETION: + case Token.COMPLETE_COMPLETION: + return ""; + + // A bit more robust. + default: + return yyNames [token]; + } +} + +/* end end end */ +} +#line default +namespace yydebug { + using System; + internal interface yyDebug { + void push (int state, Object value); + void lex (int state, int token, string name, Object value); + void shift (int from, int to, int errorFlag); + void pop (int state); + void discard (int state, int token, string name, Object value); + void reduce (int from, int to, int rule, string text, int len); + void shift (int from, int to); + void accept (Object value); + void error (string message); + void reject (); + } + + class yyDebugSimple : yyDebug { + void println (string s){ + Console.Error.WriteLine (s); + } + + public void push (int state, Object value) { + println ("push\tstate "+state+"\tvalue "+value); + } + + public void lex (int state, int token, string name, Object value) { + println("lex\tstate "+state+"\treading "+name+"\tvalue "+value); + } + + public void shift (int from, int to, int errorFlag) { + switch (errorFlag) { + default: // normally + println("shift\tfrom state "+from+" to "+to); + break; + case 0: case 1: case 2: // in error recovery + println("shift\tfrom state "+from+" to "+to + +"\t"+errorFlag+" left to recover"); + break; + case 3: // normally + println("shift\tfrom state "+from+" to "+to+"\ton error"); + break; + } + } + + public void pop (int state) { + println("pop\tstate "+state+"\ton error"); + } + + public void discard (int state, int token, string name, Object value) { + println("discard\tstate "+state+"\ttoken "+name+"\tvalue "+value); + } + + public void reduce (int from, int to, int rule, string text, int len) { + println("reduce\tstate "+from+"\tuncover "+to + +"\trule ("+rule+") "+text); + } + + public void shift (int from, int to) { + println("goto\tfrom state "+from+" to "+to); + } + + public void accept (Object value) { + println("accept\tvalue "+value); + } + + public void error (string message) { + println("error\t"+message); + } + + public void reject () { + println("reject"); + } + + } +} +// %token constants + class Token { + public const int EOF = 257; + public const int NONE = 258; + public const int ERROR = 259; + public const int FIRST_KEYWORD = 260; + public const int ABSTRACT = 261; + public const int AS = 262; + public const int ADD = 263; + public const int BASE = 264; + public const int BOOL = 265; + public const int BREAK = 266; + public const int BYTE = 267; + public const int CASE = 268; + public const int CATCH = 269; + public const int CHAR = 270; + public const int CHECKED = 271; + public const int CLASS = 272; + public const int CONST = 273; + public const int CONTINUE = 274; + public const int DECIMAL = 275; + public const int DEFAULT = 276; + public const int DELEGATE = 277; + public const int DO = 278; + public const int DOUBLE = 279; + public const int ELSE = 280; + public const int ENUM = 281; + public const int EVENT = 282; + public const int EXPLICIT = 283; + public const int EXTERN = 284; + public const int FALSE = 285; + public const int FINALLY = 286; + public const int FIXED = 287; + public const int FLOAT = 288; + public const int FOR = 289; + public const int FOREACH = 290; + public const int GOTO = 291; + public const int IF = 292; + public const int IMPLICIT = 293; + public const int IN = 294; + public const int INT = 295; + public const int INTERFACE = 296; + public const int INTERNAL = 297; + public const int IS = 298; + public const int LOCK = 299; + public const int LONG = 300; + public const int NAMESPACE = 301; + public const int NEW = 302; + public const int NULL = 303; + public const int OBJECT = 304; + public const int OPERATOR = 305; + public const int OUT = 306; + public const int OVERRIDE = 307; + public const int PARAMS = 308; + public const int PRIVATE = 309; + public const int PROTECTED = 310; + public const int PUBLIC = 311; + public const int READONLY = 312; + public const int REF = 313; + public const int RETURN = 314; + public const int REMOVE = 315; + public const int SBYTE = 316; + public const int SEALED = 317; + public const int SHORT = 318; + public const int SIZEOF = 319; + public const int STACKALLOC = 320; + public const int STATIC = 321; + public const int STRING = 322; + public const int STRUCT = 323; + public const int SWITCH = 324; + public const int THIS = 325; + public const int THROW = 326; + public const int TRUE = 327; + public const int TRY = 328; + public const int TYPEOF = 329; + public const int UINT = 330; + public const int ULONG = 331; + public const int UNCHECKED = 332; + public const int UNSAFE = 333; + public const int USHORT = 334; + public const int USING = 335; + public const int VIRTUAL = 336; + public const int VOID = 337; + public const int VOLATILE = 338; + public const int WHERE = 339; + public const int WHILE = 340; + public const int ARGLIST = 341; + public const int PARTIAL = 342; + public const int ARROW = 343; + public const int FROM = 344; + public const int FROM_FIRST = 345; + public const int JOIN = 346; + public const int ON = 347; + public const int EQUALS = 348; + public const int SELECT = 349; + public const int GROUP = 350; + public const int BY = 351; + public const int LET = 352; + public const int ORDERBY = 353; + public const int ASCENDING = 354; + public const int DESCENDING = 355; + public const int INTO = 356; + public const int INTERR_NULLABLE = 357; + public const int EXTERN_ALIAS = 358; + public const int REFVALUE = 359; + public const int REFTYPE = 360; + public const int MAKEREF = 361; + public const int ASYNC = 362; + public const int AWAIT = 363; + public const int INTERR_OPERATOR = 364; + public const int GET = 365; + public const int SET = 366; + public const int LAST_KEYWORD = 367; + public const int OPEN_BRACE = 368; + public const int CLOSE_BRACE = 369; + public const int OPEN_BRACKET = 370; + public const int CLOSE_BRACKET = 371; + public const int OPEN_PARENS = 372; + public const int CLOSE_PARENS = 373; + public const int DOT = 374; + public const int COMMA = 375; + public const int COLON = 376; + public const int SEMICOLON = 377; + public const int TILDE = 378; + public const int PLUS = 379; + public const int MINUS = 380; + public const int BANG = 381; + public const int ASSIGN = 382; + public const int OP_LT = 383; + public const int OP_GT = 384; + public const int BITWISE_AND = 385; + public const int BITWISE_OR = 386; + public const int STAR = 387; + public const int PERCENT = 388; + public const int DIV = 389; + public const int CARRET = 390; + public const int INTERR = 391; + public const int DOUBLE_COLON = 392; + public const int OP_INC = 393; + public const int OP_DEC = 394; + public const int OP_SHIFT_LEFT = 395; + public const int OP_SHIFT_RIGHT = 396; + public const int OP_LE = 397; + public const int OP_GE = 398; + public const int OP_EQ = 399; + public const int OP_NE = 400; + public const int OP_AND = 401; + public const int OP_OR = 402; + public const int OP_MULT_ASSIGN = 403; + public const int OP_DIV_ASSIGN = 404; + public const int OP_MOD_ASSIGN = 405; + public const int OP_ADD_ASSIGN = 406; + public const int OP_SUB_ASSIGN = 407; + public const int OP_SHIFT_LEFT_ASSIGN = 408; + public const int OP_SHIFT_RIGHT_ASSIGN = 409; + public const int OP_AND_ASSIGN = 410; + public const int OP_XOR_ASSIGN = 411; + public const int OP_OR_ASSIGN = 412; + public const int OP_PTR = 413; + public const int OP_COALESCING = 414; + public const int OP_GENERICS_LT = 415; + public const int OP_GENERICS_LT_DECL = 416; + public const int OP_GENERICS_GT = 417; + public const int LITERAL = 418; + public const int IDENTIFIER = 419; + public const int OPEN_PARENS_LAMBDA = 420; + public const int OPEN_PARENS_CAST = 421; + public const int GENERIC_DIMENSION = 422; + public const int DEFAULT_COLON = 423; + public const int OPEN_BRACKET_EXPR = 424; + public const int EVAL_STATEMENT_PARSER = 425; + public const int EVAL_COMPILATION_UNIT_PARSER = 426; + public const int EVAL_USING_DECLARATIONS_UNIT_PARSER = 427; + public const int DOC_SEE = 428; + public const int GENERATE_COMPLETION = 429; + public const int COMPLETE_COMPLETION = 430; + public const int UMINUS = 431; + public const int yyErrorCode = 256; + } + namespace yyParser { + using System; + /** thrown for irrecoverable syntax errors and stack overflow. + */ + internal class yyException : System.Exception { + public yyException (string message) : base (message) { + } + } + internal class yyUnexpectedEof : yyException { + public yyUnexpectedEof (string message) : base (message) { + } + public yyUnexpectedEof () : base ("") { + } + } + + /** must be implemented by a scanner object to supply input to the parser. + */ + internal interface yyInput { + /** move on to next token. + @return false if positioned beyond tokens. + @throws IOException on input error. + */ + bool advance (); // throws java.io.IOException; + /** classifies current token. + Should not be called if advance() returned false. + @return current %token or single character. + */ + int token (); + /** associated with current token. + Should not be called if advance() returned false. + @return value for token(). + */ + Object value (); + } + } +} // close outermost namespace, that MUST HAVE BEEN opened in the prolog diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/cs-parser.jay b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/cs-parser.jay new file mode 100644 index 000000000..25df449fb --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/cs-parser.jay @@ -0,0 +1,8195 @@ +%{ +// +// cs-parser.jay: The Parser for the C# compiler +// +// Authors: Miguel de Icaza (miguel@gnome.org) +// Ravi Pratap (ravi@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// Dual Licensed under the terms of the GNU GPL and the MIT X11 license +// +// (C) 2001 Ximian, Inc (http://www.ximian.com) +// (C) 2004-2011 Novell, Inc +// Copyright 2011-2012 Xamarin Inc. +// + +using System.Text; +using System.IO; +using System; +using System.Collections.Generic; + +namespace Mono.CSharp +{ + /// + /// The C# Parser + /// + public class CSharpParser + { + [Flags] + enum ParameterModifierType + { + Ref = 1 << 1, + Out = 1 << 2, + This = 1 << 3, + Params = 1 << 4, + Arglist = 1 << 5, + DefaultValue = 1 << 6, + + All = Ref | Out | This | Params | Arglist | DefaultValue, + PrimaryConstructor = Ref | Out | Params | DefaultValue + } + + static readonly object ModifierNone = 0; + + NamespaceContainer current_namespace; + TypeContainer current_container; + TypeDefinition current_type; + PropertyBase current_property; + EventProperty current_event; + EventField current_event_field; + FieldBase current_field; + + /// + /// Current block is used to add statements as we find + /// them. + /// + Block current_block; + + BlockVariable current_variable; + + Delegate current_delegate; + + AnonymousMethodExpression current_anonymous_method; + + /// + /// This is used by the unary_expression code to resolve + /// a name against a parameter. + /// + + // FIXME: This is very ugly and it's very hard to reset it correctly + // on all places, especially when some parameters are autogenerated. + ParametersCompiled current_local_parameters; + + bool parsing_anonymous_method; + + bool async_block; + + /// + /// An out-of-band stack. + /// + Stack oob_stack; + + /// + /// Controls the verbosity of the errors produced by the parser + /// + int yacc_verbose_flag; + + /// + /// Used by the interactive shell, flags whether EOF was reached + /// and an error was produced + /// + public bool UnexpectedEOF; + + /// + /// The current file. + /// + readonly CompilationSourceFile file; + + /// + /// Temporary Xml documentation cache. + /// For enum types, we need one more temporary store. + /// + string tmpComment; + string enumTypeComment; + + /// Current attribute target + string current_attr_target; + + ParameterModifierType valid_param_mod; + + bool default_parameter_used; + + /// When using the interactive parser, this holds the + /// resulting expression + public Class InteractiveResult; + + // + // Keeps track of global data changes to undo on parser error + // + public Undo undo; + + bool? interactive_async; + + Stack linq_clause_blocks; + + ModuleContainer module; + + readonly CompilerContext compiler; + readonly LanguageVersion lang_version; + readonly bool doc_support; + readonly CompilerSettings settings; + readonly Report report; + + // + // Instead of allocating carrier array everytime we + // share the bucket for very common constructs which can never + // be recursive + // + List parameters_bucket; + + // + // Full AST support members + // + LocationsBag lbag; + List> mod_locations; + Location parameterModifierLocation, savedLocation, savedEventAssignLocation; + Location savedAttrParenOpenLocation, savedAttrParenCloseLocation, savedOperatorLocation; + Stack> locationListStack = new Stack> (); // used for type parameters + Stack opt_intoStack = new Stack (); + + bool HadAttributeParens; + List attributeArgumentCommas = new List (); + List parameterListCommas = new List (); + Stack location_stack; +%} + +%token EOF +%token NONE /* This token is never returned by our lexer */ +%token ERROR // This is used not by the parser, but by the tokenizer. + // do not remove. + +/* + *These are the C# keywords + */ +%token FIRST_KEYWORD +%token ABSTRACT +%token AS +%token ADD +%token BASE +%token BOOL +%token BREAK +%token BYTE +%token CASE +%token CATCH +%token CHAR +%token CHECKED +%token CLASS +%token CONST +%token CONTINUE +%token DECIMAL +%token DEFAULT +%token DELEGATE +%token DO +%token DOUBLE +%token ELSE +%token ENUM +%token EVENT +%token EXPLICIT +%token EXTERN +%token FALSE +%token FINALLY +%token FIXED +%token FLOAT +%token FOR +%token FOREACH +%token GOTO +%token IF +%token IMPLICIT +%token IN +%token INT +%token INTERFACE +%token INTERNAL +%token IS +%token LOCK +%token LONG +%token NAMESPACE +%token NEW +%token NULL +%token OBJECT +%token OPERATOR +%token OUT +%token OVERRIDE +%token PARAMS +%token PRIVATE +%token PROTECTED +%token PUBLIC +%token READONLY +%token REF +%token RETURN +%token REMOVE +%token SBYTE +%token SEALED +%token SHORT +%token SIZEOF +%token STACKALLOC +%token STATIC +%token STRING +%token STRUCT +%token SWITCH +%token THIS +%token THROW +%token TRUE +%token TRY +%token TYPEOF +%token UINT +%token ULONG +%token UNCHECKED +%token UNSAFE +%token USHORT +%token USING +%token VIRTUAL +%token VOID +%token VOLATILE +%token WHERE +%token WHILE +%token ARGLIST +%token PARTIAL +%token ARROW +%token FROM +%token FROM_FIRST +%token JOIN +%token ON +%token EQUALS +%token SELECT +%token GROUP +%token BY +%token LET +%token ORDERBY +%token ASCENDING +%token DESCENDING +%token INTO +%token INTERR_NULLABLE +%token EXTERN_ALIAS +%token REFVALUE +%token REFTYPE +%token MAKEREF +%token ASYNC +%token AWAIT +%token INTERR_OPERATOR + +/* C# keywords which are not really keywords */ +%token GET +%token SET + +%left LAST_KEYWORD + +/* C# single character operators/punctuation. */ +%token OPEN_BRACE +%token CLOSE_BRACE +%token OPEN_BRACKET +%token CLOSE_BRACKET +%token OPEN_PARENS +%token CLOSE_PARENS + +%token DOT +%token COMMA +%token COLON +%token SEMICOLON +%token TILDE + +%token PLUS +%token MINUS +%token BANG +%token ASSIGN +%token OP_LT +%token OP_GT +%token BITWISE_AND +%token BITWISE_OR +%token STAR +%token PERCENT +%token DIV +%token CARRET +%token INTERR + +/* C# multi-character operators. */ +%token DOUBLE_COLON +%token OP_INC +%token OP_DEC +%token OP_SHIFT_LEFT +%token OP_SHIFT_RIGHT +%token OP_LE +%token OP_GE +%token OP_EQ +%token OP_NE +%token OP_AND +%token OP_OR +%token OP_MULT_ASSIGN +%token OP_DIV_ASSIGN +%token OP_MOD_ASSIGN +%token OP_ADD_ASSIGN +%token OP_SUB_ASSIGN +%token OP_SHIFT_LEFT_ASSIGN +%token OP_SHIFT_RIGHT_ASSIGN +%token OP_AND_ASSIGN +%token OP_XOR_ASSIGN +%token OP_OR_ASSIGN +%token OP_PTR +%token OP_COALESCING + +/* Generics <,> tokens */ +%token OP_GENERICS_LT +%token OP_GENERICS_LT_DECL +%token OP_GENERICS_GT + +%token LITERAL + +%token IDENTIFIER +%token OPEN_PARENS_LAMBDA +%token OPEN_PARENS_CAST +%token GENERIC_DIMENSION +%token DEFAULT_COLON +%token OPEN_BRACKET_EXPR + +// Make the parser go into eval mode parsing (statements and compilation units). +%token EVAL_STATEMENT_PARSER +%token EVAL_COMPILATION_UNIT_PARSER +%token EVAL_USING_DECLARATIONS_UNIT_PARSER + +%token DOC_SEE + +// +// This token is generated to trigger the completion engine at this point +// +%token GENERATE_COMPLETION + +// +// This token is return repeatedly after the first GENERATE_COMPLETION +// token is produced and before the final EOF +// +%token COMPLETE_COMPLETION + +/* Add precedence rules to solve dangling else s/r conflict */ +%nonassoc IF +%nonassoc ELSE + +/* Define the operator tokens and their precedences */ +%right ASSIGN +%right OP_COALESCING +%right INTERR +%left OP_OR +%left OP_AND +%left BITWISE_OR +%left BITWISE_AND +%left OP_SHIFT_LEFT OP_SHIFT_RIGHT +%left PLUS MINUS +%left STAR DIV PERCENT +%right BANG CARRET UMINUS +%nonassoc OP_INC OP_DEC +%left OPEN_PARENS +%left OPEN_BRACKET OPEN_BRACE +%left DOT + +%start compilation_unit +%% + +compilation_unit + : outer_declaration opt_EOF + { + Lexer.check_incorrect_doc_comment (); + } + | interactive_parsing { Lexer.CompleteOnEOF = false; } opt_EOF + | documentation_parsing + ; + +outer_declaration + : opt_extern_alias_directives opt_using_directives + | opt_extern_alias_directives opt_using_directives namespace_or_type_declarations opt_attributes + { + if ($4 != null) { + Attributes attrs = (Attributes) $4; + report.Error (1730, attrs.Attrs [0].Location, + "Assembly and module attributes must precede all other elements except using clauses and extern alias declarations"); + + current_namespace.UnattachedAttributes = attrs; + } + } + | opt_extern_alias_directives opt_using_directives attribute_sections + { + module.AddAttributes ((Attributes) $3, current_namespace); + } + | error + { + if (yyToken == Token.EXTERN_ALIAS) + report.Error (439, lexer.Location, "An extern alias declaration must precede all other elements"); + else + Error_SyntaxError (yyToken); + } + ; + +opt_EOF + : /* empty */ + | EOF + ; + +extern_alias_directives + : extern_alias_directive + | extern_alias_directives extern_alias_directive + ; + +extern_alias_directive + : EXTERN_ALIAS IDENTIFIER IDENTIFIER SEMICOLON + { + var lt = (LocatedToken) $2; + string s = lt.Value; + if (s != "alias") { + syntax_error (lt.Location, "`alias' expected"); + } else { + if (lang_version == LanguageVersion.ISO_1) + FeatureIsNotAvailable (lt.Location, "external alias"); + + lt = (LocatedToken) $3; + if (lt.Value == QualifiedAliasMember.GlobalAlias) { + RootNamespace.Error_GlobalNamespaceRedefined (report, lt.Location); + } + + var na = new UsingExternAlias (new SimpleMemberName (lt.Value, lt.Location), GetLocation ($1)); + current_namespace.AddUsing (na); + + lbag.AddLocation (na, GetLocation ($2), GetLocation ($4)); + } + } + | EXTERN_ALIAS error + { + Error_SyntaxError (yyToken); + } + ; + +using_directives + : using_directive + | using_directives using_directive + ; + +using_directive + : using_namespace + { + if (doc_support) + Lexer.doc_state = XmlCommentState.Allowed; + } + ; + +using_namespace + : USING namespace_or_type_expr SEMICOLON + { + var un = new UsingNamespace ((ATypeNameExpression) $2, GetLocation ($1)); + current_namespace.AddUsing (un); + + lbag.AddLocation (un, GetLocation ($3)); + } + | USING IDENTIFIER ASSIGN namespace_or_type_expr SEMICOLON + { + var lt = (LocatedToken) $2; + if (lang_version != LanguageVersion.ISO_1 && lt.Value == "global") { + report.Warning (440, 2, lt.Location, + "An alias named `global' will not be used when resolving `global::'. The global namespace will be used instead"); + } + + var un = new UsingAliasNamespace (new SimpleMemberName (lt.Value, lt.Location), (ATypeNameExpression) $4, GetLocation ($1)); + current_namespace.AddUsing (un); + lbag.AddLocation (un, GetLocation ($3), GetLocation ($5)); + } + | USING error + { + Error_SyntaxError (yyToken); + $$ = null; + } + ; + +// +// Strictly speaking, namespaces don't have attributes but +// we parse global attributes along with namespace declarations and then +// detach them +// +namespace_declaration + : opt_attributes NAMESPACE namespace_name + { + Attributes attrs = (Attributes) $1; + var name = (MemberName) $3; + if (attrs != null) { + bool valid_global_attrs = true; + if ((current_namespace.DeclarationFound || current_namespace != file)) { + valid_global_attrs = false; + } else { + foreach (var a in attrs.Attrs) { + if (a.ExplicitTarget == "assembly" || a.ExplicitTarget == "module") + continue; + + valid_global_attrs = false; + break; + } + } + + if (!valid_global_attrs) + report.Error (1671, name.Location, "A namespace declaration cannot have modifiers or attributes"); + } + + module.AddAttributes (attrs, current_namespace); + + var ns = new NamespaceContainer (name, current_namespace); + current_namespace.AddTypeContainer (ns); + current_container = current_namespace = ns; + } + OPEN_BRACE + { + if (doc_support) + Lexer.doc_state = XmlCommentState.Allowed; + } + opt_extern_alias_directives opt_using_directives opt_namespace_or_type_declarations CLOSE_BRACE opt_semicolon_error + { + if ($11 != null) + lbag.AddLocation (current_container, GetLocation ($2), GetLocation ($5), GetLocation ($10), GetLocation ($11)); + else + lbag.AddLocation (current_container, GetLocation ($2), GetLocation ($5), GetLocation ($10)); + + current_container = current_namespace = current_namespace.Parent; + } + | opt_attributes NAMESPACE namespace_name + { + report.Error (1514, lexer.Location, "Unexpected symbol `{0}', expecting `.' or `{{'", GetSymbolName (yyToken)); + + var name = (MemberName) $3; + var ns = new NamespaceContainer (name, current_namespace); + lbag.AddLocation (ns, GetLocation ($2)); + current_namespace.AddTypeContainer (ns); + } + ; + +opt_semicolon_error + : /* empty */ + | SEMICOLON + | error + { + Error_SyntaxError (yyToken); + $$ = null; + } + ; + +namespace_name + : IDENTIFIER + { + var lt = (LocatedToken) $1; + $$ = new MemberName (lt.Value, lt.Location); + } + | namespace_name DOT IDENTIFIER + { + var lt = (LocatedToken) $3; + $$ = new MemberName ((MemberName) $1, lt.Value, lt.Location); + lbag.AddLocation ($$, GetLocation ($2)); + } + | error + { + Error_SyntaxError (yyToken); + $$ = new MemberName ("", lexer.Location); + } + ; + +opt_semicolon + : /* empty */ + | SEMICOLON + ; + +opt_comma + : /* empty */ + | COMMA + ; + +opt_using_directives + : /* empty */ + | using_directives + ; + +opt_extern_alias_directives + : /* empty */ + | extern_alias_directives + ; + +opt_namespace_or_type_declarations + : /* empty */ + | namespace_or_type_declarations + ; + +namespace_or_type_declarations + : namespace_or_type_declaration + | namespace_or_type_declarations namespace_or_type_declaration + ; + +namespace_or_type_declaration + : type_declaration + { + if ($1 != null) { + TypeContainer ds = (TypeContainer)$1; + + if ((ds.ModFlags & (Modifiers.PRIVATE | Modifiers.PROTECTED)) != 0){ + report.Error (1527, ds.Location, + "Namespace elements cannot be explicitly declared as private, protected or protected internal"); + } + + // Here is a trick, for explicit attributes we don't know where they belong to until + // we parse succeeding declaration hence we parse them as normal and re-attach them + // when we know whether they are global (assembly:, module:) or local (type:). + if (ds.OptAttributes != null) { + ds.OptAttributes.ConvertGlobalAttributes (ds, current_namespace, !current_namespace.DeclarationFound && current_namespace == file); + } + } + current_namespace.DeclarationFound = true; + } + | namespace_declaration + { + current_namespace.DeclarationFound = true; + } + | attribute_sections CLOSE_BRACE { + current_namespace.UnattachedAttributes = (Attributes) $1; + report.Error (1518, lexer.Location, "Attributes must be attached to class, delegate, enum, interface or struct"); + lexer.putback ('}'); + } + ; + +type_declaration + : class_declaration + | struct_declaration + | interface_declaration + | enum_declaration + | delegate_declaration +// +// Enable this when we have handled all errors, because this acts as a generic fallback +// +// | error { +// Console.WriteLine ("Token=" + yyToken); +// report.Error (1518, GetLocation ($1), "Expected class, struct, interface, enum or delegate"); +// } + ; + +// +// Attributes +// + +opt_attributes + : /* empty */ + | attribute_sections + ; + +attribute_sections + : attribute_section + { + var sect = (List) $1; + $$ = new Attributes (sect); + } + | attribute_sections attribute_section + { + Attributes attrs = $1 as Attributes; + var sect = (List) $2; + if (attrs == null) + attrs = new Attributes (sect); + else if (sect != null) + attrs.AddAttributes (sect); + $$ = attrs; + } + ; + +attribute_section + : OPEN_BRACKET + { + PushLocation (GetLocation ($1)); + lexer.parsing_attribute_section = true; + } + attribute_section_cont + { + lexer.parsing_attribute_section = false; + $$ = $3; + } + ; + +attribute_section_cont + : attribute_target COLON + { + current_attr_target = (string) $1; + if (current_attr_target == "assembly" || current_attr_target == "module") { + Lexer.check_incorrect_doc_comment (); + } + } + attribute_list opt_comma CLOSE_BRACKET + { + // when attribute target is invalid + if (current_attr_target == string.Empty) + $$ = new List (0); + else + $$ = $4; + lbag.InsertLocation ($$, 0, GetLocation ($2)); + lbag.InsertLocation ($$, 0, PopLocation ()); + lbag.InsertLocation ($$, 0, PopLocation ()); + if ($5 != null) { + lbag.AddLocation ($$, GetLocation ($5), GetLocation ($6)); + } else { + lbag.AddLocation ($$, GetLocation ($6)); + } + + current_attr_target = null; + lexer.parsing_attribute_section = false; + } + | attribute_list opt_comma CLOSE_BRACKET + { + $$ = $1; + lbag.InsertLocation ($$, 0, PopLocation ()); + if ($2 != null) { + lbag.AddLocation ($$, GetLocation($2), GetLocation ($3)); + } else { + lbag.AddLocation ($$, GetLocation($3)); + } + } + | IDENTIFIER error + { + Error_SyntaxError (yyToken); + + var lt = (LocatedToken) $1; + var tne = new SimpleName (lt.Value, null, lt.Location); + + $$ = new List () { + new Attribute (null, tne, null, GetLocation ($1), false) + }; + } + | error + { + CheckAttributeTarget (yyToken, GetTokenName (yyToken), GetLocation ($1)); + $$ = null; + } + ; + +attribute_target + : IDENTIFIER + { + var lt = (LocatedToken) $1; + $$ = CheckAttributeTarget (yyToken, lt.Value, lt.Location); + PushLocation (GetLocation ($1)); + } + | EVENT { $$ = "event"; PushLocation (GetLocation ($1)); } + | RETURN { $$ = "return"; PushLocation (GetLocation ($1)); } + ; + +attribute_list + : attribute + { + $$ = new List (4) { (Attribute) $1 }; + } + | attribute_list COMMA attribute + { + var attrs = (List) $1; + if (attrs != null) { + attrs.Add ((Attribute) $3); + lbag.AddLocation (attrs, GetLocation ($2)); + } + + $$ = attrs; + } + ; + +attribute + : attribute_name + { + ++lexer.parsing_block; + } + opt_attribute_arguments + { + --lexer.parsing_block; + + var tne = (ATypeNameExpression) $1; + if (tne.HasTypeArguments) { + report.Error (404, tne.Location, "Attributes cannot be generic"); + } + Arguments [] arguments = (Arguments []) $3; + + $$ = new Attribute (current_attr_target, tne, (Arguments[]) $3, GetLocation ($1), lexer.IsEscapedIdentifier (tne)); + if (arguments != null) { + attributeArgumentCommas.Insert (0, savedAttrParenOpenLocation); + attributeArgumentCommas.Add (savedAttrParenCloseLocation); + lbag.AddLocation ($$, attributeArgumentCommas); + attributeArgumentCommas.Clear (); + } else if (HadAttributeParens) { + lbag.AddLocation ($$, savedAttrParenOpenLocation, savedAttrParenCloseLocation); + } + } + ; + +attribute_name + : namespace_or_type_expr + ; + +opt_attribute_arguments + : /* empty */ { $$ = null; HadAttributeParens = false; } + | OPEN_PARENS attribute_arguments CLOSE_PARENS + { + savedAttrParenOpenLocation = GetLocation ($1); + savedAttrParenCloseLocation = GetLocation ($3); + $$ = $2; + HadAttributeParens = true; + } + ; + + +attribute_arguments + : /* empty */ { $$ = null; } + | positional_or_named_argument + { + Arguments a = new Arguments (4); + a.Add ((Argument) $1); + $$ = new Arguments [] { a, null }; + } + | named_attribute_argument + { + Arguments a = new Arguments (4); + a.Add ((Argument) $1); + $$ = new Arguments [] { null, a }; + } + | attribute_arguments COMMA positional_or_named_argument + { + Arguments[] o = (Arguments[]) $1; + if (o [1] != null) { + report.Error (1016, ((Argument) $3).Expr.Location, "Named attribute arguments must appear after the positional arguments"); + o [0] = new Arguments (4); + } + + Arguments args = ((Arguments) o [0]); + if (args.Count > 0 && !($3 is NamedArgument) && args [args.Count - 1] is NamedArgument) + Error_NamedArgumentExpected ((NamedArgument) args [args.Count - 1]); + + args.Add ((Argument) $3); + attributeArgumentCommas.Add (GetLocation ($2)); + } + | attribute_arguments COMMA named_attribute_argument + { + Arguments[] o = (Arguments[]) $1; + if (o [1] == null) { + o [1] = new Arguments (4); + } + + ((Arguments) o [1]).Add ((Argument) $3); + attributeArgumentCommas.Add (GetLocation ($2)); + } + ; + +positional_or_named_argument + : expression + { + $$ = new Argument ((Expression) $1); + } + | named_argument + | error + { + Error_SyntaxError (yyToken); + $$ = null; + } + ; + +named_attribute_argument + : IDENTIFIER ASSIGN + { + ++lexer.parsing_block; + } + expression + { + --lexer.parsing_block; + var lt = (LocatedToken) $1; + $$ = new NamedArgument (lt.Value, lt.Location, (Expression) $4); + lbag.AddLocation ($$, GetLocation($2)); + } + ; + +named_argument + : identifier_inside_body COLON opt_named_modifier expression_or_error + { + if (lang_version <= LanguageVersion.V_3) + FeatureIsNotAvailable (GetLocation ($1), "named argument"); + + // Avoid boxing in common case (no modifier) + var arg_mod = $3 == null ? Argument.AType.None : (Argument.AType) $3; + + var lt = (LocatedToken) $1; + $$ = new NamedArgument (lt.Value, lt.Location, (Expression) $4, arg_mod); + lbag.AddLocation ($$, GetLocation($2)); + } + ; + +opt_named_modifier + : /* empty */ { $$ = null; } + | REF + { + $$ = Argument.AType.Ref; + } + | OUT + { + $$ = Argument.AType.Out; + } + ; + +opt_class_member_declarations + : /* empty */ + | class_member_declarations + ; + +class_member_declarations + : class_member_declaration + { + lexer.parsing_modifiers = true; + lexer.parsing_block = 0; + } + | class_member_declarations class_member_declaration + { + lexer.parsing_modifiers = true; + lexer.parsing_block = 0; + } + ; + +class_member_declaration + : constant_declaration + | field_declaration + | method_declaration + | property_declaration + | event_declaration + | indexer_declaration + | operator_declaration + | constructor_declaration + | primary_constructor_body + | destructor_declaration + | type_declaration + | attributes_without_members + | incomplete_member + | error + { + report.Error (1519, lexer.Location, "Unexpected symbol `{0}' in class, struct, or interface member declaration", + GetSymbolName (yyToken)); + $$ = null; + lexer.parsing_generic_declaration = false; + } + ; + +primary_constructor_body + : OPEN_BRACE + { + current_local_parameters = current_type.PrimaryConstructorParameters; + if (current_local_parameters == null) { + report.Error (9010, GetLocation ($1), "Primary constructor body is not allowed"); + current_local_parameters = ParametersCompiled.EmptyReadOnlyParameters; + } + + ++lexer.parsing_block; + start_block (GetLocation ($1)); + } + opt_statement_list block_end + { + current_local_parameters = null; + var t = current_type as ClassOrStruct; + if (t != null) { + var b = (ToplevelBlock) $4; + if (t.PrimaryConstructorBlock != null) { + report.Error (8041, b.StartLocation, "Primary constructor already has a body"); + } else { + t.PrimaryConstructorBlock = b; + } + } + } + ; + +struct_declaration + : opt_attributes + opt_modifiers + opt_partial + STRUCT + { + } + type_declaration_name + { + lexer.ConstraintsParsing = true; + valid_param_mod = ParameterModifierType.PrimaryConstructor; + push_current_container (new Struct (current_container, (MemberName) $6, (Modifiers) $2, (Attributes) $1), $3); + lbag.AddMember (current_container, GetModifierLocations (), GetLocation ($4)); + } + opt_primary_parameters + opt_class_base + opt_type_parameter_constraints_clauses + { + valid_param_mod = 0; + lexer.ConstraintsParsing = false; + + if ($8 != null) + current_type.PrimaryConstructorParameters = (ParametersCompiled) $8; + + if ($10 != null) + current_container.SetConstraints ((List) $10); + + if (doc_support) + current_container.PartialContainer.DocComment = Lexer.consume_doc_comment (); + + + lexer.parsing_modifiers = true; + } + OPEN_BRACE + { + if (doc_support) + Lexer.doc_state = XmlCommentState.Allowed; + } + opt_class_member_declarations CLOSE_BRACE + { + --lexer.parsing_declaration; + if (doc_support) + Lexer.doc_state = XmlCommentState.Allowed; + } + opt_semicolon + { + if ($16 == null) { + lbag.AppendToMember (current_container, GetLocation ($12), GetLocation ($15)); + } else { + lbag.AppendToMember (current_container, GetLocation ($12), GetLocation ($15), GetLocation ($17)); + } + $$ = pop_current_class (); + } + | opt_attributes opt_modifiers opt_partial STRUCT error + { + Error_SyntaxError (yyToken); + } + ; + +constant_declaration + : opt_attributes + opt_modifiers + CONST type IDENTIFIER + { + var lt = (LocatedToken) $5; + var mod = (Modifiers) $2; + current_field = new Const (current_type, (FullNamedExpression) $4, mod, new MemberName (lt.Value, lt.Location), (Attributes) $1); + current_type.AddMember (current_field); + + if ((mod & Modifiers.STATIC) != 0) { + report.Error (504, current_field.Location, "The constant `{0}' cannot be marked static", current_field.GetSignatureForError ()); + } + + $$ = current_field; + } + constant_initializer opt_constant_declarators SEMICOLON + { + if (doc_support) { + current_field.DocComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + current_field.Initializer = (ConstInitializer) $7; + lbag.AddMember (current_field, GetModifierLocations (), GetLocation ($3), GetLocation ($9)); + current_field = null; + } + | opt_attributes + opt_modifiers + CONST type error + { + Error_SyntaxError (yyToken); + + current_type.AddMember (new Const (current_type, (FullNamedExpression) $4, (Modifiers) $2, MemberName.Null, (Attributes) $1)); + } + ; + +opt_constant_declarators + : /* empty */ + | constant_declarators + ; + +constant_declarators + : constant_declarator + { + current_field.AddDeclarator ((FieldDeclarator) $1); + } + | constant_declarators constant_declarator + { + current_field.AddDeclarator ((FieldDeclarator) $2); + } + ; + +constant_declarator + : COMMA IDENTIFIER constant_initializer + { + var lt = (LocatedToken) $2; + $$ = new FieldDeclarator (new SimpleMemberName (lt.Value, lt.Location), (ConstInitializer) $3); + lbag.AddLocation ($$, GetLocation ($1)); + } + ; + +constant_initializer + : ASSIGN + { + ++lexer.parsing_block; + } + constant_initializer_expr + { + --lexer.parsing_block; + $$ = new ConstInitializer (current_field, (Expression) $3, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($1)); + } + | error + { + report.Error (145, lexer.Location, "A const field requires a value to be provided"); + $$ = null; + } + ; + +constant_initializer_expr + : constant_expression + | array_initializer + ; + +field_declaration + : opt_attributes + opt_modifiers + member_type IDENTIFIER + { + lexer.parsing_generic_declaration = false; + + FullNamedExpression type = (FullNamedExpression) $3; + if (type.Type != null && type.Type.Kind == MemberKind.Void) + report.Error (670, GetLocation ($3), "Fields cannot have void type"); + + var lt = (LocatedToken) $4; + current_field = new Field (current_type, type, (Modifiers) $2, new MemberName (lt.Value, lt.Location), (Attributes) $1); + current_type.AddField (current_field); + $$ = current_field; + } + opt_field_initializer + opt_field_declarators + SEMICOLON + { + if (doc_support) { + current_field.DocComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + lbag.AddMember (current_field, GetModifierLocations (), GetLocation ($8)); + $$ = current_field; + current_field = null; + } + | opt_attributes + opt_modifiers + FIXED simple_type IDENTIFIER + { + if (lang_version < LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation ($3), "fixed size buffers"); + + var lt = (LocatedToken) $5; + current_field = new FixedField (current_type, (FullNamedExpression) $4, (Modifiers) $2, + new MemberName (lt.Value, lt.Location), (Attributes) $1); + + current_type.AddField (current_field); + } + fixed_field_size opt_fixed_field_declarators SEMICOLON + { + if (doc_support) { + current_field.DocComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + current_field.Initializer = (ConstInitializer) $7; + lbag.AddMember (current_field, GetModifierLocations (), GetLocation ($3), GetLocation ($9)); + $$ = current_field; + current_field = null; + } + | opt_attributes + opt_modifiers + FIXED simple_type error + SEMICOLON + { + report.Error (1641, GetLocation ($5), "A fixed size buffer field must have the array size specifier after the field name"); + } + ; + +opt_field_initializer + : /* empty */ + | ASSIGN + { + ++lexer.parsing_block; + current_local_parameters = ParametersCompiled.EmptyReadOnlyParameters; + start_block (GetLocation ($1)); + } + variable_initializer + { + --lexer.parsing_block; + current_field.Initializer = (Expression) $3; + lbag.AppendToMember (current_field, GetLocation ($1)); + end_block (lexer.Location); + current_local_parameters = null; + } + ; + +opt_field_declarators + : /* empty */ + | field_declarators + ; + +field_declarators + : field_declarator + { + current_field.AddDeclarator ((FieldDeclarator) $1); + } + | field_declarators field_declarator + { + current_field.AddDeclarator ((FieldDeclarator) $2); + } + ; + +field_declarator + : COMMA IDENTIFIER + { + var lt = (LocatedToken) $2; + $$ = new FieldDeclarator (new SimpleMemberName (lt.Value, lt.Location), null); + lbag.AddLocation ($$, GetLocation ($1)); + } + | COMMA IDENTIFIER ASSIGN + { + ++lexer.parsing_block; + } + variable_initializer + { + --lexer.parsing_block; + var lt = (LocatedToken) $2; + $$ = new FieldDeclarator (new SimpleMemberName (lt.Value, lt.Location), (Expression) $5); + lbag.AddLocation ($$, GetLocation ($1), GetLocation ($3)); + } + ; + +opt_fixed_field_declarators + : /* empty */ + | fixed_field_declarators + ; + +fixed_field_declarators + : fixed_field_declarator + { + current_field.AddDeclarator ((FieldDeclarator) $1); + } + | fixed_field_declarators fixed_field_declarator + { + current_field.AddDeclarator ((FieldDeclarator) $2); + } + ; + +fixed_field_declarator + : COMMA IDENTIFIER fixed_field_size + { + var lt = (LocatedToken) $2; + $$ = new FieldDeclarator (new SimpleMemberName (lt.Value, lt.Location), (ConstInitializer) $3); + lbag.AddLocation ($$, GetLocation ($1)); + } + ; + +fixed_field_size + : OPEN_BRACKET + { + ++lexer.parsing_block; + } + expression CLOSE_BRACKET + { + --lexer.parsing_block; + $$ = new ConstInitializer (current_field, (Expression) $3, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($4)); + } + | OPEN_BRACKET error + { + report.Error (443, lexer.Location, "Value or constant expected"); + $$ = null; + } + ; + +variable_initializer + : expression + | array_initializer + | error + { + // It has to be here for the parent to safely restore artificial block + Error_SyntaxError (yyToken); + $$ = null; + } + ; + +method_declaration + : method_header + { + if (doc_support) + Lexer.doc_state = XmlCommentState.NotAllowed; + + // Was added earlier in the case of body being eof for full ast + } + method_body_expression_block + { + Method method = (Method) $1; + method.Block = (ToplevelBlock) $3; + async_block = false; + + if (method.Block == null) { + lbag.AppendToMember (method, savedLocation); // semicolon + method.ParameterInfo.CheckParameters (method); + + if ((method.ModFlags & Modifiers.ASYNC) != 0) { + report.Error (1994, method.Location, "`{0}': The async modifier can only be used with methods that have a body", + method.GetSignatureForError ()); + } + } else { + if (current_container.Kind == MemberKind.Interface) { + report.Error (531, method.Location, "`{0}': interface members cannot have a definition", + method.GetSignatureForError ()); + } + } + + current_local_parameters = null; + + if (doc_support) + Lexer.doc_state = XmlCommentState.Allowed; + } + ; + +method_header + : opt_attributes + opt_modifiers + member_type + method_declaration_name OPEN_PARENS + { + valid_param_mod = ParameterModifierType.All; + } + opt_formal_parameter_list CLOSE_PARENS + { + valid_param_mod = 0; + MemberName name = (MemberName) $4; + current_local_parameters = (ParametersCompiled) $7; + + var method = Method.Create (current_type, (FullNamedExpression) $3, (Modifiers) $2, + name, current_local_parameters, (Attributes) $1); + + current_type.AddMember (method); + + async_block = (method.ModFlags & Modifiers.ASYNC) != 0; + + if (doc_support) + method.DocComment = Lexer.consume_doc_comment (); + + lbag.AddMember (method, GetModifierLocations (), GetLocation ($5), GetLocation ($8)); + $$ = method; + + lexer.ConstraintsParsing = true; + } + opt_type_parameter_constraints_clauses + { + lexer.ConstraintsParsing = false; + + if ($10 != null) { + var method = (Method) $9; + method.SetConstraints ((List) $10); + } + + $$ = $9; + } + | opt_attributes + opt_modifiers + PARTIAL + VOID + { + lexer.parsing_generic_declaration = true; + } + method_declaration_name + OPEN_PARENS + { + lexer.parsing_generic_declaration = false; + valid_param_mod = ParameterModifierType.All; + } + opt_formal_parameter_list CLOSE_PARENS + { + lexer.ConstraintsParsing = true; + } + opt_type_parameter_constraints_clauses + { + lexer.ConstraintsParsing = false; + valid_param_mod = 0; + + MemberName name = (MemberName) $6; + current_local_parameters = (ParametersCompiled) $9; + + var modifiers = (Modifiers) $2; + modifiers |= Modifiers.PARTIAL; + + var method = Method.Create (current_type, new TypeExpression (compiler.BuiltinTypes.Void, GetLocation ($4)), + modifiers, name, current_local_parameters, (Attributes) $1); + + current_type.AddMember (method); + + async_block = (method.ModFlags & Modifiers.ASYNC) != 0; + + if ($12 != null) + method.SetConstraints ((List) $12); + + if (doc_support) + method.DocComment = Lexer.consume_doc_comment (); + + StoreModifierLocation (Modifiers.PARTIAL, GetLocation ($3)); + lbag.AddMember (method, GetModifierLocations (), GetLocation ($7), GetLocation ($10)); + $$ = method; + } + | opt_attributes + opt_modifiers + member_type + modifiers method_declaration_name OPEN_PARENS opt_formal_parameter_list CLOSE_PARENS + { + MemberName name = (MemberName) $5; + report.Error (1585, name.Location, + "Member modifier `{0}' must precede the member type and name", ModifiersExtensions.Name ((Modifiers) $4)); + + var method = Method.Create (current_type, (FullNamedExpression) $3, + 0, name, (ParametersCompiled) $7, (Attributes) $1); + + current_type.AddMember (method); + + current_local_parameters = (ParametersCompiled) $7; + + if (doc_support) + method.DocComment = Lexer.consume_doc_comment (); + + $$ = method; + } + | opt_attributes + opt_modifiers + member_type + method_declaration_name error + { + Error_SyntaxError (yyToken); + current_local_parameters = ParametersCompiled.Undefined; + + MemberName name = (MemberName) $4; + var method = Method.Create (current_type, (FullNamedExpression) $3, (Modifiers) $2, + name, current_local_parameters, (Attributes) $1); + + current_type.AddMember (method); + + if (doc_support) + method.DocComment = Lexer.consume_doc_comment (); + + $$ = method; + } + ; + +method_body_expression_block + : method_body + | expression_block + ; + +method_body + : block + | SEMICOLON { savedLocation = GetLocation ($1); $$ = null; } + ; + +expression_block + : ARROW + { + if (lang_version < LanguageVersion.V_6) { + FeatureIsNotAvailable (GetLocation ($1), "expression bodied members"); + } + + ++lexer.parsing_block; + start_block (GetLocation ($1)); + } + expression SEMICOLON + { + lexer.parsing_block = 0; + current_block.AddStatement (new ContextualReturn ((Expression) $3)); + var b = end_block (GetLocation ($4)); + b.IsCompilerGenerated = true; + $$ = b; + } + ; + +opt_formal_parameter_list + : /* empty */ { $$ = ParametersCompiled.EmptyReadOnlyParameters; } + | formal_parameter_list + ; + +formal_parameter_list + : fixed_parameters + { + var pars_list = (List) $1; + $$ = new ParametersCompiled (pars_list.ToArray ()); + lbag.AddLocation ($$, parameterListCommas); + } + | fixed_parameters COMMA parameter_array + { + var pars_list = (List) $1; + pars_list.Add ((Parameter) $3); + parameterListCommas.Add (GetLocation ($2)); + + $$ = new ParametersCompiled (pars_list.ToArray ()); + lbag.AddLocation ($$, parameterListCommas); + } + | fixed_parameters COMMA arglist_modifier + { + var pars_list = (List) $1; + pars_list.Add (new ArglistParameter (GetLocation ($3))); + parameterListCommas.Add (GetLocation ($2)); + + $$ = new ParametersCompiled (pars_list.ToArray (), true); + lbag.AddLocation ($$, parameterListCommas); + } + | parameter_array COMMA error + { + if ($1 != null) + report.Error (231, ((Parameter) $1).Location, "A params parameter must be the last parameter in a formal parameter list"); + + $$ = new ParametersCompiled (new Parameter[] { (Parameter) $1 } ); + lbag.AddLocation ($$, parameterListCommas); + } + | fixed_parameters COMMA parameter_array COMMA error + { + if ($3 != null) + report.Error (231, ((Parameter) $3).Location, "A params parameter must be the last parameter in a formal parameter list"); + + var pars_list = (List) $1; + pars_list.Add (new ArglistParameter (GetLocation ($3))); + parameterListCommas.Add (GetLocation ($2)); + parameterListCommas.Add (GetLocation ($4)); + + $$ = new ParametersCompiled (pars_list.ToArray (), true); + lbag.AddLocation ($$, parameterListCommas); + } + | arglist_modifier COMMA error + { + report.Error (257, GetLocation ($1), "An __arglist parameter must be the last parameter in a formal parameter list"); + + $$ = new ParametersCompiled (new Parameter [] { new ArglistParameter (GetLocation ($1)) }, true); + lbag.AddLocation ($$, parameterListCommas); + } + | fixed_parameters COMMA ARGLIST COMMA error + { + report.Error (257, GetLocation ($3), "An __arglist parameter must be the last parameter in a formal parameter list"); + + var pars_list = (List) $1; + pars_list.Add (new ArglistParameter (GetLocation ($3))); + parameterListCommas.Add (GetLocation ($2)); + parameterListCommas.Add (GetLocation ($4)); + + $$ = new ParametersCompiled (pars_list.ToArray (), true); + lbag.AddLocation ($$, parameterListCommas); + } + | parameter_array + { + $$ = new ParametersCompiled (new Parameter[] { (Parameter) $1 } ); + } + | arglist_modifier + { + $$ = new ParametersCompiled (new Parameter [] { new ArglistParameter (GetLocation ($1)) }, true); + } + | error + { + Error_SyntaxError (yyToken); + $$ = ParametersCompiled.EmptyReadOnlyParameters; + } + ; + +fixed_parameters + : fixed_parameter + { + parameters_bucket.Clear (); + Parameter p = (Parameter) $1; + parameters_bucket.Add (p); + parameterListCommas.Clear (); + default_parameter_used = p.HasDefaultValue; + $$ = parameters_bucket; + } + | fixed_parameters COMMA fixed_parameter + { + var pars = (List) $1; + Parameter p = (Parameter) $3; + if (p != null) { + if (p.HasExtensionMethodModifier) + report.Error (1100, p.Location, "The parameter modifier `this' can only be used on the first parameter"); + else if (!p.HasDefaultValue && default_parameter_used) + report.Error (1737, p.Location, "Optional parameter cannot precede required parameters"); + + default_parameter_used |= p.HasDefaultValue; + pars.Add (p); + + parameterListCommas.Add (GetLocation ($2)); + } + + $$ = $1; + } + ; + +fixed_parameter + : opt_attributes + opt_parameter_modifier + parameter_type + identifier_inside_body + { + var lt = (LocatedToken) $4; + $$ = new Parameter ((FullNamedExpression) $3, lt.Value, (Parameter.Modifier) $2, (Attributes) $1, lt.Location); + lbag.AddLocation ($$, parameterModifierLocation); + } + | opt_attributes + opt_parameter_modifier + parameter_type + identifier_inside_body OPEN_BRACKET CLOSE_BRACKET + { + var lt = (LocatedToken) $4; + report.Error (1552, lt.Location, "Array type specifier, [], must appear before parameter name"); + $$ = new Parameter ((FullNamedExpression) $3, lt.Value, (Parameter.Modifier) $2, (Attributes) $1, lt.Location); + lbag.AddLocation ($$, parameterModifierLocation); + } + | attribute_sections error + { + Error_SyntaxError (yyToken); + Location l = GetLocation ($2); + $$ = new Parameter (null, null, Parameter.Modifier.NONE, (Attributes) $1, l); + } + | opt_attributes + opt_parameter_modifier + parameter_type + error + { + Error_SyntaxError (yyToken); + Location l = GetLocation ($4); + $$ = new Parameter ((FullNamedExpression) $3, null, (Parameter.Modifier) $2, (Attributes) $1, l); + lbag.AddLocation ($$, parameterModifierLocation); + } + | opt_attributes + opt_parameter_modifier + parameter_type + identifier_inside_body + ASSIGN + { + ++lexer.parsing_block; + } + constant_expression + { + --lexer.parsing_block; + if (lang_version <= LanguageVersion.V_3) { + FeatureIsNotAvailable (GetLocation ($5), "optional parameter"); + } + + Parameter.Modifier mod = (Parameter.Modifier) $2; + if (mod != Parameter.Modifier.NONE) { + switch (mod) { + case Parameter.Modifier.REF: + case Parameter.Modifier.OUT: + report.Error (1741, GetLocation ($2), "Cannot specify a default value for the `{0}' parameter", + Parameter.GetModifierSignature (mod)); + break; + + case Parameter.Modifier.This: + report.Error (1743, GetLocation ($2), "Cannot specify a default value for the `{0}' parameter", + Parameter.GetModifierSignature (mod)); + break; + default: + throw new NotImplementedException (mod.ToString ()); + } + + mod = Parameter.Modifier.NONE; + } + + if ((valid_param_mod & ParameterModifierType.DefaultValue) == 0) + report.Error (1065, GetLocation ($5), "Optional parameter is not valid in this context"); + + var lt = (LocatedToken) $4; + $$ = new Parameter ((FullNamedExpression) $3, lt.Value, mod, (Attributes) $1, lt.Location); + lbag.AddLocation ($$, parameterModifierLocation, GetLocation ($5)); // parameterModifierLocation should be ignored when mod == NONE + + if ($7 != null) + ((Parameter) $$).DefaultValue = new DefaultParameterValueExpression ((Expression) $7); + } + ; + +opt_parameter_modifier + : /* empty */ { $$ = Parameter.Modifier.NONE; } + | parameter_modifiers + ; + +parameter_modifiers + : parameter_modifier + { + $$ = $1; + } + | parameter_modifiers parameter_modifier + { + Parameter.Modifier p2 = (Parameter.Modifier)$2; + Parameter.Modifier mod = (Parameter.Modifier)$1 | p2; + if (((Parameter.Modifier)$1 & p2) == p2) { + Error_DuplicateParameterModifier (lexer.Location, p2); + } else { + switch (mod & ~Parameter.Modifier.This) { + case Parameter.Modifier.REF: + report.Error (1101, lexer.Location, "The parameter modifiers `this' and `ref' cannot be used altogether"); + break; + case Parameter.Modifier.OUT: + report.Error (1102, lexer.Location, "The parameter modifiers `this' and `out' cannot be used altogether"); + break; + default: + report.Error (1108, lexer.Location, "A parameter cannot have specified more than one modifier"); + break; + } + } + $$ = mod; + } + ; + +parameter_modifier + : REF + { + if ((valid_param_mod & ParameterModifierType.Ref) == 0) + Error_ParameterModifierNotValid ("ref", GetLocation ($1)); + parameterModifierLocation = GetLocation ($1); + $$ = Parameter.Modifier.REF; + } + | OUT + { + if ((valid_param_mod & ParameterModifierType.Out) == 0) + Error_ParameterModifierNotValid ("out", GetLocation ($1)); + parameterModifierLocation = GetLocation ($1); + $$ = Parameter.Modifier.OUT; + } + | THIS + { + if ((valid_param_mod & ParameterModifierType.This) == 0) + Error_ParameterModifierNotValid ("this", GetLocation ($1)); + + if (lang_version <= LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation ($1), "extension methods"); + parameterModifierLocation = GetLocation ($1); + $$ = Parameter.Modifier.This; + } + ; + +parameter_array + : opt_attributes params_modifier type IDENTIFIER + { + var lt = (LocatedToken) $4; + $$ = new ParamsParameter ((FullNamedExpression) $3, lt.Value, (Attributes) $1, lt.Location); + lbag.AddLocation ($$, savedLocation); + } + | opt_attributes params_modifier type IDENTIFIER ASSIGN constant_expression + { + report.Error (1751, GetLocation ($2), "Cannot specify a default value for a parameter array"); + + var lt = (LocatedToken) $4; + $$ = new ParamsParameter ((FullNamedExpression) $3, lt.Value, (Attributes) $1, lt.Location); + lbag.AddLocation ($$, savedLocation); + } + | opt_attributes params_modifier type error + { + Error_SyntaxError (yyToken); + + $$ = new ParamsParameter ((FullNamedExpression) $3, null, (Attributes) $1, Location.Null); + } + ; + +params_modifier + : PARAMS + { + if ((valid_param_mod & ParameterModifierType.Params) == 0) + report.Error (1670, (GetLocation ($1)), "The `params' modifier is not allowed in current context"); + savedLocation = GetLocation ($1); + } + | PARAMS parameter_modifier + { + Parameter.Modifier mod = (Parameter.Modifier)$2; + if ((mod & Parameter.Modifier.This) != 0) { + report.Error (1104, GetLocation ($1), "The parameter modifiers `this' and `params' cannot be used altogether"); + } else { + report.Error (1611, GetLocation ($1), "The params parameter cannot be declared as ref or out"); + } + savedLocation = GetLocation ($1); + } + | PARAMS params_modifier + { + Error_DuplicateParameterModifier (GetLocation ($1), Parameter.Modifier.PARAMS); + } + ; + +arglist_modifier + : ARGLIST + { + if ((valid_param_mod & ParameterModifierType.Arglist) == 0) + report.Error (1669, GetLocation ($1), "__arglist is not valid in this context"); + } + ; + +property_declaration + : opt_attributes + opt_modifiers + member_type + member_declaration_name + { + lexer.parsing_generic_declaration = false; + if (doc_support) + tmpComment = Lexer.consume_doc_comment (); + } + OPEN_BRACE + { + var type = (FullNamedExpression) $3; + current_property = new Property (current_type, type, (Modifiers) $2, + (MemberName) $4, (Attributes) $1); + + if (type.Type != null && type.Type.Kind == MemberKind.Void) + report.Error (547, GetLocation ($3), "`{0}': property or indexer cannot have void type", current_property.GetSignatureForError ()); + + current_type.AddMember (current_property); + lbag.AddMember (current_property, GetModifierLocations (), GetLocation ($6)); + + lexer.PropertyParsing = true; + } + accessor_declarations + { + lexer.PropertyParsing = false; + + if (doc_support) + current_property.DocComment = ConsumeStoredComment (); + } + CLOSE_BRACE + { + lbag.AppendToMember (current_property, GetLocation ($10)); + lexer.parsing_modifiers = true; + } + opt_property_initializer + { + current_property = null; + } + | opt_attributes + opt_modifiers + member_type + member_declaration_name + { + lexer.parsing_generic_declaration = false; + if (doc_support) + tmpComment = Lexer.consume_doc_comment (); + current_local_parameters = ParametersCompiled.EmptyReadOnlyParameters; + } + expression_block + { + var type = (FullNamedExpression) $3; + var property = new Property (current_type, type, (Modifiers) $2, + (MemberName) $4, (Attributes) $1); + + property.Get = new Property.GetMethod (property, Modifiers.COMPILER_GENERATED, null, property.Location); + property.Get.Block = (ToplevelBlock) $6; + + if (current_container.Kind == MemberKind.Interface) { + report.Error (531, property.Get.Block.StartLocation, + "`{0}': interface members cannot have a definition", property.GetSignatureForError ()); + } + + if (type.Type != null && type.Type.Kind == MemberKind.Void) + report.Error (547, GetLocation ($3), "`{0}': property or indexer cannot have void type", property.GetSignatureForError ()); + + current_type.AddMember (property); + + current_local_parameters = null; + } + ; + +opt_property_initializer + : /* empty */ + | ASSIGN + { + ++lexer.parsing_block; + current_local_parameters = ParametersCompiled.EmptyReadOnlyParameters; + start_block (GetLocation ($1)); + } + property_initializer SEMICOLON + { + --lexer.parsing_block; + ((Property)current_property).Initializer = (Expression) $3; + lbag.AppendToMember (current_property, GetLocation ($1), GetLocation ($4)); + end_block (GetLocation ($4)); + current_local_parameters = null; + } + ; + +property_initializer + : expression + | array_initializer + ; + +indexer_declaration + : opt_attributes opt_modifiers + member_type indexer_declaration_name OPEN_BRACKET + { + valid_param_mod = ParameterModifierType.Params | ParameterModifierType.DefaultValue; + } + opt_formal_parameter_list CLOSE_BRACKET + { + valid_param_mod = 0; + var type = (FullNamedExpression) $3; + Indexer indexer = new Indexer (current_type, type, (MemberName) $4, (Modifiers) $2, (ParametersCompiled) $7, (Attributes) $1); + + current_property = indexer; + + current_type.AddIndexer (indexer); + lbag.AddMember (current_property, GetModifierLocations (), GetLocation ($5), GetLocation ($8)); + + if (type.Type != null && type.Type.Kind == MemberKind.Void) + report.Error (620, GetLocation ($3), "`{0}': indexer return type cannot be `void'", indexer.GetSignatureForError ()); + + if (indexer.ParameterInfo.IsEmpty) { + report.Error (1551, GetLocation ($5), "Indexers must have at least one parameter"); + } + + if (doc_support) { + tmpComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + lexer.PropertyParsing = true; + current_local_parameters = (ParametersCompiled) $7; + } + indexer_body + { + lexer.PropertyParsing = false; + current_local_parameters = null; + + if (current_property.AccessorFirst != null && current_property.AccessorFirst.Block == null) + ((Indexer) current_property).ParameterInfo.CheckParameters (current_property); + + if (doc_support) + current_property.DocComment = ConsumeStoredComment (); + + current_property = null; + } + ; + +indexer_body + : OPEN_BRACE accessor_declarations CLOSE_BRACE + { + lbag.AppendToMember (current_property, GetLocation ($1), GetLocation ($3)); + } + | expression_block + { + current_property.Get = new Indexer.GetIndexerMethod (current_property, Modifiers.COMPILER_GENERATED, current_local_parameters, null, current_property.Location); + current_property.Get.Block = (ToplevelBlock) $1; + } + ; + +accessor_declarations + : get_accessor_declaration + | get_accessor_declaration accessor_declarations + | set_accessor_declaration + | set_accessor_declaration accessor_declarations + | error + { + if (yyToken == Token.CLOSE_BRACE) { + report.Error (548, lexer.Location, "`{0}': property or indexer must have at least one accessor", current_property.GetSignatureForError ()); + } else { + if (yyToken == Token.SEMICOLON) + report.Error (1597, lexer.Location, "Semicolon after method or accessor block is not valid"); + else + report.Error (1014, GetLocation ($1), "A get or set accessor expected"); + } + } + ; + +get_accessor_declaration + : opt_attributes opt_modifiers GET + { + if ($2 != ModifierNone && lang_version == LanguageVersion.ISO_1) { + FeatureIsNotAvailable (GetLocation ($2), "access modifiers on properties"); + } + + if (current_property.Get != null) { + report.Error (1007, GetLocation ($3), "Property accessor already defined"); + } + + if (current_property is Indexer) { + current_property.Get = new Indexer.GetIndexerMethod (current_property, (Modifiers) $2, ((Indexer)current_property).ParameterInfo.Clone (), + (Attributes) $1, GetLocation ($3)); + } else { + current_property.Get = new Property.GetMethod (current_property, + (Modifiers) $2, (Attributes) $1, GetLocation ($3)); + } + + current_local_parameters = current_property.Get.ParameterInfo; + lexer.PropertyParsing = false; + } + accessor_body + { + if ($5 != null) { + current_property.Get.Block = (ToplevelBlock) $5; + + if (current_container.Kind == MemberKind.Interface) { + report.Error (531, current_property.Get.Block.StartLocation, + "`{0}': interface members cannot have a definition", current_property.Get.GetSignatureForError ()); + } + lbag.AddMember (current_property.Get, GetModifierLocations ()); + } else { + lbag.AddMember (current_property.Get, GetModifierLocations (), savedLocation); + } + + current_local_parameters = null; + lexer.PropertyParsing = true; + + if (doc_support) + if (Lexer.doc_state == XmlCommentState.Error) + Lexer.doc_state = XmlCommentState.NotAllowed; + } + ; + +set_accessor_declaration + : opt_attributes opt_modifiers SET + { + if ($2 != ModifierNone && lang_version == LanguageVersion.ISO_1) { + FeatureIsNotAvailable (GetLocation ($2), "access modifiers on properties"); + } + + if (current_property.Set != null) { + report.Error (1007, GetLocation ($3), "Property accessor already defined"); + } + + if (current_property is Indexer) { + current_property.Set = new Indexer.SetIndexerMethod (current_property, (Modifiers) $2, + ParametersCompiled.MergeGenerated (compiler, + ((Indexer)current_property).ParameterInfo, true, new Parameter ( + current_property.TypeExpression, "value", Parameter.Modifier.NONE, null, GetLocation ($3)), + null), + (Attributes) $1, GetLocation ($3)); + } else { + current_property.Set = new Property.SetMethod (current_property, (Modifiers) $2, + ParametersCompiled.CreateImplicitParameter (current_property.TypeExpression, GetLocation ($3)), + (Attributes) $1, GetLocation ($3)); + } + + current_local_parameters = current_property.Set.ParameterInfo; + lexer.PropertyParsing = false; + } + accessor_body + { + if ($5 != null) { + current_property.Set.Block = (ToplevelBlock) $5; + + if (current_container.Kind == MemberKind.Interface) { + report.Error (531, current_property.Set.Block.StartLocation, + "`{0}': interface members cannot have a definition", current_property.Set.GetSignatureForError ()); + } + lbag.AddMember (current_property.Set, GetModifierLocations ()); + } else { + lbag.AddMember (current_property.Set, GetModifierLocations (), savedLocation); + } + + current_local_parameters = null; + lexer.PropertyParsing = true; + + if (doc_support + && Lexer.doc_state == XmlCommentState.Error) + Lexer.doc_state = XmlCommentState.NotAllowed; + } + ; + +accessor_body + : block + | SEMICOLON + { + savedLocation = GetLocation ($1); + $$ = null; + } + | error + { + Error_SyntaxError (1043, yyToken, "Invalid accessor body"); + $$ = null; + } + ; + +interface_declaration + : opt_attributes + opt_modifiers + opt_partial + INTERFACE + { + } + type_declaration_name + { + lexer.ConstraintsParsing = true; + push_current_container (new Interface (current_container, (MemberName) $6, (Modifiers) $2, (Attributes) $1), $3); + lbag.AddMember (current_container, GetModifierLocations (), GetLocation ($4)); + } + opt_class_base + opt_type_parameter_constraints_clauses + { + lexer.ConstraintsParsing = false; + + if ($9 != null) + current_container.SetConstraints ((List) $9); + + if (doc_support) { + current_container.PartialContainer.DocComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + lexer.parsing_modifiers = true; + } + OPEN_BRACE opt_interface_member_declarations CLOSE_BRACE + { + --lexer.parsing_declaration; + if (doc_support) + Lexer.doc_state = XmlCommentState.Allowed; + } + opt_semicolon + { + if ($15 == null) { + lbag.AppendToMember (current_container, GetLocation ($11), GetLocation ($13)); + } else { + lbag.AppendToMember (current_container, GetLocation ($11), GetLocation ($13), GetLocation ($15)); + } + $$ = pop_current_class (); + } + | opt_attributes opt_modifiers opt_partial INTERFACE error + { + Error_SyntaxError (yyToken); + } + ; + +opt_interface_member_declarations + : /* empty */ + | interface_member_declarations + ; + +interface_member_declarations + : interface_member_declaration + { + lexer.parsing_modifiers = true; + lexer.parsing_block = 0; + } + | interface_member_declarations interface_member_declaration + { + lexer.parsing_modifiers = true; + lexer.parsing_block = 0; + } + ; + +interface_member_declaration + : constant_declaration + { + report.Error (525, GetLocation ($1), "Interfaces cannot contain fields or constants"); + } + | field_declaration + { + report.Error (525, GetLocation ($1), "Interfaces cannot contain fields or constants"); + } + | method_declaration + | property_declaration + | event_declaration + | indexer_declaration + | operator_declaration + { + report.Error (567, GetLocation ($1), "Interfaces cannot contain operators"); + } + | constructor_declaration + { + report.Error (526, GetLocation ($1), "Interfaces cannot contain contructors"); + } + | type_declaration + { + report.Error (524, GetLocation ($1), "Interfaces cannot declare classes, structs, interfaces, delegates, or enumerations"); + } + ; + +operator_declaration + : opt_attributes opt_modifiers operator_declarator + { + } + method_body_expression_block + { + OperatorDeclaration decl = (OperatorDeclaration) $3; + if (decl != null) { + Operator op = new Operator ( + current_type, decl.optype, decl.ret_type, (Modifiers) $2, + current_local_parameters, + (ToplevelBlock) $5, (Attributes) $1, decl.location); + + if (op.Block == null) + op.ParameterInfo.CheckParameters (op); + + if (doc_support) { + op.DocComment = tmpComment; + Lexer.doc_state = XmlCommentState.Allowed; + } + + // Note again, checking is done in semantic analysis + current_type.AddOperator (op); + + lbag.AddMember (op, GetModifierLocations (), lbag.GetLocations (decl)); + if ($5 == null) { // Semicolon + lbag.AddLocation (op, savedLocation); + } + } + + current_local_parameters = null; + } + ; + +operator_type + : type_expression_or_array + | VOID + { + report.Error (590, GetLocation ($1), "User-defined operators cannot return void"); + $$ = new TypeExpression (compiler.BuiltinTypes.Void, GetLocation ($1)); + } + ; + +operator_declarator + : operator_type OPERATOR overloadable_operator OPEN_PARENS + { + valid_param_mod = ParameterModifierType.DefaultValue; + } + opt_formal_parameter_list CLOSE_PARENS + { + valid_param_mod = 0; + + Location loc = GetLocation ($2); + Operator.OpType op = (Operator.OpType) $3; + current_local_parameters = (ParametersCompiled)$6; + + int p_count = current_local_parameters.Count; + if (p_count == 1) { + if (op == Operator.OpType.Addition) + op = Operator.OpType.UnaryPlus; + else if (op == Operator.OpType.Subtraction) + op = Operator.OpType.UnaryNegation; + } + + if (IsUnaryOperator (op)) { + if (p_count == 2) { + report.Error (1020, loc, "Overloadable binary operator expected"); + } else if (p_count != 1) { + report.Error (1535, loc, "Overloaded unary operator `{0}' takes one parameter", + Operator.GetName (op)); + } + } else { + if (p_count == 1) { + report.Error (1019, loc, "Overloadable unary operator expected"); + } else if (p_count != 2) { + report.Error (1534, loc, "Overloaded binary operator `{0}' takes two parameters", + Operator.GetName (op)); + } + } + + if (doc_support) { + tmpComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.NotAllowed; + } + + $$ = new OperatorDeclaration (op, (FullNamedExpression) $1, loc); + lbag.AddLocation ($$, GetLocation ($2), savedOperatorLocation, GetLocation ($4), GetLocation ($7)); + } + | conversion_operator_declarator + ; + +overloadable_operator +// Unary operators: + : BANG { $$ = Operator.OpType.LogicalNot; savedOperatorLocation = GetLocation ($1); } + | TILDE { $$ = Operator.OpType.OnesComplement; savedOperatorLocation = GetLocation ($1); } + | OP_INC { $$ = Operator.OpType.Increment; savedOperatorLocation = GetLocation ($1); } + | OP_DEC { $$ = Operator.OpType.Decrement; savedOperatorLocation = GetLocation ($1); } + | TRUE { $$ = Operator.OpType.True; savedOperatorLocation = GetLocation ($1); } + | FALSE { $$ = Operator.OpType.False; savedOperatorLocation = GetLocation ($1); } +// Unary and binary: + | PLUS { $$ = Operator.OpType.Addition; savedOperatorLocation = GetLocation ($1); } + | MINUS { $$ = Operator.OpType.Subtraction; savedOperatorLocation = GetLocation ($1); } +// Binary: + | STAR { $$ = Operator.OpType.Multiply; savedOperatorLocation = GetLocation ($1); } + | DIV { $$ = Operator.OpType.Division; savedOperatorLocation = GetLocation ($1); } + | PERCENT { $$ = Operator.OpType.Modulus; savedOperatorLocation = GetLocation ($1); } + | BITWISE_AND { $$ = Operator.OpType.BitwiseAnd; savedOperatorLocation = GetLocation ($1); } + | BITWISE_OR { $$ = Operator.OpType.BitwiseOr; savedOperatorLocation = GetLocation ($1); } + | CARRET { $$ = Operator.OpType.ExclusiveOr; savedOperatorLocation = GetLocation ($1); } + | OP_SHIFT_LEFT { $$ = Operator.OpType.LeftShift; savedOperatorLocation = GetLocation ($1); } + | OP_SHIFT_RIGHT { $$ = Operator.OpType.RightShift; savedOperatorLocation = GetLocation ($1); } + | OP_EQ { $$ = Operator.OpType.Equality; savedOperatorLocation = GetLocation ($1); } + | OP_NE { $$ = Operator.OpType.Inequality; savedOperatorLocation = GetLocation ($1); } + | OP_GT { $$ = Operator.OpType.GreaterThan; savedOperatorLocation = GetLocation ($1); } + | OP_LT { $$ = Operator.OpType.LessThan; savedOperatorLocation = GetLocation ($1); } + | OP_GE { $$ = Operator.OpType.GreaterThanOrEqual; savedOperatorLocation = GetLocation ($1); } + | OP_LE { $$ = Operator.OpType.LessThanOrEqual; savedOperatorLocation = GetLocation ($1); } + ; + +conversion_operator_declarator + : IMPLICIT OPERATOR type OPEN_PARENS + { + valid_param_mod = ParameterModifierType.DefaultValue; + } + opt_formal_parameter_list CLOSE_PARENS + { + valid_param_mod = 0; + + Location loc = GetLocation ($2); + current_local_parameters = (ParametersCompiled)$6; + + if (current_local_parameters.Count != 1) { + report.Error (1535, loc, "Overloaded unary operator `implicit' takes one parameter"); + } + + if (doc_support) { + tmpComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.NotAllowed; + } + + $$ = new OperatorDeclaration (Operator.OpType.Implicit, (FullNamedExpression) $3, loc); + lbag.AddLocation ($$, GetLocation ($1), GetLocation ($2), GetLocation ($4), GetLocation ($7)); + } + | EXPLICIT OPERATOR type OPEN_PARENS + { + valid_param_mod = ParameterModifierType.DefaultValue; + } + opt_formal_parameter_list CLOSE_PARENS + { + valid_param_mod = 0; + + Location loc = GetLocation ($2); + current_local_parameters = (ParametersCompiled)$6; + + if (current_local_parameters.Count != 1) { + report.Error (1535, loc, "Overloaded unary operator `explicit' takes one parameter"); + } + + if (doc_support) { + tmpComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.NotAllowed; + } + + $$ = new OperatorDeclaration (Operator.OpType.Explicit, (FullNamedExpression) $3, loc); + lbag.AddLocation ($$, GetLocation ($1), GetLocation ($2), GetLocation ($4), GetLocation ($7)); + } + | IMPLICIT error + { + Error_SyntaxError (yyToken); + current_local_parameters = ParametersCompiled.EmptyReadOnlyParameters; + $$ = new OperatorDeclaration (Operator.OpType.Implicit, null, GetLocation ($1)); + } + | EXPLICIT error + { + Error_SyntaxError (yyToken); + current_local_parameters = ParametersCompiled.EmptyReadOnlyParameters; + $$ = new OperatorDeclaration (Operator.OpType.Explicit, null, GetLocation ($1)); + } + ; + +constructor_declaration + : constructor_declarator + constructor_body + { + Constructor c = (Constructor) $1; + c.Block = (ToplevelBlock) $2; + + if (doc_support) + c.DocComment = ConsumeStoredComment (); + + current_local_parameters = null; + if (doc_support) + Lexer.doc_state = XmlCommentState.Allowed; + } + ; + +constructor_declarator + : opt_attributes + opt_modifiers + IDENTIFIER + { + if (doc_support) { + tmpComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + valid_param_mod = ParameterModifierType.All; + } + OPEN_PARENS opt_formal_parameter_list CLOSE_PARENS + { + valid_param_mod = 0; + current_local_parameters = (ParametersCompiled) $6; + + var lt = (LocatedToken) $3; + var mods = (Modifiers) $2; + var c = new Constructor (current_type, lt.Value, mods, (Attributes) $1, current_local_parameters, lt.Location); + + if (lt.Value != current_container.MemberName.Name) { + report.Error (1520, c.Location, "Class, struct, or interface method must have a return type"); + } else if ((mods & Modifiers.STATIC) != 0) { + if ((mods & Modifiers.AccessibilityMask) != 0){ + report.Error (515, c.Location, + "`{0}': static constructor cannot have an access modifier", + c.GetSignatureForError ()); + } + } + + current_type.AddConstructor (c); + lbag.AddMember (c, GetModifierLocations (), GetLocation ($5), GetLocation ($7)); + $$ = c; + + // + // start block here, so possible anonymous methods inside + // constructor initializer can get correct parent block + // + start_block (lexer.Location); + } + opt_constructor_initializer + { + if ($9 != null) { + var c = (Constructor) $8; + c.Initializer = (ConstructorInitializer) $9; + + if (c.IsStatic) { + report.Error (514, c.Location, + "`{0}': static constructor cannot have an explicit `this' or `base' constructor call", + c.GetSignatureForError ()); + } + } + + $$ = $8; + } + ; + +constructor_body + : block_prepared + | SEMICOLON { current_block = null; $$ = null; } + ; + +opt_constructor_initializer + : /* Empty */ + | constructor_initializer + ; + +constructor_initializer + : COLON BASE OPEN_PARENS + { + ++lexer.parsing_block; + } + opt_argument_list CLOSE_PARENS + { + --lexer.parsing_block; + $$ = new ConstructorBaseInitializer ((Arguments) $5, GetLocation ($2)); + lbag.AddLocation ($$, GetLocation ($1), GetLocation ($3), GetLocation ($6)); + } + | COLON THIS OPEN_PARENS + { + ++lexer.parsing_block; + } + opt_argument_list CLOSE_PARENS + { + --lexer.parsing_block; + $$ = new ConstructorThisInitializer ((Arguments) $5, GetLocation ($2)); + lbag.AddLocation ($$, GetLocation ($1), GetLocation ($3), GetLocation ($6)); + } + | COLON error + { + Error_SyntaxError (yyToken); + $$ = new ConstructorThisInitializer (null, GetLocation ($2)); + lbag.AddLocation ($$, GetLocation ($1)); + } + | error + { + Error_SyntaxError (yyToken); + $$ = null; + } + ; + +destructor_declaration + : opt_attributes opt_modifiers TILDE + { + if (doc_support) { + tmpComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.NotAllowed; + } + + current_local_parameters = ParametersCompiled.EmptyReadOnlyParameters; + } + IDENTIFIER OPEN_PARENS CLOSE_PARENS method_body + { + var lt = (LocatedToken) $5; + if (lt.Value != current_container.MemberName.Name){ + report.Error (574, lt.Location, "Name of destructor must match name of class"); + } else if (current_container.Kind != MemberKind.Class){ + report.Error (575, lt.Location, "Only class types can contain destructor"); + } + + Destructor d = new Destructor (current_type, (Modifiers) $2, + ParametersCompiled.EmptyReadOnlyParameters, (Attributes) $1, lt.Location); + d.Identifier = lt.Value; + if (doc_support) + d.DocComment = ConsumeStoredComment (); + + d.Block = (ToplevelBlock) $8; + current_type.AddMember (d); + lbag.AddMember (d, GetModifierLocations (), GetLocation ($3), GetLocation ($6), GetLocation ($7)); + + current_local_parameters = null; + } + ; + +event_declaration + : opt_attributes + opt_modifiers + EVENT type member_declaration_name + { + current_event_field = new EventField (current_type, (FullNamedExpression) $4, (Modifiers) $2, (MemberName) $5, (Attributes) $1); + current_type.AddMember (current_event_field); + + if (current_event_field.MemberName.ExplicitInterface != null) { + report.Error (71, current_event_field.Location, "`{0}': An explicit interface implementation of an event must use property syntax", + current_event_field.GetSignatureForError ()); + } + + $$ = current_event_field; + } + opt_event_initializer + opt_event_declarators + SEMICOLON + { + if (doc_support) { + current_event_field.DocComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + if (current_event_field.Initializer != null) { + lbag.AddMember (current_event_field, GetModifierLocations (), GetLocation ($3), savedEventAssignLocation, GetLocation ($9)); + } else { + lbag.AddMember (current_event_field, GetModifierLocations (), GetLocation ($3), GetLocation ($9)); + } + current_event_field = null; + } + | opt_attributes + opt_modifiers + EVENT type member_declaration_name + OPEN_BRACE + { + current_event = new EventProperty (current_type, (FullNamedExpression) $4, (Modifiers) $2, (MemberName) $5, (Attributes) $1); + current_type.AddMember (current_event); + lbag.AddMember (current_event, GetModifierLocations (), GetLocation ($3), GetLocation ($6)); + + lexer.EventParsing = true; + } + event_accessor_declarations + { + if (current_container.Kind == MemberKind.Interface) + report.Error (69, GetLocation ($6), "Event in interface cannot have add or remove accessors"); + + lexer.EventParsing = false; + } + CLOSE_BRACE + { + if (doc_support) { + current_event.DocComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + lbag.AppendToMember (current_event, GetLocation ($9)); + current_event = null; + current_local_parameters = null; + } + | opt_attributes + opt_modifiers + EVENT type error + { + Error_SyntaxError (yyToken); + + current_type.AddMember (new EventField (current_type, (FullNamedExpression) $4, (Modifiers) $2, MemberName.Null, (Attributes) $1)); + } + ; + +opt_event_initializer + : /* empty */ + | ASSIGN + { + ++lexer.parsing_block; + } + event_variable_initializer + { + --lexer.parsing_block; + savedEventAssignLocation = GetLocation ($1); + current_event_field.Initializer = (Expression) $3; + } + ; + +opt_event_declarators + : /* empty */ + | event_declarators + ; + +event_declarators + : event_declarator + { + current_event_field.AddDeclarator ((FieldDeclarator) $1); + } + | event_declarators event_declarator + { + current_event_field.AddDeclarator ((FieldDeclarator) $2); + } + ; + +event_declarator + : COMMA IDENTIFIER + { + var lt = (LocatedToken) $2; + $$ = new FieldDeclarator (new SimpleMemberName (lt.Value, lt.Location), null); + lbag.AddLocation ($$, GetLocation ($1)); + } + | COMMA IDENTIFIER ASSIGN + { + ++lexer.parsing_block; + } + event_variable_initializer + { + --lexer.parsing_block; + var lt = (LocatedToken) $2; + $$ = new FieldDeclarator (new SimpleMemberName (lt.Value, lt.Location), (Expression) $5); + lbag.AddLocation ($$, GetLocation ($1), GetLocation ($3)); + } + ; + +event_variable_initializer + : { + if (current_container.Kind == MemberKind.Interface) { + report.Error (68, lexer.Location, "`{0}': event in interface cannot have an initializer", + current_event_field.GetSignatureForError ()); + } + + if ((current_event_field.ModFlags & Modifiers.ABSTRACT) != 0) { + report.Error (74, lexer.Location, "`{0}': abstract event cannot have an initializer", + current_event_field.GetSignatureForError ()); + } + } + variable_initializer + { + $$ = $2; + } + ; + +event_accessor_declarations + : add_accessor_declaration remove_accessor_declaration + | remove_accessor_declaration add_accessor_declaration + | add_accessor_declaration + { + report.Error (65, lexer.Location, "`{0}': event property must have both add and remove accessors", + current_event.GetSignatureForError ()); + } + | remove_accessor_declaration + { + report.Error (65, lexer.Location, "`{0}': event property must have both add and remove accessors", + current_event.GetSignatureForError ()); + } + | error + { + report.Error (1055, GetLocation ($1), "An add or remove accessor expected"); + $$ = null; + } + ; + +add_accessor_declaration + : opt_attributes opt_modifiers ADD + { + if ($2 != ModifierNone) { + report.Error (1609, GetLocation ($2), "Modifiers cannot be placed on event accessor declarations"); + } + + current_event.Add = new EventProperty.AddDelegateMethod (current_event, (Attributes) $1, GetLocation ($3)); + current_local_parameters = current_event.Add.ParameterInfo; + + lbag.AddMember (current_event.Add, GetModifierLocations ()); + lexer.EventParsing = false; + } + event_accessor_block + { + lexer.EventParsing = true; + + current_event.Add.Block = (ToplevelBlock) $5; + + if (current_container.Kind == MemberKind.Interface) { + report.Error (531, current_event.Add.Block.StartLocation, + "`{0}': interface members cannot have a definition", current_event.Add.GetSignatureForError ()); + } + + current_local_parameters = null; + } + ; + +remove_accessor_declaration + : opt_attributes opt_modifiers REMOVE + { + if ($2 != ModifierNone) { + report.Error (1609, GetLocation ($2), "Modifiers cannot be placed on event accessor declarations"); + } + + current_event.Remove = new EventProperty.RemoveDelegateMethod (current_event, (Attributes) $1, GetLocation ($3)); + current_local_parameters = current_event.Remove.ParameterInfo; + + lbag.AddMember (current_event.Remove, GetModifierLocations ()); + lexer.EventParsing = false; + } + event_accessor_block + { + lexer.EventParsing = true; + + current_event.Remove.Block = (ToplevelBlock) $5; + + if (current_container.Kind == MemberKind.Interface) { + report.Error (531, current_event.Remove.Block.StartLocation, + "`{0}': interface members cannot have a definition", current_event.Remove.GetSignatureForError ()); + } + + current_local_parameters = null; + } + ; + +event_accessor_block + : opt_semicolon + { + report.Error (73, lexer.Location, "An add or remove accessor must have a body"); + $$ = null; + } + | block; + ; + +attributes_without_members + : attribute_sections CLOSE_BRACE + { + current_type.UnattachedAttributes = (Attributes) $1; + report.Error (1519, GetLocation ($1), "An attribute is missing member declaration"); + lexer.putback ('}'); + } + ; + +// For full ast try to recover incomplete ambiguous member +// declaration in form on class X { public int } +incomplete_member + : opt_attributes opt_modifiers member_type CLOSE_BRACE + { + report.Error (1519, lexer.Location, "Unexpected symbol `}' in class, struct, or interface member declaration"); + + lexer.putback ('}'); + + lexer.parsing_generic_declaration = false; + FullNamedExpression type = (FullNamedExpression) $3; + current_field = new Field (current_type, type, (Modifiers) $2, MemberName.Null, (Attributes) $1); + current_type.AddField (current_field); + lbag.AddMember (current_field, GetModifierLocations ()); + $$ = current_field; + } + ; + +enum_declaration + : opt_attributes + opt_modifiers + ENUM + type_declaration_name + opt_enum_base + { + if (doc_support) + enumTypeComment = Lexer.consume_doc_comment (); + } + OPEN_BRACE + { + if (doc_support) + Lexer.doc_state = XmlCommentState.Allowed; + + MemberName name = (MemberName) $4; + if (name.IsGeneric) { + report.Error (1675, name.Location, "Enums cannot have type parameters"); + } + + push_current_container (new Enum (current_container, (FullNamedExpression) $5, (Modifiers) $2, name, (Attributes) $1), null); + if ($5 != null) { + lbag.AddMember (current_container, GetModifierLocations (), GetLocation ($3), savedLocation, GetLocation ($7)); + } else { + lbag.AddMember (current_container, GetModifierLocations (), GetLocation ($3), GetLocation ($7)); + } + } + opt_enum_member_declarations + { + lexer.parsing_modifiers = true; + + // here will be evaluated after CLOSE_BLACE is consumed. + if (doc_support) + Lexer.doc_state = XmlCommentState.Allowed; + } + CLOSE_BRACE opt_semicolon + { + lbag.AppendToMember (current_container, GetLocation ($11)); + if ($12 != null) { + lbag.AppendToMember (current_container, GetLocation ($12)); + } + if (doc_support) + current_container.DocComment = enumTypeComment; + + --lexer.parsing_declaration; + +// if (doc_support) +// em.DocComment = ev.DocComment; + + $$ = pop_current_class (); + } + ; + +opt_enum_base + : /* empty */ + | COLON type + { + savedLocation = GetLocation ($1); + $$ = $2; + } + | COLON error + { + Error_TypeExpected (GetLocation ($1)); + $$ = null; + } + ; + +opt_enum_member_declarations + : /* empty */ + | enum_member_declarations + | enum_member_declarations COMMA + { + lbag.AppendToMember (current_container, GetLocation ($2)); + } + ; + +enum_member_declarations + : enum_member_declaration + | enum_member_declarations COMMA enum_member_declaration + { + lbag.AppendToMember (current_container, GetLocation ($2)); + $$ = $3; + } + ; + +enum_member_declaration + : opt_attributes IDENTIFIER + { + var lt = (LocatedToken) $2; + var em = new EnumMember ((Enum) current_type, new MemberName (lt.Value, lt.Location), (Attributes) $1); + ((Enum) current_type).AddEnumMember (em); + + if (doc_support) { + em.DocComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + $$ = em; + } + | opt_attributes IDENTIFIER + { + ++lexer.parsing_block; + if (doc_support) { + tmpComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.NotAllowed; + } + } + ASSIGN constant_expression + { + --lexer.parsing_block; + + var lt = (LocatedToken) $2; + var em = new EnumMember ((Enum) current_type, new MemberName (lt.Value, lt.Location), (Attributes) $1); + em.Initializer = new ConstInitializer (em, (Expression) $5, GetLocation ($4)); + ((Enum) current_type).AddEnumMember (em); + + if (doc_support) + em.DocComment = ConsumeStoredComment (); + + $$ = em; + } + | opt_attributes IDENTIFIER error + { + Error_SyntaxError (yyToken); + + var lt = (LocatedToken) $2; + var em = new EnumMember ((Enum) current_type, new MemberName (lt.Value, lt.Location), (Attributes) $1); + ((Enum) current_type).AddEnumMember (em); + + if (doc_support) { + em.DocComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + $$ = em; + } + | attributes_without_members + ; + +delegate_declaration + : opt_attributes + opt_modifiers + DELEGATE + member_type type_declaration_name + OPEN_PARENS + { + valid_param_mod = ParameterModifierType.Ref | ParameterModifierType.Out | ParameterModifierType.Params | ParameterModifierType.DefaultValue; + } + opt_formal_parameter_list CLOSE_PARENS + { + valid_param_mod = 0; + + ParametersCompiled p = (ParametersCompiled) $8; + + Delegate del = new Delegate (current_container, (FullNamedExpression) $4, (Modifiers) $2, (MemberName) $5, p, (Attributes) $1); + + p.CheckParameters (del); + + current_container.AddTypeContainer (del); + + current_delegate = del; + lexer.ConstraintsParsing = true; + } + opt_type_parameter_constraints_clauses + { + lexer.ConstraintsParsing = false; + } + SEMICOLON + { + if (doc_support) { + current_delegate.DocComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + if ($11 != null) + current_delegate.SetConstraints ((List) $11); + lbag.AddMember (current_delegate, GetModifierLocations (), GetLocation ($3), GetLocation ($6), GetLocation ($9), GetLocation ($13)); + + $$ = current_delegate; + + current_delegate = null; + } + ; + +opt_nullable + : /* empty */ + | INTERR_NULLABLE + { + if (lang_version < LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation ($1), "nullable types"); + + $$ = ComposedTypeSpecifier.CreateNullable (GetLocation ($1)); + } + ; + +namespace_or_type_expr + : member_name + | qualified_alias_member IDENTIFIER opt_type_argument_list + { + var lt1 = (LocatedToken) $1; + var lt2 = (LocatedToken) $2; + + $$ = new QualifiedAliasMember (lt1.Value, lt2.Value, (TypeArguments) $3, lt1.Location); + lbag.AddLocation ($$, savedLocation, GetLocation ($2)); + } + | qualified_alias_member IDENTIFIER generic_dimension + { + var lt1 = (LocatedToken) $1; + var lt2 = (LocatedToken) $2; + var qam = new QualifiedAliasMember (lt1.Value, lt2.Value, (int) $3, lt1.Location); + lbag.AddLocation (qam.TypeArguments, Lexer.GenericDimensionLocations); + $$ = qam; + lbag.AddLocation ($$, GetLocation ($2)); + } + ; + +member_name + : simple_name_expr + | namespace_or_type_expr DOT IDENTIFIER opt_type_argument_list + { + var lt = (LocatedToken) $3; + $$ = new MemberAccess ((Expression) $1, lt.Value, (TypeArguments) $4, lt.Location); + lbag.AddLocation ($$, GetLocation ($2)); + } + | namespace_or_type_expr DOT IDENTIFIER generic_dimension + { + var lt = (LocatedToken) $3; + var ma = new MemberAccess ((Expression) $1, lt.Value, (int) $4, lt.Location); + lbag.AddLocation (ma.TypeArguments, Lexer.GenericDimensionLocations); + $$ = ma; + lbag.AddLocation ($$, GetLocation ($2)); + } + ; + +simple_name_expr + : IDENTIFIER opt_type_argument_list + { + var lt = (LocatedToken) $1; + $$ = new SimpleName (lt.Value, (TypeArguments)$2, lt.Location); + } + | IDENTIFIER generic_dimension + { + var lt = (LocatedToken) $1; + var sn = new SimpleName (lt.Value, (int) $2, lt.Location); + lbag.AddLocation (sn.TypeArguments, Lexer.GenericDimensionLocations); + $$ = sn; + } + ; + +// +// Generics arguments (any type, without attributes) +// +opt_type_argument_list + : /* empty */ + | OP_GENERICS_LT type_arguments OP_GENERICS_GT + { + if (lang_version < LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation ($1), "generics"); + var list = locationListStack.Pop (); + list.Add (GetLocation ($1)); + list.Add (GetLocation ($2)); + lbag.AddLocation ($2, list); + + $$ = $2;; + } + | OP_GENERICS_LT error + { + Error_TypeExpected (lexer.Location); + $$ = new TypeArguments (); + } + ; + +type_arguments + : type + { + TypeArguments type_args = new TypeArguments (); + type_args.Add ((FullNamedExpression) $1); + $$ = type_args; + locationListStack.Push (new List ()); + } + | type_arguments COMMA type + { + TypeArguments type_args = (TypeArguments) $1; + type_args.Add ((FullNamedExpression) $3); + $$ = type_args; + locationListStack.Peek ().Add (GetLocation ($2)); + } + ; + +// +// Generics parameters (identifiers only, with attributes), used in type or method declarations +// +type_declaration_name + : IDENTIFIER + { + lexer.parsing_generic_declaration = true; + } + opt_type_parameter_list + { + lexer.parsing_generic_declaration = false; + var lt = (LocatedToken) $1; + $$ = new MemberName (lt.Value, (TypeParameters)$3, lt.Location); + } + ; + +member_declaration_name + : method_declaration_name + { + MemberName mn = (MemberName)$1; + if (mn.TypeParameters != null) + syntax_error (mn.Location, string.Format ("Member `{0}' cannot declare type arguments", + mn.GetSignatureForError ())); + } + ; + +method_declaration_name + : type_declaration_name + | explicit_interface IDENTIFIER opt_type_parameter_list + { + lexer.parsing_generic_declaration = false; + var lt = (LocatedToken) $2; + $$ = new MemberName (lt.Value, (TypeParameters) $3, (ATypeNameExpression) $1, lt.Location); + } + ; + +indexer_declaration_name + : THIS + { + lexer.parsing_generic_declaration = false; + $$ = new MemberName (TypeDefinition.DefaultIndexerName, GetLocation ($1)); + } + | explicit_interface THIS + { + lexer.parsing_generic_declaration = false; + $$ = new MemberName (TypeDefinition.DefaultIndexerName, null, (ATypeNameExpression) $1, GetLocation ($2)); + } + ; + +explicit_interface + : IDENTIFIER opt_type_argument_list DOT + { + var lt = (LocatedToken) $1; + $$ = new SimpleName (lt.Value, (TypeArguments) $2, lt.Location); + lbag.AddLocation ($$, GetLocation ($3)); + } + | qualified_alias_member IDENTIFIER opt_type_argument_list DOT + { + var lt1 = (LocatedToken) $1; + var lt2 = (LocatedToken) $2; + + $$ = new QualifiedAliasMember (lt1.Value, lt2.Value, (TypeArguments) $3, lt1.Location); + lbag.AddLocation ($$, savedLocation, GetLocation ($4)); + } + | explicit_interface IDENTIFIER opt_type_argument_list DOT + { + var lt = (LocatedToken) $2; + $$ = new MemberAccess ((ATypeNameExpression) $1, lt.Value, (TypeArguments) $3, lt.Location); + lbag.AddLocation ($$, GetLocation ($4)); + } + ; + +opt_type_parameter_list + : /* empty */ + | OP_GENERICS_LT_DECL type_parameters OP_GENERICS_GT + { + if (lang_version < LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation ($1), "generics"); + + $$ = $2; + var list = locationListStack.Pop (); + list.Add (GetLocation ($1)); + list.Add (GetLocation ($2)); + lbag.AddLocation ($2, list); + } + ; + +type_parameters + : type_parameter + { + var tparams = new TypeParameters (); + tparams.Add ((TypeParameter)$1); + $$ = tparams; + locationListStack.Push (new List ()); + } + | type_parameters COMMA type_parameter + { + var tparams = (TypeParameters) $1; + tparams.Add ((TypeParameter)$3); + $$ = tparams; + locationListStack.Peek ().Add (GetLocation ($2)); + } + ; + +type_parameter + : opt_attributes opt_type_parameter_variance IDENTIFIER + { + var lt = (LocatedToken)$3; + var variance = (VarianceDecl) $2; + $$ = new TypeParameter (new MemberName (lt.Value, lt.Location), (Attributes)$1, variance); + if (variance != null) + lbag.AddLocation ($$, savedLocation); + } + | error + { + if (GetTokenName (yyToken) == "type") + report.Error (81, GetLocation ($1), "Type parameter declaration must be an identifier not a type"); + else + Error_SyntaxError (yyToken); + + $$ = new TypeParameter (MemberName.Null, null, null); + } + ; + +// +// All types where void is allowed +// +type_and_void + : type_expression_or_array + | VOID + { + $$ = new TypeExpression (compiler.BuiltinTypes.Void, GetLocation ($1)); + } + ; + +member_type + : type_and_void + { + lexer.parsing_generic_declaration = true; + } + ; + +// +// A type which does not allow `void' to be used +// +type + : type_expression_or_array + | void_invalid + ; + +simple_type + : type_expression + | void_invalid + ; + +parameter_type + : type_expression_or_array + | VOID + { + report.Error (1536, GetLocation ($1), "Invalid parameter type `void'"); + $$ = new TypeExpression (compiler.BuiltinTypes.Void, GetLocation ($1)); + } + ; + +type_expression_or_array + : type_expression + | type_expression rank_specifiers + { + $$ = new ComposedCast ((FullNamedExpression) $1, (ComposedTypeSpecifier) $2); + } + ; + +type_expression + : namespace_or_type_expr opt_nullable + { + if ($2 != null) { + $$ = new ComposedCast ((ATypeNameExpression) $1, (ComposedTypeSpecifier) $2); + } else { + var sn = $1 as SimpleName; + if (sn != null && sn.Name == "var") + $$ = new VarExpr (sn.Location); + else + $$ = $1; + } + } + | namespace_or_type_expr pointer_stars + { + $$ = new ComposedCast ((ATypeNameExpression) $1, (ComposedTypeSpecifier) $2); + } + | builtin_type_expression + ; + +void_invalid + : VOID + { + Expression.Error_VoidInvalidInTheContext (GetLocation ($1), report); + $$ = new TypeExpression (compiler.BuiltinTypes.Void, GetLocation ($1)); + } + ; + +builtin_type_expression + : builtin_types opt_nullable + { + if ($2 != null) + $$ = new ComposedCast ((FullNamedExpression) $1, (ComposedTypeSpecifier) $2); + } + | builtin_types pointer_stars + { + $$ = new ComposedCast ((FullNamedExpression) $1, (ComposedTypeSpecifier) $2); + } + | VOID pointer_stars + { + $$ = new ComposedCast (new TypeExpression (compiler.BuiltinTypes.Void, GetLocation ($1)), (ComposedTypeSpecifier) $2); + } + ; + +type_list + : base_type_name + { + var types = new List (2); + types.Add ((FullNamedExpression) $1); + $$ = types; + } + | type_list COMMA base_type_name + { + var types = (List) $1; + types.Add ((FullNamedExpression) $3); + lbag.AddLocation (types, GetLocation ($2)); + $$ = types; + } + ; + +base_type_name + : type + { + if ($1 is ComposedCast) { + report.Error (1521, GetLocation ($1), "Invalid base type `{0}'", ((ComposedCast)$1).GetSignatureForError ()); + } + $$ = $1; + } + ; + +/* + * replaces all the productions for isolating the various + * simple types, but we need this to reuse it easily in variable_type + */ +builtin_types + : OBJECT { $$ = new TypeExpression (compiler.BuiltinTypes.Object, GetLocation ($1)); } + | STRING { $$ = new TypeExpression (compiler.BuiltinTypes.String, GetLocation ($1)); } + | BOOL { $$ = new TypeExpression (compiler.BuiltinTypes.Bool, GetLocation ($1)); } + | DECIMAL { $$ = new TypeExpression (compiler.BuiltinTypes.Decimal, GetLocation ($1)); } + | FLOAT { $$ = new TypeExpression (compiler.BuiltinTypes.Float, GetLocation ($1)); } + | DOUBLE { $$ = new TypeExpression (compiler.BuiltinTypes.Double, GetLocation ($1)); } + | integral_type + ; + +integral_type + : SBYTE { $$ = new TypeExpression (compiler.BuiltinTypes.SByte, GetLocation ($1)); } + | BYTE { $$ = new TypeExpression (compiler.BuiltinTypes.Byte, GetLocation ($1)); } + | SHORT { $$ = new TypeExpression (compiler.BuiltinTypes.Short, GetLocation ($1)); } + | USHORT { $$ = new TypeExpression (compiler.BuiltinTypes.UShort, GetLocation ($1)); } + | INT { $$ = new TypeExpression (compiler.BuiltinTypes.Int, GetLocation ($1)); } + | UINT { $$ = new TypeExpression (compiler.BuiltinTypes.UInt, GetLocation ($1)); } + | LONG { $$ = new TypeExpression (compiler.BuiltinTypes.Long, GetLocation ($1)); } + | ULONG { $$ = new TypeExpression (compiler.BuiltinTypes.ULong, GetLocation ($1)); } + | CHAR { $$ = new TypeExpression (compiler.BuiltinTypes.Char, GetLocation ($1)); } + ; + +// +// Expressions, section 7.5 +// + + +primary_expression + : primary_expression_or_type + | literal + | array_creation_expression + | parenthesized_expression + | default_value_expression + | invocation_expression + | element_access + | this_access + | base_access + | post_increment_expression + | post_decrement_expression + | object_or_delegate_creation_expression + | anonymous_type_expression + | typeof_expression + | sizeof_expression + | checked_expression + | unchecked_expression + | pointer_member_access + | anonymous_method_expression + | undocumented_expressions + ; + +primary_expression_or_type + : simple_name_expr + | IDENTIFIER GENERATE_COMPLETION { + var lt = (LocatedToken) $1; + $$ = new CompletionSimpleName (MemberName.MakeName (lt.Value, null), lt.Location); + } + | member_access + ; + +literal + : boolean_literal + | LITERAL + | NULL { $$ = new NullLiteral (GetLocation ($1)); } + ; + +boolean_literal + : TRUE { $$ = new BoolLiteral (compiler.BuiltinTypes, true, GetLocation ($1)); } + | FALSE { $$ = new BoolLiteral (compiler.BuiltinTypes, false, GetLocation ($1)); } + ; + + +// +// Here is the trick, tokenizer may think that parens is a special but +// parser is interested in open parens only, so we merge them. +// Consider: if (a)foo (); +// +open_parens_any + : OPEN_PARENS + | OPEN_PARENS_CAST + ; + +// +// Use this production to accept closing parenthesis or +// performing completion +// +close_parens + : CLOSE_PARENS + | COMPLETE_COMPLETION + ; + + +parenthesized_expression + : OPEN_PARENS expression CLOSE_PARENS + { + $$ = new ParenthesizedExpression ((Expression) $2, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($1), GetLocation ($3)); + } + | OPEN_PARENS expression COMPLETE_COMPLETION + { + $$ = new ParenthesizedExpression ((Expression) $2, GetLocation ($1)); + } + ; + +member_access + : primary_expression DOT identifier_inside_body opt_type_argument_list + { + var lt = (LocatedToken) $3; + $$ = new MemberAccess ((Expression) $1, lt.Value, (TypeArguments) $4, lt.Location); + lbag.AddLocation ($$, GetLocation ($2)); + } + | primary_expression DOT identifier_inside_body generic_dimension + { + var lt = (LocatedToken) $3; + var ma = new MemberAccess ((Expression) $1, lt.Value, (int) $4, lt.Location); + lbag.AddLocation (ma.TypeArguments, Lexer.GenericDimensionLocations); + $$ = ma; + lbag.AddLocation ($$, GetLocation ($2)); + } + | primary_expression INTERR_OPERATOR DOT identifier_inside_body opt_type_argument_list + { + if (lang_version < LanguageVersion.V_6) + FeatureIsNotAvailable (GetLocation ($2), "null propagating operator"); + + var lt = (LocatedToken) $4; + $$ = new ConditionalMemberAccess ((Expression) $1, lt.Value, (TypeArguments) $5, lt.Location); + lbag.AddLocation ($$, GetLocation ($2), GetLocation ($3)); + } + | builtin_types DOT identifier_inside_body opt_type_argument_list + { + var lt = (LocatedToken) $3; + $$ = new MemberAccess ((Expression) $1, lt.Value, (TypeArguments) $4, lt.Location); + lbag.AddLocation ($$, GetLocation ($2)); + } + | BASE DOT identifier_inside_body opt_type_argument_list + { + var lt = (LocatedToken) $3; + $$ = new MemberAccess (new BaseThis (GetLocation ($1)), lt.Value, (TypeArguments) $4, lt.Location); + lbag.AddLocation ($$, GetLocation ($2)); + } + | AWAIT DOT identifier_inside_body opt_type_argument_list + { + var lt = (LocatedToken) $3; + $$ = new MemberAccess (new SimpleName ("await", ((LocatedToken) $1).Location), lt.Value, (TypeArguments) $4, lt.Location); + lbag.AddLocation ($$, GetLocation ($2)); + } + | qualified_alias_member identifier_inside_body opt_type_argument_list + { + var lt1 = (LocatedToken) $1; + var lt2 = (LocatedToken) $2; + + $$ = new QualifiedAliasMember (lt1.Value, lt2.Value, (TypeArguments) $3, lt1.Location); + lbag.AddLocation ($$, savedLocation, GetLocation ($2)); + } + | qualified_alias_member identifier_inside_body generic_dimension + { + var lt1 = (LocatedToken) $1; + var lt2 = (LocatedToken) $2; + var qam = new QualifiedAliasMember (lt1.Value, lt2.Value, (int) $3, lt1.Location); + lbag.AddLocation (qam.TypeArguments, Lexer.GenericDimensionLocations); + $$ = qam; + lbag.AddLocation ($$, GetLocation ($2)); + } + | primary_expression DOT GENERATE_COMPLETION { + $$ = new CompletionMemberAccess ((Expression) $1, null,GetLocation ($3)); + } + | primary_expression DOT IDENTIFIER GENERATE_COMPLETION { + var lt = (LocatedToken) $3; + $$ = new CompletionMemberAccess ((Expression) $1, lt.Value, lt.Location); + } + | builtin_types DOT GENERATE_COMPLETION + { + $$ = new CompletionMemberAccess ((Expression) $1, null, lexer.Location); + } + | builtin_types DOT IDENTIFIER GENERATE_COMPLETION { + var lt = (LocatedToken) $3; + $$ = new CompletionMemberAccess ((Expression) $1, lt.Value, lt.Location); + } + ; + +invocation_expression + : primary_expression open_parens_any opt_argument_list close_parens + { + $$ = new Invocation ((Expression) $1, (Arguments) $3); + lbag.AddLocation ($$, GetLocation ($2), GetLocation ($4)); + } + | primary_expression open_parens_any argument_list error + { + Error_SyntaxError (yyToken); + + $$ = new Invocation ((Expression) $1, (Arguments) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | primary_expression open_parens_any error + { + Error_SyntaxError (yyToken); + + $$ = new Invocation ((Expression) $1, null); + lbag.AddLocation ($$, GetLocation ($2)); + } + ; + +opt_object_or_collection_initializer + : /* empty */ { $$ = null; } + | object_or_collection_initializer + ; + +object_or_collection_initializer + : OPEN_BRACE opt_member_initializer_list close_brace_or_complete_completion + { + if ($2 == null) { + $$ = new CollectionOrObjectInitializers (GetLocation ($1)); + } else { + $$ = new CollectionOrObjectInitializers ((List) $2, GetLocation ($1)); + } + lbag.AddLocation ($$, GetLocation ($3)); + } + | OPEN_BRACE member_initializer_list COMMA CLOSE_BRACE + { + $$ = new CollectionOrObjectInitializers ((List) $2, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($3), GetLocation ($4)); + } + ; + +opt_member_initializer_list + : /* empty */ { $$ = null; } + | member_initializer_list + { + $$ = $1; + } + ; + +member_initializer_list + : member_initializer + { + var a = new List (); + a.Add ((Expression) $1); + $$ = a; + } + | member_initializer_list COMMA member_initializer + { + var a = (List)$1; + a.Add ((Expression) $3); + lbag.AddLocation (a, GetLocation ($2)); + $$ = a; + } + | member_initializer_list error { + Error_SyntaxError (yyToken); + $$ = $1; + } + ; + +member_initializer + : IDENTIFIER ASSIGN initializer_value + { + var lt = (LocatedToken) $1; + $$ = new ElementInitializer (lt.Value, (Expression)$3, lt.Location); + lbag.AddLocation ($$, GetLocation ($2)); + } + | AWAIT ASSIGN initializer_value + { + var lt = (LocatedToken) Error_AwaitAsIdentifier ($1); + $$ = new ElementInitializer (lt.Value, (Expression)$3, lt.Location); + lbag.AddLocation ($$, GetLocation ($2)); + } + | GENERATE_COMPLETION + { + $$ = new CompletionElementInitializer (null, GetLocation ($1)); + } + | non_assignment_expression opt_COMPLETE_COMPLETION { + CompletionSimpleName csn = $1 as CompletionSimpleName; + if (csn == null) + $$ = new CollectionElementInitializer ((Expression)$1); + else + $$ = new CompletionElementInitializer (csn.Prefix, csn.Location); + } + | OPEN_BRACE expression_list CLOSE_BRACE + { + if ($2 == null) + $$ = new CollectionElementInitializer (GetLocation ($1)); + else { + $$ = new CollectionElementInitializer ((List)$2, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($2)); + } + lbag.AddLocation ($$, GetLocation ($3)); + } + | OPEN_BRACKET_EXPR expression_list CLOSE_BRACKET ASSIGN initializer_value + { + if (lang_version < LanguageVersion.V_6) + FeatureIsNotAvailable (GetLocation ($1), "dictionary initializer"); + + $$ = new DictionaryElementInitializer ((List)$2, (Expression) $5, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($3), GetLocation ($4)); + } + | OPEN_BRACE CLOSE_BRACE + { + report.Error (1920, GetLocation ($1), "An element initializer cannot be empty"); + $$ = new CollectionElementInitializer (GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($2)); + } + ; + +initializer_value + : expression + | object_or_collection_initializer + ; + +opt_argument_list + : /* empty */ { $$ = null; } + | argument_list + ; + +argument_list + : argument_or_named_argument + { + Arguments list = new Arguments (4); + list.Add ((Argument) $1); + $$ = list; + } + | argument_list COMMA argument + { + Arguments list = (Arguments) $1; + if (list [list.Count - 1] is NamedArgument) + Error_NamedArgumentExpected ((NamedArgument) list [list.Count - 1]); + + list.Add ((Argument) $3); + lbag.AddLocation (list, GetLocation ($2)); + $$ = list; + } + | argument_list COMMA named_argument + { + Arguments list = (Arguments) $1; + NamedArgument a = (NamedArgument) $3; + for (int i = 0; i < list.Count; ++i) { + NamedArgument na = list [i] as NamedArgument; + if (na != null && na.Name == a.Name) + report.Error (1740, na.Location, "Named argument `{0}' specified multiple times", + na.Name); + } + + list.Add (a); + lbag.AddLocation (list, GetLocation ($2)); + $$ = list; + } + | argument_list COMMA error + { + if (lexer.putback_char == -1) + lexer.putback (')'); // TODO: Wrong but what can I do + Error_SyntaxError (yyToken); + $$ = $1; + } + | COMMA error + { + report.Error (839, GetLocation ($1), "An argument is missing"); + $$ = null; + } + ; + +argument + : expression + { + $$ = new Argument ((Expression) $1); + } + | non_simple_argument + ; + +argument_or_named_argument + : argument + | named_argument + ; + +non_simple_argument + : REF variable_reference + { + $$ = new Argument ((Expression) $2, Argument.AType.Ref); + lbag.AddLocation ($$, GetLocation ($1)); + } + | OUT variable_reference + { + $$ = new Argument ((Expression) $2, Argument.AType.Out); + lbag.AddLocation ($$, GetLocation ($1)); + } + | ARGLIST OPEN_PARENS argument_list CLOSE_PARENS + { + $$ = new Argument (new Arglist ((Arguments) $3, GetLocation ($1))); + lbag.AddLocation ($$, GetLocation ($2), GetLocation ($4)); + } + | ARGLIST OPEN_PARENS CLOSE_PARENS + { + $$ = new Argument (new Arglist (GetLocation ($1))); + lbag.AddLocation ($$, GetLocation ($2), GetLocation ($3)); + } + ; + +variable_reference + : expression + ; + +element_access + : primary_expression OPEN_BRACKET_EXPR expression_list_arguments CLOSE_BRACKET + { + $$ = new ElementAccess ((Expression) $1, (Arguments) $3, GetLocation ($2)); + lbag.AddLocation ($$, GetLocation ($4)); + } + | primary_expression INTERR_OPERATOR OPEN_BRACKET_EXPR expression_list_arguments CLOSE_BRACKET + { + if (lang_version < LanguageVersion.V_6) + FeatureIsNotAvailable (GetLocation ($2), "null propagating operator"); + + $$ = new ElementAccess ((Expression) $1, (Arguments) $4, GetLocation ($3)) { + ConditionalAccess = true + }; + + lbag.AddLocation ($$, GetLocation ($2), GetLocation ($5)); + } + | primary_expression OPEN_BRACKET_EXPR expression_list_arguments error + { + Error_SyntaxError (yyToken); + $$ = new ElementAccess ((Expression) $1, (Arguments) $3, GetLocation ($2)); + } + | primary_expression OPEN_BRACKET_EXPR error + { + Error_SyntaxError (yyToken); + $$ = new ElementAccess ((Expression) $1, null, GetLocation ($2)); + } + ; + +expression_list + : expression_or_error + { + var list = new List (4); + list.Add ((Expression) $1); + $$ = list; + } + | expression_list COMMA expression_or_error + { + var list = (List) $1; + list.Add ((Expression) $3); + lbag.AddLocation (list, GetLocation ($2)); + $$ = list; + } + ; + +expression_list_arguments + : expression_list_argument + { + Arguments args = new Arguments (4); + args.Add ((Argument) $1); + $$ = args; + } + | expression_list_arguments COMMA expression_list_argument + { + Arguments args = (Arguments) $1; + if (args [args.Count - 1] is NamedArgument && !($3 is NamedArgument)) + Error_NamedArgumentExpected ((NamedArgument) args [args.Count - 1]); + + args.Add ((Argument) $3); + lbag.AddLocation (args, GetLocation ($2)); + $$ = args; + } + ; + +expression_list_argument + : expression + { + $$ = new Argument ((Expression) $1); + } + | named_argument + ; + +this_access + : THIS + { + $$ = new This (GetLocation ($1)); + } + ; + +base_access + : BASE OPEN_BRACKET_EXPR expression_list_arguments CLOSE_BRACKET + { + $$ = new ElementAccess (new BaseThis (GetLocation ($1)), (Arguments) $3, GetLocation ($2)); + lbag.AddLocation ($$, GetLocation ($4)); + } + | BASE OPEN_BRACKET error + { + Error_SyntaxError (yyToken); + $$ = new ElementAccess (null, null, GetLocation ($2)); + } + ; + +post_increment_expression + : primary_expression OP_INC + { + $$ = new UnaryMutator (UnaryMutator.Mode.PostIncrement, (Expression) $1, GetLocation ($2)); + } + ; + +post_decrement_expression + : primary_expression OP_DEC + { + $$ = new UnaryMutator (UnaryMutator.Mode.PostDecrement, (Expression) $1, GetLocation ($2)); + } + ; + +object_or_delegate_creation_expression + : NEW new_expr_type open_parens_any opt_argument_list CLOSE_PARENS opt_object_or_collection_initializer + { + if ($6 != null) { + if (lang_version <= LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation ($1), "object initializers"); + + $$ = new NewInitialize ((FullNamedExpression) $2, (Arguments) $4, (CollectionOrObjectInitializers) $6, GetLocation ($1)); + } else { + $$ = new New ((FullNamedExpression) $2, (Arguments) $4, GetLocation ($1)); + } + + lbag.AddLocation ($$, GetLocation ($3), GetLocation ($5)); + } + | NEW new_expr_type object_or_collection_initializer + { + if (lang_version <= LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation ($1), "collection initializers"); + + $$ = new NewInitialize ((FullNamedExpression) $2, null, (CollectionOrObjectInitializers) $3, GetLocation ($1)); + } + ; + +array_creation_expression + : NEW new_expr_type OPEN_BRACKET_EXPR expression_list CLOSE_BRACKET + opt_rank_specifier + opt_array_initializer + { + $$ = new ArrayCreation ((FullNamedExpression) $2, (List) $4, + new ComposedTypeSpecifier (((List) $4).Count, GetLocation ($3)) { + Next = (ComposedTypeSpecifier) $6 + }, (ArrayInitializer) $7, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($3), GetLocation ($5)); + } + | NEW new_expr_type rank_specifiers opt_array_initializer + { + if ($4 == null) + report.Error (1586, GetLocation ($1), "Array creation must have array size or array initializer"); + + $$ = new ArrayCreation ((FullNamedExpression) $2, (ComposedTypeSpecifier) $3, (ArrayInitializer) $4, GetLocation ($1)); + } + | NEW rank_specifier array_initializer + { + if (lang_version <= LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation ($1), "implicitly typed arrays"); + + $$ = new ImplicitlyTypedArrayCreation ((ComposedTypeSpecifier) $2, (ArrayInitializer) $3, GetLocation ($1)); + } + | NEW new_expr_type OPEN_BRACKET CLOSE_BRACKET OPEN_BRACKET_EXPR error CLOSE_BRACKET + { + report.Error (178, GetLocation ($6), "Invalid rank specifier, expecting `,' or `]'"); + $$ = new ArrayCreation ((FullNamedExpression) $2, null, GetLocation ($1)); + } + | NEW new_expr_type error + { + Error_SyntaxError (yyToken); + // It can be any of new expression, create the most common one + $$ = new New ((FullNamedExpression) $2, null, GetLocation ($1)); + } + ; + +new_expr_type + : { + ++lexer.parsing_type; + } + simple_type + { + --lexer.parsing_type; + $$ = $2; + } + ; + +anonymous_type_expression + : NEW OPEN_BRACE anonymous_type_parameters_opt_comma CLOSE_BRACE + { + if (lang_version <= LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation ($1), "anonymous types"); + + $$ = new NewAnonymousType ((List) $3, current_container, GetLocation ($1)); + + // TODO: lbag comma location + lbag.AddLocation ($$, GetLocation ($2), GetLocation ($4)); + } + | NEW OPEN_BRACE GENERATE_COMPLETION + { + $$ = new EmptyCompletion (); + } + ; + +anonymous_type_parameters_opt_comma + : anonymous_type_parameters_opt + | anonymous_type_parameters COMMA + ; + +anonymous_type_parameters_opt + : { $$ = null; } + | anonymous_type_parameters + ; + +anonymous_type_parameters + : anonymous_type_parameter + { + var a = new List (4); + a.Add ((AnonymousTypeParameter) $1); + $$ = a; + } + | anonymous_type_parameters COMMA anonymous_type_parameter + { + var a = (List) $1; + a.Add ((AnonymousTypeParameter) $3); + lbag.AddLocation (a, GetLocation ($2)); + + $$ = a; + } + | COMPLETE_COMPLETION + { + $$ = new EmptyCompletion (); + } + | anonymous_type_parameter COMPLETE_COMPLETION + { + $$ = $1; + } + ; + +anonymous_type_parameter + : identifier_inside_body ASSIGN variable_initializer + { + var lt = (LocatedToken)$1; + $$ = new AnonymousTypeParameter ((Expression)$3, lt.Value, lt.Location); + lbag.AddLocation ($$, GetLocation ($2)); + } + | identifier_inside_body + { + var lt = (LocatedToken)$1; + $$ = new AnonymousTypeParameter (new SimpleName (lt.Value, lt.Location), + lt.Value, lt.Location); + } + | member_access + { + MemberAccess ma = (MemberAccess) $1; + $$ = new AnonymousTypeParameter (ma, ma.Name, ma.Location); + } + | error + { + report.Error (746, lexer.Location, + "Invalid anonymous type member declarator. Anonymous type members must be a member assignment, simple name or member access expression"); + $$ = null; + } + ; + +opt_rank_specifier + : /* empty */ + | rank_specifiers + ; + +rank_specifiers + : rank_specifier + | rank_specifier rank_specifiers + { + ((ComposedTypeSpecifier) $1).Next = (ComposedTypeSpecifier) $2; + $$ = $1; + } + ; + +rank_specifier + : OPEN_BRACKET CLOSE_BRACKET + { + $$ = ComposedTypeSpecifier.CreateArrayDimension (1, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($2)); + } + | OPEN_BRACKET dim_separators CLOSE_BRACKET + { + $$ = ComposedTypeSpecifier.CreateArrayDimension ((int)$2, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($3)); + } + ; + +dim_separators + : COMMA + { + $$ = 2; + } + | dim_separators COMMA + { + $$ = ((int) $1) + 1; + } + ; + +opt_array_initializer + : /* empty */ + { + $$ = null; + } + | array_initializer + { + $$ = $1; + } + ; + +array_initializer + : OPEN_BRACE CLOSE_BRACE + { + var ai = new ArrayInitializer (0, GetLocation ($1)); + ai.VariableDeclaration = current_variable; + lbag.AddLocation (ai, GetLocation ($2)); + $$ = ai; + } + | OPEN_BRACE variable_initializer_list opt_comma CLOSE_BRACE + { + var ai = new ArrayInitializer ((List) $2, GetLocation ($1)); + ai.VariableDeclaration = current_variable; + if ($3 != null) { + lbag.AddLocation (ai, GetLocation ($3), GetLocation ($4)); + } else { + lbag.AddLocation (ai, GetLocation ($4)); + } + $$ = ai; + } + ; + +variable_initializer_list + : variable_initializer + { + var list = new List (4); + list.Add ((Expression) $1); + $$ = list; + } + | variable_initializer_list COMMA variable_initializer + { + var list = (List) $1; + list.Add ((Expression) $3); + lbag.AddLocation (list, GetLocation ($2)); + $$ = list; + } + ; + +typeof_expression + : TYPEOF open_parens_any typeof_type_expression CLOSE_PARENS + { + $$ = new TypeOf ((FullNamedExpression) $3, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($2), GetLocation ($4)); + } + ; + +typeof_type_expression + : type_and_void + | error + { + Error_TypeExpected (lexer.Location); + $$ = null; + } + ; + +generic_dimension + : GENERIC_DIMENSION + { + if (lang_version < LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation ($1), "generics"); + + $$ = $1; + } + ; + +qualified_alias_member + : IDENTIFIER DOUBLE_COLON + { + var lt = (LocatedToken) $1; + if (lang_version == LanguageVersion.ISO_1) + FeatureIsNotAvailable (lt.Location, "namespace alias qualifier"); + savedLocation = GetLocation ($2); + $$ = lt; + } + ; + +sizeof_expression + : SIZEOF open_parens_any type CLOSE_PARENS + { + $$ = new SizeOf ((Expression) $3, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($2), GetLocation ($4)); + } + | SIZEOF open_parens_any type error + { + Error_SyntaxError (yyToken); + + $$ = new SizeOf ((Expression) $3, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($2)); + } + ; + +checked_expression + : CHECKED open_parens_any expression CLOSE_PARENS + { + $$ = new CheckedExpr ((Expression) $3, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($2), GetLocation ($4)); + } + | CHECKED error + { + Error_SyntaxError (yyToken); + + $$ = new CheckedExpr (null, GetLocation ($1)); + } + ; + +unchecked_expression + : UNCHECKED open_parens_any expression CLOSE_PARENS + { + $$ = new UnCheckedExpr ((Expression) $3, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($2), GetLocation ($4)); + } + | UNCHECKED error + { + Error_SyntaxError (yyToken); + + $$ = new UnCheckedExpr (null, GetLocation ($1)); + } + ; + +pointer_member_access + : primary_expression OP_PTR IDENTIFIER opt_type_argument_list + { + var lt = (LocatedToken) $3; + $$ = new MemberAccess (new Indirection ((Expression) $1, GetLocation ($2)), lt.Value, (TypeArguments) $4, lt.Location); + } + ; + +anonymous_method_expression + : DELEGATE opt_anonymous_method_signature + { + start_anonymous (false, (ParametersCompiled) $2, false, GetLocation ($1)); + } + block + { + $$ = end_anonymous ((ParametersBlock) $4); + if ((ParametersCompiled) $2 != ParametersCompiled.Undefined) { + lbag.AddLocation ($$, GetLocation ($1), PopLocation (), PopLocation ()); + } else { + lbag.AddLocation ($$, GetLocation ($1)); + } + } + | ASYNC DELEGATE opt_anonymous_method_signature + { + start_anonymous (false, (ParametersCompiled) $3, true, GetLocation ($1)); + } + block + { + $$ = end_anonymous ((ParametersBlock) $5); + + if ((ParametersCompiled) $3 != ParametersCompiled.Undefined) { + lbag.AddLocation ($$, GetLocation ($1), GetLocation ($2), PopLocation (), PopLocation ()); + } else { + lbag.AddLocation ($$, GetLocation ($1), GetLocation ($2)); + } + } + ; + +opt_anonymous_method_signature + : + { + $$ = ParametersCompiled.Undefined; + } + | anonymous_method_signature + ; + +anonymous_method_signature + : OPEN_PARENS + { + valid_param_mod = ParameterModifierType.Ref | ParameterModifierType.Out; + } + opt_formal_parameter_list CLOSE_PARENS + { + valid_param_mod = 0; + $$ = $3; + PushLocation (GetLocation ($3)); + PushLocation (GetLocation ($1)); + } + ; + +default_value_expression + : DEFAULT open_parens_any type CLOSE_PARENS + { + if (lang_version < LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation ($1), "default value expression"); + + $$ = new DefaultValueExpression ((Expression) $3, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($2), GetLocation ($4)); + } + ; + +unary_expression + : primary_expression + | BANG prefixed_unary_expression + { + $$ = new Unary (Unary.Operator.LogicalNot, (Expression) $2, GetLocation ($1)); + } + | TILDE prefixed_unary_expression + { + $$ = new Unary (Unary.Operator.OnesComplement, (Expression) $2, GetLocation ($1)); + } + | OPEN_PARENS_CAST type CLOSE_PARENS prefixed_unary_expression + { + $$ = new Cast ((FullNamedExpression) $2, (Expression) $4, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($3)); + } + | AWAIT prefixed_unary_expression + { + if (!async_block) { + if (current_anonymous_method is LambdaExpression) { + report.Error (4034, GetLocation ($1), + "The `await' operator can only be used when its containing lambda expression is marked with the `async' modifier"); + } else if (current_anonymous_method != null) { + report.Error (4035, GetLocation ($1), + "The `await' operator can only be used when its containing anonymous method is marked with the `async' modifier"); + } else if (interactive_async != null) { + current_block.Explicit.RegisterAsyncAwait (); + interactive_async = true; + } else { + report.Error (4033, GetLocation ($1), + "The `await' operator can only be used when its containing method is marked with the `async' modifier"); + } + } else { + current_block.Explicit.RegisterAsyncAwait (); + } + + $$ = new Await ((Expression) $2, GetLocation ($1)); + } + | BANG error + { + Error_SyntaxError (yyToken); + + $$ = new Unary (Unary.Operator.LogicalNot, null, GetLocation ($1)); + } + | TILDE error + { + Error_SyntaxError (yyToken); + + $$ = new Unary (Unary.Operator.OnesComplement, null, GetLocation ($1)); + } + | OPEN_PARENS_CAST type CLOSE_PARENS error + { + Error_SyntaxError (yyToken); + + $$ = new Cast ((FullNamedExpression) $2, null, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($3)); + } + | AWAIT error + { + Error_SyntaxError (yyToken); + + $$ = new Await (null, GetLocation ($1)); + } + ; + + // + // The idea to split this out is from Rhys' grammar + // to solve the problem with casts. + // +prefixed_unary_expression + : unary_expression + | PLUS prefixed_unary_expression + { + $$ = new Unary (Unary.Operator.UnaryPlus, (Expression) $2, GetLocation ($1)); + } + | MINUS prefixed_unary_expression + { + $$ = new Unary (Unary.Operator.UnaryNegation, (Expression) $2, GetLocation ($1)); + } + | OP_INC prefixed_unary_expression + { + $$ = new UnaryMutator (UnaryMutator.Mode.PreIncrement, (Expression) $2, GetLocation ($1)); + } + | OP_DEC prefixed_unary_expression + { + $$ = new UnaryMutator (UnaryMutator.Mode.PreDecrement, (Expression) $2, GetLocation ($1)); + } + | STAR prefixed_unary_expression + { + $$ = new Indirection ((Expression) $2, GetLocation ($1)); + } + | BITWISE_AND prefixed_unary_expression + { + $$ = new Unary (Unary.Operator.AddressOf, (Expression) $2, GetLocation ($1)); + } + | PLUS error + { + Error_SyntaxError (yyToken); + + $$ = new Unary (Unary.Operator.UnaryPlus, null, GetLocation ($1)); + } + | MINUS error + { + Error_SyntaxError (yyToken); + + $$ = new Unary (Unary.Operator.UnaryNegation, null, GetLocation ($1)); + } + | OP_INC error + { + Error_SyntaxError (yyToken); + + $$ = new UnaryMutator (UnaryMutator.Mode.PreIncrement, null, GetLocation ($1)); + } + | OP_DEC error + { + Error_SyntaxError (yyToken); + + $$ = new UnaryMutator (UnaryMutator.Mode.PreDecrement, null, GetLocation ($1)); + } + | STAR error + { + Error_SyntaxError (yyToken); + + $$ = new Indirection (null, GetLocation ($1)); + } + | BITWISE_AND error + { + Error_SyntaxError (yyToken); + + $$ = new Unary (Unary.Operator.AddressOf, null, GetLocation ($1)); + } + ; + +multiplicative_expression + : prefixed_unary_expression + | multiplicative_expression STAR prefixed_unary_expression + { + $$ = new Binary (Binary.Operator.Multiply, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | multiplicative_expression DIV prefixed_unary_expression + { + $$ = new Binary (Binary.Operator.Division, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | multiplicative_expression PERCENT prefixed_unary_expression + { + $$ = new Binary (Binary.Operator.Modulus, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | multiplicative_expression STAR error + { + Error_SyntaxError (yyToken); + + $$ = new Binary (Binary.Operator.Multiply, (Expression) $1, null); + lbag.AddLocation ($$, GetLocation ($2)); + } + | multiplicative_expression DIV error + { + Error_SyntaxError (yyToken); + + $$ = new Binary (Binary.Operator.Division, (Expression) $1, null); + lbag.AddLocation ($$, GetLocation ($2)); + } + | multiplicative_expression PERCENT error + { + Error_SyntaxError (yyToken); + + $$ = new Binary (Binary.Operator.Modulus, (Expression) $1, null); + lbag.AddLocation ($$, GetLocation ($2)); + } + ; + +additive_expression + : multiplicative_expression + | additive_expression PLUS multiplicative_expression + { + $$ = new Binary (Binary.Operator.Addition, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | additive_expression MINUS multiplicative_expression + { + $$ = new Binary (Binary.Operator.Subtraction, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | additive_expression PLUS error + { + Error_SyntaxError (yyToken); + + $$ = new Binary (Binary.Operator.Addition, (Expression) $1, null); + lbag.AddLocation ($$, GetLocation ($2)); + } + | additive_expression MINUS error + { + Error_SyntaxError (yyToken); + + $$ = new Binary (Binary.Operator.Subtraction, (Expression) $1, null); + lbag.AddLocation ($$, GetLocation ($2)); + } + | additive_expression AS type + { + $$ = new As ((Expression) $1, (Expression) $3, GetLocation ($2)); + } + | additive_expression IS is_match_expr opt_identifier + { + var is_expr = new Is ((Expression) $1, (Expression) $3, GetLocation ($2)); + if ($4 != null) { + if (lang_version != LanguageVersion.Experimental) + FeatureIsNotAvailable (GetLocation ($4), "type pattern matching"); + + var lt = (LocatedToken) $4; + is_expr.Variable = new LocalVariable (current_block, lt.Value, lt.Location); + current_block.AddLocalName (is_expr.Variable); + } + + $$ = is_expr; + } + | additive_expression AS error + { + Error_SyntaxError (yyToken); + + $$ = new As ((Expression) $1, null, GetLocation ($2)); + } + | additive_expression IS error + { + Error_SyntaxError (yyToken); + + $$ = new Is ((Expression) $1, null, GetLocation ($2)); + } + | AWAIT IS type + { + var lt = (LocatedToken) $1; + $$ = new Is (new SimpleName (lt.Value, lt.Location), (Expression) $3, GetLocation ($2)); + } + | AWAIT AS type + { + var lt = (LocatedToken) $1; + $$ = new As (new SimpleName (lt.Value, lt.Location), (Expression) $3, GetLocation ($2)); + } + ; + +is_match_expr + : match_type + | match_type rank_specifiers + { + if ($1 is VarExpr) + $1 = new SimpleName ("var", ((VarExpr) $1).Location); + + $$ = new ComposedCast ((FullNamedExpression) $1, (ComposedTypeSpecifier) $2); + } + | literal + | PLUS prefixed_unary_expression + { + $$ = new Unary (Unary.Operator.UnaryPlus, (Expression) $2, GetLocation ($1)); + } + | MINUS prefixed_unary_expression + { + $$ = new Unary (Unary.Operator.UnaryNegation, (Expression) $2, GetLocation ($1)); + } + ; + +match_type + : primary_expression_or_type opt_nullable + { + Expression expr = (Expression) $1; + if ($2 == null) { + SimpleName sn = expr as SimpleName; + if (sn != null && sn.Name == "var") + $$ = new VarExpr (sn.Location); + else + $$ = $1; + } else if (expr is ATypeNameExpression) { + $$ = new ComposedCast ((ATypeNameExpression)expr, (ComposedTypeSpecifier) $2); + } else { + Error_ExpectingTypeName (expr); + $$ = null; + } + } + | primary_expression_or_type pointer_stars + { + ATypeNameExpression expr = $1 as ATypeNameExpression; + + if (expr != null) { + $$ = new ComposedCast (expr, (ComposedTypeSpecifier) $2); + } else { + Error_ExpectingTypeName ((Expression)$1); + $$ = expr; + } + } + | builtin_type_expression + | void_invalid + ; + +shift_expression + : additive_expression + | shift_expression OP_SHIFT_LEFT additive_expression + { + $$ = new Binary (Binary.Operator.LeftShift, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | shift_expression OP_SHIFT_RIGHT additive_expression + { + $$ = new Binary (Binary.Operator.RightShift, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | shift_expression OP_SHIFT_LEFT error + { + Error_SyntaxError (yyToken); + + $$ = new Binary (Binary.Operator.LeftShift, (Expression) $1, null); + lbag.AddLocation ($$, GetLocation ($2)); + } + | shift_expression OP_SHIFT_RIGHT error + { + Error_SyntaxError (yyToken); + + $$ = new Binary (Binary.Operator.RightShift, (Expression) $1, null); + lbag.AddLocation ($$, GetLocation ($2)); + } + ; + +relational_expression + : shift_expression + | relational_expression OP_LT shift_expression + { + $$ = new Binary (Binary.Operator.LessThan, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | relational_expression OP_GT shift_expression + { + $$ = new Binary (Binary.Operator.GreaterThan, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | relational_expression OP_LE shift_expression + { + $$ = new Binary (Binary.Operator.LessThanOrEqual, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | relational_expression OP_GE shift_expression + { + $$ = new Binary (Binary.Operator.GreaterThanOrEqual, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | relational_expression OP_LT error + { + Error_SyntaxError (yyToken); + + $$ = new Binary (Binary.Operator.LessThan, (Expression) $1, null); + lbag.AddLocation ($$, GetLocation ($2)); + } + | relational_expression OP_GT error + { + Error_SyntaxError (yyToken); + + $$ = new Binary (Binary.Operator.GreaterThan, (Expression) $1, null); + lbag.AddLocation ($$, GetLocation ($2)); + } + | relational_expression OP_LE error + { + Error_SyntaxError (yyToken); + + $$ = new Binary (Binary.Operator.LessThanOrEqual, (Expression) $1, null); + lbag.AddLocation ($$, GetLocation ($2)); + } + | relational_expression OP_GE error + { + Error_SyntaxError (yyToken); + + $$ = new Binary (Binary.Operator.GreaterThanOrEqual, (Expression) $1, null); + lbag.AddLocation ($$, GetLocation ($2)); + } + ; + +equality_expression + : relational_expression + | equality_expression OP_EQ relational_expression + { + $$ = new Binary (Binary.Operator.Equality, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | equality_expression OP_NE relational_expression + { + $$ = new Binary (Binary.Operator.Inequality, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | equality_expression OP_EQ error + { + Error_SyntaxError (yyToken); + + $$ = new Binary (Binary.Operator.Equality, (Expression) $1, null); + lbag.AddLocation ($$, GetLocation ($2)); + } + | equality_expression OP_NE error + { + Error_SyntaxError (yyToken); + + $$ = new Binary (Binary.Operator.Inequality, (Expression) $1, null); + lbag.AddLocation ($$, GetLocation ($2)); + } + ; + +and_expression + : equality_expression + | and_expression BITWISE_AND equality_expression + { + $$ = new Binary (Binary.Operator.BitwiseAnd, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | and_expression BITWISE_AND error + { + Error_SyntaxError (yyToken); + + $$ = new Binary (Binary.Operator.BitwiseAnd, (Expression) $1, null); + lbag.AddLocation ($$, GetLocation ($2)); + } + ; + +exclusive_or_expression + : and_expression + | exclusive_or_expression CARRET and_expression + { + $$ = new Binary (Binary.Operator.ExclusiveOr, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | exclusive_or_expression CARRET error + { + Error_SyntaxError (yyToken); + + $$ = new Binary (Binary.Operator.ExclusiveOr, (Expression) $1, null); + lbag.AddLocation ($$, GetLocation ($2)); + } + ; + +inclusive_or_expression + : exclusive_or_expression + | inclusive_or_expression BITWISE_OR exclusive_or_expression + { + $$ = new Binary (Binary.Operator.BitwiseOr, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | inclusive_or_expression BITWISE_OR error + { + Error_SyntaxError (yyToken); + + $$ = new Binary (Binary.Operator.BitwiseOr, (Expression) $1, null); + lbag.AddLocation ($$, GetLocation ($2)); + } + ; + +conditional_and_expression + : inclusive_or_expression + | conditional_and_expression OP_AND inclusive_or_expression + { + $$ = new Binary (Binary.Operator.LogicalAnd, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | conditional_and_expression OP_AND error + { + Error_SyntaxError (yyToken); + + $$ = new Binary (Binary.Operator.LogicalAnd, (Expression) $1, null); + lbag.AddLocation ($$, GetLocation ($2)); + } + ; + +conditional_or_expression + : conditional_and_expression + | conditional_or_expression OP_OR conditional_and_expression + { + $$ = new Binary (Binary.Operator.LogicalOr, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | conditional_or_expression OP_OR error + { + Error_SyntaxError (yyToken); + + $$ = new Binary (Binary.Operator.LogicalOr, (Expression) $1, null); + lbag.AddLocation ($$, GetLocation ($2)); + } + ; + +null_coalescing_expression + : conditional_or_expression + | conditional_or_expression OP_COALESCING null_coalescing_expression + { + if (lang_version < LanguageVersion.ISO_2) + FeatureIsNotAvailable (GetLocation ($2), "null coalescing operator"); + + $$ = new Nullable.NullCoalescingOperator ((Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + ; + +conditional_expression + : null_coalescing_expression + | null_coalescing_expression INTERR expression COLON expression + { + $$ = new Conditional (new BooleanExpression ((Expression) $1), (Expression) $3, (Expression) $5, GetLocation ($2)); + lbag.AddLocation ($$, GetLocation ($4)); + } + | null_coalescing_expression INTERR expression error + { + Error_SyntaxError (yyToken); + + $$ = new Conditional (new BooleanExpression ((Expression) $1), (Expression) $3, null, GetLocation ($2)); + } + | null_coalescing_expression INTERR expression COLON error + { + Error_SyntaxError (yyToken); + + $$ = new Conditional (new BooleanExpression ((Expression) $1), (Expression) $3, null, GetLocation ($2)); + lbag.AddLocation ($$, GetLocation ($4)); + } + | null_coalescing_expression INTERR expression COLON CLOSE_BRACE + { + Error_SyntaxError (Token.CLOSE_BRACE); + + $$ = new Conditional (new BooleanExpression ((Expression) $1), (Expression) $3, null, GetLocation ($2)); + lbag.AddLocation ($$, GetLocation ($4)); + lexer.putback ('}'); + } + ; + +assignment_expression + : prefixed_unary_expression ASSIGN expression + { + $$ = new SimpleAssign ((Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | prefixed_unary_expression OP_MULT_ASSIGN expression + { + $$ = new CompoundAssign (Binary.Operator.Multiply, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | prefixed_unary_expression OP_DIV_ASSIGN expression + { + $$ = new CompoundAssign (Binary.Operator.Division, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | prefixed_unary_expression OP_MOD_ASSIGN expression + { + $$ = new CompoundAssign (Binary.Operator.Modulus, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | prefixed_unary_expression OP_ADD_ASSIGN expression + { + $$ = new CompoundAssign (Binary.Operator.Addition, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | prefixed_unary_expression OP_SUB_ASSIGN expression + { + $$ = new CompoundAssign (Binary.Operator.Subtraction, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | prefixed_unary_expression OP_SHIFT_LEFT_ASSIGN expression + { + $$ = new CompoundAssign (Binary.Operator.LeftShift, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | prefixed_unary_expression OP_SHIFT_RIGHT_ASSIGN expression + { + $$ = new CompoundAssign (Binary.Operator.RightShift, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | prefixed_unary_expression OP_AND_ASSIGN expression + { + $$ = new CompoundAssign (Binary.Operator.BitwiseAnd, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | prefixed_unary_expression OP_OR_ASSIGN expression + { + $$ = new CompoundAssign (Binary.Operator.BitwiseOr, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + | prefixed_unary_expression OP_XOR_ASSIGN expression + { + $$ = new CompoundAssign (Binary.Operator.ExclusiveOr, (Expression) $1, (Expression) $3); + lbag.AddLocation ($$, GetLocation ($2)); + } + ; + +lambda_parameter_list + : lambda_parameter + { + var pars = new List (4); + pars.Add ((Parameter) $1); + parameterListCommas.Clear (); + $$ = pars; + } + | lambda_parameter_list COMMA lambda_parameter + { + var pars = (List) $1; + Parameter p = (Parameter)$3; + if (pars[0].GetType () != p.GetType ()) { + report.Error (748, p.Location, "All lambda parameters must be typed either explicitly or implicitly"); + } + + pars.Add (p); + parameterListCommas.Add (GetLocation ($2)); + + $$ = pars; + } + ; + +lambda_parameter + : parameter_modifier parameter_type identifier_inside_body + { + var lt = (LocatedToken) $3; + + $$ = new Parameter ((FullNamedExpression) $2, lt.Value, (Parameter.Modifier) $1, null, lt.Location); + } + | parameter_type identifier_inside_body + { + var lt = (LocatedToken) $2; + + $$ = new Parameter ((FullNamedExpression) $1, lt.Value, Parameter.Modifier.NONE, null, lt.Location); + } + | IDENTIFIER + { + var lt = (LocatedToken) $1; + $$ = new ImplicitLambdaParameter (lt.Value, lt.Location); + } + | AWAIT + { + var lt = (LocatedToken) Error_AwaitAsIdentifier ($1); + $$ = new ImplicitLambdaParameter (lt.Value, lt.Location); + } + ; + +opt_lambda_parameter_list + : /* empty */ { $$ = ParametersCompiled.EmptyReadOnlyParameters; } + | lambda_parameter_list { + var pars_list = (List) $1; + $$ = new ParametersCompiled (pars_list.ToArray ()); + lbag.AddLocation ($$, parameterListCommas); + } + ; + +lambda_expression_body + : { + start_block (Location.Null); + } + expression // All expressions must handle error or current block won't be restored and breaking ast completely + { + Block b = end_block (Location.Null); + b.IsCompilerGenerated = true; + b.AddStatement (new ContextualReturn ((Expression) $2)); + $$ = b; + } + | block + | error + { + // Handles only cases like foo = x.FirstOrDefault (l => ); + // where we must restore current_variable + Block b = end_block (Location.Null); + b.IsCompilerGenerated = true; + + Error_SyntaxError (yyToken); + $$ = null; + } + ; + +expression_or_error + : expression + | error + { + Error_SyntaxError (yyToken); + $$ = null; + } + ; + +lambda_expression + : IDENTIFIER ARROW + { + var lt = (LocatedToken) $1; + Parameter p = new ImplicitLambdaParameter (lt.Value, lt.Location); + start_anonymous (true, new ParametersCompiled (p), false, lt.Location); + } + lambda_expression_body + { + $$ = end_anonymous ((ParametersBlock) $4); + lbag.AddLocation ($$, GetLocation ($2)); + } + | AWAIT ARROW + { + var lt = (LocatedToken) Error_AwaitAsIdentifier ($1); + Parameter p = new ImplicitLambdaParameter (lt.Value, lt.Location); + start_anonymous (true, new ParametersCompiled (p), false, lt.Location); + } + lambda_expression_body + { + $$ = end_anonymous ((ParametersBlock) $4); + lbag.AddLocation ($$, GetLocation ($2)); + } + | ASYNC identifier_inside_body ARROW + { + var lt = (LocatedToken) $2; + Parameter p = new ImplicitLambdaParameter (lt.Value, lt.Location); + start_anonymous (true, new ParametersCompiled (p), true, lt.Location); + } + lambda_expression_body + { + $$ = end_anonymous ((ParametersBlock) $5); + lbag.AddLocation ($$, GetLocation ($1), GetLocation ($3)); + } + | OPEN_PARENS_LAMBDA + { + valid_param_mod = ParameterModifierType.Ref | ParameterModifierType.Out; + } + opt_lambda_parameter_list CLOSE_PARENS ARROW + { + valid_param_mod = 0; + start_anonymous (true, (ParametersCompiled) $3, false, GetLocation ($1)); + } + lambda_expression_body + { + $$ = end_anonymous ((ParametersBlock) $7); + lbag.AddLocation ($$, GetLocation ($1), GetLocation ($4), GetLocation ($5)); + } + | ASYNC OPEN_PARENS_LAMBDA + { + valid_param_mod = ParameterModifierType.Ref | ParameterModifierType.Out; + } + opt_lambda_parameter_list CLOSE_PARENS ARROW + { + valid_param_mod = 0; + start_anonymous (true, (ParametersCompiled) $4, true, GetLocation ($1)); + } + lambda_expression_body + { + $$ = end_anonymous ((ParametersBlock) $8); + lbag.AddLocation ($$, GetLocation ($1), GetLocation ($2), GetLocation ($5), GetLocation ($6)); + } + ; + +expression + : assignment_expression + | non_assignment_expression + ; + +non_assignment_expression + : conditional_expression + | lambda_expression + | query_expression + | ARGLIST + { + $$ = new ArglistAccess (GetLocation ($1)); + } + ; + +undocumented_expressions + : REFVALUE OPEN_PARENS non_assignment_expression COMMA type CLOSE_PARENS + { + $$ = new RefValueExpr ((Expression) $3, (FullNamedExpression) $5, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($2), GetLocation ($4), GetLocation ($6)); + } + | REFTYPE open_parens_any expression CLOSE_PARENS + { + $$ = new RefTypeExpr ((Expression) $3, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($2), GetLocation ($4)); + } + | MAKEREF open_parens_any expression CLOSE_PARENS + { + $$ = new MakeRefExpr ((Expression) $3, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($2), GetLocation ($4)); + } + ; + +constant_expression + : expression + ; + +boolean_expression + : expression + { + $$ = new BooleanExpression ((Expression) $1); + } + ; + +opt_primary_parameters + : /* empty */ + { + $$ = null; + } + | primary_parameters + ; + +primary_parameters + : OPEN_PARENS opt_formal_parameter_list CLOSE_PARENS + { + $$ = $2; + + // Cannot use opt_formal_parameter_list because it can be shared instance for empty parameters + lbag.AppendToMember (current_container, GetLocation ($1), GetLocation ($3)); + + if (lang_version < LanguageVersion.V_6) + FeatureIsNotAvailable (GetLocation ($1), "primary constructor"); + } + ; + +opt_primary_parameters_with_class_base + : /* empty */ + { + $$ = null; + } + | class_base + { + $$ = null; + } + | primary_parameters + { + $$ = $1; + } + | primary_parameters class_base + { + $$ = $1; + } + | primary_parameters class_base OPEN_PARENS + { + ++lexer.parsing_block; + current_type.PrimaryConstructorBaseArgumentsStart = GetLocation ($3); + } + opt_argument_list CLOSE_PARENS + { + lbag.AppendToMember (current_container, GetLocation ($6)); + current_type.PrimaryConstructorBaseArguments = (Arguments) $5; + --lexer.parsing_block; + + $$ = $1; + } + ; + +// +// 10 classes +// +class_declaration + : opt_attributes + opt_modifiers + opt_partial + CLASS + { + } + type_declaration_name + { + lexer.ConstraintsParsing = true; + + Class c = new Class (current_container, (MemberName) $6, (Modifiers) $2, (Attributes) $1); + if (((c.ModFlags & Modifiers.STATIC) != 0) && lang_version == LanguageVersion.ISO_1) { + FeatureIsNotAvailable (c.Location, "static classes"); + } + + push_current_container (c, $3); + lbag.AddMember (current_container, GetModifierLocations (), GetLocation ($4)); + valid_param_mod = ParameterModifierType.PrimaryConstructor; + } + opt_primary_parameters_with_class_base + opt_type_parameter_constraints_clauses + { + valid_param_mod = 0; + lexer.ConstraintsParsing = false; + + if ($8 != null) + current_type.PrimaryConstructorParameters = (ParametersCompiled) $8; + + if ($9 != null) + current_container.SetConstraints ((List) $9); + + if (doc_support) { + current_container.PartialContainer.DocComment = Lexer.consume_doc_comment (); + Lexer.doc_state = XmlCommentState.Allowed; + } + + lexer.parsing_modifiers = true; + } + OPEN_BRACE opt_class_member_declarations CLOSE_BRACE + { + --lexer.parsing_declaration; + if (doc_support) + Lexer.doc_state = XmlCommentState.Allowed; + } + opt_semicolon + { + if ($15 == null) { + lbag.AppendToMember (current_container, GetLocation ($11), GetLocation ($13)); + } else { + lbag.AppendToMember (current_container, GetLocation ($11), GetLocation ($13), GetLocation ($15)); + } + $$ = pop_current_class (); + } + ; + +opt_partial + : /* empty */ + { $$ = null; } + | PARTIAL + { $$ = $1; StoreModifierLocation (Modifiers.PARTIAL, GetLocation ($1)); } // location + ; + +opt_modifiers + : /* empty */ + { + mod_locations = null; + $$ = ModifierNone; + lexer.parsing_modifiers = false; + } + | modifiers + { + lexer.parsing_modifiers = false; + } + ; + +modifiers + : modifier + | modifiers modifier + { + var m1 = (Modifiers) $1; + var m2 = (Modifiers) $2; + + if ((m1 & m2) != 0) { + report.Error (1004, lexer.Location - ModifiersExtensions.Name (m2).Length, + "Duplicate `{0}' modifier", ModifiersExtensions.Name (m2)); + } else if ((m2 & Modifiers.AccessibilityMask) != 0 && (m1 & Modifiers.AccessibilityMask) != 0 && + ((m2 | m1 & Modifiers.AccessibilityMask) != (Modifiers.PROTECTED | Modifiers.INTERNAL))) { + report.Error (107, lexer.Location - ModifiersExtensions.Name (m2).Length, + "More than one protection modifier specified"); + } + + $$ = m1 | m2; + } + ; + +modifier + : NEW + { + $$ = Modifiers.NEW; + StoreModifierLocation ($$, GetLocation ($1)); + + if (current_container.Kind == MemberKind.Namespace) + report.Error (1530, GetLocation ($1), "Keyword `new' is not allowed on namespace elements"); + } + | PUBLIC + { + $$ = Modifiers.PUBLIC; + StoreModifierLocation ($$, GetLocation ($1)); + } + | PROTECTED + { + $$ = Modifiers.PROTECTED; + StoreModifierLocation ($$, GetLocation ($1)); + } + | INTERNAL + { + $$ = Modifiers.INTERNAL; + StoreModifierLocation ($$, GetLocation ($1)); + } + | PRIVATE + { + $$ = Modifiers.PRIVATE; + StoreModifierLocation ($$, GetLocation ($1)); + } + | ABSTRACT + { + $$ = Modifiers.ABSTRACT; + StoreModifierLocation ($$, GetLocation ($1)); + } + | SEALED + { + $$ = Modifiers.SEALED; + StoreModifierLocation ($$, GetLocation ($1)); + } + | STATIC + { + $$ = Modifiers.STATIC; + StoreModifierLocation ($$, GetLocation ($1)); + } + | READONLY + { + $$ = Modifiers.READONLY; + StoreModifierLocation ($$, GetLocation ($1)); + } + | VIRTUAL + { + $$ = Modifiers.VIRTUAL; + StoreModifierLocation ($$, GetLocation ($1)); + } + | OVERRIDE + { + $$ = Modifiers.OVERRIDE; + StoreModifierLocation ($$, GetLocation ($1)); + } + | EXTERN + { + $$ = Modifiers.EXTERN; + StoreModifierLocation ($$, GetLocation ($1)); + } + | VOLATILE + { + $$ = Modifiers.VOLATILE; + StoreModifierLocation ($$, GetLocation ($1)); + } + | UNSAFE + { + $$ = Modifiers.UNSAFE; + StoreModifierLocation ($$, GetLocation ($1)); + if (!settings.Unsafe) + Error_UnsafeCodeNotAllowed (GetLocation ($1)); + } + | ASYNC + { + $$ = Modifiers.ASYNC; + StoreModifierLocation ($$, GetLocation ($1)); + } + ; + +opt_class_base + : /* empty */ + | class_base + ; + +class_base + : COLON type_list + { + current_type.SetBaseTypes ((List) $2); + lbag.AppendToMember (current_type, GetLocation ($1)); + } + | COLON type_list error + { + Error_SyntaxError (yyToken); + + current_type.SetBaseTypes ((List) $2); + } + ; + +opt_type_parameter_constraints_clauses + : /* empty */ + | type_parameter_constraints_clauses + { + $$ = $1; + } + ; + +type_parameter_constraints_clauses + : type_parameter_constraints_clause + { + var constraints = new List (1); + constraints.Add ((Constraints) $1); + $$ = constraints; + } + | type_parameter_constraints_clauses type_parameter_constraints_clause + { + var constraints = (List) $1; + Constraints new_constraint = (Constraints)$2; + + foreach (Constraints c in constraints) { + if (new_constraint.TypeParameter.Value == c.TypeParameter.Value) { + report.Error (409, new_constraint.Location, + "A constraint clause has already been specified for type parameter `{0}'", + new_constraint.TypeParameter.Value); + } + } + + constraints.Add (new_constraint); + $$ = constraints; + } + ; + +type_parameter_constraints_clause + : WHERE IDENTIFIER COLON type_parameter_constraints + { + var lt = (LocatedToken) $2; + $$ = new Constraints (new SimpleMemberName (lt.Value, lt.Location), (List) $4, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($3)); + } + | WHERE IDENTIFIER error + { + Error_SyntaxError (yyToken); + + var lt = (LocatedToken) $2; + $$ = new Constraints (new SimpleMemberName (lt.Value, lt.Location), null, GetLocation ($1)); + } + ; + +type_parameter_constraints + : type_parameter_constraint + { + var constraints = new List (1); + constraints.Add ((FullNamedExpression) $1); + $$ = constraints; + } + | type_parameter_constraints COMMA type_parameter_constraint + { + var constraints = (List) $1; + var prev = constraints [constraints.Count - 1] as SpecialContraintExpr; + if (prev != null && (prev.Constraint & SpecialConstraint.Constructor) != 0) { + report.Error (401, GetLocation ($2), "The `new()' constraint must be the last constraint specified"); + } + + prev = $3 as SpecialContraintExpr; + if (prev != null) { + if ((prev.Constraint & (SpecialConstraint.Class | SpecialConstraint.Struct)) != 0) { + report.Error (449, prev.Location, "The `class' or `struct' constraint must be the first constraint specified"); + } else { + prev = constraints [0] as SpecialContraintExpr; + if (prev != null && (prev.Constraint & SpecialConstraint.Struct) != 0) { + report.Error (451, GetLocation ($3), "The `new()' constraint cannot be used with the `struct' constraint"); + } + } + } + + constraints.Add ((FullNamedExpression) $3); + lbag.AddLocation (constraints, GetLocation ($2)); + $$ = constraints; + } + ; + +type_parameter_constraint + : type + { + if ($1 is ComposedCast) + report.Error (706, GetLocation ($1), "Invalid constraint type `{0}'", ((ComposedCast)$1).GetSignatureForError ()); + + $$ = $1; + } + | NEW OPEN_PARENS CLOSE_PARENS + { + $$ = new SpecialContraintExpr (SpecialConstraint.Constructor, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($2), GetLocation ($3)); + } + | CLASS + { + $$ = new SpecialContraintExpr (SpecialConstraint.Class, GetLocation ($1)); + } + | STRUCT + { + $$ = new SpecialContraintExpr (SpecialConstraint.Struct, GetLocation ($1)); + } + ; + +opt_type_parameter_variance + : /* empty */ + { + $$ = null; + } + | type_parameter_variance + { + if (lang_version <= LanguageVersion.V_3) + FeatureIsNotAvailable (lexer.Location, "generic type variance"); + + $$ = $1; + } + ; + +type_parameter_variance + : OUT + { + $$ = new VarianceDecl (Variance.Covariant, GetLocation ($1)); + savedLocation = GetLocation ($1); + } + | IN + { + $$ = new VarianceDecl (Variance.Contravariant, GetLocation ($1)); + savedLocation = GetLocation ($1); + } + ; + +// +// Statements (8.2) +// + +// +// A block is "contained" on the following places: +// method_body +// property_declaration as part of the accessor body (get/set) +// operator_declaration +// constructor_declaration +// destructor_declaration +// event_declaration as part of add_accessor_declaration or remove_accessor_declaration +// +block + : OPEN_BRACE + { + ++lexer.parsing_block; + start_block (GetLocation ($1)); + } + opt_statement_list block_end + { + $$ = $4; + } + ; + +block_end + : CLOSE_BRACE + { + --lexer.parsing_block; + $$ = end_block (GetLocation ($1)); + } + | COMPLETE_COMPLETION + { + --lexer.parsing_block; + $$ = end_block (lexer.Location); + } + ; + + +block_prepared + : OPEN_BRACE + { + ++lexer.parsing_block; + current_block.StartLocation = GetLocation ($1); + } + opt_statement_list CLOSE_BRACE + { + --lexer.parsing_block; + $$ = end_block (GetLocation ($4)); + } | CLOSE_BRACE + { + report.Error (1525, GetLocation ($1), "Unexpected symbol '}', expected '{'"); + lexer.putback ('}'); + $$ = end_block (GetLocation ($1)); + } + ; + +block_prepared_strict + : OPEN_BRACE + { + ++lexer.parsing_block; + current_block.StartLocation = GetLocation ($1); + } + opt_statement_list CLOSE_BRACE + { + --lexer.parsing_block; + $$ = end_block (GetLocation ($4)); + } + ; + +opt_statement_list + : /* empty */ + | statement_list + ; + +statement_list + : statement + | statement_list statement + ; + +statement + : block_variable_declaration + { + current_block.AddStatement ((Statement) $1); + } + | valid_declaration_statement + { + current_block.AddStatement ((Statement) $1); + } + | labeled_statement +// WORKAROUND:Remove that rule, if it is >really< fixed. + | IDENTIFIER error + { + Error_SyntaxError (yyToken); + var lt =(LocatedToken) $1; + var sn = new SimpleName (lt.Value, lt.Location); + current_block.AddStatement(new StatementErrorExpression (sn)); + $$ = null; + } +//////// + | error + { + Error_SyntaxError (yyToken); + $$ = null; + } + ; + +// +// The interactive_statement and its derivatives are only +// used to provide a special version of `expression_statement' +// that has a side effect of assigning the expression to +// $retval +// +interactive_statement_list + : interactive_statement + | interactive_statement_list interactive_statement + ; + +interactive_statement + : block_variable_declaration + { + current_block.AddStatement ((Statement) $1); + } + | interactive_valid_declaration_statement + { + current_block.AddStatement ((Statement) $1); + } + | labeled_statement + ; + +valid_declaration_statement + : block + | empty_statement + | expression_statement + | selection_statement + | iteration_statement + | jump_statement + | try_statement + | checked_statement + | unchecked_statement + | lock_statement + | using_statement + | unsafe_statement + | fixed_statement + ; + +interactive_valid_declaration_statement + : block + | empty_statement + | interactive_expression_statement + | selection_statement + | iteration_statement + | jump_statement + | try_statement + | checked_statement + | unchecked_statement + | lock_statement + | using_statement + | unsafe_statement + | fixed_statement + ; + +embedded_statement + : valid_declaration_statement + | block_variable_declaration + { + report.Error (1023, GetLocation ($1), "An embedded statement may not be a declaration or labeled statement"); + $$ = null; + } + | labeled_statement + { + report.Error (1023, GetLocation ($1), "An embedded statement may not be a declaration or labeled statement"); + $$ = null; + } + | error + { + Error_SyntaxError (yyToken); + $$ = new EmptyStatement (GetLocation ($1)); + } + ; + +empty_statement + : SEMICOLON + { + // Uses lexer.Location because semicolon location is not kept in quick mode + $$ = new EmptyStatement (lexer.Location); + } + ; + +labeled_statement + : identifier_inside_body COLON + { + var lt = (LocatedToken) $1; + LabeledStatement labeled = new LabeledStatement (lt.Value, current_block, lt.Location); + lbag.AddLocation (labeled, GetLocation ($2)); + current_block.AddLabel (labeled); + current_block.AddStatement (labeled); + } + statement + ; + +variable_type + : variable_type_simple + | variable_type_simple rank_specifiers + { + if ($1 is VarExpr) + $1 = new SimpleName ("var", ((VarExpr) $1).Location); + + $$ = new ComposedCast ((FullNamedExpression) $1, (ComposedTypeSpecifier) $2); + } + ; + +/* + * The following is from Rhys' grammar: + * > Types in local variable declarations must be recognized as + * > expressions to prevent reduce/reduce errors in the grammar. + * > The expressions are converted into types during semantic analysis. + */ +variable_type_simple + : primary_expression_or_type opt_nullable + { + // Ok, the above "primary_expression" is there to get rid of + // both reduce/reduce and shift/reduces in the grammar, it should + // really just be "type_name". If you use type_name, a reduce/reduce + // creeps up. If you use namespace_or_type_name (which is all we need + // really) two shift/reduces appear. + // + + // So the super-trick is that primary_expression + // can only be either a SimpleName or a MemberAccess. + // The MemberAccess case arises when you have a fully qualified type-name like : + // Foo.Bar.Blah i; + // SimpleName is when you have + // Blah i; + + Expression expr = (Expression) $1; + if ($2 == null) { + SimpleName sn = expr as SimpleName; + if (sn != null && sn.Name == "var") + $$ = new VarExpr (sn.Location); + else + $$ = $1; + } else if (expr is ATypeNameExpression) { + $$ = new ComposedCast ((ATypeNameExpression)expr, (ComposedTypeSpecifier) $2); + } else { + Error_ExpectingTypeName (expr); + $$ = null; + } + } + | primary_expression_or_type pointer_stars + { + ATypeNameExpression expr = $1 as ATypeNameExpression; + + if (expr != null) { + $$ = new ComposedCast (expr, (ComposedTypeSpecifier) $2); + } else { + Error_ExpectingTypeName ((Expression)$1); + $$ = expr; + } + } + | builtin_type_expression + | void_invalid + ; + +pointer_stars + : pointer_star + | pointer_star pointer_stars + { + ((ComposedTypeSpecifier) $1).Next = (ComposedTypeSpecifier) $2; + $$ = $1; + } + ; + +pointer_star + : STAR + { + $$ = ComposedTypeSpecifier.CreatePointer (GetLocation ($1)); + } + ; + +identifier_inside_body + : IDENTIFIER + | AWAIT + { + $$ = Error_AwaitAsIdentifier ($1); + } + ; + +block_variable_declaration + : variable_type identifier_inside_body + { + var lt = (LocatedToken) $2; + var li = new LocalVariable (current_block, lt.Value, lt.Location); + current_block.AddLocalName (li); + current_variable = new BlockVariable ((FullNamedExpression) $1, li); + } + opt_local_variable_initializer opt_variable_declarators semicolon_or_handle_error_close_brace + { + $$ = current_variable; + current_variable = null; + if ($4 != null) + lbag.AddLocation ($$, PopLocation (), GetLocation ($6)); + else + lbag.AddLocation ($$, GetLocation ($6)); + } + | CONST variable_type identifier_inside_body + { + var lt = (LocatedToken) $3; + var li = new LocalVariable (current_block, lt.Value, LocalVariable.Flags.Constant, lt.Location); + current_block.AddLocalName (li); + current_variable = new BlockConstant ((FullNamedExpression) $2, li); + } + const_variable_initializer opt_const_declarators SEMICOLON + { + if (current_variable.Initializer != null) { + lbag.AddLocation (current_variable, GetLocation ($1), savedLocation, GetLocation ($7)); + } else { + lbag.AddLocation (current_variable, GetLocation ($1), GetLocation ($7)); + } + $$ = current_variable;; + current_variable = null; + } + ; + +semicolon_or_handle_error_close_brace + : SEMICOLON + | CLOSE_BRACE { + // Redundant, but wont regress + report.Error (1525, lexer.Location, "Unexpected symbol }"); + lexer.putback ('}'); + $$ = $1; + } + ; + +opt_local_variable_initializer + : /* empty */ + | ASSIGN block_variable_initializer + { + current_variable.Initializer = (Expression) $2; + PushLocation (GetLocation ($1)); + $$ = current_variable; + } + | error + { + if (yyToken == Token.OPEN_BRACKET_EXPR) { + report.Error (650, lexer.Location, + "Syntax error, bad array declarator. To declare a managed array the rank specifier precedes the variable's identifier. To declare a fixed size buffer field, use the fixed keyword before the field type"); + } else { + Error_SyntaxError (yyToken); + } + } + ; + +opt_variable_declarators + : /* empty */ + | variable_declarators + ; + +opt_using_or_fixed_variable_declarators + : /* empty */ + | variable_declarators + { + foreach (var d in current_variable.Declarators) { + if (d.Initializer == null) + Error_MissingInitializer (d.Variable.Location); + } + } + ; + +variable_declarators + : variable_declarator + | variable_declarators variable_declarator + ; + +variable_declarator + : COMMA identifier_inside_body + { + var lt = (LocatedToken) $2; + var li = new LocalVariable (current_variable.Variable, lt.Value, lt.Location); + var d = new BlockVariableDeclarator (li, null); + current_variable.AddDeclarator (d); + current_block.AddLocalName (li); + lbag.AddLocation (d, GetLocation ($1)); + } + | COMMA identifier_inside_body ASSIGN block_variable_initializer + { + var lt = (LocatedToken) $2; + var li = new LocalVariable (current_variable.Variable, lt.Value, lt.Location); + var d = new BlockVariableDeclarator (li, (Expression) $4); + current_variable.AddDeclarator (d); + current_block.AddLocalName (li); + lbag.AddLocation (d, GetLocation ($1), GetLocation ($3)); + } + ; + +const_variable_initializer + : /* empty */ + { + report.Error (145, lexer.Location, "A const field requires a value to be provided"); + } + | ASSIGN constant_initializer_expr + { + savedLocation = GetLocation ($1); + current_variable.Initializer = (Expression) $2; + } + ; + +opt_const_declarators + : /* empty */ + | const_declarators + ; + +const_declarators + : const_declarator + | const_declarators const_declarator + ; + +const_declarator + : COMMA identifier_inside_body ASSIGN constant_initializer_expr + { + var lt = (LocatedToken) $2; + var li = new LocalVariable (current_block, lt.Value, LocalVariable.Flags.Constant, lt.Location); + var d = new BlockVariableDeclarator (li, (Expression) $4); + current_variable.AddDeclarator (d); + current_block.AddLocalName (li); + lbag.AddLocation (d, GetLocation ($1), GetLocation ($3)); + } + ; + +block_variable_initializer + : variable_initializer + | STACKALLOC simple_type OPEN_BRACKET_EXPR expression CLOSE_BRACKET + { + $$ = new StackAlloc ((Expression) $2, (Expression) $4, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($3), GetLocation ($5)); + } + | STACKALLOC simple_type + { + report.Error (1575, GetLocation ($1), "A stackalloc expression requires [] after type"); + $$ = new StackAlloc ((Expression) $2, null, GetLocation ($1)); + } + ; + +expression_statement + : statement_expression SEMICOLON + { + $$ = $1; + lbag.AddStatement ($$, GetLocation ($2)); + } + | statement_expression COMPLETE_COMPLETION { $$ = $1; } + | statement_expression CLOSE_BRACE + { + $$ = $1; + report.Error (1002, GetLocation ($2), "; expected"); + lexer.putback ('}'); + } + ; + +interactive_expression_statement + : interactive_statement_expression SEMICOLON { $$ = $1; } + | interactive_statement_expression COMPLETE_COMPLETION { $$ = $1; } + ; + + // + // We have to do the wrapping here and not in the case above, + // because statement_expression is used for example in for_statement + // +statement_expression + : expression + { + ExpressionStatement s = $1 as ExpressionStatement; + if (s == null) { + var expr = $1 as Expression; + $$ = new StatementErrorExpression (expr); + } else { + $$ = new StatementExpression (s); + } + } + ; + +interactive_statement_expression + : expression + { + Expression expr = (Expression) $1; + $$ = new StatementExpression (new OptionalAssign (expr, lexer.Location)); + } + | error + { + Error_SyntaxError (yyToken); + $$ = new EmptyStatement (GetLocation ($1)); + } + ; + +selection_statement + : if_statement + | switch_statement + ; + +if_statement + : IF open_parens_any boolean_expression CLOSE_PARENS + embedded_statement + { + if ($5 is EmptyStatement) + Warning_EmptyStatement (GetLocation ($5)); + + $$ = new If ((BooleanExpression) $3, (Statement) $5, GetLocation ($1)); + lbag.AddStatement ($$, GetLocation ($2), GetLocation ($4)); + } + | IF open_parens_any boolean_expression CLOSE_PARENS + embedded_statement ELSE embedded_statement + { + $$ = new If ((BooleanExpression) $3, (Statement) $5, (Statement) $7, GetLocation ($1)); + lbag.AddStatement ($$, GetLocation ($2), GetLocation ($4), GetLocation ($6)); + + if ($5 is EmptyStatement) + Warning_EmptyStatement (GetLocation ($5)); + if ($7 is EmptyStatement) + Warning_EmptyStatement (GetLocation ($7)); + } + | IF open_parens_any boolean_expression error + { + Error_SyntaxError (yyToken); + + $$ = new If ((BooleanExpression) $3, null, GetLocation ($1)); + lbag.AddStatement ($$, GetLocation ($2)); + } + ; + +switch_statement + : SWITCH open_parens_any expression CLOSE_PARENS OPEN_BRACE + { + start_block (GetLocation ($5)); + } + opt_switch_sections CLOSE_BRACE + { + $$ = new Switch ((Expression) $3, (ExplicitBlock) current_block.Explicit, GetLocation ($1)); + end_block (GetLocation ($8)); + lbag.AddStatement ($$, GetLocation ($2), GetLocation ($4), GetLocation ($5), GetLocation ($8)); + } + | SWITCH open_parens_any expression error + { + Error_SyntaxError (yyToken); + + $$ = new Switch ((Expression) $3, null, GetLocation ($1)); + lbag.AddStatement ($$, GetLocation ($2)); + } + ; + +opt_switch_sections + : /* empty */ + { + report.Warning (1522, 1, current_block.StartLocation, "Empty switch block"); + } + | switch_sections + ; + +switch_sections + : switch_section + | switch_sections switch_section + | error + { + Error_SyntaxError (yyToken); + } + ; + +switch_section + : switch_labels statement_list + ; + +switch_labels + : switch_label + { + var label = (SwitchLabel) $1; + label.SectionStart = true; + current_block.AddStatement (label); + } + | switch_labels switch_label + { + current_block.AddStatement ((Statement) $2); + } + ; + +switch_label + : CASE constant_expression COLON + { + $$ = new SwitchLabel ((Expression) $2, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($3)); + } + | CASE constant_expression error + { + Error_SyntaxError (yyToken); + $$ = new SwitchLabel ((Expression) $2, GetLocation ($1)); + } + | DEFAULT_COLON + { + $$ = new SwitchLabel (null, GetLocation ($1)); + } + ; + +iteration_statement + : while_statement + | do_statement + | for_statement + | foreach_statement + ; + +while_statement + : WHILE open_parens_any boolean_expression CLOSE_PARENS embedded_statement + { + if ($5 is EmptyStatement && lexer.peek_token () == Token.OPEN_BRACE) + Warning_EmptyStatement (GetLocation ($5)); + + $$ = new While ((BooleanExpression) $3, (Statement) $5, GetLocation ($1)); + lbag.AddStatement ($$, GetLocation ($2), GetLocation ($4)); + } + | WHILE open_parens_any boolean_expression error + { + Error_SyntaxError (yyToken); + + $$ = new While ((BooleanExpression) $3, null, GetLocation ($1)); + lbag.AddStatement ($$, GetLocation ($2)); + } + ; + +do_statement + : DO embedded_statement WHILE open_parens_any boolean_expression CLOSE_PARENS SEMICOLON + { + $$ = new Do ((Statement) $2, (BooleanExpression) $5, GetLocation ($1), GetLocation ($3)); + lbag.AddStatement ($$, GetLocation ($3), GetLocation ($4), GetLocation ($6), GetLocation ($7)); + } + | DO embedded_statement error + { + Error_SyntaxError (yyToken); + $$ = new Do ((Statement) $2, null, GetLocation ($1), Location.Null); + } + | DO embedded_statement WHILE open_parens_any boolean_expression error + { + Error_SyntaxError (yyToken); + + $$ = new Do ((Statement) $2, (BooleanExpression) $5, GetLocation ($1), GetLocation ($3)); + lbag.AddStatement ($$, GetLocation ($3), GetLocation ($4)); + } + ; + +for_statement + : FOR open_parens_any + { + start_block (GetLocation ($2)); + current_block.IsCompilerGenerated = true; + For f = new For (GetLocation ($1)); + current_block.AddStatement (f); + lbag.AddStatement (f, current_block.StartLocation); + $$ = f; + } + for_statement_cont + { + $$ = $4; + } + ; + +// Has to use be extra rule to recover started block +for_statement_cont + : opt_for_initializer SEMICOLON + { + For f = (For) $0; + f.Initializer = (Statement) $1; + lbag.AddLocation (f, GetLocation ($2)); + $$ = f; + } + for_statement_condition + { + $$ = $4; + } + | opt_for_initializer CLOSE_PARENS { + report.Error (1525, GetLocation ($2), "Unexpected symbol ')', expected ';'"); + For f = (For) $0; + f.Initializer = (Statement) $1; + lbag.AddLocation (f, GetLocation ($2)); + $$ = end_block (GetLocation ($2)); + } + ; + +for_statement_condition + : opt_for_condition SEMICOLON + { + For f = (For) $0; + f.Condition = (BooleanExpression) $1; + lbag.AddLocation (f, GetLocation ($2)); + $$ = f; + } + for_statement_end + { + $$ = $4; + } + + | boolean_expression CLOSE_PARENS { + report.Error (1525, GetLocation ($2), "Unexpected symbol ')', expected ';'"); + For f = (For) $0; + f.Condition = (BooleanExpression) $1; + lbag.AddLocation (f, GetLocation ($2)); + $$ = end_block (GetLocation ($2)); + } + ; + +for_statement_end + : opt_for_iterator CLOSE_PARENS + embedded_statement + { + For f = (For) $0; + f.Iterator = (Statement) $1; + + if ($3 is EmptyStatement && lexer.peek_token () == Token.OPEN_BRACE) + Warning_EmptyStatement (GetLocation ($3)); + + f.Statement = (Statement) $3; + lbag.AddLocation (f, GetLocation ($2)); + + $$ = end_block (GetLocation ($2)); + } + | error + { + Error_SyntaxError (yyToken); + $$ = end_block (current_block.StartLocation); + } + ; + +opt_for_initializer + : /* empty */ { $$ = new EmptyStatement (lexer.Location); } + | for_initializer + ; + +for_initializer + : variable_type identifier_inside_body + { + var lt = (LocatedToken) $2; + var li = new LocalVariable (current_block, lt.Value, lt.Location); + current_block.AddLocalName (li); + current_variable = new BlockVariable ((FullNamedExpression) $1, li); + } + opt_local_variable_initializer opt_variable_declarators + { + $$ = current_variable; + if ($4 != null) + lbag.AddLocation (current_variable, PopLocation ()); + + current_variable = null; + } + | statement_expression_list + ; + +opt_for_condition + : /* empty */ { $$ = null; } + | boolean_expression + ; + +opt_for_iterator + : /* empty */ { $$ = new EmptyStatement (lexer.Location); } + | for_iterator + ; + +for_iterator + : statement_expression_list + ; + +statement_expression_list + : statement_expression + | statement_expression_list COMMA statement_expression + { + var sl = $1 as StatementList; + if (sl == null) { + sl = new StatementList ((Statement) $1, (Statement) $3); + lbag.AddStatement (sl, GetLocation ($2)); + } else { + sl.Add ((Statement) $3); + lbag.AddLocation (sl, GetLocation ($2)); + + } + + $$ = sl; + } + ; + +foreach_statement + : FOREACH open_parens_any type error + { + report.Error (230, GetLocation ($1), "Type and identifier are both required in a foreach statement"); + + start_block (GetLocation ($2)); + current_block.IsCompilerGenerated = true; + + Foreach f = new Foreach ((Expression) $3, null, null, null, null, GetLocation ($1)); + current_block.AddStatement (f); + + lbag.AddStatement (f, GetLocation ($2)); + $$ = end_block (GetLocation ($4)); + } + | FOREACH open_parens_any type identifier_inside_body error + { + Error_SyntaxError (yyToken); + + start_block (GetLocation ($2)); + current_block.IsCompilerGenerated = true; + + var lt = (LocatedToken) $4; + var li = new LocalVariable (current_block, lt.Value, LocalVariable.Flags.ForeachVariable | LocalVariable.Flags.Used, lt.Location); + current_block.AddLocalName (li); + + Foreach f = new Foreach ((Expression) $3, li, null, null, null, GetLocation ($1)); + current_block.AddStatement (f); + + lbag.AddStatement (f, GetLocation ($2)); + $$ = end_block (GetLocation ($5)); + } + | FOREACH open_parens_any type identifier_inside_body IN expression CLOSE_PARENS + { + start_block (GetLocation ($2)); + current_block.IsCompilerGenerated = true; + + var lt = (LocatedToken) $4; + var li = new LocalVariable (current_block, lt.Value, LocalVariable.Flags.ForeachVariable | LocalVariable.Flags.Used, lt.Location); + current_block.AddLocalName (li); + $$ = li; + } + embedded_statement + { + if ($9 is EmptyStatement && lexer.peek_token () == Token.OPEN_BRACE) + Warning_EmptyStatement (GetLocation ($9)); + + Foreach f = new Foreach ((Expression) $3, (LocalVariable) $8, (Expression) $6, (Statement) $9, current_block, GetLocation ($1)); + lbag.AddStatement (f, GetLocation ($2), GetLocation ($5), GetLocation ($7)); + end_block (GetLocation ($7)); + + $$ = f; + } + | FOREACH open_parens_any type identifier_inside_body error + { + start_block (GetLocation ($2)); + current_block.IsCompilerGenerated = true; + var lt = $4 as LocatedToken; + var li = lt != null ? new LocalVariable (current_block, lt.Value, LocalVariable.Flags.ForeachVariable | LocalVariable.Flags.Used, lt.Location) : null; + + Foreach f = new Foreach ((Expression) $3, li, null, null, null, GetLocation ($1)); + current_block.AddStatement (f); + + lbag.AddStatement (f, GetLocation ($2)); + $$ = end_block (GetLocation ($5)); + } + | FOREACH open_parens_any type error + { + Foreach f = new Foreach ((Expression) $3, null, null, null, null, GetLocation ($1)); + current_block.AddStatement (f); + + lbag.AddStatement (f, GetLocation ($2)); + $$ = f; + } + ; + +jump_statement + : break_statement + | continue_statement + | goto_statement + | return_statement + | throw_statement + | yield_statement + ; + +break_statement + : BREAK SEMICOLON + { + $$ = new Break (GetLocation ($1)); + lbag.AddStatement ($$, GetLocation ($2)); + } + ; + +continue_statement + : CONTINUE SEMICOLON + { + $$ = new Continue (GetLocation ($1)); + lbag.AddStatement ($$, GetLocation ($2)); + } + | CONTINUE error + { + Error_SyntaxError (yyToken); + $$ = new Continue (GetLocation ($1)); + } + ; + +goto_statement + : GOTO identifier_inside_body SEMICOLON + { + var lt = (LocatedToken) $2; + $$ = new Goto (lt.Value, GetLocation ($1)); + lbag.AddStatement ($$, GetLocation ($2), GetLocation ($3)); + } + | GOTO CASE constant_expression SEMICOLON + { + $$ = new GotoCase ((Expression) $3, GetLocation ($1)); + lbag.AddStatement ($$, GetLocation ($2), GetLocation ($4)); + } + | GOTO DEFAULT SEMICOLON + { + $$ = new GotoDefault (GetLocation ($1)); + lbag.AddStatement ($$, GetLocation ($2), GetLocation ($3)); + } + ; + +return_statement + : RETURN opt_expression SEMICOLON + { + $$ = new Return ((Expression) $2, GetLocation ($1)); + lbag.AddStatement ($$, GetLocation ($3)); + } + | RETURN expression error + { + Error_SyntaxError (yyToken); + $$ = new Return ((Expression) $2, GetLocation ($1)); + } + | RETURN error + { + Error_SyntaxError (yyToken); + $$ = new Return (null, GetLocation ($1)); + } + ; + +throw_statement + : THROW opt_expression SEMICOLON + { + $$ = new Throw ((Expression) $2, GetLocation ($1)); + lbag.AddStatement ($$, GetLocation ($3)); + } + | THROW expression error + { + Error_SyntaxError (yyToken); + $$ = new Throw ((Expression) $2, GetLocation ($1)); + } + | THROW error + { + Error_SyntaxError (yyToken); + $$ = new Throw (null, GetLocation ($1)); + } + ; + +yield_statement + : identifier_inside_body RETURN opt_expression SEMICOLON + { + var lt = (LocatedToken) $1; + string s = lt.Value; + if (s != "yield"){ + report.Error (1003, lt.Location, "; expected"); + } else if ($3 == null) { + report.Error (1627, GetLocation ($4), "Expression expected after yield return"); + } else if (lang_version == LanguageVersion.ISO_1){ + FeatureIsNotAvailable (lt.Location, "iterators"); + } + + current_block.Explicit.RegisterIteratorYield (); + $$ = new Yield ((Expression) $3, lt.Location); + lbag.AddStatement ($$, GetLocation ($2), GetLocation ($4)); + } + | identifier_inside_body RETURN expression error + { + Error_SyntaxError (yyToken); + + var lt = (LocatedToken) $1; + string s = lt.Value; + if (s != "yield"){ + report.Error (1003, lt.Location, "; expected"); + } else if ($3 == null) { + report.Error (1627, GetLocation ($4), "Expression expected after yield return"); + } else if (lang_version == LanguageVersion.ISO_1){ + FeatureIsNotAvailable (lt.Location, "iterators"); + } + + current_block.Explicit.RegisterIteratorYield (); + $$ = new Yield ((Expression) $3, lt.Location); + lbag.AddStatement ($$, GetLocation ($2)); + } + | identifier_inside_body BREAK SEMICOLON + { + var lt = (LocatedToken) $1; + string s = lt.Value; + if (s != "yield"){ + report.Error (1003, lt.Location, "; expected"); + } else if (lang_version == LanguageVersion.ISO_1){ + FeatureIsNotAvailable (lt.Location, "iterators"); + } + + current_block.ParametersBlock.TopBlock.IsIterator = true; + $$ = new YieldBreak (lt.Location); + lbag.AddStatement ($$, GetLocation ($2), GetLocation ($3)); + } + ; + +opt_expression + : /* empty */ + | expression + ; + +try_statement + : TRY block catch_clauses + { + $$ = new TryCatch ((Block) $2, (List) $3, GetLocation ($1), false); + } + | TRY block FINALLY block + { + $$ = new TryFinally ((Statement) $2, (ExplicitBlock) $4, GetLocation ($1)); + lbag.AddStatement ($$, GetLocation ($3)); + } + | TRY block catch_clauses FINALLY block + { + $$ = new TryFinally (new TryCatch ((Block) $2, (List) $3, GetLocation ($1), true), (ExplicitBlock) $5, GetLocation ($1)); + lbag.AddStatement ($$, GetLocation ($4)); + } + | TRY block error + { + Error_SyntaxError (1524, yyToken); + $$ = new TryCatch ((Block) $2, null, GetLocation ($1), false); + } + ; + +catch_clauses + : catch_clause + { + var l = new List (2); + + l.Add ((Catch) $1); + $$ = l; + } + | catch_clauses catch_clause + { + var l = (List) $1; + + Catch c = (Catch) $2; + var prev_catch = l [l.Count - 1]; + if (prev_catch.IsGeneral && prev_catch.Filter == null) { + report.Error (1017, c.loc, "Try statement already has an empty catch block"); + } + + l.Add (c); + $$ = l; + } + ; + +opt_identifier + : /* empty */ + | identifier_inside_body + ; + +catch_clause + : CATCH opt_catch_filter block + { + var c = new Catch ((ExplicitBlock) $3, GetLocation ($1)); + c.Filter = (CatchFilterExpression) $2; + $$ = c; + } + | CATCH open_parens_any type opt_identifier CLOSE_PARENS + { + start_block (GetLocation ($2)); + var c = new Catch ((ExplicitBlock) current_block, GetLocation ($1)); + c.TypeExpression = (FullNamedExpression) $3; + + if ($4 != null) { + var lt = (LocatedToken) $4; + c.Variable = new LocalVariable (current_block, lt.Value, lt.Location); + current_block.AddLocalName (c.Variable); + } + + lbag.AddLocation (c, GetLocation ($2), GetLocation ($5)); + $$ = c; + } + opt_catch_filter block_prepared + { + ((Catch) $6).Filter = (CatchFilterExpression) $7; + $$ = $6; + } + | CATCH open_parens_any error + { + if (yyToken == Token.CLOSE_PARENS) { + report.Error (1015, lexer.Location, + "A type that derives from `System.Exception', `object', or `string' expected"); + } else { + Error_SyntaxError (yyToken); + } + + $$ = new Catch (null, GetLocation ($1)); + } + | CATCH open_parens_any type opt_identifier CLOSE_PARENS error + { + Error_SyntaxError (yyToken); + + // Required otherwise missing block could not be detected because + // start_block is run early + var c = new Catch (null, GetLocation ($1)); + c.TypeExpression = (FullNamedExpression) $3; + + if ($4 != null) { + var lt = (LocatedToken) $4; + c.Variable = new LocalVariable (current_block, lt.Value, lt.Location); + } + + if ($4 != null) { + var lt = (LocatedToken) $4; + c.Variable = new LocalVariable (current_block, lt.Value, lt.Location); + } + + lbag.AddLocation (c, GetLocation ($2), GetLocation ($5)); + + $$ = c; + } + ; + +opt_catch_filter + : /* empty */ + | IF open_parens_any expression CLOSE_PARENS + { + if (lang_version <= LanguageVersion.V_5) + FeatureIsNotAvailable (GetLocation ($1), "exception filter"); + + $$ = new CatchFilterExpression ((Expression) $3, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($2), GetLocation ($4)); + } + ; + +checked_statement + : CHECKED block + { + $$ = new Checked ((Block) $2, GetLocation ($1)); + } + ; + +unchecked_statement + : UNCHECKED block + { + $$ = new Unchecked ((Block) $2, GetLocation ($1)); + } + ; + +unsafe_statement + : UNSAFE + { + if (!settings.Unsafe) + Error_UnsafeCodeNotAllowed (GetLocation ($1)); + } block { + $$ = new Unsafe ((Block) $3, GetLocation ($1)); + } + ; + +lock_statement + : LOCK open_parens_any expression CLOSE_PARENS embedded_statement + { + if ($5 is EmptyStatement && lexer.peek_token () == Token.OPEN_BRACE) + Warning_EmptyStatement (GetLocation ($5)); + + $$ = new Lock ((Expression) $3, (Statement) $5, GetLocation ($1)); + lbag.AddStatement ($$, GetLocation ($2), GetLocation ($4)); + } + | LOCK open_parens_any expression error + { + Error_SyntaxError (yyToken); + + $$ = new Lock ((Expression) $3, null, GetLocation ($1)); + lbag.AddStatement ($$, GetLocation ($2)); + } + ; + +fixed_statement + : FIXED open_parens_any variable_type identifier_inside_body + { + start_block (GetLocation ($2)); + + current_block.IsCompilerGenerated = true; + var lt = (LocatedToken) $4; + var li = new LocalVariable (current_block, lt.Value, LocalVariable.Flags.FixedVariable | LocalVariable.Flags.Used, lt.Location); + current_block.AddLocalName (li); + current_variable = new Fixed.VariableDeclaration ((FullNamedExpression) $3, li); + } + using_or_fixed_variable_initializer opt_using_or_fixed_variable_declarators CLOSE_PARENS + { + $$ = current_variable; + current_variable = null; + } + embedded_statement + { + if ($10 is EmptyStatement && lexer.peek_token () == Token.OPEN_BRACE) + Warning_EmptyStatement (GetLocation ($10)); + + Fixed f = new Fixed ((Fixed.VariableDeclaration) $9, (Statement) $10, GetLocation ($1)); + current_block.AddStatement (f); + lbag.AddStatement (f, GetLocation ($2), GetLocation ($8)); + $$ = end_block (GetLocation ($8)); + } + ; + +using_statement + : USING open_parens_any variable_type identifier_inside_body + { + start_block (GetLocation ($2)); + + current_block.IsCompilerGenerated = true; + var lt = (LocatedToken) $4; + var li = new LocalVariable (current_block, lt.Value, LocalVariable.Flags.UsingVariable | LocalVariable.Flags.Used, lt.Location); + current_block.AddLocalName (li); + current_variable = new Using.VariableDeclaration ((FullNamedExpression) $3, li); + } + using_initialization CLOSE_PARENS + { + $$ = current_variable; + current_variable = null; + } + embedded_statement + { + if ($9 is EmptyStatement && lexer.peek_token () == Token.OPEN_BRACE) + Warning_EmptyStatement (GetLocation ($9)); + + Using u = new Using ((Using.VariableDeclaration) $8, (Statement) $9, GetLocation ($1)); + lbag.AddStatement (u, GetLocation ($2), GetLocation ($7)); + current_block.AddStatement (u); + $$ = end_block (GetLocation ($7)); + } + | USING open_parens_any expression CLOSE_PARENS embedded_statement + { + if ($5 is EmptyStatement && lexer.peek_token () == Token.OPEN_BRACE) + Warning_EmptyStatement (GetLocation ($5)); + + $$ = new Using ((Expression) $3, (Statement) $5, GetLocation ($1)); + lbag.AddStatement ($$, GetLocation ($2), GetLocation ($4)); + } + | USING open_parens_any expression error + { + Error_SyntaxError (yyToken); + + $$ = new Using ((Expression) $3, null, GetLocation ($1)); + lbag.AddStatement ($$, GetLocation ($2)); + } + ; + +using_initialization + : using_or_fixed_variable_initializer opt_using_or_fixed_variable_declarators + | error + { + // It has to be here for the parent to safely restore artificial block + Error_SyntaxError (yyToken); + } + ; + +using_or_fixed_variable_initializer + : /* empty */ + { + Error_MissingInitializer (lexer.Location); + } + | ASSIGN variable_initializer + { + current_variable.Initializer = (Expression) $2; + lbag.AddLocation (current_variable, GetLocation ($1)); + $$ = current_variable; + } + ; + + +// LINQ + +query_expression + : first_from_clause query_body + { + lexer.query_parsing = false; + + Linq.AQueryClause from = $1 as Linq.AQueryClause; + + from.Tail.Next = (Linq.AQueryClause)$2; + $$ = from; + + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + } + | nested_from_clause query_body + { + Linq.AQueryClause from = $1 as Linq.AQueryClause; + + from.Tail.Next = (Linq.AQueryClause)$2; + $$ = from; + + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + } + + // Bubble up COMPLETE_COMPLETION productions + | first_from_clause COMPLETE_COMPLETION { + lexer.query_parsing = false; + $$ = $1; + + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + } + | nested_from_clause COMPLETE_COMPLETION { + $$ = $1; + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + } + ; + +first_from_clause + : FROM_FIRST identifier_inside_body IN expression + { + current_block = new Linq.QueryBlock (current_block, lexer.Location); + + var lt = (LocatedToken) $2; + var rv = new Linq.RangeVariable (lt.Value, lt.Location); + var clause = new Linq.QueryStartClause ((Linq.QueryBlock)current_block, (Expression)$4, rv, GetLocation ($1)); + lbag.AddLocation (clause, GetLocation ($3)); + $$ = new Linq.QueryExpression (clause); + } + | FROM_FIRST type identifier_inside_body IN expression + { + current_block = new Linq.QueryBlock (current_block, lexer.Location); + + var lt = (LocatedToken) $3; + var rv = new Linq.RangeVariable (lt.Value, lt.Location); + var clause = new Linq.QueryStartClause ((Linq.QueryBlock)current_block, (Expression)$5, rv, GetLocation ($1)) { + IdentifierType = (FullNamedExpression)$2 + }; + lbag.AddLocation (clause, GetLocation ($4)); + $$ = new Linq.QueryExpression (clause); + } + ; + +nested_from_clause + : FROM identifier_inside_body IN expression + { + current_block = new Linq.QueryBlock (current_block, lexer.Location); + + var lt = (LocatedToken) $2; + var rv = new Linq.RangeVariable (lt.Value, lt.Location); + var clause = new Linq.QueryStartClause ((Linq.QueryBlock)current_block, (Expression)$4, rv, GetLocation ($1)); + lbag.AddLocation (clause, GetLocation ($3)); + $$ = new Linq.QueryExpression (clause); + } + | FROM type identifier_inside_body IN expression + { + current_block = new Linq.QueryBlock (current_block, lexer.Location); + + var lt = (LocatedToken) $3; + var rv = new Linq.RangeVariable (lt.Value, lt.Location); + var clause = new Linq.QueryStartClause ((Linq.QueryBlock)current_block, (Expression)$5, rv, GetLocation ($1)) { + IdentifierType = (FullNamedExpression)$2 + }; + lbag.AddLocation (clause, GetLocation ($4)); + $$ = new Linq.QueryExpression (clause); + } + ; + +from_clause + : FROM identifier_inside_body IN + { + current_block = new Linq.QueryBlock (current_block, lexer.Location); + } + expression_or_error + { + var lt = (LocatedToken) $2; + var sn = new Linq.RangeVariable (lt.Value, lt.Location); + $$ = new Linq.SelectMany ((Linq.QueryBlock)current_block, sn, (Expression)$5, GetLocation ($1)); + + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + ((Linq.QueryBlock)current_block).AddRangeVariable (sn); + lbag.AddLocation ($$, GetLocation ($3)); + } + | FROM type identifier_inside_body IN + { + current_block = new Linq.QueryBlock (current_block, lexer.Location); + } + expression_or_error + { + var lt = (LocatedToken) $3; + var sn = new Linq.RangeVariable (lt.Value, lt.Location); + + $$ = new Linq.SelectMany ((Linq.QueryBlock)current_block, sn, (Expression)$6, GetLocation ($1)) { + IdentifierType = (FullNamedExpression)$2 + }; + + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + + ((Linq.QueryBlock)current_block).AddRangeVariable (sn); + + lbag.AddLocation ($$, GetLocation ($4)); + } + ; + +query_body + : query_body_clauses select_or_group_clause opt_query_continuation + { + Linq.AQueryClause head = (Linq.AQueryClause)$2; + + if ($3 != null) + head.Next = (Linq.AQueryClause)$3; + + if ($1 != null) { + Linq.AQueryClause clause = (Linq.AQueryClause)$1; + clause.Tail.Next = head; + head = clause; + } + + $$ = head; + } + | select_or_group_clause opt_query_continuation + { + Linq.AQueryClause head = (Linq.AQueryClause)$2; + + if ($1 != null) { + Linq.AQueryClause clause = (Linq.AQueryClause)$1; + clause.Tail.Next = head; + head = clause; + } + + $$ = head; + } + | query_body_clauses COMPLETE_COMPLETION + | query_body_clauses error + { + report.Error (742, GetLocation ($2), "Unexpected symbol `{0}'. A query body must end with select or group clause", GetSymbolName (yyToken)); + $$ = $1; + } + | error + { + Error_SyntaxError (yyToken); + $$ = null; + } + ; + +select_or_group_clause + : SELECT + { + current_block = new Linq.QueryBlock (current_block, lexer.Location); + } + expression_or_error + { + $$ = new Linq.Select ((Linq.QueryBlock)current_block, (Expression)$3, GetLocation ($1)); + + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + } + | GROUP + { + if (linq_clause_blocks == null) + linq_clause_blocks = new Stack (); + + current_block = new Linq.QueryBlock (current_block, lexer.Location); + linq_clause_blocks.Push ((Linq.QueryBlock)current_block); + } + expression_or_error + { + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + + current_block = new Linq.QueryBlock (current_block, lexer.Location); + } + by_expression + { + var obj = (object[]) $5; + + $$ = new Linq.GroupBy ((Linq.QueryBlock)current_block, (Expression)$3, linq_clause_blocks.Pop (), (Expression)obj[0], GetLocation ($1)); + lbag.AddLocation ($$, (Location) obj[1]); + + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + } + ; + +by_expression + : BY expression_or_error + { + $$ = new object[] { $2, GetLocation ($1) }; + } + | error + { + Error_SyntaxError (yyToken); + $$ = new object[2] { null, Location.Null }; + } + ; + +query_body_clauses + : query_body_clause + | query_body_clauses query_body_clause + { + ((Linq.AQueryClause)$1).Tail.Next = (Linq.AQueryClause)$2; + $$ = $1; + } + ; + +query_body_clause + : from_clause + | let_clause + | where_clause + | join_clause + | orderby_clause + ; + +let_clause + : LET identifier_inside_body ASSIGN + { + current_block = new Linq.QueryBlock (current_block, lexer.Location); + } + expression_or_error + { + var lt = (LocatedToken) $2; + var sn = new Linq.RangeVariable (lt.Value, lt.Location); + $$ = new Linq.Let ((Linq.QueryBlock) current_block, sn, (Expression)$5, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($3)); + + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + + ((Linq.QueryBlock)current_block).AddRangeVariable (sn); + } + ; + +where_clause + : WHERE + { + current_block = new Linq.QueryBlock (current_block, lexer.Location); + } + expression_or_error + { + $$ = new Linq.Where ((Linq.QueryBlock)current_block, (Expression)$3, GetLocation ($1)); + + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + } + ; + +join_clause + : JOIN identifier_inside_body IN + { + if (linq_clause_blocks == null) + linq_clause_blocks = new Stack (); + + current_block = new Linq.QueryBlock (current_block, lexer.Location); + linq_clause_blocks.Push ((Linq.QueryBlock) current_block); + } + expression_or_error ON + { + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + + current_block = new Linq.QueryBlock (current_block, lexer.Location); + linq_clause_blocks.Push ((Linq.QueryBlock) current_block); + } + expression_or_error EQUALS + { + current_block.AddStatement (new ContextualReturn ((Expression) $8)); + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + + current_block = new Linq.QueryBlock (current_block, lexer.Location); + } + expression_or_error opt_join_into + { + current_block.AddStatement (new ContextualReturn ((Expression) $11)); + current_block.SetEndLocation (lexer.Location); + + var outer_selector = linq_clause_blocks.Pop (); + var block = linq_clause_blocks.Pop (); + + var lt = (LocatedToken) $2; + var sn = new Linq.RangeVariable (lt.Value, lt.Location); + Linq.RangeVariable into; + + if ($12 == null) { + into = sn; + $$ = new Linq.Join (block, sn, (Expression)$5, outer_selector, (Linq.QueryBlock) current_block, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($3), GetLocation ($6), GetLocation ($9)); + } else { + // + // Set equals right side parent to beginning of linq query, it is not accessible therefore cannot cause name collisions + // + var parent = block.Parent; + while (parent is Linq.QueryBlock) { + parent = parent.Parent; + } + current_block.Parent = parent; + + ((Linq.QueryBlock)current_block).AddRangeVariable (sn); + + lt = (LocatedToken) $12; + into = new Linq.RangeVariable (lt.Value, lt.Location); + + $$ = new Linq.GroupJoin (block, sn, (Expression)$5, outer_selector, (Linq.QueryBlock) current_block, into, GetLocation ($1)); + lbag.AddLocation ($$, GetLocation ($3), GetLocation ($6), GetLocation ($9), opt_intoStack.Pop ()); + } + + current_block = block.Parent; + ((Linq.QueryBlock)current_block).AddRangeVariable (into); + } + | JOIN type identifier_inside_body IN + { + if (linq_clause_blocks == null) + linq_clause_blocks = new Stack (); + + current_block = new Linq.QueryBlock (current_block, lexer.Location); + linq_clause_blocks.Push ((Linq.QueryBlock) current_block); + } + expression_or_error ON + { + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + + current_block = new Linq.QueryBlock (current_block, lexer.Location); + linq_clause_blocks.Push ((Linq.QueryBlock) current_block); + } + expression_or_error EQUALS + { + current_block.AddStatement (new ContextualReturn ((Expression) $9)); + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + + current_block = new Linq.QueryBlock (current_block, lexer.Location); + } + expression_or_error opt_join_into + { + current_block.AddStatement (new ContextualReturn ((Expression) $12)); + current_block.SetEndLocation (lexer.Location); + + var outer_selector = linq_clause_blocks.Pop (); + var block = linq_clause_blocks.Pop (); + + var lt = (LocatedToken) $3; + var sn = new Linq.RangeVariable (lt.Value, lt.Location); + Linq.RangeVariable into; + + if ($13 == null) { + into = sn; + $$ = new Linq.Join (block, sn, (Expression)$6, outer_selector, (Linq.QueryBlock) current_block, GetLocation ($1)) { + IdentifierType = (FullNamedExpression)$2 + }; + lbag.AddLocation ($$, GetLocation ($3), GetLocation ($6), GetLocation ($9)); + } else { + // + // Set equals right side parent to beginning of linq query, it is not accessible therefore cannot cause name collisions + // + var parent = block.Parent; + while (parent is Linq.QueryBlock) { + parent = parent.Parent; + } + current_block.Parent = parent; + + ((Linq.QueryBlock)current_block).AddRangeVariable (sn); + + lt = (LocatedToken) $13; + into = new Linq.RangeVariable (lt.Value, lt.Location); // TODO: + + $$ = new Linq.GroupJoin (block, sn, (Expression)$6, outer_selector, (Linq.QueryBlock) current_block, into, GetLocation ($1)) { + IdentifierType = (FullNamedExpression)$2 + }; + lbag.AddLocation ($$, GetLocation ($3), GetLocation ($6), GetLocation ($9), opt_intoStack.Pop ()); + } + + current_block = block.Parent; + ((Linq.QueryBlock)current_block).AddRangeVariable (into); + } + ; + +opt_join_into + : /* empty */ + | INTO identifier_inside_body + { + opt_intoStack.Push (GetLocation ($1)); + $$ = $2; + } + ; + +orderby_clause + : ORDERBY + { + current_block = new Linq.QueryBlock (current_block, lexer.Location); + lbag.AddLocation (current_block, GetLocation ($1)); + } + orderings + { + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + + $$ = $3; + } + ; + +orderings + : order_by + | order_by COMMA + { + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + + current_block = new Linq.QueryBlock (current_block, lexer.Location); + } + orderings_then_by + { + ((Linq.AQueryClause)$1).Next = (Linq.AQueryClause)$4; + $$ = $1; + } + ; + +orderings_then_by + : then_by + | orderings_then_by COMMA + { + current_block.SetEndLocation (lexer.Location); + current_block = current_block.Parent; + + current_block = new Linq.QueryBlock ((Linq.QueryBlock) current_block, lexer.Location); + } + then_by + { + ((Linq.AQueryClause)$1).Tail.Next = (Linq.AQueryClause)$4; + $$ = $1; + } + ; + +order_by + : expression + { + $$ = new Linq.OrderByAscending ((Linq.QueryBlock) current_block, (Expression)$1); + } + | expression ASCENDING + { + $$ = new Linq.OrderByAscending ((Linq.QueryBlock) current_block, (Expression)$1); + lbag.AddLocation ($$, GetLocation ($2)); + } + | expression DESCENDING + { + $$ = new Linq.OrderByDescending ((Linq.QueryBlock) current_block, (Expression)$1); + lbag.AddLocation ($$, GetLocation ($2)); + } + ; + +then_by + : expression + { + $$ = new Linq.ThenByAscending ((Linq.QueryBlock) current_block, (Expression)$1); + } + | expression ASCENDING + { + $$ = new Linq.ThenByAscending ((Linq.QueryBlock) current_block, (Expression)$1); + lbag.AddLocation ($$, GetLocation ($2)); + } + | expression DESCENDING + { + $$ = new Linq.ThenByDescending ((Linq.QueryBlock) current_block, (Expression)$1); + lbag.AddLocation ($$, GetLocation ($2)); + } + ; + + +opt_query_continuation + : /* empty */ + | INTO identifier_inside_body + { + // query continuation block is not linked with query block but with block + // before. This means each query can use same range variable names for + // different identifiers. + + current_block.SetEndLocation (GetLocation ($1)); + current_block = current_block.Parent; + + current_block = new Linq.QueryBlock (current_block, lexer.Location); + + if (linq_clause_blocks == null) + linq_clause_blocks = new Stack (); + + linq_clause_blocks.Push ((Linq.QueryBlock) current_block); + } + query_body + { + var current_block = linq_clause_blocks.Pop (); + var lt = (LocatedToken) $2; + var rv = new Linq.RangeVariable (lt.Value, lt.Location); + $$ = new Linq.QueryStartClause ((Linq.QueryBlock)current_block, null, rv, GetLocation ($1)) { + next = (Linq.AQueryClause)$4 + }; + } + ; + +// +// Support for using the compiler as an interactive parser +// +// The INTERACTIVE_PARSER token is first sent to parse our +// productions; If the result is a Statement, the parsing +// is repeated, this time with INTERACTIVE_PARSE_WITH_BLOCK +// to setup the blocks in advance. +// +// This setup is here so that in the future we can add +// support for other constructs (type parsing, namespaces, etc) +// that do not require a block to be setup in advance +// + +interactive_parsing + : EVAL_STATEMENT_PARSER EOF + | EVAL_USING_DECLARATIONS_UNIT_PARSER using_directives opt_COMPLETE_COMPLETION + | EVAL_STATEMENT_PARSER + { + current_container = current_type = new Class (current_container, new MemberName (""), Modifiers.PUBLIC, null); + + // (ref object retval) + Parameter [] mpar = new Parameter [1]; + mpar [0] = new Parameter (new TypeExpression (compiler.BuiltinTypes.Object, Location.Null), "$retval", Parameter.Modifier.REF, null, Location.Null); + + ParametersCompiled pars = new ParametersCompiled (mpar); + var mods = Modifiers.PUBLIC | Modifiers.STATIC; + if (settings.Unsafe) + mods |= Modifiers.UNSAFE; + + current_local_parameters = pars; + var method = new InteractiveMethod ( + current_type, + new TypeExpression (compiler.BuiltinTypes.Void, Location.Null), + mods, + pars); + + current_type.AddMember (method); + oob_stack.Push (method); + + interactive_async = false; + + ++lexer.parsing_block; + start_block (lexer.Location); + } + interactive_statement_list opt_COMPLETE_COMPLETION + { + --lexer.parsing_block; + var method = (InteractiveMethod) oob_stack.Pop (); + method.Block = (ToplevelBlock) end_block(lexer.Location); + + if (interactive_async == true) { + method.ChangeToAsync (); + } + + InteractiveResult = (Class) pop_current_class (); + current_local_parameters = null; + } + | EVAL_COMPILATION_UNIT_PARSER interactive_compilation_unit + ; + +interactive_compilation_unit + : opt_extern_alias_directives opt_using_directives + | opt_extern_alias_directives opt_using_directives namespace_or_type_declarations + ; + +opt_COMPLETE_COMPLETION + : /* nothing */ + | COMPLETE_COMPLETION + ; + +close_brace_or_complete_completion + : CLOSE_BRACE + | COMPLETE_COMPLETION + ; + +// +// XML documentation code references micro parser +// +documentation_parsing + : DOC_SEE doc_cref + { + module.DocumentationBuilder.ParsedName = (MemberName) $2; + } + ; + +doc_cref + : doc_type_declaration_name opt_doc_method_sig + { + module.DocumentationBuilder.ParsedParameters = (List)$2; + } + | builtin_types opt_doc_method_sig + { + module.DocumentationBuilder.ParsedBuiltinType = (TypeExpression)$1; + module.DocumentationBuilder.ParsedParameters = (List)$2; + $$ = null; + } + | VOID opt_doc_method_sig + { + module.DocumentationBuilder.ParsedBuiltinType = new TypeExpression (compiler.BuiltinTypes.Void, GetLocation ($1)); + module.DocumentationBuilder.ParsedParameters = (List)$2; + $$ = null; + } + | builtin_types DOT IDENTIFIER opt_doc_method_sig + { + module.DocumentationBuilder.ParsedBuiltinType = (TypeExpression)$1; + module.DocumentationBuilder.ParsedParameters = (List)$4; + var lt = (LocatedToken) $3; + $$ = new MemberName (lt.Value); + } + | doc_type_declaration_name DOT THIS + { + $$ = new MemberName ((MemberName) $1, MemberCache.IndexerNameAlias, Location.Null); + } + | doc_type_declaration_name DOT THIS OPEN_BRACKET + { + valid_param_mod = ParameterModifierType.Ref | ParameterModifierType.Out; + } + opt_doc_parameters CLOSE_BRACKET + { + module.DocumentationBuilder.ParsedParameters = (List)$6; + $$ = new MemberName ((MemberName) $1, MemberCache.IndexerNameAlias, Location.Null); + } + | EXPLICIT OPERATOR type opt_doc_method_sig + { + var p = (List)$4 ?? new List (1); + p.Add (new DocumentationParameter ((FullNamedExpression) $3)); + module.DocumentationBuilder.ParsedParameters = p; + module.DocumentationBuilder.ParsedOperator = Operator.OpType.Explicit; + $$ = null; + } + | IMPLICIT OPERATOR type opt_doc_method_sig + { + var p = (List)$4 ?? new List (1); + p.Add (new DocumentationParameter ((FullNamedExpression) $3)); + module.DocumentationBuilder.ParsedParameters = p; + module.DocumentationBuilder.ParsedOperator = Operator.OpType.Implicit; + $$ = null; + } + | OPERATOR overloadable_operator opt_doc_method_sig + { + var p = (List)$3; + module.DocumentationBuilder.ParsedParameters = p; + module.DocumentationBuilder.ParsedOperator = (Operator.OpType) $2; + $$ = null; + } + ; + +doc_type_declaration_name + : type_declaration_name + | doc_type_declaration_name DOT type_declaration_name + { + $$ = new MemberName (((MemberName) $1), (MemberName) $3); + } + ; + +opt_doc_method_sig + : /* empty */ + | OPEN_PARENS + { + valid_param_mod = ParameterModifierType.Ref | ParameterModifierType.Out; + } + opt_doc_parameters CLOSE_PARENS + { + $$ = $3; + } + ; + +opt_doc_parameters + : /* empty */ + { + $$ = new List (0); + } + | doc_parameters + ; + +doc_parameters + : doc_parameter + { + var parameters = new List (); + parameters.Add ((DocumentationParameter) $1); + $$ = parameters; + } + | doc_parameters COMMA doc_parameter + { + var parameters = $1 as List; + parameters.Add ((DocumentationParameter) $3); + $$ = parameters; + } + ; + +doc_parameter + : opt_parameter_modifier parameter_type + { + if ($1 != null) + $$ = new DocumentationParameter ((Parameter.Modifier) $1, (FullNamedExpression) $2); + else + $$ = new DocumentationParameter ((FullNamedExpression) $2); + } + ; + +%% + +// +// A class used to hold info about an operator declarator +// +class OperatorDeclaration { + public readonly Operator.OpType optype; + public readonly FullNamedExpression ret_type; + public readonly Location location; + + public OperatorDeclaration (Operator.OpType op, FullNamedExpression ret_type, Location location) + { + optype = op; + this.ret_type = ret_type; + this.location = location; + } +} + +void Error_ExpectingTypeName (Expression expr) +{ + if (expr is Invocation){ + report.Error (1002, expr.Location, "Expecting `;'"); + } else { + expr.Error_InvalidExpressionStatement (report); + } +} + +void Error_ParameterModifierNotValid (string modifier, Location loc) +{ + report.Error (631, loc, "The parameter modifier `{0}' is not valid in this context", + modifier); +} + +void Error_DuplicateParameterModifier (Location loc, Parameter.Modifier mod) +{ + report.Error (1107, loc, "Duplicate parameter modifier `{0}'", + Parameter.GetModifierSignature (mod)); +} + +void Error_TypeExpected (Location loc) +{ + report.Error (1031, loc, "Type expected"); +} + +void Error_UnsafeCodeNotAllowed (Location loc) +{ + report.Error (227, loc, "Unsafe code requires the `unsafe' command line option to be specified"); +} + +void Warning_EmptyStatement (Location loc) +{ + report.Warning (642, 3, loc, "Possible mistaken empty statement"); +} + +void Error_NamedArgumentExpected (NamedArgument a) +{ + report.Error (1738, a.Location, "Named arguments must appear after the positional arguments"); +} + +void Error_MissingInitializer (Location loc) +{ + report.Error (210, loc, "You must provide an initializer in a fixed or using statement declaration"); +} + +object Error_AwaitAsIdentifier (object token) +{ + if (async_block) { + report.Error (4003, GetLocation (token), "`await' cannot be used as an identifier within an async method or lambda expression"); + return new LocatedToken ("await", GetLocation (token)); + } + + return token; +} + +void push_current_container (TypeDefinition tc, object partial_token) +{ + if (module.Evaluator != null){ + tc.Definition.Modifiers = tc.ModFlags = (tc.ModFlags & ~Modifiers.AccessibilityMask) | Modifiers.PUBLIC; + if (undo == null) + undo = new Undo (); + + undo.AddTypeContainer (current_container, tc); + } + + if (partial_token != null) + current_container.AddPartial (tc); + else + current_container.AddTypeContainer (tc); + + ++lexer.parsing_declaration; + current_container = tc; + current_type = tc; +} + +TypeContainer pop_current_class () +{ + var retval = current_container; + + current_container = current_container.Parent; + current_type = current_type.Parent as TypeDefinition; + + return retval; +} + +[System.Diagnostics.Conditional ("FULL_AST")] +void StoreModifierLocation (object token, Location loc) +{ + if (lbag == null) + return; + + if (mod_locations == null) + mod_locations = new List> (); + + mod_locations.Add (Tuple.Create ((Modifiers) token, loc)); +} + +List> GetModifierLocations () +{ + var result = mod_locations; + mod_locations = null; + return result; +} + +[System.Diagnostics.Conditional ("FULL_AST")] +void PushLocation (Location loc) +{ + if (location_stack == null) + location_stack = new Stack (); + + location_stack.Push (loc); +} + +Location PopLocation () +{ + if (location_stack == null) + return Location.Null; + + return location_stack.Pop (); +} + +string CheckAttributeTarget (int token, string a, Location l) +{ + switch (a) { + case "assembly" : case "module" : case "field" : case "method" : case "param" : case "property" : case "type" : + return a; + } + + if (!Tokenizer.IsValidIdentifier (a)) { + Error_SyntaxError (token); + } else { + report.Warning (658, 1, l, + "`{0}' is invalid attribute target. All attributes in this attribute section will be ignored", a); + } + + return string.Empty; +} + +static bool IsUnaryOperator (Operator.OpType op) +{ + switch (op) { + + case Operator.OpType.LogicalNot: + case Operator.OpType.OnesComplement: + case Operator.OpType.Increment: + case Operator.OpType.Decrement: + case Operator.OpType.True: + case Operator.OpType.False: + case Operator.OpType.UnaryPlus: + case Operator.OpType.UnaryNegation: + return true; + } + return false; +} + +void syntax_error (Location l, string msg) +{ + report.Error (1003, l, "Syntax error, " + msg); +} + +Tokenizer lexer; + +public Tokenizer Lexer { + get { + return lexer; + } +} + +public CSharpParser (SeekableStreamReader reader, CompilationSourceFile file, ParserSession session) + : this (reader, file, file.Compiler.Report, session) +{ +} + +public CSharpParser (SeekableStreamReader reader, CompilationSourceFile file, Report report, ParserSession session) +{ + this.file = file; + current_container = current_namespace = file; + + this.module = file.Module; + this.compiler = file.Compiler; + this.settings = compiler.Settings; + this.report = report; + + lang_version = settings.Version; + yacc_verbose_flag = settings.VerboseParserFlag; + doc_support = settings.DocumentationFile != null; + lexer = new Tokenizer (reader, file, session, report); + oob_stack = new Stack (); + lbag = session.LocationsBag; + use_global_stacks = session.UseJayGlobalArrays; + parameters_bucket = session.ParametersStack; +} + +public void parse () +{ + eof_token = Token.EOF; + + try { + if (yacc_verbose_flag > 1) + yyparse (lexer, new yydebug.yyDebugSimple ()); + else + yyparse (lexer); + + Tokenizer tokenizer = lexer as Tokenizer; + tokenizer.cleanup (); + } catch (Exception e){ + if (e is yyParser.yyUnexpectedEof) { + Error_SyntaxError (yyToken); + UnexpectedEOF = true; + return; + } + + if (e is yyParser.yyException) { + if (report.Errors == 0) + report.Error (-25, lexer.Location, "Parsing error"); + } else { + // Used by compiler-tester to test internal errors + if (yacc_verbose_flag > 0 || e is FatalException) + throw; + + report.Error (589, lexer.Location, "Internal compiler error during parsing" + e); + } + } +} + +void CheckToken (int error, int yyToken, string msg, Location loc) +{ + if (yyToken >= Token.FIRST_KEYWORD && yyToken <= Token.LAST_KEYWORD) + report.Error (error, loc, "{0}: `{1}' is a keyword", msg, GetTokenName (yyToken)); + else + report.Error (error, loc, msg); +} + +string ConsumeStoredComment () +{ + string s = tmpComment; + tmpComment = null; + Lexer.doc_state = XmlCommentState.Allowed; + return s; +} + +void FeatureIsNotAvailable (Location loc, string feature) +{ + report.FeatureIsNotAvailable (compiler, loc, feature); +} + +Location GetLocation (object obj) +{ + var lt = obj as LocatedToken; + if (lt != null) + return lt.Location; + + var mn = obj as MemberName; + if (mn != null) + return mn.Location; + + var expr = obj as Expression; + if (expr != null) + return expr.Location; + + return lexer.Location; +} + +void start_block (Location loc) +{ + if (current_block == null) { + current_block = new ToplevelBlock (compiler, current_local_parameters, loc); + parsing_anonymous_method = false; + } else if (parsing_anonymous_method) { + current_block = new ParametersBlock (current_block, current_local_parameters, loc); + parsing_anonymous_method = false; + } else { + current_block = new ExplicitBlock (current_block, loc, Location.Null); + } +} + +Block +end_block (Location loc) +{ + Block retval = current_block.Explicit; + retval.SetEndLocation (loc); + current_block = retval.Parent; + return retval; +} + +void start_anonymous (bool isLambda, ParametersCompiled parameters, bool isAsync, Location loc) +{ + oob_stack.Push (current_anonymous_method); + oob_stack.Push (current_local_parameters); + oob_stack.Push (current_variable); + oob_stack.Push (async_block); + + current_local_parameters = parameters; + if (isLambda) { + if (lang_version <= LanguageVersion.ISO_2) + FeatureIsNotAvailable (loc, "lambda expressions"); + + current_anonymous_method = new LambdaExpression (loc); + } else { + if (lang_version == LanguageVersion.ISO_1) + FeatureIsNotAvailable (loc, "anonymous methods"); + + current_anonymous_method = new AnonymousMethodExpression (loc); + } + current_anonymous_method.IsAsync = isAsync; + + async_block = isAsync; + // Force the next block to be created as a ToplevelBlock + parsing_anonymous_method = true; +} + +/* + * Completes the anonymous method processing, if lambda_expr is null, this + * means that we have a Statement instead of an Expression embedded + */ +AnonymousMethodExpression end_anonymous (ParametersBlock anon_block) +{ + AnonymousMethodExpression retval; + + if (async_block) + anon_block.IsAsync = true; + + current_anonymous_method.Block = anon_block; + retval = current_anonymous_method; + + async_block = (bool) oob_stack.Pop (); + current_variable = (BlockVariable) oob_stack.Pop (); + current_local_parameters = (ParametersCompiled) oob_stack.Pop (); + current_anonymous_method = (AnonymousMethodExpression) oob_stack.Pop (); + + return retval; +} + +void Error_SyntaxError (int token) +{ + Error_SyntaxError (0, token); +} + +void Error_SyntaxError (int error_code, int token) +{ + Error_SyntaxError (error_code, token, "Unexpected symbol"); +} + +void Error_SyntaxError (int error_code, int token, string msg) +{ + Lexer.CompleteOnEOF = false; + + // An error message has been reported by tokenizer + if (token == Token.ERROR) + return; + + // Avoid duplicit error message after unterminated string literals + if (token == Token.LITERAL && lexer.Location.Column == 0) + return; + + string symbol = GetSymbolName (token); + string expecting = GetExpecting (); + var loc = lexer.Location - symbol.Length; + + if (error_code == 0) { + if (expecting == "`identifier'") { + if (token > Token.FIRST_KEYWORD && token < Token.LAST_KEYWORD) { + report.Error (1041, loc, "Identifier expected, `{0}' is a keyword", symbol); + return; + } + + error_code = 1001; + expecting = "identifier"; + } else if (expecting == "`)'") { + error_code = 1026; + } else { + error_code = 1525; + } + } + + if (string.IsNullOrEmpty (expecting)) + report.Error (error_code, loc, "{1} `{0}'", symbol, msg); + else + report.Error (error_code, loc, "{2} `{0}', expecting {1}", symbol, expecting, msg); +} + +string GetExpecting () +{ + int [] tokens = yyExpectingTokens (yyExpectingState); + var names = new List (tokens.Length); + bool has_type = false; + bool has_identifier = false; + for (int i = 0; i < tokens.Length; i++){ + int token = tokens [i]; + has_identifier |= token == Token.IDENTIFIER; + + string name = GetTokenName (token); + if (name == "") + continue; + + has_type |= name == "type"; + if (names.Contains (name)) + continue; + + names.Add (name); + } + + // + // Too many tokens to enumerate + // + if (names.Count > 8) + return null; + + if (has_type && has_identifier) + names.Remove ("identifier"); + + if (names.Count == 1) + return "`" + GetTokenName (tokens [0]) + "'"; + + StringBuilder sb = new StringBuilder (); + names.Sort (); + int count = names.Count; + for (int i = 0; i < count; i++){ + bool last = i + 1 == count; + if (last) + sb.Append ("or "); + sb.Append ('`'); + sb.Append (names [i]); + sb.Append (last ? "'" : count < 3 ? "' " : "', "); + } + return sb.ToString (); +} + + +string GetSymbolName (int token) +{ + switch (token){ + case Token.LITERAL: + return ((Constant)lexer.Value).GetValue ().ToString (); + case Token.IDENTIFIER: + return ((LocatedToken)lexer.Value).Value; + + case Token.BOOL: + return "bool"; + case Token.BYTE: + return "byte"; + case Token.CHAR: + return "char"; + case Token.VOID: + return "void"; + case Token.DECIMAL: + return "decimal"; + case Token.DOUBLE: + return "double"; + case Token.FLOAT: + return "float"; + case Token.INT: + return "int"; + case Token.LONG: + return "long"; + case Token.SBYTE: + return "sbyte"; + case Token.SHORT: + return "short"; + case Token.STRING: + return "string"; + case Token.UINT: + return "uint"; + case Token.ULONG: + return "ulong"; + case Token.USHORT: + return "ushort"; + case Token.OBJECT: + return "object"; + + case Token.PLUS: + return "+"; + case Token.UMINUS: + case Token.MINUS: + return "-"; + case Token.BANG: + return "!"; + case Token.BITWISE_AND: + return "&"; + case Token.BITWISE_OR: + return "|"; + case Token.STAR: + return "*"; + case Token.PERCENT: + return "%"; + case Token.DIV: + return "/"; + case Token.CARRET: + return "^"; + case Token.OP_INC: + return "++"; + case Token.OP_DEC: + return "--"; + case Token.OP_SHIFT_LEFT: + return "<<"; + case Token.OP_SHIFT_RIGHT: + return ">>"; + case Token.OP_LT: + return "<"; + case Token.OP_GT: + return ">"; + case Token.OP_LE: + return "<="; + case Token.OP_GE: + return ">="; + case Token.OP_EQ: + return "=="; + case Token.OP_NE: + return "!="; + case Token.OP_AND: + return "&&"; + case Token.OP_OR: + return "||"; + case Token.OP_PTR: + return "->"; + case Token.OP_COALESCING: + return "??"; + case Token.OP_MULT_ASSIGN: + return "*="; + case Token.OP_DIV_ASSIGN: + return "/="; + case Token.OP_MOD_ASSIGN: + return "%="; + case Token.OP_ADD_ASSIGN: + return "+="; + case Token.OP_SUB_ASSIGN: + return "-="; + case Token.OP_SHIFT_LEFT_ASSIGN: + return "<<="; + case Token.OP_SHIFT_RIGHT_ASSIGN: + return ">>="; + case Token.OP_AND_ASSIGN: + return "&="; + case Token.OP_XOR_ASSIGN: + return "^="; + case Token.OP_OR_ASSIGN: + return "|="; + } + + return GetTokenName (token); +} + +static string GetTokenName (int token) +{ + switch (token){ + case Token.ABSTRACT: + return "abstract"; + case Token.AS: + return "as"; + case Token.ADD: + return "add"; + case Token.ASYNC: + return "async"; + case Token.BASE: + return "base"; + case Token.BREAK: + return "break"; + case Token.CASE: + return "case"; + case Token.CATCH: + return "catch"; + case Token.CHECKED: + return "checked"; + case Token.CLASS: + return "class"; + case Token.CONST: + return "const"; + case Token.CONTINUE: + return "continue"; + case Token.DEFAULT: + return "default"; + case Token.DELEGATE: + return "delegate"; + case Token.DO: + return "do"; + case Token.ELSE: + return "else"; + case Token.ENUM: + return "enum"; + case Token.EVENT: + return "event"; + case Token.EXPLICIT: + return "explicit"; + case Token.EXTERN: + case Token.EXTERN_ALIAS: + return "extern"; + case Token.FALSE: + return "false"; + case Token.FINALLY: + return "finally"; + case Token.FIXED: + return "fixed"; + case Token.FOR: + return "for"; + case Token.FOREACH: + return "foreach"; + case Token.GOTO: + return "goto"; + case Token.IF: + return "if"; + case Token.IMPLICIT: + return "implicit"; + case Token.IN: + return "in"; + case Token.INTERFACE: + return "interface"; + case Token.INTERNAL: + return "internal"; + case Token.IS: + return "is"; + case Token.LOCK: + return "lock"; + case Token.NAMESPACE: + return "namespace"; + case Token.NEW: + return "new"; + case Token.NULL: + return "null"; + case Token.OPERATOR: + return "operator"; + case Token.OUT: + return "out"; + case Token.OVERRIDE: + return "override"; + case Token.PARAMS: + return "params"; + case Token.PRIVATE: + return "private"; + case Token.PROTECTED: + return "protected"; + case Token.PUBLIC: + return "public"; + case Token.READONLY: + return "readonly"; + case Token.REF: + return "ref"; + case Token.RETURN: + return "return"; + case Token.REMOVE: + return "remove"; + case Token.SEALED: + return "sealed"; + case Token.SIZEOF: + return "sizeof"; + case Token.STACKALLOC: + return "stackalloc"; + case Token.STATIC: + return "static"; + case Token.STRUCT: + return "struct"; + case Token.SWITCH: + return "switch"; + case Token.THIS: + return "this"; + case Token.THROW: + return "throw"; + case Token.TRUE: + return "true"; + case Token.TRY: + return "try"; + case Token.TYPEOF: + return "typeof"; + case Token.UNCHECKED: + return "unchecked"; + case Token.UNSAFE: + return "unsafe"; + case Token.USING: + return "using"; + case Token.VIRTUAL: + return "virtual"; + case Token.VOLATILE: + return "volatile"; + case Token.WHERE: + return "where"; + case Token.WHILE: + return "while"; + case Token.ARGLIST: + return "__arglist"; + case Token.REFVALUE: + return "__refvalue"; + case Token.REFTYPE: + return "__reftype"; + case Token.MAKEREF: + return "__makeref"; + case Token.PARTIAL: + return "partial"; + case Token.ARROW: + return "=>"; + case Token.FROM: + case Token.FROM_FIRST: + return "from"; + case Token.JOIN: + return "join"; + case Token.ON: + return "on"; + case Token.EQUALS: + return "equals"; + case Token.SELECT: + return "select"; + case Token.GROUP: + return "group"; + case Token.BY: + return "by"; + case Token.LET: + return "let"; + case Token.ORDERBY: + return "orderby"; + case Token.ASCENDING: + return "ascending"; + case Token.DESCENDING: + return "descending"; + case Token.INTO: + return "into"; + case Token.GET: + return "get"; + case Token.SET: + return "set"; + case Token.OPEN_BRACE: + return "{"; + case Token.CLOSE_BRACE: + return "}"; + case Token.OPEN_BRACKET: + case Token.OPEN_BRACKET_EXPR: + return "["; + case Token.CLOSE_BRACKET: + return "]"; + case Token.OPEN_PARENS_CAST: + case Token.OPEN_PARENS_LAMBDA: + case Token.OPEN_PARENS: + return "("; + case Token.CLOSE_PARENS: + return ")"; + case Token.DOT: + return "."; + case Token.COMMA: + return ","; + case Token.DEFAULT_COLON: + return "default:"; + case Token.COLON: + return ":"; + case Token.SEMICOLON: + return ";"; + case Token.TILDE: + return "~"; + + case Token.PLUS: + case Token.UMINUS: + case Token.MINUS: + case Token.BANG: + case Token.OP_LT: + case Token.OP_GT: + case Token.BITWISE_AND: + case Token.BITWISE_OR: + case Token.STAR: + case Token.PERCENT: + case Token.DIV: + case Token.CARRET: + case Token.OP_INC: + case Token.OP_DEC: + case Token.OP_SHIFT_LEFT: + case Token.OP_SHIFT_RIGHT: + case Token.OP_LE: + case Token.OP_GE: + case Token.OP_EQ: + case Token.OP_NE: + case Token.OP_AND: + case Token.OP_OR: + case Token.OP_PTR: + case Token.OP_COALESCING: + case Token.OP_MULT_ASSIGN: + case Token.OP_DIV_ASSIGN: + case Token.OP_MOD_ASSIGN: + case Token.OP_ADD_ASSIGN: + case Token.OP_SUB_ASSIGN: + case Token.OP_SHIFT_LEFT_ASSIGN: + case Token.OP_SHIFT_RIGHT_ASSIGN: + case Token.OP_AND_ASSIGN: + case Token.OP_XOR_ASSIGN: + case Token.OP_OR_ASSIGN: + return ""; + + case Token.BOOL: + case Token.BYTE: + case Token.CHAR: + case Token.VOID: + case Token.DECIMAL: + case Token.DOUBLE: + case Token.FLOAT: + case Token.INT: + case Token.LONG: + case Token.SBYTE: + case Token.SHORT: + case Token.STRING: + case Token.UINT: + case Token.ULONG: + case Token.USHORT: + case Token.OBJECT: + return "type"; + + case Token.ASSIGN: + return "="; + case Token.OP_GENERICS_LT: + case Token.GENERIC_DIMENSION: + return "<"; + case Token.OP_GENERICS_GT: + return ">"; + case Token.INTERR: + case Token.INTERR_NULLABLE: + return "?"; + case Token.DOUBLE_COLON: + return "::"; + case Token.LITERAL: + return "value"; + case Token.IDENTIFIER: + case Token.AWAIT: + return "identifier"; + + case Token.EOF: + return "end-of-file"; + + // All of these are internal. + case Token.NONE: + case Token.ERROR: + case Token.FIRST_KEYWORD: + case Token.EVAL_COMPILATION_UNIT_PARSER: + case Token.EVAL_USING_DECLARATIONS_UNIT_PARSER: + case Token.EVAL_STATEMENT_PARSER: + case Token.LAST_KEYWORD: + case Token.GENERATE_COMPLETION: + case Token.COMPLETE_COMPLETION: + return ""; + + // A bit more robust. + default: + return yyNames [token]; + } +} + +/* end end end */ +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/cs-tokenizer.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/cs-tokenizer.cs new file mode 100644 index 000000000..a423eb50f --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/cs-tokenizer.cs @@ -0,0 +1,4033 @@ +// +// cs-tokenizer.cs: The Tokenizer for the C# compiler +// This also implements the preprocessor +// +// Author: Miguel de Icaza (miguel@gnu.org) +// Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2001, 2002 Ximian, Inc (http://www.ximian.com) +// Copyright 2004-2008 Novell, Inc +// Copyright 2011 Xamarin, Inc (http://www.xamarin.com) +// +using System; +using System.Text; +using System.Collections.Generic; +using System.Globalization; +using System.Diagnostics; +using System.Collections; + +namespace Mono.CSharp +{ + // + // This class has to be used by parser only, it reuses token + // details once a file is parsed + // + public class LocatedToken + { + public int row, column; + public string value; + public SourceFile file; + + public LocatedToken () + { + } + + public LocatedToken (string value, Location loc) + { + this.value = value; + file = loc.SourceFile; + row = loc.Row; + column = loc.Column; + } + + public override string ToString () + { + return string.Format ("Token '{0}' at {1},{2}", Value, row, column); + } + + public Location Location + { + get { return new Location (file, row, column); } + } + + public string Value + { + get { return value; } + } + } + + /// + /// Tokenizer for C# source code. + /// + public class Tokenizer : yyParser.yyInput + { + class KeywordEntry + { + public readonly T Token; + public KeywordEntry Next; + public readonly char[] Value; + + public KeywordEntry (string value,T token) + { + this.Value = value.ToCharArray (); + this.Token = token; + } + } + + sealed class IdentifiersComparer : IEqualityComparer + { + readonly int length; + + public IdentifiersComparer (int length) + { + this.length = length; + } + + public bool Equals (char[] x, char[] y) + { + for (int i = 0; i < length; ++i) + if (x [i] != y [i]) + return false; + + return true; + } + + public int GetHashCode (char[] obj) + { + int h = 0; + for (int i = 0; i < length; ++i) + h = (h << 5) - h + obj [i]; + + return h; + } + } + + public class LocatedTokenBuffer + { + readonly LocatedToken[] buffer; + public int pos; + + public LocatedTokenBuffer () + { + buffer = new LocatedToken[0]; + } + + public LocatedTokenBuffer (LocatedToken[] buffer) + { + this.buffer = buffer ?? new LocatedToken[0]; + } + + public LocatedToken Create (SourceFile file, int row, int column) + { + return Create (null, file, row, column); + } + + public LocatedToken Create (string value, SourceFile file, int row, int column) + { + // + // TODO: I am not very happy about the logic but it's the best + // what I could come up with for now. + // Ideally we should be using just tiny buffer (256 elements) which + // is enough to hold all details for currect stack and recycle elements + // poped from the stack but there is a trick needed to recycle + // them properly. + // + LocatedToken entry; + if (pos >= buffer.Length) { + entry = new LocatedToken (); + } else { + entry = buffer[pos]; + if (entry == null) { + entry = new LocatedToken (); + buffer[pos] = entry; + } + + ++pos; + } + entry.value = value; + entry.file = file; + entry.row = row; + entry.column = column; + return entry; + } + + // + // Used for token not required by expression evaluator + // + [Conditional ("FULL_AST")] + public void CreateOptional (SourceFile file, int row, int col, ref object token) + { + token = Create (file, row, col); + } + } + + public enum PreprocessorDirective + { + Invalid = 0, + + Region = 1, + Endregion = 2, + If = 3 | RequiresArgument, + Endif = 4, + Elif = 5 | RequiresArgument, + Else = 6, + Define = 7 | RequiresArgument, + Undef = 8 | RequiresArgument, + Error = 9, + Warning = 10, + Pragma = 11 | CustomArgumentsParsing, + Line = 12 | CustomArgumentsParsing, + + CustomArgumentsParsing = 1 << 10, + RequiresArgument = 1 << 11 + } + + readonly SeekableStreamReader reader; + readonly CompilationSourceFile source_file; + public CompilationSourceFile SourceFile { get { return source_file; } } + readonly CompilerContext context; + readonly Report Report; + + + SourceFile current_source; + Location hidden_block_start; + int ref_line = 1; + int line = 1; + int col = 0; + int previous_col; + int current_token; + readonly int tab_size; + bool handle_get_set = false; + bool handle_remove_add = false; + bool handle_where; + bool lambda_arguments_parsing; + List escaped_identifiers; + int parsing_generic_less_than; + readonly bool doc_processing; + readonly LocatedTokenBuffer ltb; + + // + // Used mainly for parser optimizations. Some expressions for instance + // can appear only in block (including initializer, base initializer) + // scope only + // + public int parsing_block; + internal bool query_parsing; + + // + // When parsing type only, useful for ambiguous nullable types + // + public int parsing_type; + + // + // Set when parsing generic declaration (type or method header) + // + public bool parsing_generic_declaration; + public bool parsing_generic_declaration_doc; + + // + // The value indicates that we have not reach any declaration or + // namespace yet + // + public int parsing_declaration; + public bool parsing_attribute_section; + + public bool parsing_modifiers; + + // + // The special characters to inject on streams to run the unit parser + // in the special expression mode. Using private characters from + // Plane Sixteen (U+100000 to U+10FFFD) + // + // This character is only tested just before the tokenizer is about to report + // an error; So on the regular operation mode, this addition will have no + // impact on the tokenizer's performance. + // + + public const int EvalStatementParserCharacter = 0x100000; + public const int EvalCompilationUnitParserCharacter = 0x100001; + public const int EvalUsingDeclarationsParserCharacter = 0x100002; + public const int DocumentationXref = 0x100003; + + const int UnicodeLS = 0x2028; + const int UnicodePS = 0x2029; + + // + // XML documentation buffer. The save point is used to divide + // comments on types and comments on members. + // + StringBuilder xml_comment_buffer; + + // + // See comment on XmlCommentState enumeration. + // + XmlCommentState xml_doc_state = XmlCommentState.Allowed; + + // + // Whether tokens have been seen on this line + // + bool tokens_seen = false; + + // + // Set to true once the GENERATE_COMPLETION token has bee + // returned. This helps produce one GENERATE_COMPLETION, + // as many COMPLETE_COMPLETION as necessary to complete the + // AST tree and one final EOF. + // + bool generated; + + // + // Whether a token has been seen on the file + // This is needed because `define' is not allowed to be used + // after a token has been seen. + // + bool any_token_seen; + + // + // Class variables + // + static readonly KeywordEntry[][] keywords; + static readonly KeywordEntry[][] keywords_preprocessor; + static readonly HashSet keyword_strings; + static readonly NumberStyles styles; + static readonly NumberFormatInfo csharp_format_info; + + // Pragma arguments + static readonly char[] pragma_warning = "warning".ToCharArray (); + static readonly char[] pragma_warning_disable = "disable".ToCharArray (); + static readonly char[] pragma_warning_restore = "restore".ToCharArray (); + static readonly char[] pragma_checksum = "checksum".ToCharArray (); + static readonly char[] line_hidden = "hidden".ToCharArray (); + static readonly char[] line_default = "default".ToCharArray (); + + static readonly char[] simple_whitespaces = new char[] { ' ', '\t' }; + bool startsLine = true; + internal SpecialsBag sbag; + + public bool PropertyParsing { + get { return handle_get_set; } + set { handle_get_set = value; } + } + + public bool EventParsing { + get { return handle_remove_add; } + set { handle_remove_add = value; } + } + + public bool ConstraintsParsing { + get { return handle_where; } + set { handle_where = value; } + } + + public XmlCommentState doc_state { + get { return xml_doc_state; } + set { + if (value == XmlCommentState.Allowed) { + check_incorrect_doc_comment (); + reset_doc_comment (); + } + xml_doc_state = value; + } + } + + // + // This is used to trigger completion generation on the parser + public bool CompleteOnEOF; + + void AddEscapedIdentifier (Location loc) + { + if (escaped_identifiers == null) + escaped_identifiers = new List (); + + escaped_identifiers.Add (loc); + } + + public bool IsEscapedIdentifier (ATypeNameExpression name) + { + return escaped_identifiers != null && escaped_identifiers.Contains (name.Location); + } + + // + // Values for the associated token returned + // + internal int putback_char; // Used by repl only + object val; + + // + // Pre-processor + // + const int TAKING = 1; + const int ELSE_SEEN = 4; + const int PARENT_TAKING = 8; + const int REGION = 16; + + // + // pre-processor if stack state: + // + Stack ifstack; + + public const int MaxIdentifierLength = 512; + public const int MaxNumberLength = 512; + + readonly char[] id_builder; + readonly Dictionary[] identifiers; + readonly char[] number_builder; + int number_pos; + + char[] value_builder = new char[64]; + + public int Line { + get { + return ref_line; + } + set { + ref_line = value; + } + } + + public int Column { + get { + return col; + } + set { + col = value; + } + } + + // + // This is used when the tokenizer needs to save + // the current position as it needs to do some parsing + // on its own to deamiguate a token in behalf of the + // parser. + // + Stack position_stack = new Stack (2); + + class Position + { + public int position; + public int line; + public int ref_line; + public int col; + public Location hidden; + public int putback_char; + public int previous_col; + public Stack ifstack; + public int parsing_generic_less_than; + public int current_token; + public object val; + + public Position (Tokenizer t) + { + position = t.reader.Position; + line = t.line; + ref_line = t.ref_line; + col = t.col; + hidden = t.hidden_block_start; + putback_char = t.putback_char; + previous_col = t.previous_col; + if (t.ifstack != null && t.ifstack.Count != 0) { + // There is no simple way to clone Stack all + // methods reverse the order + var clone = t.ifstack.ToArray (); + Array.Reverse (clone); + ifstack = new Stack (clone); + } + parsing_generic_less_than = t.parsing_generic_less_than; + current_token = t.current_token; + val = t.val; + } + } + + public Tokenizer (SeekableStreamReader input, CompilationSourceFile file, ParserSession session, Report report) + { + this.source_file = file; + this.context = file.Compiler; + this.current_source = file.SourceFile; + this.identifiers = session.Identifiers; + this.id_builder = session.IDBuilder; + this.number_builder = session.NumberBuilder; + this.ltb = new LocatedTokenBuffer (session.LocatedTokens); + this.Report = report; + + reader = input; + + putback_char = -1; + + xml_comment_buffer = new StringBuilder (); + doc_processing = context.Settings.DocumentationFile != null; + + tab_size = context.Settings.TabSize; + } + + public void PushPosition () + { + position_stack.Push (new Position (this)); + } + + public void PopPosition () + { + Position p = position_stack.Pop (); + + reader.Position = p.position; + ref_line = p.ref_line; + line = p.line; + col = p.col; + hidden_block_start = p.hidden; + putback_char = p.putback_char; + previous_col = p.previous_col; + ifstack = p.ifstack; + parsing_generic_less_than = p.parsing_generic_less_than; + current_token = p.current_token; + val = p.val; + } + + // Do not reset the position, ignore it. + public void DiscardPosition () + { + position_stack.Pop (); + } + + static void AddKeyword (string kw, int token) + { + keyword_strings.Add (kw); + + AddKeyword (keywords, kw, token); +} + + static void AddPreprocessorKeyword (string kw, PreprocessorDirective directive) + { + AddKeyword (keywords_preprocessor, kw, directive); + } + + static void AddKeyword (KeywordEntry[][] keywords, string kw, T token) + { + int length = kw.Length; + if (keywords[length] == null) { + keywords[length] = new KeywordEntry['z' - '_' + 1]; + } + + int char_index = kw[0] - '_'; + var kwe = keywords[length][char_index]; + if (kwe == null) { + keywords[length][char_index] = new KeywordEntry (kw, token); + return; + } + + while (kwe.Next != null) { + kwe = kwe.Next; + } + + kwe.Next = new KeywordEntry (kw, token); + } + + // + // Class initializer + // + static Tokenizer () + { + keyword_strings = new HashSet (); + + // 11 is the length of the longest keyword for now + keywords = new KeywordEntry[11][]; + + AddKeyword ("__arglist", Token.ARGLIST); + AddKeyword ("__makeref", Token.MAKEREF); + AddKeyword ("__reftype", Token.REFTYPE); + AddKeyword ("__refvalue", Token.REFVALUE); + AddKeyword ("abstract", Token.ABSTRACT); + AddKeyword ("as", Token.AS); + AddKeyword ("add", Token.ADD); + AddKeyword ("base", Token.BASE); + AddKeyword ("bool", Token.BOOL); + AddKeyword ("break", Token.BREAK); + AddKeyword ("byte", Token.BYTE); + AddKeyword ("case", Token.CASE); + AddKeyword ("catch", Token.CATCH); + AddKeyword ("char", Token.CHAR); + AddKeyword ("checked", Token.CHECKED); + AddKeyword ("class", Token.CLASS); + AddKeyword ("const", Token.CONST); + AddKeyword ("continue", Token.CONTINUE); + AddKeyword ("decimal", Token.DECIMAL); + AddKeyword ("default", Token.DEFAULT); + AddKeyword ("delegate", Token.DELEGATE); + AddKeyword ("do", Token.DO); + AddKeyword ("double", Token.DOUBLE); + AddKeyword ("else", Token.ELSE); + AddKeyword ("enum", Token.ENUM); + AddKeyword ("event", Token.EVENT); + AddKeyword ("explicit", Token.EXPLICIT); + AddKeyword ("extern", Token.EXTERN); + AddKeyword ("false", Token.FALSE); + AddKeyword ("finally", Token.FINALLY); + AddKeyword ("fixed", Token.FIXED); + AddKeyword ("float", Token.FLOAT); + AddKeyword ("for", Token.FOR); + AddKeyword ("foreach", Token.FOREACH); + AddKeyword ("goto", Token.GOTO); + AddKeyword ("get", Token.GET); + AddKeyword ("if", Token.IF); + AddKeyword ("implicit", Token.IMPLICIT); + AddKeyword ("in", Token.IN); + AddKeyword ("int", Token.INT); + AddKeyword ("interface", Token.INTERFACE); + AddKeyword ("internal", Token.INTERNAL); + AddKeyword ("is", Token.IS); + AddKeyword ("lock", Token.LOCK); + AddKeyword ("long", Token.LONG); + AddKeyword ("namespace", Token.NAMESPACE); + AddKeyword ("new", Token.NEW); + AddKeyword ("null", Token.NULL); + AddKeyword ("object", Token.OBJECT); + AddKeyword ("operator", Token.OPERATOR); + AddKeyword ("out", Token.OUT); + AddKeyword ("override", Token.OVERRIDE); + AddKeyword ("params", Token.PARAMS); + AddKeyword ("private", Token.PRIVATE); + AddKeyword ("protected", Token.PROTECTED); + AddKeyword ("public", Token.PUBLIC); + AddKeyword ("readonly", Token.READONLY); + AddKeyword ("ref", Token.REF); + AddKeyword ("remove", Token.REMOVE); + AddKeyword ("return", Token.RETURN); + AddKeyword ("sbyte", Token.SBYTE); + AddKeyword ("sealed", Token.SEALED); + AddKeyword ("set", Token.SET); + AddKeyword ("short", Token.SHORT); + AddKeyword ("sizeof", Token.SIZEOF); + AddKeyword ("stackalloc", Token.STACKALLOC); + AddKeyword ("static", Token.STATIC); + AddKeyword ("string", Token.STRING); + AddKeyword ("struct", Token.STRUCT); + AddKeyword ("switch", Token.SWITCH); + AddKeyword ("this", Token.THIS); + AddKeyword ("throw", Token.THROW); + AddKeyword ("true", Token.TRUE); + AddKeyword ("try", Token.TRY); + AddKeyword ("typeof", Token.TYPEOF); + AddKeyword ("uint", Token.UINT); + AddKeyword ("ulong", Token.ULONG); + AddKeyword ("unchecked", Token.UNCHECKED); + AddKeyword ("unsafe", Token.UNSAFE); + AddKeyword ("ushort", Token.USHORT); + AddKeyword ("using", Token.USING); + AddKeyword ("virtual", Token.VIRTUAL); + AddKeyword ("void", Token.VOID); + AddKeyword ("volatile", Token.VOLATILE); + AddKeyword ("while", Token.WHILE); + AddKeyword ("partial", Token.PARTIAL); + AddKeyword ("where", Token.WHERE); + + // LINQ keywords + AddKeyword ("from", Token.FROM); + AddKeyword ("join", Token.JOIN); + AddKeyword ("on", Token.ON); + AddKeyword ("equals", Token.EQUALS); + AddKeyword ("select", Token.SELECT); + AddKeyword ("group", Token.GROUP); + AddKeyword ("by", Token.BY); + AddKeyword ("let", Token.LET); + AddKeyword ("orderby", Token.ORDERBY); + AddKeyword ("ascending", Token.ASCENDING); + AddKeyword ("descending", Token.DESCENDING); + AddKeyword ("into", Token.INTO); + + // Contextual async keywords + AddKeyword ("async", Token.ASYNC); + AddKeyword ("await", Token.AWAIT); + + keywords_preprocessor = new KeywordEntry[10][]; + + AddPreprocessorKeyword ("region", PreprocessorDirective.Region); + AddPreprocessorKeyword ("endregion", PreprocessorDirective.Endregion); + AddPreprocessorKeyword ("if", PreprocessorDirective.If); + AddPreprocessorKeyword ("endif", PreprocessorDirective.Endif); + AddPreprocessorKeyword ("elif", PreprocessorDirective.Elif); + AddPreprocessorKeyword ("else", PreprocessorDirective.Else); + AddPreprocessorKeyword ("define", PreprocessorDirective.Define); + AddPreprocessorKeyword ("undef", PreprocessorDirective.Undef); + AddPreprocessorKeyword ("error", PreprocessorDirective.Error); + AddPreprocessorKeyword ("warning", PreprocessorDirective.Warning); + AddPreprocessorKeyword ("pragma", PreprocessorDirective.Pragma); + AddPreprocessorKeyword ("line", PreprocessorDirective.Line); + + csharp_format_info = NumberFormatInfo.InvariantInfo; + styles = NumberStyles.Float; + } + + int GetKeyword (char[] id, int id_len) + { + // + // Keywords are stored in an array of arrays grouped by their + // length and then by the first character + // + if (id_len >= keywords.Length || keywords [id_len] == null) + return -1; + + int first_index = id [0] - '_'; + if (first_index > 'z' - '_') + return -1; + + var kwe = keywords [id_len] [first_index]; + if (kwe == null) + return -1; + + int res; + do { + res = kwe.Token; + for (int i = 1; i < id_len; ++i) { + if (id [i] != kwe.Value [i]) { + res = 0; + kwe = kwe.Next; + break; + } + } + } while (res == 0 && kwe != null); + + if (res == 0) + return -1; + + int next_token; + switch (res) { + case Token.GET: + case Token.SET: + if (!handle_get_set) + res = -1; + break; + case Token.REMOVE: + case Token.ADD: + if (!handle_remove_add) + res = -1; + break; + case Token.EXTERN: + if (parsing_declaration == 0) + res = Token.EXTERN_ALIAS; + break; + case Token.DEFAULT: + if (peek_token () == Token.COLON) { + token (); + res = Token.DEFAULT_COLON; + } + break; + case Token.WHERE: + if (!(handle_where && current_token != Token.COLON) && !query_parsing) + res = -1; + break; + case Token.FROM: + // + // A query expression is any expression that starts with `from identifier' + // followed by any token except ; , = + // + if (!query_parsing) { + if (lambda_arguments_parsing || parsing_block == 0) { + res = -1; + break; + } + + PushPosition (); + // HACK: to disable generics micro-parser, because PushPosition does not + // store identifiers array + parsing_generic_less_than = 1; + switch (xtoken ()) { + case Token.IDENTIFIER: + case Token.INT: + case Token.BOOL: + case Token.BYTE: + case Token.CHAR: + case Token.DECIMAL: + case Token.DOUBLE: + case Token.FLOAT: + case Token.LONG: + case Token.OBJECT: + case Token.STRING: + case Token.UINT: + case Token.ULONG: + next_token = xtoken (); + if (next_token == Token.SEMICOLON || next_token == Token.COMMA || next_token == Token.EQUALS || next_token == Token.ASSIGN) + goto default; + + res = Token.FROM_FIRST; + query_parsing = true; + if (context.Settings.Version <= LanguageVersion.ISO_2) + Report.FeatureIsNotAvailable (context, Location, "query expressions"); + break; + case Token.VOID: + Expression.Error_VoidInvalidInTheContext (Location, Report); + break; + default: + PopPosition (); + // HACK: A token is not a keyword so we need to restore identifiers buffer + // which has been overwritten before we grabbed the identifier + id_builder [0] = 'f'; id_builder [1] = 'r'; id_builder [2] = 'o'; id_builder [3] = 'm'; + return -1; + } + PopPosition (); + } + break; + case Token.JOIN: + case Token.ON: + case Token.EQUALS: + case Token.SELECT: + case Token.GROUP: + case Token.BY: + case Token.LET: + case Token.ORDERBY: + case Token.ASCENDING: + case Token.DESCENDING: + case Token.INTO: + if (!query_parsing) + res = -1; + break; + + case Token.USING: + case Token.NAMESPACE: + // TODO: some explanation needed + check_incorrect_doc_comment (); + parsing_modifiers = false; + break; + + case Token.PARTIAL: + if (parsing_block > 0) { + res = -1; + break; + } + + // Save current position and parse next token. + PushPosition (); + + next_token = token (); + bool ok = (next_token == Token.CLASS) || + (next_token == Token.STRUCT) || + (next_token == Token.INTERFACE) || + (next_token == Token.VOID); + + PopPosition (); + + if (ok) { + if (next_token == Token.VOID) { + if (context.Settings.Version <= LanguageVersion.ISO_2) + Report.FeatureIsNotAvailable (context, Location, "partial methods"); + } else if (context.Settings.Version == LanguageVersion.ISO_1) + Report.FeatureIsNotAvailable (context, Location, "partial types"); + + return res; + } + + if (next_token < Token.LAST_KEYWORD) { + Report.Error (267, Location, + "The `partial' modifier can be used only immediately before `class', `struct', `interface', or `void' keyword"); + return token (); + } + + // HACK: A token is not a keyword so we need to restore identifiers buffer + // which has been overwritten before we grabbed the identifier + id_builder[0] = 'p'; + id_builder[1] = 'a'; + id_builder[2] = 'r'; + id_builder[3] = 't'; + id_builder[4] = 'i'; + id_builder[5] = 'a'; + id_builder[6] = 'l'; + res = -1; + break; + + case Token.ASYNC: + if (parsing_modifiers) { + // + // Skip attributes section or constructor called async + // + if (parsing_attribute_section || peek_token () == Token.OPEN_PARENS) { + res = -1; + } else { + // async is keyword + } + } else if (parsing_block > 0) { + switch (peek_token ()) { + case Token.DELEGATE: + case Token.OPEN_PARENS_LAMBDA: + // async is keyword + break; + case Token.IDENTIFIER: + PushPosition (); + xtoken (); + if (xtoken () != Token.ARROW) { + PopPosition (); + goto default; + } + + PopPosition (); + break; + default: + // peek_token could overwrite id_buffer + id_builder [0] = 'a'; id_builder [1] = 's'; id_builder [2] = 'y'; id_builder [3] = 'n'; id_builder [4] = 'c'; + res = -1; + break; + } + } else { + res = -1; + } + + if (res == Token.ASYNC && context.Settings.Version <= LanguageVersion.V_4) { + Report.FeatureIsNotAvailable (context, Location, "asynchronous functions"); + } + + break; + + case Token.AWAIT: + if (parsing_block == 0) + res = -1; + + break; + } + + return res; + } + + static PreprocessorDirective GetPreprocessorDirective (char[] id, int id_len) + { + // + // Keywords are stored in an array of arrays grouped by their + // length and then by the first character + // + if (id_len >= keywords_preprocessor.Length || keywords_preprocessor[id_len] == null) + return PreprocessorDirective.Invalid; + + int first_index = id[0] - '_'; + if (first_index > 'z' - '_') + return PreprocessorDirective.Invalid; + + var kwe = keywords_preprocessor[id_len][first_index]; + if (kwe == null) + return PreprocessorDirective.Invalid; + + PreprocessorDirective res = PreprocessorDirective.Invalid; + do { + res = kwe.Token; + for (int i = 1; i < id_len; ++i) { + if (id[i] != kwe.Value[i]) { + res = 0; + kwe = kwe.Next; + break; + } + } + } while (res == PreprocessorDirective.Invalid && kwe != null); + + return res; + } + + public Location Location { + get { + return new Location (current_source, ref_line, col); + } + } + + static bool is_identifier_start_character (int c) + { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || Char.IsLetter ((char)c); + } + + static bool is_identifier_part_character (char c) + { + if (c >= 'a' && c <= 'z') + return true; + + if (c >= 'A' && c <= 'Z') + return true; + + if (c == '_' || (c >= '0' && c <= '9')) + return true; + + if (c < 0x80) + return false; + + return is_identifier_part_character_slow_part (c); + } + + static bool is_identifier_part_character_slow_part (char c) + { + if (Char.IsLetter (c)) + return true; + + switch (Char.GetUnicodeCategory (c)) { + case UnicodeCategory.ConnectorPunctuation: + + // combining-character: A Unicode character of classes Mn or Mc + case UnicodeCategory.NonSpacingMark: + case UnicodeCategory.SpacingCombiningMark: + + // decimal-digit-character: A Unicode character of the class Nd + case UnicodeCategory.DecimalDigitNumber: + return true; + } + + return false; + } + + public static bool IsKeyword (string s) + { + return keyword_strings.Contains (s); + } + + // + // Open parens micro parser. Detects both lambda and cast ambiguity. + // + int TokenizeOpenParens () + { + int ptoken; + current_token = -1; + + int bracket_level = 0; + bool is_type = false; + bool can_be_type = false; + + while (true) { + ptoken = current_token; + token (); + + switch (current_token) { + case Token.CLOSE_PARENS: + token (); + + // + // Expression inside parens is lambda, (int i) => + // + if (current_token == Token.ARROW) + return Token.OPEN_PARENS_LAMBDA; + + // + // Expression inside parens is single type, (int[]) + // + if (is_type) { + if (current_token == Token.SEMICOLON) + return Token.OPEN_PARENS; + + return Token.OPEN_PARENS_CAST; + } + + // + // Expression is possible cast, look at next token, (T)null + // + if (can_be_type) { + switch (current_token) { + case Token.OPEN_PARENS: + case Token.BANG: + case Token.TILDE: + case Token.IDENTIFIER: + case Token.LITERAL: + case Token.BASE: + case Token.CHECKED: + case Token.DELEGATE: + case Token.FALSE: + case Token.FIXED: + case Token.NEW: + case Token.NULL: + case Token.SIZEOF: + case Token.THIS: + case Token.THROW: + case Token.TRUE: + case Token.TYPEOF: + case Token.UNCHECKED: + case Token.UNSAFE: + case Token.DEFAULT: + case Token.AWAIT: + + // + // These can be part of a member access + // + case Token.INT: + case Token.UINT: + case Token.SHORT: + case Token.USHORT: + case Token.LONG: + case Token.ULONG: + case Token.DOUBLE: + case Token.FLOAT: + case Token.CHAR: + case Token.BYTE: + case Token.DECIMAL: + case Token.BOOL: + return Token.OPEN_PARENS_CAST; + } + } + return Token.OPEN_PARENS; + + case Token.DOT: + case Token.DOUBLE_COLON: + if (ptoken != Token.IDENTIFIER && ptoken != Token.OP_GENERICS_GT) + goto default; + + continue; + + case Token.IDENTIFIER: + case Token.AWAIT: + switch (ptoken) { + case Token.DOT: + if (bracket_level == 0) { + is_type = false; + can_be_type = true; + } + + continue; + case Token.OP_GENERICS_LT: + case Token.COMMA: + case Token.DOUBLE_COLON: + case -1: + if (bracket_level == 0) + can_be_type = true; + continue; + default: + can_be_type = is_type = false; + continue; + } + + case Token.OBJECT: + case Token.STRING: + case Token.BOOL: + case Token.DECIMAL: + case Token.FLOAT: + case Token.DOUBLE: + case Token.SBYTE: + case Token.BYTE: + case Token.SHORT: + case Token.USHORT: + case Token.INT: + case Token.UINT: + case Token.LONG: + case Token.ULONG: + case Token.CHAR: + case Token.VOID: + if (bracket_level == 0) + is_type = true; + continue; + + case Token.COMMA: + if (bracket_level == 0) { + bracket_level = 100; + can_be_type = is_type = false; + } + continue; + + case Token.OP_GENERICS_LT: + case Token.OPEN_BRACKET: + if (bracket_level++ == 0) + is_type = true; + continue; + + case Token.OP_GENERICS_GT: + case Token.CLOSE_BRACKET: + --bracket_level; + continue; + + case Token.INTERR_NULLABLE: + case Token.STAR: + if (bracket_level == 0) + is_type = true; + continue; + + case Token.REF: + case Token.OUT: + can_be_type = is_type = false; + continue; + + default: + return Token.OPEN_PARENS; + } + } + } + + public static bool IsValidIdentifier (string s) + { + if (s == null || s.Length == 0) + return false; + + if (!is_identifier_start_character (s [0])) + return false; + + for (int i = 1; i < s.Length; i ++) + if (! is_identifier_part_character (s [i])) + return false; + + return true; + } + + Stack> genericDimensionLocations = new Stack> (); + + public List GenericDimensionLocations { + get { + if (genericDimensionLocations.Count == 0) + return null; + return genericDimensionLocations.Pop (); + } + } + + bool parse_less_than (ref int genericDimension) + { + genericDimensionLocations.Push (new List ()); + genericDimensionLocations.Peek ().Add (Location); + start: + int the_token = token (); + if (the_token == Token.OPEN_BRACKET) { + while (true) { + the_token = token (); + if (the_token == Token.EOF) + return true; + + if (the_token == Token.CLOSE_BRACKET) + break; + } + the_token = token (); + } else if (the_token == Token.IN || the_token == Token.OUT) { + the_token = token (); + } + switch (the_token) { + case Token.IDENTIFIER: + case Token.OBJECT: + case Token.STRING: + case Token.BOOL: + case Token.DECIMAL: + case Token.FLOAT: + case Token.DOUBLE: + case Token.SBYTE: + case Token.BYTE: + case Token.SHORT: + case Token.USHORT: + case Token.INT: + case Token.UINT: + case Token.LONG: + case Token.ULONG: + case Token.CHAR: + case Token.VOID: + break; + case Token.OP_GENERICS_GT: + genericDimension = 1; + genericDimensionLocations.Peek ().Add (Location); + return true; + case Token.IN: + case Token.OUT: + genericDimensionLocations.Pop (); + return true; + case Token.COMMA: + do { + ++genericDimension; + if (genericDimensionLocations.Count > 0) + genericDimensionLocations.Peek ().Add (Location); + the_token = token (); + } while (the_token == Token.COMMA); + + if (the_token == Token.OP_GENERICS_GT) { + ++genericDimension; + if (genericDimensionLocations.Count > 0) + genericDimensionLocations.Peek ().Add (Location); + return true; + } + + genericDimensionLocations.Pop (); + return false; + default: + genericDimensionLocations.Pop (); + return false; + } + again: + the_token = token (); + if (the_token == Token.OP_GENERICS_GT) { + genericDimensionLocations.Peek ().Add (Location); + return true; + } + else if (the_token == Token.COMMA || the_token == Token.DOT || the_token == Token.DOUBLE_COLON) + goto start; + else if (the_token == Token.INTERR_NULLABLE || the_token == Token.STAR) + goto again; + else if (the_token == Token.OP_GENERICS_LT) { + if (!parse_less_than (ref genericDimension)) { + genericDimensionLocations.Pop (); + return false; + } + goto again; + } else if (the_token == Token.OPEN_BRACKET) { + rank_specifiers: + the_token = token (); + if (the_token == Token.CLOSE_BRACKET) + goto again; + else if (the_token == Token.COMMA) + goto rank_specifiers; + genericDimensionLocations.Pop (); + return false; + } + + genericDimensionLocations.Pop (); + return false; + } + + + public int peek_token () + { + int the_token; + + PushPosition (); + sbag.Suppress = true; + the_token = token (); + sbag.Suppress = false; + PopPosition (); + + return the_token; + } + + // + // Tonizes `?' using custom disambiguous rules to return one + // of following tokens: INTERR_NULLABLE, OP_COALESCING, INTERR + // + // Tricky expression looks like: + // + // Foo ? a = x ? b : c; + // + int TokenizePossibleNullableType () + { + if (parsing_block == 0 || parsing_type > 0) + return Token.INTERR_NULLABLE; + + int d = peek_char (); + if (d == '?') { + get_char (); + return Token.OP_COALESCING; + } + + if (d == '.') { + return Token.INTERR_OPERATOR; + } + + if (d != ' ') { + if (d == ',' || d == ';' || d == '>') + return Token.INTERR_NULLABLE; + if (d == '*' || (d >= '0' && d <= '9')) + return Token.INTERR; + } + + PushPosition (); + current_token = Token.NONE; + int next_token; + int parens = 0; + int generics = 0; + + var nt = xtoken (); + switch (nt) { + case Token.DOT: + case Token.OPEN_BRACKET_EXPR: + next_token = Token.INTERR_OPERATOR; + break; + case Token.LITERAL: + case Token.TRUE: + case Token.FALSE: + case Token.NULL: + case Token.THIS: + case Token.NEW: + next_token = Token.INTERR; + break; + + case Token.SEMICOLON: + case Token.COMMA: + case Token.CLOSE_PARENS: + case Token.OPEN_BRACKET: + case Token.OP_GENERICS_GT: + case Token.INTERR: + case Token.OP_COALESCING: + case Token.COLON: + next_token = Token.INTERR_NULLABLE; + break; + + case Token.OPEN_PARENS: + case Token.OPEN_PARENS_CAST: + case Token.OPEN_PARENS_LAMBDA: + next_token = -1; + ++parens; + break; + + case Token.OP_GENERICS_LT: + case Token.OP_GENERICS_LT_DECL: + case Token.GENERIC_DIMENSION: + next_token = -1; + ++generics; + break; + + default: + next_token = -1; + break; + } + + if (next_token == -1) { + switch (xtoken ()) { + case Token.COMMA: + case Token.SEMICOLON: + case Token.OPEN_BRACE: + case Token.IN: + next_token = Token.INTERR_NULLABLE; + break; + + case Token.COLON: + next_token = Token.INTERR; + break; + + case Token.OPEN_PARENS: + case Token.OPEN_PARENS_CAST: + case Token.OPEN_PARENS_LAMBDA: + ++parens; + goto default; + + case Token.CLOSE_PARENS: + --parens; + goto default; + + case Token.OP_GENERICS_LT: + case Token.OP_GENERICS_LT_DECL: + case Token.GENERIC_DIMENSION: + ++generics; + goto default; + + default: + int ntoken; + int interrs = 1; + int colons = 0; + int braces = 0; + int brackets = 0; + // + // All shorcuts failed, do it hard way + // + while ((ntoken = xtoken ()) != Token.EOF) { + switch (ntoken) { + case Token.OPEN_BRACE: + ++braces; + continue; + case Token.OPEN_PARENS: + case Token.OPEN_PARENS_CAST: + case Token.OPEN_PARENS_LAMBDA: + ++parens; + continue; + case Token.CLOSE_BRACE: + --braces; + continue; + case Token.OP_GENERICS_LT: + case Token.OP_GENERICS_LT_DECL: + case Token.GENERIC_DIMENSION: + ++generics; + continue; + case Token.OPEN_BRACKET: + case Token.OPEN_BRACKET_EXPR: + ++brackets; + continue; + case Token.CLOSE_BRACKET: + --brackets; + continue; + case Token.CLOSE_PARENS: + if (parens > 0) { + --parens; + continue; + } + + PopPosition (); + return Token.INTERR_NULLABLE; + + case Token.OP_GENERICS_GT: + if (generics > 0) { + --generics; + continue; + } + + PopPosition (); + return Token.INTERR_NULLABLE; + } + + if (braces != 0) + continue; + + if (ntoken == Token.SEMICOLON) + break; + + if (parens != 0) + continue; + + if (ntoken == Token.COMMA) { + if (generics != 0 || brackets != 0) + continue; + + PopPosition (); + return Token.INTERR_NULLABLE; + } + + if (ntoken == Token.COLON) { + if (++colons == interrs) + break; + continue; + } + + if (ntoken == Token.INTERR) { + ++interrs; + continue; + } + } + + next_token = colons != interrs && braces == 0 ? Token.INTERR_NULLABLE : Token.INTERR; + break; + } + } + + PopPosition (); + return next_token; + } + + bool decimal_digits (int c) + { + int d; + bool seen_digits = false; + + if (c != -1){ + if (number_pos == MaxNumberLength) + Error_NumericConstantTooLong (); + number_builder [number_pos++] = (char) c; + } + + // + // We use peek_char2, because decimal_digits needs to do a + // 2-character look-ahead (5.ToString for example). + // + while ((d = peek_char2 ()) != -1){ + if (d >= '0' && d <= '9'){ + if (number_pos == MaxNumberLength) + Error_NumericConstantTooLong (); + number_builder [number_pos++] = (char) d; + get_char (); + seen_digits = true; + } else + break; + } + + return seen_digits; + } + + static bool is_hex (int e) + { + return (e >= '0' && e <= '9') || (e >= 'A' && e <= 'F') || (e >= 'a' && e <= 'f'); + } + + static TypeCode real_type_suffix (int c) + { + switch (c){ + case 'F': case 'f': + return TypeCode.Single; + case 'D': case 'd': + return TypeCode.Double; + case 'M': case 'm': + return TypeCode.Decimal; + default: + return TypeCode.Empty; + } + } + + ILiteralConstant integer_type_suffix (ulong ul, int c, Location loc) + { + bool is_unsigned = false; + bool is_long = false; + + if (c != -1){ + bool scanning = true; + do { + switch (c){ + case 'U': case 'u': + if (is_unsigned) + scanning = false; + is_unsigned = true; + get_char (); + break; + + case 'l': + if (!is_unsigned){ + // + // if we have not seen anything in between + // report this error + // + Report.Warning (78, 4, Location, "The `l' suffix is easily confused with the digit `1' (use `L' for clarity)"); + } + + goto case 'L'; + + case 'L': + if (is_long) + scanning = false; + is_long = true; + get_char (); + break; + + default: + scanning = false; + break; + } + c = peek_char (); + } while (scanning); + } + + if (is_long && is_unsigned){ + return new ULongLiteral (context.BuiltinTypes, ul, loc); + } + + if (is_unsigned){ + // uint if possible, or ulong else. + + if ((ul & 0xffffffff00000000) == 0) + return new UIntLiteral (context.BuiltinTypes, (uint) ul, loc); + else + return new ULongLiteral (context.BuiltinTypes, ul, loc); + } else if (is_long){ + // long if possible, ulong otherwise + if ((ul & 0x8000000000000000) != 0) + return new ULongLiteral (context.BuiltinTypes, ul, loc); + else + return new LongLiteral (context.BuiltinTypes, (long) ul, loc); + } else { + // int, uint, long or ulong in that order + if ((ul & 0xffffffff00000000) == 0){ + uint ui = (uint) ul; + + if ((ui & 0x80000000) != 0) + return new UIntLiteral (context.BuiltinTypes, ui, loc); + else + return new IntLiteral (context.BuiltinTypes, (int) ui, loc); + } else { + if ((ul & 0x8000000000000000) != 0) + return new ULongLiteral (context.BuiltinTypes, ul, loc); + else + return new LongLiteral (context.BuiltinTypes, (long) ul, loc); + } + } + } + + // + // given `c' as the next char in the input decide whether + // we need to convert to a special type, and then choose + // the best representation for the integer + // + ILiteralConstant adjust_int (int c, Location loc) + { + try { + if (number_pos > 9){ + ulong ul = (uint) (number_builder [0] - '0'); + + for (int i = 1; i < number_pos; i++){ + ul = checked ((ul * 10) + ((uint)(number_builder [i] - '0'))); + } + + return integer_type_suffix (ul, c, loc); + } else { + uint ui = (uint) (number_builder [0] - '0'); + + for (int i = 1; i < number_pos; i++){ + ui = checked ((ui * 10) + ((uint)(number_builder [i] - '0'))); + } + + return integer_type_suffix (ui, c, loc); + } + } catch (OverflowException) { + Error_NumericConstantTooLong (); + return new IntLiteral (context.BuiltinTypes, 0, loc); + } + catch (FormatException) { + Report.Error (1013, Location, "Invalid number"); + return new IntLiteral (context.BuiltinTypes, 0, loc); + } + } + + ILiteralConstant adjust_real (TypeCode t, Location loc) + { + string s = new string (number_builder, 0, number_pos); + const string error_details = "Floating-point constant is outside the range of type `{0}'"; + + switch (t){ + case TypeCode.Decimal: + try { + return new DecimalLiteral (context.BuiltinTypes, decimal.Parse (s, styles, csharp_format_info), loc); + } catch (OverflowException) { + Report.Error (594, Location, error_details, "decimal"); + return new DecimalLiteral (context.BuiltinTypes, 0, loc); + } + case TypeCode.Single: + try { + return new FloatLiteral (context.BuiltinTypes, float.Parse (s, styles, csharp_format_info), loc); + } catch (OverflowException) { + Report.Error (594, Location, error_details, "float"); + return new FloatLiteral (context.BuiltinTypes, 0, loc); + } + default: + try { + return new DoubleLiteral (context.BuiltinTypes, double.Parse (s, styles, csharp_format_info), loc); + } catch (OverflowException) { + Report.Error (594, loc, error_details, "double"); + return new DoubleLiteral (context.BuiltinTypes, 0, loc); + } + } + } + + ILiteralConstant handle_hex (Location loc) + { + int d; + ulong ul; + + get_char (); + while ((d = peek_char ()) != -1){ + if (is_hex (d)){ + number_builder [number_pos++] = (char) d; + get_char (); + } else + break; + } + + string s = new String (number_builder, 0, number_pos); + + try { + if (number_pos <= 8) + ul = System.UInt32.Parse (s, NumberStyles.HexNumber); + else + ul = System.UInt64.Parse (s, NumberStyles.HexNumber); + + return integer_type_suffix (ul, peek_char (), loc); + } catch (OverflowException){ + Error_NumericConstantTooLong (); + return new IntLiteral (context.BuiltinTypes, 0, loc); + } + catch (FormatException) { + Report.Error (1013, Location, "Invalid number"); + return new IntLiteral (context.BuiltinTypes, 0, loc); + } + } + + // + // Invoked if we know we have .digits or digits + // + int is_number (int c, bool dotLead) + { + ILiteralConstant res; + +#if FULL_AST + int read_start = reader.Position - 1; + if (dotLead) { + // + // Caller did peek_char + // + --read_start; + } +#endif + number_pos = 0; + var loc = Location; + bool hasLeadingDot = c == '.'; + + if (!dotLead){ + if (c == '0'){ + int peek = peek_char (); + + if (peek == 'x' || peek == 'X') { + val = res = handle_hex (loc); +#if FULL_AST + res.ParsedValue = reader.ReadChars (read_start, reader.Position - 1); +#endif + + return Token.LITERAL; + } + } + decimal_digits (c); + c = peek_char (); + } + + // + // We need to handle the case of + // "1.1" vs "1.string" (LITERAL_FLOAT vs NUMBER DOT IDENTIFIER) + // + bool is_real = false; + if (c == '.'){ + if (!dotLead) + get_char (); + + if (decimal_digits ('.')){ + is_real = true; + c = peek_char (); + } else { + putback ('.'); + number_pos--; + val = res = adjust_int (-1, loc); +#if FULL_AST + res.ParsedValue = reader.ReadChars (read_start, reader.Position - 1); +#endif + return Token.LITERAL; + } + } + + if (c == 'e' || c == 'E'){ + is_real = true; + get_char (); + if (number_pos == MaxNumberLength) + Error_NumericConstantTooLong (); + number_builder [number_pos++] = (char) c; + c = get_char (); + + if (c == '+'){ + if (number_pos == MaxNumberLength) + Error_NumericConstantTooLong (); + number_builder [number_pos++] = '+'; + c = -1; + } else if (c == '-') { + if (number_pos == MaxNumberLength) + Error_NumericConstantTooLong (); + number_builder [number_pos++] = '-'; + c = -1; + } else { + if (number_pos == MaxNumberLength) + Error_NumericConstantTooLong (); + number_builder [number_pos++] = '+'; + } + + decimal_digits (c); + c = peek_char (); + } + + var type = real_type_suffix (c); + if (type == TypeCode.Empty && !is_real) { + res = adjust_int (c, loc); + } else { + is_real = true; + + if (type != TypeCode.Empty) { + get_char (); + } + + res = adjust_real (type, loc); + } + + val = res; +#if FULL_AST + var chars = reader.ReadChars (read_start, reader.Position - (type == TypeCode.Empty && c > 0 ? 1 : 0)); + if (chars[chars.Length - 1] == '\r') + Array.Resize (ref chars, chars.Length - 1); + res.ParsedValue = chars; +#endif + + return Token.LITERAL; + } + + // + // Accepts exactly count (4 or 8) hex, no more no less + // + int getHex (int count, out int surrogate, out bool error) + { + int i; + int total = 0; + int c; + int top = count != -1 ? count : 4; + + get_char (); + error = false; + surrogate = 0; + for (i = 0; i < top; i++){ + c = get_char (); + + if (c >= '0' && c <= '9') + c = (int) c - (int) '0'; + else if (c >= 'A' && c <= 'F') + c = (int) c - (int) 'A' + 10; + else if (c >= 'a' && c <= 'f') + c = (int) c - (int) 'a' + 10; + else { + error = true; + return 0; + } + + total = (total * 16) + c; + if (count == -1){ + int p = peek_char (); + if (p == -1) + break; + if (!is_hex ((char)p)) + break; + } + } + + if (top == 8) { + if (total > 0x0010FFFF) { + error = true; + return 0; + } + + if (total >= 0x00010000) { + surrogate = ((total - 0x00010000) % 0x0400 + 0xDC00); + total = ((total - 0x00010000) / 0x0400 + 0xD800); + } + } + + return total; + } + + int escape (int c, out int surrogate) + { + bool error; + int d; + int v; + + d = peek_char (); + if (c != '\\') { + surrogate = 0; + return c; + } + + switch (d){ + case 'a': + v = '\a'; break; + case 'b': + v = '\b'; break; + case 'n': + v = '\n'; break; + case 't': + v = '\t'; break; + case 'v': + v = '\v'; break; + case 'r': + v = '\r'; break; + case '\\': + v = '\\'; break; + case 'f': + v = '\f'; break; + case '0': + v = 0; break; + case '"': + v = '"'; break; + case '\'': + v = '\''; break; + case 'x': + v = getHex (-1, out surrogate, out error); + if (error) + goto default; + return v; + case 'u': + case 'U': + return EscapeUnicode (d, out surrogate); + default: + surrogate = 0; + Report.Error (1009, Location, "Unrecognized escape sequence `\\{0}'", ((char)d).ToString ()); + return d; + } + + get_char (); + surrogate = 0; + return v; + } + + int EscapeUnicode (int ch, out int surrogate) + { + bool error; + if (ch == 'U') { + ch = getHex (8, out surrogate, out error); + } else { + ch = getHex (4, out surrogate, out error); + } + + if (error) + Report.Error (1009, Location, "Unrecognized escape sequence"); + + return ch; + } + + int get_char () + { + int x; + if (putback_char != -1) { + x = putback_char; + putback_char = -1; + } else { + x = reader.Read (); + } + + if (x <= 13) { + if (x == '\r') { + if (peek_char () == '\n') { + putback_char = -1; + advance_line (SpecialsBag.NewLine.Windows); + } else { + advance_line (SpecialsBag.NewLine.Unix); + } + + x = '\n'; + } else if (x == '\n') { + advance_line (SpecialsBag.NewLine.Unix); + } else { + col++; + } + } else if (x >= UnicodeLS && x <= UnicodePS) { + advance_line (SpecialsBag.NewLine.Unix); + } else { + col++; + } + + return x; + } + + bool recordNewLine = true; + void advance_line (SpecialsBag.NewLine newLine) + { + if (recordNewLine) + sbag.AddNewLine (line, col, newLine); + line++; + ref_line++; + previous_col = col; + col = 0; + startsLine = true; + } + + int peek_char () + { + if (putback_char == -1) + putback_char = reader.Read (); + return putback_char; + } + + int peek_char2 () + { + if (putback_char != -1) + return putback_char; + return reader.Peek (); + } + + public void putback (int c) + { + if (putback_char != -1) { + throw new InternalErrorException (string.Format ("Secondary putback [{0}] putting back [{1}] is not allowed", (char)putback_char, (char) c), Location); + } + + if (c == '\n' || col == 0 || (c >= UnicodeLS && c <= UnicodePS)) { + // It won't happen though. + line--; + ref_line--; + col = previous_col; + } + else + col--; + putback_char = c; + } + + public bool advance () + { + return peek_char () != -1 || CompleteOnEOF; + } + + public Object Value { + get { + return val; + } + } + + public Object value () + { + return val; + } + + public int token () + { + current_token = xtoken (); + return current_token; + } + + int TokenizePreprocessorIdentifier (out int c) + { + int startCol, endLine, endCol; + return TokenizePreprocessorIdentifier (out c, out startCol, out endLine, out endCol); + } + + int TokenizePreprocessorIdentifier (out int c, out int startCol, out int endLine, out int endCol) + { + // skip over white space + do { + endLine = line; + endCol = col; + c = get_char (); + } while (c == ' ' || c == '\t'); + startCol = col; + int pos = 0; + while (c != -1 && c >= 'a' && c <= 'z') { + id_builder[pos++] = (char) c; + endCol = col + 1; + c = get_char (); + if (c == '\\') { + int peek = peek_char (); + if (peek == 'U' || peek == 'u') { + int surrogate; + c = EscapeUnicode (c, out surrogate); + if (surrogate != 0) { + if (is_identifier_part_character ((char) c)) { + id_builder[pos++] = (char) c; + } + c = surrogate; + } + } + } + } + + return pos; + } + + PreprocessorDirective get_cmd_arg (out string arg) + { + int c; + int startLine = line, startCol = col; + + tokens_seen = false; + arg = ""; + + int startCol2, endLine, endCol; + var cmd = GetPreprocessorDirective (id_builder, TokenizePreprocessorIdentifier (out c, out startCol2, out endLine, out endCol)); + + if ((cmd & PreprocessorDirective.CustomArgumentsParsing) != 0) { + if (position_stack.Count == 0) + sbag.AddPreProcessorDirective (startLine, startCol, line, col, cmd, null); + return cmd; + } + + // skip over white space + while (c == ' ' || c == '\t') { + c = get_char (); + } + int has_identifier_argument = (int)(cmd & PreprocessorDirective.RequiresArgument); + int pos = 0; + while (c != -1 && c != '\n' && c != UnicodeLS && c != UnicodePS) { + if (c == '\\' && has_identifier_argument >= 0) { + if (has_identifier_argument != 0) { + has_identifier_argument = 1; + + int peek = peek_char (); + if (peek == 'U' || peek == 'u') { + int surrogate; + c = EscapeUnicode (c, out surrogate); + if (surrogate != 0) { + if (is_identifier_part_character ((char)c)) { + if (pos == value_builder.Length) + Array.Resize (ref value_builder, pos * 2); + + value_builder [pos++] = (char)c; + } + c = surrogate; + } + } + } else { + has_identifier_argument = -1; + } + } else if (c == '/' && peek_char () == '/') { + // + // Eat single-line comments + // + get_char (); + ReadToEndOfLine (); + break; + } + + endLine = line; + endCol = col + 1; + + if (pos == value_builder.Length) + Array.Resize (ref value_builder, pos * 2); + + value_builder[pos++] = (char) c; + c = get_char (); + } + + if (pos != 0) { + if (pos > MaxIdentifierLength) + arg = new string (value_builder, 0, pos); + else + arg = InternIdentifier (value_builder, pos); + + // Eat any trailing whitespaces + arg = arg.Trim (simple_whitespaces); + } + if (position_stack.Count == 0) + sbag.AddPreProcessorDirective (startLine, startCol, endLine, endCol, cmd, arg); + + return cmd; + } + + // + // Handles the #line directive + // + bool PreProcessLine () + { + Location loc = Location; + #if FULL_AST + var lineDirective = sbag.GetCurrentLineProcessorDirective(); + #endif + + int c; + + int length = TokenizePreprocessorIdentifier (out c); + if (length == line_default.Length) { + if (!IsTokenIdentifierEqual (line_default)) + return false; + + current_source = source_file.SourceFile; + if (!hidden_block_start.IsNull) { + current_source.RegisterHiddenScope (hidden_block_start, loc); + hidden_block_start = Location.Null; + } + + //ref_line = line; + return true; + } + + if (length == line_hidden.Length) { + if (!IsTokenIdentifierEqual (line_hidden)) + return false; + + if (hidden_block_start.IsNull) + hidden_block_start = loc; + + return true; + } + + if (length != 0 || c < '0' || c > '9') { + // + // Eat any remaining characters to continue parsing on next line + // + ReadToEndOfLine (); + return false; + } + + int new_line = TokenizeNumber (c); + if (new_line < 1) { + // + // Eat any remaining characters to continue parsing on next line + // + ReadToEndOfLine (); + return new_line != 0; + } + #if FULL_AST + lineDirective.LineNumber = new_line; + #endif + + c = get_char (); + if (c == ' ') { + // skip over white space + do { + c = get_char (); + } while (c == ' ' || c == '\t'); + } else if (c == '"') { + c = 0; + } + + if (c != '\n' && c != '/' && c != '"' && c != UnicodeLS && c != UnicodePS) { + // + // Eat any remaining characters to continue parsing on next line + // + ReadToEndOfLine (); + + Report.Error (1578, loc, "Filename, single-line comment or end-of-line expected"); + return true; + } + + string new_file_name = null; + if (c == '"') { + new_file_name = TokenizeFileName (ref c); + #if FULL_AST + lineDirective.FileName = new_file_name; + #endif + + // skip over white space + while (c == ' ' || c == '\t') { + c = get_char (); + } + } + + if (c == '\n' || c == UnicodeLS || c == UnicodePS) { + + } else if (c == '/') { + ReadSingleLineComment (); + } else { + // + // Eat any remaining characters to continue parsing on next line + // + ReadToEndOfLine (); + + Error_EndLineExpected (); + return true; + } + + if (new_file_name != null) { + current_source = context.LookupFile (source_file, new_file_name); + source_file.AddIncludeFile (current_source); + } + + if (!hidden_block_start.IsNull) { + current_source.RegisterHiddenScope (hidden_block_start, loc); + hidden_block_start = Location.Null; + } + + //ref_line = new_line; + return true; + } + + // + // Handles #define and #undef + // + void PreProcessDefinition (bool is_define, string ident, bool caller_is_taking) + { + if (ident.Length == 0 || ident == "true" || ident == "false"){ + Report.Error (1001, Location, "Missing identifier to pre-processor directive"); + return; + } + + if (ident.IndexOfAny (simple_whitespaces) != -1){ + Error_EndLineExpected (); + return; + } + + if (!is_identifier_start_character (ident [0])) + Report.Error (1001, Location, "Identifier expected: {0}", ident); + + foreach (char c in ident.Substring (1)){ + if (!is_identifier_part_character (c)){ + Report.Error (1001, Location, "Identifier expected: {0}", ident); + return; + } + } + + if (!caller_is_taking) + return; + + if (is_define) { + // + // #define ident + // + if (context.Settings.IsConditionalSymbolDefined (ident)) + return; + + source_file.AddDefine (ident); + } else { + // + // #undef ident + // + source_file.AddUndefine (ident); + } + } + + byte read_hex (out bool error) + { + int total; + int c = get_char (); + + if ((c >= '0') && (c <= '9')) + total = (int) c - (int) '0'; + else if ((c >= 'A') && (c <= 'F')) + total = (int) c - (int) 'A' + 10; + else if ((c >= 'a') && (c <= 'f')) + total = (int) c - (int) 'a' + 10; + else { + error = true; + return 0; + } + + total *= 16; + c = get_char (); + + if ((c >= '0') && (c <= '9')) + total += (int) c - (int) '0'; + else if ((c >= 'A') && (c <= 'F')) + total += (int) c - (int) 'A' + 10; + else if ((c >= 'a') && (c <= 'f')) + total += (int) c - (int) 'a' + 10; + else { + error = true; + return 0; + } + + error = false; + return (byte) total; + } + + // + // Parses #pragma checksum + // + bool ParsePragmaChecksum () + { + // + // The syntax is ` "foo.txt" "{guid}" "hash"' + // + // guid is predefined hash algorithm guid {406ea660-64cf-4c82-b6f0-42d48172a799} for md5 + // + int c = get_char (); + + if (c != '"') + return false; + + string file_name = TokenizeFileName (ref c); + + // TODO: Any white-spaces count + if (c != ' ') + return false; + + SourceFile file = context.LookupFile (source_file, file_name); + + if (get_char () != '"' || get_char () != '{') + return false; + + bool error; + byte[] guid_bytes = new byte [16]; + int i = 0; + + for (; i < 4; i++) { + guid_bytes [i] = read_hex (out error); + if (error) + return false; + } + + if (get_char () != '-') + return false; + + for (; i < 10; i++) { + guid_bytes [i] = read_hex (out error); + if (error) + return false; + + guid_bytes [i++] = read_hex (out error); + if (error) + return false; + + if (get_char () != '-') + return false; + } + + for (; i < 16; i++) { + guid_bytes [i] = read_hex (out error); + if (error) + return false; + } + + if (get_char () != '}' || get_char () != '"') + return false; + + // TODO: Any white-spaces count + c = get_char (); + if (c != ' ') + return false; + + if (get_char () != '"') + return false; + + // Any length of checksum + List checksum_bytes = new List (16); + + var checksum_location = Location; + c = peek_char (); + while (c != '"' && c != -1) { + checksum_bytes.Add (read_hex (out error)); + if (error) + return false; + + c = peek_char (); + } + + if (c == '/') { + ReadSingleLineComment (); + } else if (get_char () != '"') { + return false; + } + + if (context.Settings.GenerateDebugInfo) { + var chsum = checksum_bytes.ToArray (); + + if (file.HasChecksum) { + if (!ArrayComparer.IsEqual (file.Checksum, chsum)) { + // TODO: Report.SymbolRelatedToPreviousError + Report.Warning (1697, 1, checksum_location, "Different checksum values specified for file `{0}'", file.Name); + } + } + + file.SetChecksum (guid_bytes, chsum); + current_source.AutoGenerated = true; + } + + return true; + } + + bool IsTokenIdentifierEqual (char[] identifier) + { + for (int i = 0; i < identifier.Length; ++i) { + if (identifier[i] != id_builder[i]) + return false; + } + + return true; + } + + int TokenizeNumber (int value) + { + number_pos = 0; + + decimal_digits (value); + uint ui = (uint) (number_builder[0] - '0'); + + try { + for (int i = 1; i < number_pos; i++) { + ui = checked ((ui * 10) + ((uint) (number_builder[i] - '0'))); + } + + return (int) ui; + } catch (OverflowException) { + Error_NumericConstantTooLong (); + return -1; + } + } + + string TokenizeFileName (ref int c) + { + var string_builder = new StringBuilder (); + while (c != -1 && c != '\n' && c != UnicodeLS && c != UnicodePS) { + c = get_char (); + if (c == '"') { + c = get_char (); + break; + } + + string_builder.Append ((char) c); + } + + if (string_builder.Length == 0) { + Report.Warning (1709, 1, Location, "Filename specified for preprocessor directive is empty"); + } + + + return string_builder.ToString (); + } + + int TokenizePragmaNumber (ref int c) + { + number_pos = 0; + + int number; + + if (c >= '0' && c <= '9') { + number = TokenizeNumber (c); + + c = get_char (); + + // skip over white space + while (c == ' ' || c == '\t') + c = get_char (); + + if (c == ',') { + c = get_char (); + } + + // skip over white space + while (c == ' ' || c == '\t') + c = get_char (); + } else { + number = -1; + if (c == '/') { + ReadSingleLineComment (); + } else { + Report.Warning (1692, 1, Location, "Invalid number"); + + // Read everything till the end of the line or file + ReadToEndOfLine (); + } + } + + return number; + } + + void ReadToEndOfLine () + { + int c; + do { + c = get_char (); + } while (c != -1 && c != '\n' && c != UnicodeLS && c != UnicodePS); + } + + void ReadSingleLineComment () + { + if (peek_char () != '/') + Report.Warning (1696, 1, Location, "Single-line comment or end-of-line expected"); + if (position_stack.Count == 0) + sbag.StartComment (SpecialsBag.CommentType.Single, startsLine, line, col - 1); + // Read everything till the end of the line or file + int c; + do { + c = get_char (); + if (position_stack.Count == 0) + sbag.PushCommentChar (c); + var pc = peek_char (); + if ((pc == '\n' || pc == -1 || pc == UnicodeLS || pc == UnicodePS) && position_stack.Count == 0) + sbag.EndComment (line, col + 1); + } while (c != -1 && c != '\n' && c != UnicodeLS && c != UnicodePS); + } + + /// + /// Handles #pragma directive + /// + void ParsePragmaDirective () + { + int c; + int startCol, endLine, endCol; + int length = TokenizePreprocessorIdentifier (out c, out startCol, out endLine, out endCol); + #if FULL_AST + var pragmaDirective = sbag.GetPragmaPreProcessorDirective(); + if (pragmaDirective != null) + pragmaDirective.WarningColumn = startCol; + #endif + if (length == pragma_warning.Length && IsTokenIdentifierEqual (pragma_warning)) { + length = TokenizePreprocessorIdentifier (out c, out startCol, out endLine, out endCol); + #if FULL_AST + if (pragmaDirective != null) + pragmaDirective.DisableRestoreColumn = startCol; + #endif + + // + // #pragma warning disable + // #pragma warning restore + // + if (length == pragma_warning_disable.Length) { + bool disable = IsTokenIdentifierEqual (pragma_warning_disable); + #if FULL_AST + if (pragmaDirective != null) + pragmaDirective.Disalbe = disable; + #endif + if (disable || IsTokenIdentifierEqual (pragma_warning_restore)) { + // skip over white space + while (c == ' ' || c == '\t') + c = get_char (); + + var loc = Location; + + if (c == '\n' || c == '/' || c == UnicodeLS || c == UnicodePS) { + if (c == '/') + ReadSingleLineComment (); + + // + // Disable/Restore all warnings + // + if (disable) { + Report.RegisterWarningRegion (loc).WarningDisable (loc.Row); + } else { + Report.RegisterWarningRegion (loc).WarningEnable (loc.Row); + } + } else { + // + // Disable/Restore a warning or group of warnings + // + int code; + do { + var startLoc = loc; + #if FULL_AST + // int read_start = reader.Position; + #endif + code = TokenizePragmaNumber (ref c); + if (code > 0) { + #if FULL_AST + var literal = new IntConstant(context.BuiltinTypes, code, startLoc); + if (pragmaDirective != null) + pragmaDirective.Codes.Add (literal); + // literal.ParsedValue = reader.ReadChars (read_start, reader.Position + 1); + #endif + if (disable) { + Report.RegisterWarningRegion (loc).WarningDisable (loc, code, context.Report); + } else { + Report.RegisterWarningRegion (loc).WarningEnable (loc, code, context); + } + } + } while (code >= 0 && c != '\n' && c != -1 && c != UnicodeLS && c != UnicodePS); + } + + return; + } + } + + Report.Warning (1634, 1, Location, "Expected disable or restore"); + + // Eat any remaining characters on the line + ReadToEndOfLine (); + + return; + } + + + // + // #pragma checksum + // + if (length == pragma_checksum.Length && IsTokenIdentifierEqual (pragma_checksum)) { + if (c != ' ' || !ParsePragmaChecksum ()) { + Report.Warning (1695, 1, Location, + "Invalid #pragma checksum syntax. Expected \"filename\" \"{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}\" \"XXXX...\""); + } + + return; + } + + Report.Warning (1633, 1, Location, "Unrecognized #pragma directive"); + } + + bool eval_val (string s) + { + if (s == "true") + return true; + if (s == "false") + return false; + + return source_file.IsConditionalDefined (s); + } + + bool pp_primary (ref string s) + { + s = s.Trim (); + int len = s.Length; + + if (len > 0){ + char c = s [0]; + + if (c == '('){ + s = s.Substring (1); + bool val = pp_expr (ref s, false); + if (s.Length > 0 && s [0] == ')'){ + s = s.Substring (1); + return val; + } + Error_InvalidDirective (); + return false; + } + + if (is_identifier_start_character (c)){ + int j = 1; + + while (j < len){ + c = s [j]; + + if (is_identifier_part_character (c)){ + j++; + continue; + } + bool v = eval_val (s.Substring (0, j)); + s = s.Substring (j); + return v; + } + bool vv = eval_val (s); + s = ""; + return vv; + } + } + Error_InvalidDirective (); + return false; + } + + bool pp_unary (ref string s) + { + s = s.Trim (); + int len = s.Length; + + if (len > 0){ + if (s [0] == '!'){ + if (len > 1 && s [1] == '='){ + Error_InvalidDirective (); + return false; + } + s = s.Substring (1); + return ! pp_primary (ref s); + } else + return pp_primary (ref s); + } else { + Error_InvalidDirective (); + return false; + } + } + + bool pp_eq (ref string s) + { + bool va = pp_unary (ref s); + + s = s.Trim (); + int len = s.Length; + if (len > 0){ + if (s [0] == '='){ + if (len > 2 && s [1] == '='){ + s = s.Substring (2); + return va == pp_unary (ref s); + } else { + Error_InvalidDirective (); + return false; + } + } else if (s [0] == '!' && len > 1 && s [1] == '='){ + s = s.Substring (2); + + return va != pp_unary (ref s); + + } + } + + return va; + + } + + bool pp_and (ref string s) + { + bool va = pp_eq (ref s); + + s = s.Trim (); + int len = s.Length; + if (len > 0){ + if (s [0] == '&'){ + if (len > 2 && s [1] == '&'){ + s = s.Substring (2); + return (va & pp_and (ref s)); + } else { + Error_InvalidDirective (); + return false; + } + } + } + return va; + } + + // + // Evaluates an expression for `#if' or `#elif' + // + bool pp_expr (ref string s, bool isTerm) + { + bool va = pp_and (ref s); + s = s.Trim (); + int len = s.Length; + if (len > 0){ + char c = s [0]; + + if (c == '|'){ + if (len > 2 && s [1] == '|'){ + s = s.Substring (2); + return va | pp_expr (ref s, isTerm); + } else { + Error_InvalidDirective (); + return false; + } + } + if (isTerm) { + Error_EndLineExpected (); + return false; + } + } + + return va; + } + + bool eval (string s) + { + bool v = pp_expr (ref s, true); + s = s.Trim (); + if (s.Length != 0){ + return false; + } + + return v; + } + + void Error_NumericConstantTooLong () + { + Report.Error (1021, Location, "Integral constant is too large"); + } + + void Error_InvalidDirective () + { + Report.Error (1517, Location, "Invalid preprocessor directive"); + } + + void Error_UnexpectedDirective (string extra) + { + Report.Error ( + 1028, Location, + "Unexpected processor directive ({0})", extra); + } + + void Error_TokensSeen () + { + Report.Error (1032, Location, + "Cannot define or undefine preprocessor symbols after first token in file"); + } + + void Eror_WrongPreprocessorLocation () + { + Report.Error (1040, Location, + "Preprocessor directives must appear as the first non-whitespace character on a line"); + } + + void Error_EndLineExpected () + { + Report.Error (1025, Location, "Single-line comment or end-of-line expected"); + } + + // + // Raises a warning when tokenizer found documentation comment + // on unexpected place + // + void WarningMisplacedComment (Location loc) + { + if (doc_state != XmlCommentState.Error) { + doc_state = XmlCommentState.Error; + Report.Warning (1587, 2, loc, "XML comment is not placed on a valid language element"); + } + } + + // + // if true, then the code continues processing the code + // if false, the code stays in a loop until another directive is + // reached. + // When caller_is_taking is false we ignore all directives except the ones + // which can help us to identify where the #if block ends + bool ParsePreprocessingDirective (bool caller_is_taking) + { + string arg; + bool region_directive = false; + + var directive = get_cmd_arg (out arg); + + // + // The first group of pre-processing instructions is always processed + // + switch (directive) { + case PreprocessorDirective.Region: + region_directive = true; + arg = "true"; + goto case PreprocessorDirective.If; + + case PreprocessorDirective.Endregion: + if (ifstack == null || ifstack.Count == 0){ + Error_UnexpectedDirective ("no #region for this #endregion"); + return true; + } + int pop = ifstack.Pop (); + + if ((pop & REGION) == 0) + Report.Error (1027, Location, "Expected `#endif' directive"); + + return caller_is_taking; + + case PreprocessorDirective.If: + if (ifstack == null) + ifstack = new Stack (2); + + int flags = region_directive ? REGION : 0; + if (ifstack.Count == 0){ + flags |= PARENT_TAKING; + } else { + int state = ifstack.Peek (); + if ((state & TAKING) != 0) { + flags |= PARENT_TAKING; + } + } + + if (eval (arg) && caller_is_taking) { + ifstack.Push (flags | TAKING); + return true; + } + sbag.SkipIf (); + ifstack.Push (flags); + return false; + + case PreprocessorDirective.Endif: + if (ifstack == null || ifstack.Count == 0){ + Error_UnexpectedDirective ("no #if for this #endif"); + return true; + } else { + pop = ifstack.Pop (); + + if ((pop & REGION) != 0) + Report.Error (1038, Location, "#endregion directive expected"); + + if (arg.Length != 0) { + Error_EndLineExpected (); + } + + if (ifstack.Count == 0) + return true; + + int state = ifstack.Peek (); + return (state & TAKING) != 0; + } + + case PreprocessorDirective.Elif: + if (ifstack == null || ifstack.Count == 0){ + Error_UnexpectedDirective ("no #if for this #elif"); + return true; + } else { + int state = ifstack.Pop (); + + if ((state & REGION) != 0) { + Report.Error (1038, Location, "#endregion directive expected"); + return true; + } + + if ((state & ELSE_SEEN) != 0){ + Error_UnexpectedDirective ("#elif not valid after #else"); + return true; + } + + if ((state & TAKING) != 0) { + sbag.SkipIf (); + ifstack.Push (0); + return false; + } + + if (eval (arg) && ((state & PARENT_TAKING) != 0)){ + ifstack.Push (state | TAKING); + return true; + } + + sbag.SkipIf (); + ifstack.Push (state); + return false; + } + + case PreprocessorDirective.Else: + if (ifstack == null || ifstack.Count == 0){ + Error_UnexpectedDirective ("no #if for this #else"); + return true; + } else { + int state = ifstack.Peek (); + + if ((state & REGION) != 0) { + Report.Error (1038, Location, "#endregion directive expected"); + return true; + } + + if ((state & ELSE_SEEN) != 0){ + Error_UnexpectedDirective ("#else within #else"); + return true; + } + + ifstack.Pop (); + + if (arg.Length != 0) { + Error_EndLineExpected (); + return true; + } + + bool ret = false; + if ((state & PARENT_TAKING) != 0) { + ret = (state & TAKING) == 0; + + if (ret) + state |= TAKING; + else + state &= ~TAKING; + } + + ifstack.Push (state | ELSE_SEEN); + + return ret; + } + case PreprocessorDirective.Define: + if (any_token_seen){ + if (caller_is_taking) + Error_TokensSeen (); + return caller_is_taking; + } + PreProcessDefinition (true, arg, caller_is_taking); + return caller_is_taking; + + case PreprocessorDirective.Undef: + if (any_token_seen){ + if (caller_is_taking) + Error_TokensSeen (); + return caller_is_taking; + } + PreProcessDefinition (false, arg, caller_is_taking); + return caller_is_taking; + + case PreprocessorDirective.Invalid: + Report.Error (1024, Location, "Wrong preprocessor directive"); + return true; + } + + // + // These are only processed if we are in a `taking' block + // + if (!caller_is_taking) + return false; + + switch (directive){ + case PreprocessorDirective.Error: + Report.Error (1029, Location, "#error: '{0}'", arg); + return true; + + case PreprocessorDirective.Warning: + Report.Warning (1030, 1, Location, "#warning: `{0}'", arg); + return true; + + case PreprocessorDirective.Pragma: + if (context.Settings.Version == LanguageVersion.ISO_1) { + Report.FeatureIsNotAvailable (context, Location, "#pragma"); + } + + ParsePragmaDirective (); + return true; + + case PreprocessorDirective.Line: + Location loc = Location; + if (!PreProcessLine ()) + Report.Error (1576, loc, "The line number specified for #line directive is missing or invalid"); + + return caller_is_taking; + } + + throw new NotImplementedException (directive.ToString ()); + } + + private int consume_string (bool quoted) + { + int c; + int pos = 0; + Location start_location = Location; + if (quoted) { + start_location = start_location - 1; + recordNewLine = false; + } + +#if FULL_AST + int reader_pos = reader.Position; +#endif + + while (true){ + // Cannot use get_char because of \r in quoted strings + if (putback_char != -1) { + c = putback_char; + putback_char = -1; + } else { + c = reader.Read (); + } + + if (c == '"') { + ++col; + + if (quoted && peek_char () == '"') { + if (pos == value_builder.Length) + Array.Resize (ref value_builder, pos * 2); + + value_builder[pos++] = (char) c; + get_char (); + continue; + } + + string s; + if (pos == 0) + s = string.Empty; + else if (pos <= 4) + s = InternIdentifier (value_builder, pos); + else + s = new string (value_builder, 0, pos); + + ILiteralConstant res = new StringLiteral (context.BuiltinTypes, s, start_location); + val = res; +#if FULL_AST + res.ParsedValue = quoted ? + reader.ReadChars (reader_pos - 2, reader.Position - 1) : + reader.ReadChars (reader_pos - 1, reader.Position); +#endif + recordNewLine = true; + return Token.LITERAL; + } + + if (c == '\n' || c == UnicodeLS || c == UnicodePS) { + if (!quoted) { + Report.Error (1010, Location, "Newline in constant"); + + + // Don't add \r to string literal + if (pos > 1 && value_builder [pos - 1] == '\r') { + advance_line (SpecialsBag.NewLine.Windows); + --pos; + } else { + advance_line (SpecialsBag.NewLine.Unix); + } + + val = new StringLiteral (context.BuiltinTypes, new string (value_builder, 0, pos), start_location); + recordNewLine = true; + return Token.LITERAL; + } + + advance_line (SpecialsBag.NewLine.Unix); + } else if (c == '\\' && !quoted) { + ++col; + int surrogate; + c = escape (c, out surrogate); + if (c == -1) { + recordNewLine = true; + return Token.ERROR; + } + if (surrogate != 0) { + if (pos == value_builder.Length) + Array.Resize (ref value_builder, pos * 2); + + value_builder[pos++] = (char) c; + c = surrogate; + } + } else if (c == -1) { + Report.Error (1039, Location, "Unterminated string literal"); + recordNewLine = true; + return Token.EOF; + } else { + ++col; + } + + if (pos == value_builder.Length) + Array.Resize (ref value_builder, pos * 2); + + value_builder[pos++] = (char) c; + } + recordNewLine = true; + } + + private int consume_identifier (int s) + { + int res = consume_identifier (s, false); + + if (doc_state == XmlCommentState.Allowed) + doc_state = XmlCommentState.NotAllowed; + startsLine = false; + return res; + } + + int consume_identifier (int c, bool quoted) + { + // + // This method is very performance sensitive. It accounts + // for approximately 25% of all parser time + // + + int pos = 0; + int column = col; + if (quoted) + --column; + + if (c == '\\') { + int surrogate; + c = escape (c, out surrogate); + if (surrogate != 0) { + id_builder [pos++] = (char) c; + c = surrogate; + } + } + + id_builder [pos++] = (char) c; + + try { + while (true) { + c = reader.Read (); + + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || (c >= '0' && c <= '9')) { + id_builder [pos++] = (char) c; + continue; + } + + if (c < 0x80) { + if (c == '\\') { + int surrogate; + c = escape (c, out surrogate); + if (is_identifier_part_character ((char) c)) + id_builder[pos++] = (char) c; + + if (surrogate != 0) { + c = surrogate; + } + + continue; + } + } else if (is_identifier_part_character_slow_part ((char) c)) { + id_builder [pos++] = (char) c; + continue; + } + + putback_char = c; + break; + } + } catch (IndexOutOfRangeException) { + Report.Error (645, Location, "Identifier too long (limit is 512 chars)"); + --pos; + col += pos; + } + + col += pos - 1; + + // + // Optimization: avoids doing the keyword lookup + // on uppercase letters + // + if (id_builder [0] >= '_' && !quoted) { + int keyword = GetKeyword (id_builder, pos); + if (keyword != -1) { + val = ltb.Create (keyword == Token.AWAIT ? "await" : null, current_source, ref_line, column); + return keyword; + } + } + + string s = InternIdentifier (id_builder, pos); +#if FULL_AST + if (quoted) { + val = ltb.Create ("@" + s, current_source, ref_line, column - 1); + } else { + val = ltb.Create (s, current_source, ref_line, column); + } +#else + val = ltb.Create (s, current_source, ref_line, column); +#endif + if (quoted && parsing_attribute_section) + AddEscapedIdentifier (((LocatedToken) val).Location); + + return Token.IDENTIFIER; + } + + string InternIdentifier (char[] charBuffer, int length) + { + // + // Keep identifiers in an array of hashtables to avoid needless + // allocations + // + var identifiers_group = identifiers [length]; + string s; + if (identifiers_group != null) { + if (identifiers_group.TryGetValue (charBuffer, out s)) { + return s; + } + } else { + // TODO: this should be number of files dependant + // corlib compilation peaks at 1000 and System.Core at 150 + int capacity = length > 20 ? 10 : 100; + identifiers_group = new Dictionary (capacity, new IdentifiersComparer (length)); + identifiers [length] = identifiers_group; + } + + char[] chars = new char[length]; + Array.Copy (charBuffer, chars, length); + + s = new string (charBuffer, 0, length); + identifiers_group.Add (chars, s); + + return s; + } + + public int xtoken () + { + int d, c; + + // Whether we have seen comments on the current line + bool comments_seen = false; + while ((c = get_char ()) != -1) { + switch (c) { + case '\t': + col = ((col - 1 + tab_size) / tab_size) * tab_size; + continue; + + case ' ': + case '\f': + case '\v': + case 0xa0: + case 0: + case 0xFEFF: // Ignore BOM anywhere in the file + continue; + +/* This is required for compatibility with .NET + case 0xEF: + if (peek_char () == 0xBB) { + PushPosition (); + get_char (); + if (get_char () == 0xBF) + continue; + PopPosition (); + } + break; +*/ + case '\\': + tokens_seen = true; + return consume_identifier (c); + + case '{': + val = ltb.Create (current_source, ref_line, col); + return Token.OPEN_BRACE; + case '}': + val = ltb.Create (current_source, ref_line, col); + return Token.CLOSE_BRACE; + case '[': + // To block doccomment inside attribute declaration. + if (doc_state == XmlCommentState.Allowed) + doc_state = XmlCommentState.NotAllowed; + + val = ltb.Create (current_source, ref_line, col); + + if (parsing_block == 0 || lambda_arguments_parsing) + return Token.OPEN_BRACKET; + + int next = peek_char (); + switch (next) { + case ']': + case ',': + return Token.OPEN_BRACKET; + + case ' ': + case '\f': + case '\v': + case '\r': + case '\n': + case UnicodeLS: + case UnicodePS: + case '/': + next = peek_token (); + if (next == Token.COMMA || next == Token.CLOSE_BRACKET) + return Token.OPEN_BRACKET; + + return Token.OPEN_BRACKET_EXPR; + default: + return Token.OPEN_BRACKET_EXPR; + } + case ']': + ltb.CreateOptional (current_source, ref_line, col, ref val); + return Token.CLOSE_BRACKET; + case '(': + val = ltb.Create (current_source, ref_line, col); + // + // An expression versions of parens can appear in block context only + // + if (parsing_block != 0 && !lambda_arguments_parsing) { + + // + // Optmize most common case where we know that parens + // is not special + // + switch (current_token) { + case Token.IDENTIFIER: + case Token.IF: + case Token.FOR: + case Token.FOREACH: + case Token.TYPEOF: + case Token.WHILE: + case Token.SWITCH: + case Token.USING: + case Token.DEFAULT: + case Token.DELEGATE: + case Token.OP_GENERICS_GT: + return Token.OPEN_PARENS; + } + + // Optimize using peek + int xx = peek_char (); + switch (xx) { + case '(': + case '\'': + case '"': + case '0': + case '1': + return Token.OPEN_PARENS; + } + + lambda_arguments_parsing = true; + PushPosition (); + d = TokenizeOpenParens (); + PopPosition (); + lambda_arguments_parsing = false; + return d; + } + + return Token.OPEN_PARENS; + case ')': + ltb.CreateOptional (current_source, ref_line, col, ref val); + return Token.CLOSE_PARENS; + case ',': + ltb.CreateOptional (current_source, ref_line, col, ref val); + return Token.COMMA; + case ';': + ltb.CreateOptional (current_source, ref_line, col, ref val); + return Token.SEMICOLON; + case '~': + val = ltb.Create (current_source, ref_line, col); + return Token.TILDE; + case '?': + val = ltb.Create (current_source, ref_line, col); + return TokenizePossibleNullableType (); + case '<': + val = ltb.Create (current_source, ref_line, col); + if (parsing_generic_less_than++ > 0) + return Token.OP_GENERICS_LT; + + return TokenizeLessThan (); + + case '>': + val = ltb.Create (current_source, ref_line, col); + d = peek_char (); + + if (d == '=') { + get_char (); + return Token.OP_GE; + } + + if (parsing_generic_less_than > 1 || (parsing_generic_less_than == 1 && d != '>')) { + parsing_generic_less_than--; + return Token.OP_GENERICS_GT; + } + + if (d == '>') { + get_char (); + d = peek_char (); + + if (d == '=') { + get_char (); + return Token.OP_SHIFT_RIGHT_ASSIGN; + } + return Token.OP_SHIFT_RIGHT; + } + + return Token.OP_GT; + + case '+': + val = ltb.Create (current_source, ref_line, col); + d = peek_char (); + if (d == '+') { + d = Token.OP_INC; + } else if (d == '=') { + d = Token.OP_ADD_ASSIGN; + } else { + return Token.PLUS; + } + get_char (); + return d; + + case '-': + val = ltb.Create (current_source, ref_line, col); + d = peek_char (); + if (d == '-') { + d = Token.OP_DEC; + } else if (d == '=') + d = Token.OP_SUB_ASSIGN; + else if (d == '>') + d = Token.OP_PTR; + else { + return Token.MINUS; + } + get_char (); + return d; + + case '!': + val = ltb.Create (current_source, ref_line, col); + if (peek_char () == '='){ + get_char (); + return Token.OP_NE; + } + return Token.BANG; + + case '=': + val = ltb.Create (current_source, ref_line, col); + d = peek_char (); + if (d == '=') { + get_char (); + return Token.OP_EQ; + } + if (d == '>') { + get_char (); + return Token.ARROW; + } + + return Token.ASSIGN; + + case '&': + val = ltb.Create (current_source, ref_line, col); + d = peek_char (); + if (d == '&') { + get_char (); + return Token.OP_AND; + } + if (d == '=') { + get_char (); + return Token.OP_AND_ASSIGN; + } + return Token.BITWISE_AND; + + case '|': + val = ltb.Create (current_source, ref_line, col); + d = peek_char (); + if (d == '|') { + get_char (); + return Token.OP_OR; + } + if (d == '=') { + get_char (); + return Token.OP_OR_ASSIGN; + } + return Token.BITWISE_OR; + + case '*': + val = ltb.Create (current_source, ref_line, col); + if (peek_char () == '='){ + get_char (); + return Token.OP_MULT_ASSIGN; + } + return Token.STAR; + + case '/': + d = peek_char (); + if (d == '='){ + val = ltb.Create (current_source, ref_line, col); + get_char (); + return Token.OP_DIV_ASSIGN; + } + // Handle double-slash comments. + if (d == '/') { + get_char (); + if (doc_processing) { + if (peek_char () == '/') { + get_char (); + // Don't allow ////. + if ((d = peek_char ()) != '/') { + if (position_stack.Count == 0) + sbag.PushCommentChar (d); + if (doc_state == XmlCommentState.Allowed) + handle_one_line_xml_comment (); + else if (doc_state == XmlCommentState.NotAllowed) + WarningMisplacedComment (Location - 3); + } + } else { + if (xml_comment_buffer.Length > 0) + doc_state = XmlCommentState.NotAllowed; + } + } else { + bool isDoc = peek_char () == '/'; + if (position_stack.Count == 0) + sbag.StartComment (isDoc ? SpecialsBag.CommentType.Documentation : SpecialsBag.CommentType.Single, startsLine, line, col - 1); + if (isDoc) + get_char (); + } + + d = peek_char (); + int endLine = line, endCol = col; + while ((d = get_char ()) != -1 && (d != '\n') && d != '\r' && d != UnicodePS && d != UnicodeLS) { + if (position_stack.Count == 0) + sbag.PushCommentChar (d); + endLine = line; + endCol = col; + } + if (position_stack.Count == 0) + sbag.EndComment (endLine, endCol + 1); + any_token_seen |= tokens_seen; + tokens_seen = false; + comments_seen = false; + continue; + } else if (d == '*'){ + if (position_stack.Count == 0) { + sbag.StartComment (SpecialsBag.CommentType.Multi, startsLine, line, col); + recordNewLine = false; + } + get_char (); + bool docAppend = false; + if (doc_processing && peek_char () == '*') { + int ch = get_char (); + // But when it is /**/, just do nothing. + if (peek_char () == '/') { + ch = get_char (); + if (position_stack.Count == 0) { + recordNewLine = true; + sbag.EndComment (line, col + 1); + } + continue; + } else { + if (position_stack.Count == 0) + sbag.PushCommentChar (ch); + } + if (doc_state == XmlCommentState.Allowed) + docAppend = true; + else if (doc_state == XmlCommentState.NotAllowed) { + WarningMisplacedComment (Location - 2); + } + } + + int current_comment_start = 0; + if (docAppend) { + current_comment_start = xml_comment_buffer.Length; + xml_comment_buffer.Append (Environment.NewLine); + } + + while ((d = get_char ()) != -1){ + if (d == '*' && peek_char () == '/'){ + get_char (); + if (position_stack.Count == 0) { + recordNewLine = true; + sbag.EndComment (line, col + 1); + } + comments_seen = true; + break; + } else { + if (position_stack.Count == 0) + sbag.PushCommentChar (d); + } + if (docAppend) + xml_comment_buffer.Append ((char) d); + + if (d == '\n' || d == UnicodeLS || d == UnicodePS){ + any_token_seen |= tokens_seen; + tokens_seen = false; + // + // Reset 'comments_seen' just to be consistent. + // It doesn't matter either way, here. + // + comments_seen = false; + } + } + + if (!comments_seen) + Report.Error (1035, Location, "End-of-file found, '*/' expected"); + + if (docAppend) + update_formatted_doc_comment (current_comment_start); + continue; + } + val = ltb.Create (current_source, ref_line, col); + return Token.DIV; + + case '%': + val = ltb.Create (current_source, ref_line, col); + if (peek_char () == '='){ + get_char (); + return Token.OP_MOD_ASSIGN; + } + return Token.PERCENT; + + case '^': + val = ltb.Create (current_source, ref_line, col); + if (peek_char () == '='){ + get_char (); + return Token.OP_XOR_ASSIGN; + } + return Token.CARRET; + + case ':': + val = ltb.Create (current_source, ref_line, col); + if (peek_char () == ':') { + get_char (); + return Token.DOUBLE_COLON; + } + return Token.COLON; + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + tokens_seen = true; + return is_number (c, false); + + case '\n': // white space + case UnicodeLS: + case UnicodePS: + any_token_seen |= tokens_seen; + tokens_seen = false; + comments_seen = false; + continue; + + case '.': + tokens_seen = true; + d = peek_char (); + if (d >= '0' && d <= '9') + return is_number (c, true); + + ltb.CreateOptional (current_source, ref_line, col, ref val); + return Token.DOT; + + case '#': + if (tokens_seen || comments_seen) { + Eror_WrongPreprocessorLocation(); + return Token.ERROR; + } + + if (ParsePreprocessingDirective(true)) + continue; + sbag.StartComment(SpecialsBag.CommentType.InactiveCode, false, line, 1); + recordNewLine = false; + bool directive_expected = false; + while ((c = get_char ()) != -1) { + if (col == 1) { + directive_expected = true; + } else if (!directive_expected) { + // TODO: Implement comment support for disabled code and uncomment this code +// if (c == '#') { +// Eror_WrongPreprocessorLocation (); +// return Token.ERROR; +// } + if (c != '#') + sbag.PushCommentChar (c); + continue; + } + + if (c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '\v' || c == UnicodeLS || c == UnicodePS) { + sbag.PushCommentChar (c); + continue; + } + + if (c == '#') { + var oldNL = recordNewLine; + recordNewLine = true; + var continueNormalLexing = ParsePreprocessingDirective(false); + recordNewLine = oldNL; + if (continueNormalLexing) + break; + sbag.StartComment(SpecialsBag.CommentType.InactiveCode, false, line, 1); + } + sbag.PushCommentChar (c); + directive_expected = false; + } + recordNewLine = true; + sbag.EndComment (line, col); + if (c != -1) { + tokens_seen = false; + continue; + } + + return Token.EOF; + + case '"': + return consume_string (false); + + case '\'': + return TokenizeBackslash (); + + case '@': + c = get_char (); + if (c == '"') { + tokens_seen = true; + return consume_string (true); + } + + if (is_identifier_start_character (c)){ + return consume_identifier (c, true); + } + + Report.Error (1646, Location, "Keyword, identifier, or string expected after verbatim specifier: @"); + return Token.ERROR; + + case EvalStatementParserCharacter: + return Token.EVAL_STATEMENT_PARSER; + case EvalCompilationUnitParserCharacter: + return Token.EVAL_COMPILATION_UNIT_PARSER; + case EvalUsingDeclarationsParserCharacter: + return Token.EVAL_USING_DECLARATIONS_UNIT_PARSER; + case DocumentationXref: + return Token.DOC_SEE; + } + + if (is_identifier_start_character (c)) { + tokens_seen = true; + return consume_identifier (c); + } + + if (char.IsWhiteSpace ((char) c)) + continue; + + Report.Error (1056, Location, "Unexpected character `{0}'", ((char) c).ToString ()); + } + + if (CompleteOnEOF){ + if (generated) + return Token.COMPLETE_COMPLETION; + + generated = true; + return Token.GENERATE_COMPLETION; + } + + + return Token.EOF; + } + + int TokenizeBackslash () + { +#if FULL_AST + int read_start = reader.Position; +#endif + Location start_location = Location; + int c = get_char (); + tokens_seen = true; + if (c == '\'') { + val = new CharLiteral (context.BuiltinTypes, (char) c, start_location); + Report.Error (1011, start_location, "Empty character literal"); + return Token.LITERAL; + } + + if (c == '\n' || c == UnicodeLS || c == UnicodePS) { + Report.Error (1010, start_location, "Newline in constant"); + return Token.ERROR; + } + + int d; + c = escape (c, out d); + if (c == -1) + return Token.ERROR; + if (d != 0) + throw new NotImplementedException (); + + ILiteralConstant res = new CharLiteral (context.BuiltinTypes, (char) c, start_location); + val = res; + c = get_char (); + + if (c != '\'') { + Report.Error (1012, start_location, "Too many characters in character literal"); + + // Try to recover, read until newline or next "'" + while ((c = get_char ()) != -1) { + if (c == '\n' || c == '\'' || c == UnicodeLS || c == UnicodePS) + break; + } + } + +#if FULL_AST + res.ParsedValue = reader.ReadChars (read_start - 1, reader.Position); +#endif + + return Token.LITERAL; + } + + int TokenizeLessThan () + { + int d; + + // Save current position and parse next token. + PushPosition (); + int generic_dimension = 0; + if (parse_less_than (ref generic_dimension)) { + if (parsing_generic_declaration && (parsing_generic_declaration_doc || token () != Token.DOT)) { + d = Token.OP_GENERICS_LT_DECL; + } else { + if (generic_dimension > 0) { + val = generic_dimension; + DiscardPosition (); + return Token.GENERIC_DIMENSION; + } + + d = Token.OP_GENERICS_LT; + } + PopPosition (); + return d; + } + + PopPosition (); + parsing_generic_less_than = 0; + + d = peek_char (); + if (d == '<') { + get_char (); + d = peek_char (); + + if (d == '=') { + get_char (); + return Token.OP_SHIFT_LEFT_ASSIGN; + } + return Token.OP_SHIFT_LEFT; + } + + if (d == '=') { + get_char (); + return Token.OP_LE; + } + return Token.OP_LT; + } + + // + // Handles one line xml comment + // + private void handle_one_line_xml_comment () + { + int c; + while ((c = peek_char ()) == ' ') { + if (position_stack.Count == 0) + sbag.PushCommentChar (c); + get_char (); // skip heading whitespaces. + } + while ((c = peek_char ()) != -1 && c != '\n' && c != '\r') { + if (position_stack.Count == 0) + sbag.PushCommentChar (c); + xml_comment_buffer.Append ((char) get_char ()); + } + if (c == '\r' || c == '\n') + xml_comment_buffer.Append (Environment.NewLine); + } + + // + // Remove heading "*" in Javadoc-like xml documentation. + // + private void update_formatted_doc_comment (int current_comment_start) + { + int length = xml_comment_buffer.Length - current_comment_start; + string [] lines = xml_comment_buffer.ToString ( + current_comment_start, + length).Replace ("\r", "").Split ('\n'); + + // The first line starts with /**, thus it is not target + // for the format check. + for (int i = 1; i < lines.Length; i++) { + string s = lines [i]; + int idx = s.IndexOf ('*'); + string head = null; + if (idx < 0) { + if (i < lines.Length - 1) + return; + head = s; + } else + head = s.Substring (0, idx); + foreach (char c in head) + if (c != ' ') + return; + lines [i] = s.Substring (idx + 1); + } + xml_comment_buffer.Remove (current_comment_start, length); + xml_comment_buffer.Insert (current_comment_start, String.Join (Environment.NewLine, lines)); + } + + // + // Checks if there was incorrect doc comments and raise + // warnings. + // + public void check_incorrect_doc_comment () + { + if (xml_comment_buffer.Length > 0) + WarningMisplacedComment (Location); + } + + // + // Consumes the saved xml comment lines (if any) + // as for current target member or type. + // + public string consume_doc_comment () + { + if (xml_comment_buffer.Length > 0) { + string ret = xml_comment_buffer.ToString (); + reset_doc_comment (); + return ret; + } + return null; + } + + void reset_doc_comment () + { + xml_comment_buffer.Length = 0; + } + + public void cleanup () + { + if (ifstack != null && ifstack.Count >= 1) { + int state = ifstack.Pop (); + if ((state & REGION) != 0) + Report.Error (1038, Location, "#endregion directive expected"); + else + Report.Error (1027, Location, "Expected `#endif' directive"); + } + } + } + + // + // Indicates whether it accepts XML documentation or not. + // + public enum XmlCommentState { + // comment is allowed in this state. + Allowed, + // comment is not allowed in this state. + NotAllowed, + // once comments appeared when it is NotAllowed, then the + // state is changed to it, until the state is changed to + // .Allowed. + Error + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/decl.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/decl.cs new file mode 100644 index 000000000..56d423ad2 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/decl.cs @@ -0,0 +1,1297 @@ +// +// decl.cs: Declaration base class for structs, classes, enums and interfaces. +// +// Author: Miguel de Icaza (miguel@gnu.org) +// Marek Safar (marek.safar@seznam.cz) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2001 Ximian, Inc (http://www.ximian.com) +// Copyright 2004-2008 Novell, Inc +// Copyright 2011 Xamarin Inc +// +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; +using Mono.CompilerServices.SymbolWriter; + +#if NET_2_1 +using XmlElement = System.Object; +#else +using System.Xml; +#endif + +#if STATIC +using IKVM.Reflection; +using IKVM.Reflection.Emit; +#else +using System.Reflection; +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp { + + // + // Better name would be DottenName + // + [DebuggerDisplay ("{GetSignatureForError()}")] + public class MemberName + { + public static readonly MemberName Null = new MemberName (""); + + public readonly string Name; + public TypeParameters TypeParameters; + public readonly FullNamedExpression ExplicitInterface; + public readonly Location Location; + + public readonly MemberName Left; + + public MemberName (string name) + : this (name, Location.Null) + { } + + public MemberName (string name, Location loc) + : this (null, name, loc) + { } + + public MemberName (string name, TypeParameters tparams, Location loc) + { + this.Name = name; + this.Location = loc; + + this.TypeParameters = tparams; + } + + public MemberName (string name, TypeParameters tparams, FullNamedExpression explicitInterface, Location loc) + : this (name, tparams, loc) + { + this.ExplicitInterface = explicitInterface; + } + + public MemberName (MemberName left, string name, Location loc) + { + this.Name = name; + this.Location = loc; + this.Left = left; + } + + public MemberName (MemberName left, string name, FullNamedExpression explicitInterface, Location loc) + : this (left, name, loc) + { + this.ExplicitInterface = explicitInterface; + } + + public MemberName (MemberName left, MemberName right) + { + this.Name = right.Name; + this.Location = right.Location; + this.TypeParameters = right.TypeParameters; + this.Left = left; + } + + public int Arity { + get { + return TypeParameters == null ? 0 : TypeParameters.Count; + } + } + + public bool IsGeneric { + get { + return TypeParameters != null; + } + } + + public string Basename { + get { + if (TypeParameters != null) + return MakeName (Name, TypeParameters); + return Name; + } + } + + public void CreateMetadataName (StringBuilder sb) + { + if (Left != null) + Left.CreateMetadataName (sb); + + if (sb.Length != 0) { + sb.Append ("."); + } + + sb.Append (Basename); + } + + public string GetSignatureForDocumentation () + { + var s = Basename; + + if (ExplicitInterface != null) + s = ExplicitInterface.GetSignatureForError () + "." + s; + + if (Left == null) + return s; + + return Left.GetSignatureForDocumentation () + "." + s; + } + + public string GetSignatureForError () + { + string s = TypeParameters == null ? null : "<" + TypeParameters.GetSignatureForError () + ">"; + s = Name + s; + + if (ExplicitInterface != null) + s = ExplicitInterface.GetSignatureForError () + "." + s; + + if (Left == null) + return s; + + return Left.GetSignatureForError () + "." + s; + } + + public override bool Equals (object other) + { + return Equals (other as MemberName); + } + + public bool Equals (MemberName other) + { + if (this == other) + return true; + if (other == null || Name != other.Name) + return false; + + if ((TypeParameters != null) && + (other.TypeParameters == null || TypeParameters.Count != other.TypeParameters.Count)) + return false; + + if ((TypeParameters == null) && (other.TypeParameters != null)) + return false; + + if (Left == null) + return other.Left == null; + + return Left.Equals (other.Left); + } + + public override int GetHashCode () + { + int hash = Name.GetHashCode (); + for (MemberName n = Left; n != null; n = n.Left) + hash ^= n.Name.GetHashCode (); + + if (TypeParameters != null) + hash ^= TypeParameters.Count << 5; + + return hash & 0x7FFFFFFF; + } + + public static string MakeName (string name, TypeParameters args) + { + if (args == null) + return name; + + return name + "`" + args.Count; + } + + public static string MakeName (string name, int count) + { + return name + "`" + count; + } + } + + public class SimpleMemberName + { + public string Value; + public Location Location; + + public SimpleMemberName (string name, Location loc) + { + this.Value = name; + this.Location = loc; + } + } + + /// + /// Base representation for members. This is used to keep track + /// of Name, Location and Modifier flags, and handling Attributes. + /// + [System.Diagnostics.DebuggerDisplay ("{GetSignatureForError()}")] + public abstract class MemberCore : Attributable, IMemberContext, IMemberDefinition + { + string IMemberDefinition.Name { + get { + return member_name.Name; + } + } + + // Is not readonly because of IndexerName attribute + private MemberName member_name; + public MemberName MemberName { + get { return member_name; } + } + + /// + /// Modifier flags that the user specified in the source code + /// + private Modifiers mod_flags; + public Modifiers ModFlags { + set { + mod_flags = value; + if ((value & Modifiers.COMPILER_GENERATED) != 0) + caching_flags = Flags.IsUsed | Flags.IsAssigned; + } + get { + return mod_flags; + } + } + + public virtual ModuleContainer Module { + get { + return Parent.Module; + } + } + + public /*readonly*/ TypeContainer Parent; + + /// + /// Location where this declaration happens + /// + public Location Location { + get { return member_name.Location; } + } + + /// + /// XML documentation comment + /// + protected string comment; + + /// + /// Represents header string for documentation comment + /// for each member types. + /// + public abstract string DocCommentHeader { get; } + + [Flags] + public enum Flags { + Obsolete_Undetected = 1, // Obsolete attribute has not been detected yet + Obsolete = 1 << 1, // Type has obsolete attribute + ClsCompliance_Undetected = 1 << 2, // CLS Compliance has not been detected yet + ClsCompliant = 1 << 3, // Type is CLS Compliant + CloseTypeCreated = 1 << 4, // Tracks whether we have Closed the type + HasCompliantAttribute_Undetected = 1 << 5, // Presence of CLSCompliantAttribute has not been detected + HasClsCompliantAttribute = 1 << 6, // Type has CLSCompliantAttribute + ClsCompliantAttributeFalse = 1 << 7, // Member has CLSCompliant(false) + Excluded_Undetected = 1 << 8, // Conditional attribute has not been detected yet + Excluded = 1 << 9, // Method is conditional + MethodOverloadsExist = 1 << 10, // Test for duplication must be performed + IsUsed = 1 << 11, + IsAssigned = 1 << 12, // Field is assigned + HasExplicitLayout = 1 << 13, + PartialDefinitionExists = 1 << 14, // Set when corresponding partial method definition exists + HasStructLayout = 1 << 15, // Has StructLayoutAttribute + HasInstanceConstructor = 1 << 16, + HasUserOperators = 1 << 17, + CanBeReused = 1 << 18, + InterfacesExpanded = 1 << 19 + } + + /// + /// MemberCore flags at first detected then cached + /// + internal Flags caching_flags; + + protected MemberCore (TypeContainer parent, MemberName name, Attributes attrs) + { + this.Parent = parent; + member_name = name; + caching_flags = Flags.Obsolete_Undetected | Flags.ClsCompliance_Undetected | Flags.HasCompliantAttribute_Undetected | Flags.Excluded_Undetected; + AddAttributes (attrs, this); + } + + protected virtual void SetMemberName (MemberName new_name) + { + member_name = new_name; + } + + public virtual void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + + protected bool CheckAbstractAndExtern (bool has_block) + { + if (Parent.PartialContainer.Kind == MemberKind.Interface) + return true; + + if (has_block) { + if ((ModFlags & Modifiers.EXTERN) != 0) { + Report.Error (179, Location, "`{0}' cannot declare a body because it is marked extern", + GetSignatureForError ()); + return false; + } + + if ((ModFlags & Modifiers.ABSTRACT) != 0) { + Report.Error (500, Location, "`{0}' cannot declare a body because it is marked abstract", + GetSignatureForError ()); + return false; + } + } else { + if ((ModFlags & (Modifiers.ABSTRACT | Modifiers.EXTERN | Modifiers.PARTIAL)) == 0 && !(Parent is Delegate)) { + if (Compiler.Settings.Version >= LanguageVersion.V_3) { + Property.PropertyMethod pm = this as Property.PropertyMethod; + if (pm is Indexer.GetIndexerMethod || pm is Indexer.SetIndexerMethod) + pm = null; + + if (pm != null && pm.Property.AccessorSecond == null) { + Report.Error (840, Location, + "`{0}' must have a body because it is not marked abstract or extern. The property can be automatically implemented when you define both accessors", + GetSignatureForError ()); + return false; + } + } + + Report.Error (501, Location, "`{0}' must have a body because it is not marked abstract, extern, or partial", + GetSignatureForError ()); + return false; + } + } + + return true; + } + + protected void CheckProtectedModifier () + { + if ((ModFlags & Modifiers.PROTECTED) == 0) + return; + + if (Parent.PartialContainer.Kind == MemberKind.Struct) { + Report.Error (666, Location, "`{0}': Structs cannot contain protected members", + GetSignatureForError ()); + return; + } + + if ((Parent.ModFlags & Modifiers.STATIC) != 0) { + Report.Error (1057, Location, "`{0}': Static classes cannot contain protected members", + GetSignatureForError ()); + return; + } + + if ((Parent.ModFlags & Modifiers.SEALED) != 0 && (ModFlags & Modifiers.OVERRIDE) == 0 && + !(this is Destructor)) { + Report.Warning (628, 4, Location, "`{0}': new protected member declared in sealed class", + GetSignatureForError ()); + return; + } + } + + public abstract bool Define (); + + public virtual string DocComment { + get { + return comment; + } + set { + comment = value; + } + } + + // + // Returns full member name for error message + // + public virtual string GetSignatureForError () + { + var parent = Parent.GetSignatureForError (); + if (parent == null) + return member_name.GetSignatureForError (); + + return parent + "." + member_name.GetSignatureForError (); + } + + /// + /// Base Emit method. This is also entry point for CLS-Compliant verification. + /// + public virtual void Emit () + { + if (!Compiler.Settings.VerifyClsCompliance) + return; + + VerifyClsCompliance (); + } + + public bool IsAvailableForReuse { + get { + return (caching_flags & Flags.CanBeReused) != 0; + } + set { + caching_flags = value ? (caching_flags | Flags.CanBeReused) : (caching_flags & ~Flags.CanBeReused); + } + } + + public bool IsCompilerGenerated { + get { + if ((mod_flags & Modifiers.COMPILER_GENERATED) != 0) + return true; + + return Parent != null && Parent.IsCompilerGenerated; + } + } + + public bool IsImported { + get { + return false; + } + } + + public virtual bool IsUsed { + get { + return (caching_flags & Flags.IsUsed) != 0; + } + } + + protected Report Report { + get { + return Compiler.Report; + } + } + + public void SetIsUsed () + { + caching_flags |= Flags.IsUsed; + } + + public void SetIsAssigned () + { + caching_flags |= Flags.IsAssigned; + } + + public virtual void SetConstraints (List constraints_list) + { + var tparams = member_name.TypeParameters; + if (tparams == null) { + Report.Error (80, Location, "Constraints are not allowed on non-generic declarations"); + return; + } + + foreach (var c in constraints_list) { + var tp = tparams.Find (c.TypeParameter.Value); + if (tp == null) { + Report.Error (699, c.Location, "`{0}': A constraint references nonexistent type parameter `{1}'", + GetSignatureForError (), c.TypeParameter.Value); + continue; + } + + tp.Constraints = c; + } + } + + /// + /// Returns instance of ObsoleteAttribute for this MemberCore + /// + public virtual ObsoleteAttribute GetAttributeObsolete () + { + if ((caching_flags & (Flags.Obsolete_Undetected | Flags.Obsolete)) == 0) + return null; + + caching_flags &= ~Flags.Obsolete_Undetected; + + if (OptAttributes == null) + return null; + + Attribute obsolete_attr = OptAttributes.Search (Module.PredefinedAttributes.Obsolete); + if (obsolete_attr == null) + return null; + + caching_flags |= Flags.Obsolete; + + ObsoleteAttribute obsolete = obsolete_attr.GetObsoleteAttribute (); + if (obsolete == null) + return null; + + return obsolete; + } + + /// + /// Checks for ObsoleteAttribute presence. It's used for testing of all non-types elements + /// + public virtual void CheckObsoleteness (Location loc) + { + ObsoleteAttribute oa = GetAttributeObsolete (); + if (oa != null) + AttributeTester.Report_ObsoleteMessage (oa, GetSignatureForError (), loc, Report); + } + + // + // Checks whether the type P is as accessible as this member + // + public bool IsAccessibleAs (TypeSpec p) + { + // + // if M is private, its accessibility is the same as this declspace. + // we already know that P is accessible to T before this method, so we + // may return true. + // + if ((mod_flags & Modifiers.PRIVATE) != 0) + return true; + + while (TypeManager.HasElementType (p)) + p = TypeManager.GetElementType (p); + + if (p.IsGenericParameter) + return true; + + for (TypeSpec p_parent; p != null; p = p_parent) { + p_parent = p.DeclaringType; + + if (p.IsGeneric) { + foreach (TypeSpec t in p.TypeArguments) { + if (!IsAccessibleAs (t)) + return false; + } + } + + var pAccess = p.Modifiers & Modifiers.AccessibilityMask; + if (pAccess == Modifiers.PUBLIC) + continue; + + bool same_access_restrictions = false; + for (MemberCore mc = this; !same_access_restrictions && mc != null && mc.Parent != null; mc = mc.Parent) { + var al = mc.ModFlags & Modifiers.AccessibilityMask; + switch (pAccess) { + case Modifiers.INTERNAL: + if (al == Modifiers.PRIVATE || al == Modifiers.INTERNAL) + same_access_restrictions = p.MemberDefinition.IsInternalAsPublic (mc.Module.DeclaringAssembly); + + break; + + case Modifiers.PROTECTED: + if (al == Modifiers.PROTECTED) { + same_access_restrictions = mc.Parent.PartialContainer.IsBaseTypeDefinition (p_parent); + break; + } + + if (al == Modifiers.PRIVATE) { + // + // When type is private and any of its parents derives from + // protected type then the type is accessible + // + while (mc.Parent != null && mc.Parent.PartialContainer != null) { + if (mc.Parent.PartialContainer.IsBaseTypeDefinition (p_parent)) + same_access_restrictions = true; + mc = mc.Parent; + } + } + + break; + + case Modifiers.PROTECTED | Modifiers.INTERNAL: + if (al == Modifiers.INTERNAL) + same_access_restrictions = p.MemberDefinition.IsInternalAsPublic (mc.Module.DeclaringAssembly); + else if (al == (Modifiers.PROTECTED | Modifiers.INTERNAL)) + same_access_restrictions = mc.Parent.PartialContainer.IsBaseTypeDefinition (p_parent) && p.MemberDefinition.IsInternalAsPublic (mc.Module.DeclaringAssembly); + else + goto case Modifiers.PROTECTED; + + break; + + case Modifiers.PRIVATE: + // + // Both are private and share same parent + // + if (al == Modifiers.PRIVATE) { + var decl = mc.Parent; + do { + same_access_restrictions = decl.CurrentType.MemberDefinition == p_parent.MemberDefinition; + } while (!same_access_restrictions && !decl.PartialContainer.IsTopLevel && (decl = decl.Parent) != null); + } + + break; + + default: + throw new InternalErrorException (al.ToString ()); + } + } + + if (!same_access_restrictions) + return false; + } + + return true; + } + + /// + /// Analyze whether CLS-Compliant verification must be execute for this MemberCore. + /// + public override bool IsClsComplianceRequired () + { + if ((caching_flags & Flags.ClsCompliance_Undetected) == 0) + return (caching_flags & Flags.ClsCompliant) != 0; + + caching_flags &= ~Flags.ClsCompliance_Undetected; + + if (HasClsCompliantAttribute) { + if ((caching_flags & Flags.ClsCompliantAttributeFalse) != 0) + return false; + + caching_flags |= Flags.ClsCompliant; + return true; + } + + if (Parent.IsClsComplianceRequired ()) { + caching_flags |= Flags.ClsCompliant; + return true; + } + + return false; + } + + public virtual string[] ConditionalConditions () + { + return null; + } + + /// + /// Returns true when MemberCore is exposed from assembly. + /// + public bool IsExposedFromAssembly () + { + if ((ModFlags & (Modifiers.PUBLIC | Modifiers.PROTECTED)) == 0) + return this is NamespaceContainer; + + var parentContainer = Parent.PartialContainer; + while (parentContainer != null) { + if ((parentContainer.ModFlags & (Modifiers.PUBLIC | Modifiers.PROTECTED)) == 0) + return false; + + parentContainer = parentContainer.Parent.PartialContainer; + } + + return true; + } + + // + // Does extension methods look up to find a method which matches name and extensionType. + // Search starts from this namespace and continues hierarchically up to top level. + // + public ExtensionMethodCandidates LookupExtensionMethod (TypeSpec extensionType, string name, int arity) + { + var m = Parent; + do { + var ns = m as NamespaceContainer; + if (ns != null) + return ns.LookupExtensionMethod (this, extensionType, name, arity, 0); + + m = m.Parent; + } while (m != null); + + return null; + } + + public virtual FullNamedExpression LookupNamespaceAlias (string name) + { + return Parent.LookupNamespaceAlias (name); + } + + public virtual FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc) + { + return Parent.LookupNamespaceOrType (name, arity, mode, loc); + } + + /// + /// Goes through class hierarchy and gets value of first found CLSCompliantAttribute. + /// If no is attribute exists then assembly CLSCompliantAttribute is returned. + /// + public bool? CLSAttributeValue { + get { + if ((caching_flags & Flags.HasCompliantAttribute_Undetected) == 0) { + if ((caching_flags & Flags.HasClsCompliantAttribute) == 0) + return null; + + return (caching_flags & Flags.ClsCompliantAttributeFalse) == 0; + } + + caching_flags &= ~Flags.HasCompliantAttribute_Undetected; + + if (OptAttributes != null) { + Attribute cls_attribute = OptAttributes.Search (Module.PredefinedAttributes.CLSCompliant); + if (cls_attribute != null) { + caching_flags |= Flags.HasClsCompliantAttribute; + if (cls_attribute.GetClsCompliantAttributeValue ()) + return true; + + caching_flags |= Flags.ClsCompliantAttributeFalse; + return false; + } + } + + return null; + } + } + + /// + /// Returns true if MemberCore is explicitly marked with CLSCompliantAttribute + /// + protected bool HasClsCompliantAttribute { + get { + return CLSAttributeValue.HasValue; + } + } + + /// + /// Returns true when a member supports multiple overloads (methods, indexers, etc) + /// + public virtual bool EnableOverloadChecks (MemberCore overload) + { + return false; + } + + /// + /// The main virtual method for CLS-Compliant verifications. + /// The method returns true if member is CLS-Compliant and false if member is not + /// CLS-Compliant which means that CLS-Compliant tests are not necessary. A descendants override it + /// and add their extra verifications. + /// + protected virtual bool VerifyClsCompliance () + { + if (HasClsCompliantAttribute) { + if (!Module.DeclaringAssembly.HasCLSCompliantAttribute) { + Attribute a = OptAttributes.Search (Module.PredefinedAttributes.CLSCompliant); + if ((caching_flags & Flags.ClsCompliantAttributeFalse) != 0) { + Report.Warning (3021, 2, a.Location, + "`{0}' does not need a CLSCompliant attribute because the assembly is not marked as CLS-compliant", + GetSignatureForError ()); + } else { + Report.Warning (3014, 1, a.Location, + "`{0}' cannot be marked as CLS-compliant because the assembly is not marked as CLS-compliant", + GetSignatureForError ()); + } + return false; + } + + if (!IsExposedFromAssembly ()) { + Attribute a = OptAttributes.Search (Module.PredefinedAttributes.CLSCompliant); + Report.Warning (3019, 2, a.Location, "CLS compliance checking will not be performed on `{0}' because it is not visible from outside this assembly", GetSignatureForError ()); + return false; + } + + if ((caching_flags & Flags.ClsCompliantAttributeFalse) != 0) { + if (Parent is Interface && Parent.IsClsComplianceRequired ()) { + Report.Warning (3010, 1, Location, "`{0}': CLS-compliant interfaces must have only CLS-compliant members", GetSignatureForError ()); + } else if (Parent.Kind == MemberKind.Class && (ModFlags & Modifiers.ABSTRACT) != 0 && Parent.IsClsComplianceRequired ()) { + Report.Warning (3011, 1, Location, "`{0}': only CLS-compliant members can be abstract", GetSignatureForError ()); + } + + return false; + } + + if (Parent.Kind != MemberKind.Namespace && Parent.Kind != 0 && !Parent.IsClsComplianceRequired ()) { + Attribute a = OptAttributes.Search (Module.PredefinedAttributes.CLSCompliant); + Report.Warning (3018, 1, a.Location, "`{0}' cannot be marked as CLS-compliant because it is a member of non CLS-compliant type `{1}'", + GetSignatureForError (), Parent.GetSignatureForError ()); + return false; + } + } else { + if (!IsExposedFromAssembly ()) + return false; + + if (!Parent.IsClsComplianceRequired ()) + return false; + } + + if (member_name.Name [0] == '_') { + Warning_IdentifierNotCompliant (); + } + + if (member_name.TypeParameters != null) + member_name.TypeParameters.VerifyClsCompliance (); + + return true; + } + + protected void Warning_IdentifierNotCompliant () + { + Report.Warning (3008, 1, MemberName.Location, "Identifier `{0}' is not CLS-compliant", GetSignatureForError ()); + } + + public virtual string GetCallerMemberName () + { + return MemberName.Name; + } + + // + // Returns a string that represents the signature for this + // member which should be used in XML documentation. + // + public abstract string GetSignatureForDocumentation (); + + public virtual void GetCompletionStartingWith (string prefix, List results) + { + Parent.GetCompletionStartingWith (prefix, results); + } + + // + // Generates xml doc comments (if any), and if required, + // handle warning report. + // + internal virtual void GenerateDocComment (DocumentationBuilder builder) + { + if (DocComment == null) { + if (IsExposedFromAssembly ()) { + Constructor c = this as Constructor; + if (c == null || !c.IsDefault ()) + Report.Warning (1591, 4, Location, + "Missing XML comment for publicly visible type or member `{0}'", GetSignatureForError ()); + } + + return; + } + + try { + builder.GenerateDocumentationForMember (this); + } catch (Exception e) { + throw new InternalErrorException (this, e); + } + } + + public virtual void WriteDebugSymbol (MonoSymbolFile file) + { + } + + #region IMemberContext Members + + public virtual CompilerContext Compiler { + get { + return Module.Compiler; + } + } + + public virtual TypeSpec CurrentType { + get { return Parent.CurrentType; } + } + + public MemberCore CurrentMemberDefinition { + get { return this; } + } + + public virtual TypeParameters CurrentTypeParameters { + get { return null; } + } + + public bool IsObsolete { + get { + if (GetAttributeObsolete () != null) + return true; + + return Parent != null && Parent.IsObsolete; + } + } + + public bool IsUnsafe { + get { + if ((ModFlags & Modifiers.UNSAFE) != 0) + return true; + + return Parent != null && Parent.IsUnsafe; + } + } + + public bool IsStatic { + get { + return (ModFlags & Modifiers.STATIC) != 0; + } + } + + #endregion + } + + // + // Base member specification. A member specification contains + // member details which can alter in the context (e.g. generic instances) + // + public abstract class MemberSpec + { + [Flags] + public enum StateFlags + { + Obsolete_Undetected = 1, // Obsolete attribute has not been detected yet + Obsolete = 1 << 1, // Member has obsolete attribute + CLSCompliant_Undetected = 1 << 2, // CLSCompliant attribute has not been detected yet + CLSCompliant = 1 << 3, // Member is CLS Compliant + MissingDependency_Undetected = 1 << 4, + MissingDependency = 1 << 5, + HasDynamicElement = 1 << 6, + ConstraintsChecked = 1 << 7, + + IsAccessor = 1 << 9, // Method is an accessor + IsGeneric = 1 << 10, // Member contains type arguments + + PendingMetaInflate = 1 << 12, + PendingMakeMethod = 1 << 13, + PendingMemberCacheMembers = 1 << 14, + PendingBaseTypeInflate = 1 << 15, + InterfacesExpanded = 1 << 16, + IsNotCSharpCompatible = 1 << 17, + SpecialRuntimeType = 1 << 18, + InflatedExpressionType = 1 << 19, + InflatedNullableType = 1 << 20, + GenericIterateInterface = 1 << 21, + GenericTask = 1 << 22, + InterfacesImported = 1 << 23, + } + + // + // Some flags can be copied directly from other member + // + protected const StateFlags SharedStateFlags = + StateFlags.CLSCompliant | StateFlags.CLSCompliant_Undetected | + StateFlags.Obsolete | StateFlags.Obsolete_Undetected | + StateFlags.MissingDependency | StateFlags.MissingDependency_Undetected | + StateFlags.HasDynamicElement; + + protected Modifiers modifiers; + public StateFlags state; + protected IMemberDefinition definition; + public readonly MemberKind Kind; + protected TypeSpec declaringType; + +#if DEBUG + static int counter; + public int ID = counter++; +#endif + + protected MemberSpec (MemberKind kind, TypeSpec declaringType, IMemberDefinition definition, Modifiers modifiers) + { + this.Kind = kind; + this.declaringType = declaringType; + this.definition = definition; + this.modifiers = modifiers; + + if (kind == MemberKind.MissingType) + state = StateFlags.MissingDependency; + else + state = StateFlags.Obsolete_Undetected | StateFlags.CLSCompliant_Undetected | StateFlags.MissingDependency_Undetected; + } + + #region Properties + + public virtual int Arity { + get { + return 0; + } + } + + public TypeSpec DeclaringType { + get { + return declaringType; + } + set { + declaringType = value; + } + } + + public IMemberDefinition MemberDefinition { + get { + return definition; + } + } + + public Modifiers Modifiers { + get { + return modifiers; + } + set { + modifiers = value; + } + } + + public virtual string Name { + get { + return definition.Name; + } + } + + public bool IsAbstract { + get { return (modifiers & Modifiers.ABSTRACT) != 0; } + } + + public bool IsAccessor { + get { + return (state & StateFlags.IsAccessor) != 0; + } + set { + state = value ? state | StateFlags.IsAccessor : state & ~StateFlags.IsAccessor; + } + } + + // + // Return true when this member is a generic in C# terms + // A nested non-generic type of generic type will return false + // + public bool IsGeneric { + get { + return (state & StateFlags.IsGeneric) != 0; + } + set { + state = value ? state | StateFlags.IsGeneric : state & ~StateFlags.IsGeneric; + } + } + + // + // Returns true for imported members which are not compatible with C# language + // + public bool IsNotCSharpCompatible { + get { + return (state & StateFlags.IsNotCSharpCompatible) != 0; + } + set { + state = value ? state | StateFlags.IsNotCSharpCompatible : state & ~StateFlags.IsNotCSharpCompatible; + } + } + + public bool IsPrivate { + get { return (modifiers & Modifiers.PRIVATE) != 0; } + } + + public bool IsPublic { + get { return (modifiers & Modifiers.PUBLIC) != 0; } + } + + public bool IsStatic { + get { + return (modifiers & Modifiers.STATIC) != 0; + } + } + + #endregion + + public virtual ObsoleteAttribute GetAttributeObsolete () + { + if ((state & (StateFlags.Obsolete | StateFlags.Obsolete_Undetected)) == 0) + return null; + + state &= ~StateFlags.Obsolete_Undetected; + + var oa = definition.GetAttributeObsolete (); + if (oa != null) + state |= StateFlags.Obsolete; + + return oa; + } + + // + // Returns a list of missing dependencies of this member. The list + // will contain types only but it can have numerous values for members + // like methods where both return type and all parameters are checked + // + public List GetMissingDependencies () + { + return GetMissingDependencies (this); + } + + public List GetMissingDependencies (MemberSpec caller) + { + if ((state & (StateFlags.MissingDependency | StateFlags.MissingDependency_Undetected)) == 0) + return null; + + state &= ~StateFlags.MissingDependency_Undetected; + + var imported = definition as ImportedDefinition; + List missing; + if (imported != null) { + missing = ResolveMissingDependencies (caller); + } else if (this is ElementTypeSpec) { + missing = ((ElementTypeSpec) this).Element.GetMissingDependencies (caller); + } else { + missing = null; + } + + if (missing != null) { + state |= StateFlags.MissingDependency; + } + + return missing; + } + + public abstract List ResolveMissingDependencies (MemberSpec caller); + + protected virtual bool IsNotCLSCompliant (out bool attrValue) + { + var cls = MemberDefinition.CLSAttributeValue; + attrValue = cls ?? false; + return cls == false; + } + + public virtual string GetSignatureForDocumentation () + { + return DeclaringType.GetSignatureForDocumentation () + "." + Name; + } + + public virtual string GetSignatureForError () + { + var bf = MemberDefinition as Property.BackingField; + string name; + if (bf == null) { + name = Name; + } else { + name = bf.OriginalProperty.MemberName.Name; + } + + return DeclaringType.GetSignatureForError () + "." + name; + } + + public virtual MemberSpec InflateMember (TypeParameterInflator inflator) + { + var inflated = (MemberSpec) MemberwiseClone (); + inflated.declaringType = inflator.TypeInstance; + if (DeclaringType.IsGenericOrParentIsGeneric) + inflated.state |= StateFlags.PendingMetaInflate; +#if DEBUG + inflated.ID += 1000000; +#endif + return inflated; + } + + // + // Is this member accessible from invocation context + // + public bool IsAccessible (IMemberContext ctx) + { + var ma = Modifiers & Modifiers.AccessibilityMask; + if (ma == Modifiers.PUBLIC) + return true; + + var parentType = /* this as TypeSpec ?? */ DeclaringType; + var ctype = ctx.CurrentType; + + if (ma == Modifiers.PRIVATE) { + if (ctype == null || parentType == null) + return false; + // + // It's only accessible to the current class or children + // + if (parentType.MemberDefinition == ctype.MemberDefinition) + return true; + + return TypeManager.IsNestedChildOf (ctype, parentType.MemberDefinition); + } + + if ((ma & Modifiers.INTERNAL) != 0) { + bool b; + var assembly = ctype == null ? ctx.Module.DeclaringAssembly : ctype.MemberDefinition.DeclaringAssembly; + + if (parentType == null) { + b = ((ITypeDefinition) MemberDefinition).IsInternalAsPublic (assembly); + } else { + b = DeclaringType.MemberDefinition.IsInternalAsPublic (assembly); + } + + if (b || ma == Modifiers.INTERNAL) + return b; + } + + // + // Checks whether `ctype' is a subclass or nested child of `parentType'. + // + while (ctype != null) { + if (TypeManager.IsFamilyAccessible (ctype, parentType)) + return true; + + // Handle nested types. + ctype = ctype.DeclaringType; // TODO: Untested ??? + } + + return false; + } + + // + // Returns member CLS compliance based on full member hierarchy + // + public bool IsCLSCompliant () + { + if ((state & StateFlags.CLSCompliant_Undetected) != 0) { + state &= ~StateFlags.CLSCompliant_Undetected; + + bool compliant; + if (IsNotCLSCompliant (out compliant)) + return false; + + if (!compliant) { + if (DeclaringType != null) { + compliant = DeclaringType.IsCLSCompliant (); + } else { + compliant = ((ITypeDefinition) MemberDefinition).DeclaringAssembly.IsCLSCompliant; + } + } + + if (compliant) + state |= StateFlags.CLSCompliant; + } + + return (state & StateFlags.CLSCompliant) != 0; + } + + public bool IsConditionallyExcluded (IMemberContext ctx) + { + if ((Kind & (MemberKind.Class | MemberKind.Method)) == 0) + return false; + + var conditions = MemberDefinition.ConditionalConditions (); + if (conditions == null) + return false; + + var m = ctx.CurrentMemberDefinition; + CompilationSourceFile unit = null; + while (m != null && unit == null) { + unit = m as CompilationSourceFile; + m = m.Parent; + } + + if (unit != null) { + foreach (var condition in conditions) { + if (unit.IsConditionalDefined (condition)) + return false; + } + } + + return true; + } + + public override string ToString () + { + return GetSignatureForError (); + } + } + + // + // Member details which are same between all member + // specifications + // + public interface IMemberDefinition + { + bool? CLSAttributeValue { get; } + string Name { get; } + bool IsImported { get; } + + string[] ConditionalConditions (); + ObsoleteAttribute GetAttributeObsolete (); + void SetIsAssigned (); + void SetIsUsed (); + } + + public interface IMethodDefinition : IMemberDefinition + { + MethodBase Metadata { get; } + } + + public interface IParametersMember : IInterfaceMemberSpec + { + AParametersCollection Parameters { get; } + } + + public interface IInterfaceMemberSpec + { + TypeSpec MemberType { get; } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/delegate.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/delegate.cs new file mode 100644 index 000000000..612d11636 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/delegate.cs @@ -0,0 +1,952 @@ +// +// delegate.cs: Delegate Handler +// +// Authors: +// Ravi Pratap (ravi@ximian.com) +// Miguel de Icaza (miguel@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2001 Ximian, Inc (http://www.ximian.com) +// Copyright 2003-2009 Novell, Inc (http://www.novell.com) +// Copyright 2011 Xamarin Inc +// + +using System; + +#if STATIC +using IKVM.Reflection; +using IKVM.Reflection.Emit; +#else +using System.Reflection; +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp { + + // + // Delegate container implementation + // + public class Delegate : TypeDefinition, IParametersMember + { + public FullNamedExpression ReturnType; + readonly ParametersCompiled parameters; + + Constructor Constructor; + Method InvokeBuilder; + Method BeginInvokeBuilder; + Method EndInvokeBuilder; + + static readonly string[] attribute_targets = new string [] { "type", "return" }; + + public static readonly string InvokeMethodName = "Invoke"; + + Expression instance_expr; + ReturnParameter return_attributes; + + const Modifiers MethodModifiers = Modifiers.PUBLIC | Modifiers.VIRTUAL; + + const Modifiers AllowedModifiers = + Modifiers.NEW | + Modifiers.PUBLIC | + Modifiers.PROTECTED | + Modifiers.INTERNAL | + Modifiers.UNSAFE | + Modifiers.PRIVATE; + + public Delegate (TypeContainer parent, FullNamedExpression type, Modifiers mod_flags, MemberName name, ParametersCompiled param_list, + Attributes attrs) + : base (parent, name, attrs, MemberKind.Delegate) + + { + this.ReturnType = type; + ModFlags = ModifiersExtensions.Check (AllowedModifiers, mod_flags, + IsTopLevel ? Modifiers.INTERNAL : + Modifiers.PRIVATE, name.Location, Report); + parameters = param_list; + spec = new TypeSpec (Kind, null, this, null, ModFlags | Modifiers.SEALED); + } + + #region Properties + public TypeSpec MemberType { + get { + return ReturnType.Type; + } + } + + public AParametersCollection Parameters { + get { + return parameters; + } + } + + public FullNamedExpression TypExpression { + get { + return ReturnType; + } + } + + #endregion + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.Target == AttributeTargets.ReturnValue) { + if (return_attributes == null) + return_attributes = new ReturnParameter (this, InvokeBuilder.MethodBuilder, Location); + + return_attributes.ApplyAttributeBuilder (a, ctor, cdata, pa); + return; + } + + base.ApplyAttributeBuilder (a, ctor, cdata, pa); + } + + public override AttributeTargets AttributeTargets { + get { + return AttributeTargets.Delegate; + } + } + + protected override bool DoDefineMembers () + { + var builtin_types = Compiler.BuiltinTypes; + + var ctor_parameters = ParametersCompiled.CreateFullyResolved ( + new [] { + new Parameter (new TypeExpression (builtin_types.Object, Location), "object", Parameter.Modifier.NONE, null, Location), + new Parameter (new TypeExpression (builtin_types.IntPtr, Location), "method", Parameter.Modifier.NONE, null, Location) + }, + new [] { + builtin_types.Object, + builtin_types.IntPtr + } + ); + + Constructor = new Constructor (this, Constructor.ConstructorName, + Modifiers.PUBLIC, null, ctor_parameters, Location); + Constructor.Define (); + + // + // Here the various methods like Invoke, BeginInvoke etc are defined + // + // First, call the `out of band' special method for + // defining recursively any types we need: + // + var p = parameters; + + if (!p.Resolve (this)) + return false; + + // + // Invoke method + // + + // Check accessibility + foreach (var partype in p.Types) { + if (!IsAccessibleAs (partype)) { + Report.SymbolRelatedToPreviousError (partype); + Report.Error (59, Location, + "Inconsistent accessibility: parameter type `{0}' is less accessible than delegate `{1}'", + partype.GetSignatureForError (), GetSignatureForError ()); + } + } + + var ret_type = ReturnType.ResolveAsType (this); + if (ret_type == null) + return false; + + // + // We don't have to check any others because they are all + // guaranteed to be accessible - they are standard types. + // + if (!IsAccessibleAs (ret_type)) { + Report.SymbolRelatedToPreviousError (ret_type); + Report.Error (58, Location, + "Inconsistent accessibility: return type `" + + ret_type.GetSignatureForError () + "' is less " + + "accessible than delegate `" + GetSignatureForError () + "'"); + return false; + } + + CheckProtectedModifier (); + + if (Compiler.Settings.StdLib && ret_type.IsSpecialRuntimeType) { + Method.Error1599 (Location, ret_type, Report); + return false; + } + + VarianceDecl.CheckTypeVariance (ret_type, Variance.Covariant, this); + + var resolved_rt = new TypeExpression (ret_type, Location); + InvokeBuilder = new Method (this, resolved_rt, MethodModifiers, new MemberName (InvokeMethodName), p, null); + InvokeBuilder.Define (); + + // + // Don't emit async method for compiler generated delegates (e.g. dynamic site containers) + // + if (!IsCompilerGenerated) { + DefineAsyncMethods (resolved_rt); + } + + return true; + } + + void DefineAsyncMethods (TypeExpression returnType) + { + var iasync_result = Module.PredefinedTypes.IAsyncResult; + var async_callback = Module.PredefinedTypes.AsyncCallback; + + // + // It's ok when async types don't exist, the delegate will have Invoke method only + // + if (!iasync_result.Define () || !async_callback.Define ()) + return; + + // + // BeginInvoke + // + ParametersCompiled async_parameters; + if (Parameters.Count == 0) { + async_parameters = ParametersCompiled.EmptyReadOnlyParameters; + } else { + var compiled = new Parameter[Parameters.Count]; + for (int i = 0; i < compiled.Length; ++i) { + var p = parameters[i]; + compiled[i] = new Parameter (new TypeExpression (parameters.Types[i], Location), + p.Name, + p.ModFlags & Parameter.Modifier.RefOutMask, + p.OptAttributes == null ? null : p.OptAttributes.Clone (), Location); + } + + async_parameters = new ParametersCompiled (compiled); + } + + async_parameters = ParametersCompiled.MergeGenerated (Compiler, async_parameters, false, + new Parameter[] { + new Parameter (new TypeExpression (async_callback.TypeSpec, Location), "callback", Parameter.Modifier.NONE, null, Location), + new Parameter (new TypeExpression (Compiler.BuiltinTypes.Object, Location), "object", Parameter.Modifier.NONE, null, Location) + }, + new [] { + async_callback.TypeSpec, + Compiler.BuiltinTypes.Object + } + ); + + BeginInvokeBuilder = new Method (this, + new TypeExpression (iasync_result.TypeSpec, Location), MethodModifiers, + new MemberName ("BeginInvoke"), async_parameters, null); + BeginInvokeBuilder.Define (); + + // + // EndInvoke is a bit more interesting, all the parameters labeled as + // out or ref have to be duplicated here. + // + + // + // Define parameters, and count out/ref parameters + // + ParametersCompiled end_parameters; + int out_params = 0; + + foreach (Parameter p in Parameters.FixedParameters) { + if ((p.ModFlags & Parameter.Modifier.RefOutMask) != 0) + ++out_params; + } + + if (out_params > 0) { + Parameter[] end_params = new Parameter[out_params]; + + int param = 0; + for (int i = 0; i < Parameters.FixedParameters.Length; ++i) { + Parameter p = parameters [i]; + if ((p.ModFlags & Parameter.Modifier.RefOutMask) == 0) + continue; + + end_params [param++] = new Parameter (new TypeExpression (p.Type, Location), + p.Name, + p.ModFlags & Parameter.Modifier.RefOutMask, + p.OptAttributes == null ? null : p.OptAttributes.Clone (), Location); + } + + end_parameters = new ParametersCompiled (end_params); + } else { + end_parameters = ParametersCompiled.EmptyReadOnlyParameters; + } + + end_parameters = ParametersCompiled.MergeGenerated (Compiler, end_parameters, false, + new Parameter ( + new TypeExpression (iasync_result.TypeSpec, Location), + "result", Parameter.Modifier.NONE, null, Location), + iasync_result.TypeSpec); + + // + // Create method, define parameters, register parameters with type system + // + EndInvokeBuilder = new Method (this, returnType, MethodModifiers, new MemberName ("EndInvoke"), end_parameters, null); + EndInvokeBuilder.Define (); + } + + public override void PrepareEmit () + { + if (!Parameters.IsEmpty) { + parameters.ResolveDefaultValues (this); + } + + InvokeBuilder.PrepareEmit (); + if (BeginInvokeBuilder != null) { + BeginInvokeBuilder.PrepareEmit (); + EndInvokeBuilder.PrepareEmit (); + } + } + + public override void Emit () + { + base.Emit (); + + if (ReturnType.Type != null) { + if (ReturnType.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + return_attributes = new ReturnParameter (this, InvokeBuilder.MethodBuilder, Location); + Module.PredefinedAttributes.Dynamic.EmitAttribute (return_attributes.Builder); + } else if (ReturnType.Type.HasDynamicElement) { + return_attributes = new ReturnParameter (this, InvokeBuilder.MethodBuilder, Location); + Module.PredefinedAttributes.Dynamic.EmitAttribute (return_attributes.Builder, ReturnType.Type, Location); + } + + ConstraintChecker.Check (this, ReturnType.Type, ReturnType.Location); + } + + Constructor.ParameterInfo.ApplyAttributes (this, Constructor.ConstructorBuilder); + Constructor.ConstructorBuilder.SetImplementationFlags (MethodImplAttributes.Runtime); + + parameters.CheckConstraints (this); + parameters.ApplyAttributes (this, InvokeBuilder.MethodBuilder); + InvokeBuilder.MethodBuilder.SetImplementationFlags (MethodImplAttributes.Runtime); + + if (BeginInvokeBuilder != null) { + BeginInvokeBuilder.ParameterInfo.ApplyAttributes (this, BeginInvokeBuilder.MethodBuilder); + EndInvokeBuilder.ParameterInfo.ApplyAttributes (this, EndInvokeBuilder.MethodBuilder); + + BeginInvokeBuilder.MethodBuilder.SetImplementationFlags (MethodImplAttributes.Runtime); + EndInvokeBuilder.MethodBuilder.SetImplementationFlags (MethodImplAttributes.Runtime); + } + } + + protected override TypeSpec[] ResolveBaseTypes (out FullNamedExpression base_class) + { + base_type = Compiler.BuiltinTypes.MulticastDelegate; + base_class = null; + return null; + } + + protected override TypeAttributes TypeAttr { + get { + return base.TypeAttr | TypeAttributes.Class | TypeAttributes.Sealed; + } + } + + public override string[] ValidAttributeTargets { + get { + return attribute_targets; + } + } + + //TODO: duplicate + protected override bool VerifyClsCompliance () + { + if (!base.VerifyClsCompliance ()) { + return false; + } + + parameters.VerifyClsCompliance (this); + + if (!InvokeBuilder.MemberType.IsCLSCompliant ()) { + Report.Warning (3002, 1, Location, "Return type of `{0}' is not CLS-compliant", + GetSignatureForError ()); + } + return true; + } + + + public static MethodSpec GetConstructor (TypeSpec delType) + { + var ctor = MemberCache.FindMember (delType, MemberFilter.Constructor (null), BindingRestriction.DeclaredOnly); + return (MethodSpec) ctor; + } + + // + // Returns the "Invoke" from a delegate type + // + public static MethodSpec GetInvokeMethod (TypeSpec delType) + { + var invoke = MemberCache.FindMember (delType, + MemberFilter.Method (InvokeMethodName, 0, null, null), + BindingRestriction.DeclaredOnly); + + return (MethodSpec) invoke; + } + + public static AParametersCollection GetParameters (TypeSpec delType) + { + var invoke_mb = GetInvokeMethod (delType); + return invoke_mb.Parameters; + } + + // + // 15.2 Delegate compatibility + // + public static bool IsTypeCovariant (ResolveContext rc, TypeSpec a, TypeSpec b) + { + // + // For each value parameter (a parameter with no ref or out modifier), an + // identity conversion or implicit reference conversion exists from the + // parameter type in D to the corresponding parameter type in M + // + if (a == b) + return true; + + if (rc.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) + return false; + + if (a.IsGenericParameter && b.IsGenericParameter) + return a == b; + + return Convert.ImplicitReferenceConversionExists (a, b); + } + + public static string FullDelegateDesc (MethodSpec invoke_method) + { + return TypeManager.GetFullNameSignature (invoke_method).Replace (".Invoke", ""); + } + + public Expression InstanceExpression { + get { + return instance_expr; + } + set { + instance_expr = value; + } + } + } + + // + // Base class for `NewDelegate' and `ImplicitDelegateCreation' + // + public abstract class DelegateCreation : Expression, OverloadResolver.IErrorHandler + { + bool conditional_access_receiver; + protected MethodSpec constructor_method; + protected MethodGroupExpr method_group; + + public bool AllowSpecialMethodsInvocation { get; set; } + + public override bool ContainsEmitWithAwait () + { + var instance = method_group.InstanceExpression; + return instance != null && instance.ContainsEmitWithAwait (); + } + + public static Arguments CreateDelegateMethodArguments (ResolveContext rc, AParametersCollection pd, TypeSpec[] types, Location loc) + { + Arguments delegate_arguments = new Arguments (pd.Count); + for (int i = 0; i < pd.Count; ++i) { + Argument.AType atype_modifier; + switch (pd.FixedParameters [i].ModFlags & Parameter.Modifier.RefOutMask) { + case Parameter.Modifier.REF: + atype_modifier = Argument.AType.Ref; + break; + case Parameter.Modifier.OUT: + atype_modifier = Argument.AType.Out; + break; + default: + atype_modifier = 0; + break; + } + + var ptype = types[i]; + if (ptype.BuiltinType == BuiltinTypeSpec.Type.Dynamic) + ptype = rc.BuiltinTypes.Object; + + delegate_arguments.Add (new Argument (new TypeExpression (ptype, loc), atype_modifier)); + } + + return delegate_arguments; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + MemberAccess ma = new MemberAccess (new MemberAccess (new QualifiedAliasMember ("global", "System", loc), "Delegate", loc), "CreateDelegate", loc); + + Arguments args = new Arguments (3); + args.Add (new Argument (new TypeOf (type, loc))); + + if (method_group.InstanceExpression == null) + args.Add (new Argument (new NullLiteral (loc))); + else + args.Add (new Argument (method_group.InstanceExpression)); + + args.Add (new Argument (method_group.CreateExpressionTree (ec))); + Expression e = new Invocation (ma, args).Resolve (ec); + if (e == null) + return null; + + e = Convert.ExplicitConversion (ec, e, type, loc); + if (e == null) + return null; + + return e.CreateExpressionTree (ec); + } + + protected override Expression DoResolve (ResolveContext ec) + { + constructor_method = Delegate.GetConstructor (type); + + var invoke_method = Delegate.GetInvokeMethod (type); + + if (!ec.HasSet (ResolveContext.Options.ConditionalAccessReceiver)) { + if (method_group.HasConditionalAccess ()) { + conditional_access_receiver = true; + ec.Set (ResolveContext.Options.ConditionalAccessReceiver); + } + } + + Arguments arguments = CreateDelegateMethodArguments (ec, invoke_method.Parameters, invoke_method.Parameters.Types, loc); + method_group = method_group.OverloadResolve (ec, ref arguments, this, OverloadResolver.Restrictions.CovariantDelegate); + + if (conditional_access_receiver) + ec.With (ResolveContext.Options.ConditionalAccessReceiver, false); + + if (method_group == null) + return null; + + var delegate_method = method_group.BestCandidate; + + if (delegate_method.DeclaringType.IsNullableType) { + ec.Report.Error (1728, loc, "Cannot create delegate from method `{0}' because it is a member of System.Nullable type", + delegate_method.GetSignatureForError ()); + return null; + } + + if (!AllowSpecialMethodsInvocation) + Invocation.IsSpecialMethodInvocation (ec, delegate_method, loc); + + ExtensionMethodGroupExpr emg = method_group as ExtensionMethodGroupExpr; + if (emg != null) { + method_group.InstanceExpression = emg.ExtensionExpression; + TypeSpec e_type = emg.ExtensionExpression.Type; + if (TypeSpec.IsValueType (e_type)) { + ec.Report.Error (1113, loc, "Extension method `{0}' of value type `{1}' cannot be used to create delegates", + delegate_method.GetSignatureForError (), e_type.GetSignatureForError ()); + } + } + + TypeSpec rt = method_group.BestCandidateReturnType; + if (rt.BuiltinType == BuiltinTypeSpec.Type.Dynamic) + rt = ec.BuiltinTypes.Object; + + if (!Delegate.IsTypeCovariant (ec, rt, invoke_method.ReturnType)) { + Expression ret_expr = new TypeExpression (delegate_method.ReturnType, loc); + Error_ConversionFailed (ec, delegate_method, ret_expr); + } + + if (method_group.IsConditionallyExcluded) { + ec.Report.SymbolRelatedToPreviousError (delegate_method); + MethodOrOperator m = delegate_method.MemberDefinition as MethodOrOperator; + if (m != null && m.IsPartialDefinition) { + ec.Report.Error (762, loc, "Cannot create delegate from partial method declaration `{0}'", + delegate_method.GetSignatureForError ()); + } else { + ec.Report.Error (1618, loc, "Cannot create delegate with `{0}' because it has a Conditional attribute", + TypeManager.CSharpSignature (delegate_method)); + } + } + + var expr = method_group.InstanceExpression; + if (expr != null && (expr.Type.IsGenericParameter || !TypeSpec.IsReferenceType (expr.Type))) + method_group.InstanceExpression = new BoxedCast (expr, ec.BuiltinTypes.Object); + + eclass = ExprClass.Value; + return this; + } + + public override void Emit (EmitContext ec) + { + if (conditional_access_receiver) + ec.ConditionalAccess = new ConditionalAccessContext (type, ec.DefineLabel ()); + + if (method_group.InstanceExpression == null) { + ec.EmitNull (); + } else { + var ie = new InstanceEmitter (method_group.InstanceExpression, false); + ie.Emit (ec, method_group.ConditionalAccess); + } + + var delegate_method = method_group.BestCandidate; + + // Any delegate must be sealed + if (!delegate_method.DeclaringType.IsDelegate && delegate_method.IsVirtual && !method_group.IsBase) { + ec.Emit (OpCodes.Dup); + ec.Emit (OpCodes.Ldvirtftn, delegate_method); + } else { + ec.Emit (OpCodes.Ldftn, delegate_method); + } + + ec.Emit (OpCodes.Newobj, constructor_method); + + if (conditional_access_receiver) + ec.CloseConditionalAccess (null); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + base.FlowAnalysis (fc); + method_group.FlowAnalysis (fc); + + if (conditional_access_receiver) + fc.ConditionalAccessEnd (); + } + + void Error_ConversionFailed (ResolveContext ec, MethodSpec method, Expression return_type) + { + var invoke_method = Delegate.GetInvokeMethod (type); + string member_name = method_group.InstanceExpression != null ? + Delegate.FullDelegateDesc (method) : + TypeManager.GetFullNameSignature (method); + + ec.Report.SymbolRelatedToPreviousError (type); + ec.Report.SymbolRelatedToPreviousError (method); + if (ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) { + ec.Report.Error (410, loc, "A method or delegate `{0} {1}' parameters and return type must be same as delegate `{2} {3}' parameters and return type", + method.ReturnType.GetSignatureForError (), member_name, + invoke_method.ReturnType.GetSignatureForError (), Delegate.FullDelegateDesc (invoke_method)); + return; + } + + if (return_type == null) { + ec.Report.Error (123, loc, "A method or delegate `{0}' parameters do not match delegate `{1}' parameters", + member_name, Delegate.FullDelegateDesc (invoke_method)); + return; + } + + ec.Report.Error (407, loc, "A method or delegate `{0} {1}' return type does not match delegate `{2} {3}' return type", + return_type.GetSignatureForError (), member_name, + invoke_method.ReturnType.GetSignatureForError (), Delegate.FullDelegateDesc (invoke_method)); + } + + public static bool ImplicitStandardConversionExists (ResolveContext ec, MethodGroupExpr mg, TypeSpec target_type) + { +// if (target_type == TypeManager.delegate_type || target_type == TypeManager.multicast_delegate_type) +// return false; + + var invoke = Delegate.GetInvokeMethod (target_type); + + Arguments arguments = CreateDelegateMethodArguments (ec, invoke.Parameters, invoke.Parameters.Types, mg.Location); + return mg.OverloadResolve (ec, ref arguments, null, OverloadResolver.Restrictions.CovariantDelegate | OverloadResolver.Restrictions.ProbingOnly) != null; + } + + #region IErrorHandler Members + + bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous) + { + return false; + } + + bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index) + { + Error_ConversionFailed (rc, best as MethodSpec, null); + return true; + } + + bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best) + { + Error_ConversionFailed (rc, best as MethodSpec, null); + return true; + } + + bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best) + { + return false; + } + + #endregion + } + + // + // Created from the conversion code + // + public class ImplicitDelegateCreation : DelegateCreation + { + Field mg_cache; + + public ImplicitDelegateCreation (TypeSpec delegateType, MethodGroupExpr mg, Location loc) + { + type = delegateType; + this.method_group = mg; + this.loc = loc; + } + + // + // Returns true when type is MVAR or has MVAR reference + // + public static bool ContainsMethodTypeParameter (TypeSpec type) + { + var tps = type as TypeParameterSpec; + if (tps != null) + return tps.IsMethodOwned; + + var ec = type as ElementTypeSpec; + if (ec != null) + return ContainsMethodTypeParameter (ec.Element); + + foreach (var t in type.TypeArguments) { + if (ContainsMethodTypeParameter (t)) { + return true; + } + } + + if (type.IsNested) + return ContainsMethodTypeParameter (type.DeclaringType); + + return false; + } + + bool HasMvar () + { + if (ContainsMethodTypeParameter (type)) + return false; + + var best = method_group.BestCandidate; + if (ContainsMethodTypeParameter (best.DeclaringType)) + return false; + + if (best.TypeArguments != null) { + foreach (var ta in best.TypeArguments) { + if (ContainsMethodTypeParameter (ta)) + return false; + } + } + + return true; + } + + protected override Expression DoResolve (ResolveContext ec) + { + var expr = base.DoResolve (ec); + if (expr == null) + return ErrorExpression.Instance; + + if (ec.IsInProbingMode) + return expr; + + // + // Cache any static delegate creation + // + if (method_group.InstanceExpression != null) + return expr; + + // + // Cannot easily cache types with MVAR + // + if (!HasMvar ()) + return expr; + + // + // Create type level cache for a delegate instance + // + var parent = ec.CurrentMemberDefinition.Parent.PartialContainer; + int id = parent.MethodGroupsCounter++; + + mg_cache = new Field (parent, new TypeExpression (type, loc), + Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED, + new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "mg$cache", id), loc), null); + mg_cache.Define (); + parent.AddField (mg_cache); + + return expr; + } + + public override void Emit (EmitContext ec) + { + Label l_initialized = ec.DefineLabel (); + + if (mg_cache != null) { + ec.Emit (OpCodes.Ldsfld, mg_cache.Spec); + ec.Emit (OpCodes.Brtrue_S, l_initialized); + } + + base.Emit (ec); + + if (mg_cache != null) { + ec.Emit (OpCodes.Stsfld, mg_cache.Spec); + ec.MarkLabel (l_initialized); + ec.Emit (OpCodes.Ldsfld, mg_cache.Spec); + } + } + } + + // + // A delegate-creation-expression, invoked from the `New' class + // + public class NewDelegate : DelegateCreation + { + public Arguments Arguments; + + // + // This constructor is invoked from the `New' expression + // + public NewDelegate (TypeSpec type, Arguments Arguments, Location loc) + { + this.type = type; + this.Arguments = Arguments; + this.loc = loc; + } + + protected override Expression DoResolve (ResolveContext ec) + { + if (Arguments == null || Arguments.Count != 1) { + ec.Report.Error (149, loc, "Method name expected"); + return null; + } + + Argument a = Arguments [0]; + if (!a.ResolveMethodGroup (ec)) + return null; + + Expression e = a.Expr; + + AnonymousMethodExpression ame = e as AnonymousMethodExpression; + if (ame != null && ec.Module.Compiler.Settings.Version != LanguageVersion.ISO_1) { + e = ame.Compatible (ec, type); + if (e == null) + return null; + + return e.Resolve (ec); + } + + method_group = e as MethodGroupExpr; + if (method_group == null) { + if (e.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + e = Convert.ImplicitConversionRequired (ec, e, type, loc); + } else if (!e.Type.IsDelegate) { + e.Error_UnexpectedKind (ec, ResolveFlags.MethodGroup | ResolveFlags.Type, loc); + return null; + } + + // + // An argument is not a method but another delegate + // + method_group = new MethodGroupExpr (Delegate.GetInvokeMethod (e.Type), e.Type, loc); + method_group.InstanceExpression = e; + } + + return base.DoResolve (ec); + } + } + + // + // Invocation converted to delegate Invoke call + // + class DelegateInvocation : ExpressionStatement + { + readonly Expression InstanceExpr; + readonly bool conditionalAccessReceiver; + Arguments arguments; + MethodSpec method; + + public DelegateInvocation (Expression instance_expr, Arguments args, bool conditionalAccessReceiver, Location loc) + { + this.InstanceExpr = instance_expr; + this.arguments = args; + this.conditionalAccessReceiver = conditionalAccessReceiver; + this.loc = loc; + } + + public override bool ContainsEmitWithAwait () + { + return InstanceExpr.ContainsEmitWithAwait () || (arguments != null && arguments.ContainsEmitWithAwait ()); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Arguments args = Arguments.CreateForExpressionTree (ec, this.arguments, + InstanceExpr.CreateExpressionTree (ec)); + + return CreateExpressionFactoryCall (ec, "Invoke", args); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + InstanceExpr.FlowAnalysis (fc); + if (arguments != null) + arguments.FlowAnalysis (fc); + } + + protected override Expression DoResolve (ResolveContext ec) + { + TypeSpec del_type = InstanceExpr.Type; + if (del_type == null) + return null; + + // + // Do only core overload resolution the rest of the checks has been + // done on primary expression + // + method = Delegate.GetInvokeMethod (del_type); + var res = new OverloadResolver (new MemberSpec[] { method }, OverloadResolver.Restrictions.DelegateInvoke, loc); + var valid = res.ResolveMember (ec, ref arguments); + if (valid == null && !res.BestCandidateIsDynamic) + return null; + + type = method.ReturnType; + if (conditionalAccessReceiver) + type = LiftMemberType (ec, type); + + eclass = ExprClass.Value; + return this; + } + + public override void Emit (EmitContext ec) + { + if (conditionalAccessReceiver) { + ec.ConditionalAccess = new ConditionalAccessContext (type, ec.DefineLabel ()); + } + + // + // Invocation on delegates call the virtual Invoke member + // so we are always `instance' calls + // + var call = new CallEmitter (); + call.InstanceExpression = InstanceExpr; + call.Emit (ec, method, arguments, loc); + + if (conditionalAccessReceiver) + ec.CloseConditionalAccess (type.IsNullableType && type != method.ReturnType ? type : null); + } + + public override void EmitStatement (EmitContext ec) + { + if (conditionalAccessReceiver) { + ec.ConditionalAccess = new ConditionalAccessContext (type, ec.DefineLabel ()) { + Statement = true + }; + } + + var call = new CallEmitter (); + call.InstanceExpression = InstanceExpr; + call.EmitStatement (ec, method, arguments, loc); + + if (conditionalAccessReceiver) + ec.CloseConditionalAccess (null); + } + + public override System.Linq.Expressions.Expression MakeExpression (BuilderContext ctx) + { + return Invocation.MakeExpression (ctx, InstanceExpr, method, arguments); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/doc.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/doc.cs new file mode 100644 index 000000000..35fe58529 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/doc.cs @@ -0,0 +1,755 @@ +// +// doc.cs: Support for XML documentation comment. +// +// Authors: +// Atsushi Enomoto +// Marek Safar (marek.safar@gmail.com> +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2004 Novell, Inc. +// Copyright 2011 Xamarin Inc +// +// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Xml; +using System.Linq; + +namespace Mono.CSharp +{ + // + // Implements XML documentation generation. + // + class DocumentationBuilder + { + // + // Used to create element which helps well-formedness checking. + // + readonly XmlDocument XmlDocumentation; + + readonly ModuleContainer module; + readonly ModuleContainer doc_module; + + // + // The output for XML documentation. + // + XmlWriter XmlCommentOutput; + + static readonly string line_head = Environment.NewLine + " "; + + // + // Stores XmlDocuments that are included in XML documentation. + // Keys are included filenames, values are XmlDocuments. + // + Dictionary StoredDocuments = new Dictionary (); + + ParserSession session; + + public DocumentationBuilder (ModuleContainer module) + { + doc_module = new ModuleContainer (module.Compiler); + doc_module.DocumentationBuilder = this; + + this.module = module; + XmlDocumentation = new XmlDocument (); + XmlDocumentation.PreserveWhitespace = false; + } + + Report Report { + get { + return module.Compiler.Report; + } + } + + public MemberName ParsedName { + get; set; + } + + public List ParsedParameters { + get; set; + } + + public TypeExpression ParsedBuiltinType { + get; set; + } + + public Operator.OpType? ParsedOperator { + get; set; + } + + XmlNode GetDocCommentNode (MemberCore mc, string name) + { + // FIXME: It could be even optimizable as not + // to use XmlDocument. But anyways the nodes + // are not kept in memory. + XmlDocument doc = XmlDocumentation; + try { + XmlElement el = doc.CreateElement ("member"); + el.SetAttribute ("name", name); + string normalized = mc.DocComment; + el.InnerXml = normalized; + // csc keeps lines as written in the sources + // and inserts formatting indentation (which + // is different from XmlTextWriter.Formatting + // one), but when a start tag contains an + // endline, it joins the next line. We don't + // have to follow such a hacky behavior. + string [] split = + normalized.Split ('\n'); + int j = 0; + for (int i = 0; i < split.Length; i++) { + string s = split [i].TrimEnd (); + if (s.Length > 0) + split [j++] = s; + } + el.InnerXml = line_head + String.Join ( + line_head, split, 0, j); + return el; + } catch (Exception ex) { + Report.Warning (1570, 1, mc.Location, "XML documentation comment on `{0}' is not well-formed XML markup ({1})", + mc.GetSignatureForError (), ex.Message); + + return doc.CreateComment (String.Format ("FIXME: Invalid documentation markup was found for member {0}", name)); + } + } + + // + // Generates xml doc comments (if any), and if required, + // handle warning report. + // + internal void GenerateDocumentationForMember (MemberCore mc) + { + string name = mc.DocCommentHeader + mc.GetSignatureForDocumentation (); + + XmlNode n = GetDocCommentNode (mc, name); + + XmlElement el = n as XmlElement; + if (el != null) { + var pm = mc as IParametersMember; + if (pm != null) { + CheckParametersComments (mc, pm, el); + } + + // FIXME: it could be done with XmlReader + XmlNodeList nl = n.SelectNodes (".//include"); + if (nl.Count > 0) { + // It could result in current node removal, so prepare another list to iterate. + var al = new List (nl.Count); + foreach (XmlNode inc in nl) + al.Add (inc); + foreach (XmlElement inc in al) + if (!HandleInclude (mc, inc)) + inc.ParentNode.RemoveChild (inc); + } + + // FIXME: it could be done with XmlReader + + foreach (XmlElement see in n.SelectNodes (".//see")) + HandleSee (mc, see); + foreach (XmlElement seealso in n.SelectNodes (".//seealso")) + HandleSeeAlso (mc, seealso); + foreach (XmlElement see in n.SelectNodes (".//exception")) + HandleException (mc, see); + foreach (XmlElement node in n.SelectNodes (".//typeparam")) + HandleTypeParam (mc, node); + foreach (XmlElement node in n.SelectNodes (".//typeparamref")) + HandleTypeParamRef (mc, node); + } + + n.WriteTo (XmlCommentOutput); + } + + // + // Processes "include" element. Check included file and + // embed the document content inside this documentation node. + // + bool HandleInclude (MemberCore mc, XmlElement el) + { + bool keep_include_node = false; + string file = el.GetAttribute ("file"); + string path = el.GetAttribute ("path"); + + if (file == "") { + Report.Warning (1590, 1, mc.Location, "Invalid XML `include' element. Missing `file' attribute"); + el.ParentNode.InsertBefore (el.OwnerDocument.CreateComment (" Include tag is invalid "), el); + keep_include_node = true; + } else if (path.Length == 0) { + Report.Warning (1590, 1, mc.Location, "Invalid XML `include' element. Missing `path' attribute"); + el.ParentNode.InsertBefore (el.OwnerDocument.CreateComment (" Include tag is invalid "), el); + keep_include_node = true; + } else { + XmlDocument doc; + Exception exception = null; + var full_path = Path.Combine (Path.GetDirectoryName (mc.Location.NameFullPath), file); + + if (!StoredDocuments.TryGetValue (full_path, out doc)) { + try { + doc = new XmlDocument (); + doc.Load (full_path); + StoredDocuments.Add (full_path, doc); + } catch (Exception e) { + exception = e; + el.ParentNode.InsertBefore (el.OwnerDocument.CreateComment (String.Format (" Badly formed XML in at comment file `{0}': cannot be included ", file)), el); + } + } + + if (doc != null) { + try { + XmlNodeList nl = doc.SelectNodes (path); + if (nl.Count == 0) { + el.ParentNode.InsertBefore (el.OwnerDocument.CreateComment (" No matching elements were found for the include tag embedded here. "), el); + + keep_include_node = true; + } + foreach (XmlNode n in nl) + el.ParentNode.InsertBefore (el.OwnerDocument.ImportNode (n, true), el); + } catch (Exception ex) { + exception = ex; + el.ParentNode.InsertBefore (el.OwnerDocument.CreateComment (" Failed to insert some or all of included XML "), el); + } + } + + if (exception != null) { + Report.Warning (1589, 1, mc.Location, "Unable to include XML fragment `{0}' of file `{1}'. {2}", + path, file, exception.Message); + } + } + + return keep_include_node; + } + + // + // Handles elements. + // + void HandleSee (MemberCore mc, XmlElement see) + { + HandleXrefCommon (mc, see); + } + + // + // Handles elements. + // + void HandleSeeAlso (MemberCore mc, XmlElement seealso) + { + HandleXrefCommon (mc, seealso); + } + + // + // Handles elements. + // + void HandleException (MemberCore mc, XmlElement seealso) + { + HandleXrefCommon (mc, seealso); + } + + // + // Handles node + // + static void HandleTypeParam (MemberCore mc, XmlElement node) + { + if (!node.HasAttribute ("name")) + return; + + string tp_name = node.GetAttribute ("name"); + if (mc.CurrentTypeParameters != null) { + if (mc.CurrentTypeParameters.Find (tp_name) != null) + return; + } + + // TODO: CS1710, CS1712 + + mc.Compiler.Report.Warning (1711, 2, mc.Location, + "XML comment on `{0}' has a typeparam name `{1}' but there is no type parameter by that name", + mc.GetSignatureForError (), tp_name); + } + + // + // Handles node + // + static void HandleTypeParamRef (MemberCore mc, XmlElement node) + { + if (!node.HasAttribute ("name")) + return; + + string tp_name = node.GetAttribute ("name"); + var member = mc; + do { + if (member.CurrentTypeParameters != null) { + if (member.CurrentTypeParameters.Find (tp_name) != null) + return; + } + + member = member.Parent; + } while (member != null); + + mc.Compiler.Report.Warning (1735, 2, mc.Location, + "XML comment on `{0}' has a typeparamref name `{1}' that could not be resolved", + mc.GetSignatureForError (), tp_name); + } + + FullNamedExpression ResolveMemberName (IMemberContext context, MemberName mn) + { + if (mn.Left == null) + return context.LookupNamespaceOrType (mn.Name, mn.Arity, LookupMode.Probing, Location.Null); + + var left = ResolveMemberName (context, mn.Left); + var ns = left as NamespaceExpression; + if (ns != null) + return ns.LookupTypeOrNamespace (context, mn.Name, mn.Arity, LookupMode.Probing, Location.Null); + + TypeExpr texpr = left as TypeExpr; + if (texpr != null) { + var found = MemberCache.FindNestedType (texpr.Type, mn.Name, mn.Arity); + if (found != null) + return new TypeExpression (found, Location.Null); + + return null; + } + + return left; + } + + // + // Processes "see" or "seealso" elements from cref attribute. + // + void HandleXrefCommon (MemberCore mc, XmlElement xref) + { + string cref = xref.GetAttribute ("cref"); + // when, XmlReader, "if (cref == null)" + if (!xref.HasAttribute ("cref")) + return; + + // Nothing to be resolved the reference is marked explicitly + if (cref.Length > 2 && cref [1] == ':') + return; + + // Additional symbols for < and > are allowed for easier XML typing + cref = cref.Replace ('{', '<').Replace ('}', '>'); + + var encoding = module.Compiler.Settings.Encoding; + var s = new MemoryStream (encoding.GetBytes (cref)); + + var source_file = new CompilationSourceFile (doc_module, mc.Location.SourceFile); + var report = new Report (doc_module.Compiler, new NullReportPrinter ()); + + if (session == null) + session = new ParserSession { + UseJayGlobalArrays = true + }; + + SeekableStreamReader seekable = new SeekableStreamReader (s, encoding, session.StreamReaderBuffer); + + var parser = new CSharpParser (seekable, source_file, report, session); + ParsedParameters = null; + ParsedName = null; + ParsedBuiltinType = null; + ParsedOperator = null; + 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); + + xref.SetAttribute ("cref", "!:" + cref); + return; + } + + MemberSpec member; + string prefix = null; + FullNamedExpression fne = null; + + // + // Try built-in type first because we are using ParsedName as identifier of + // member names on built-in types + // + if (ParsedBuiltinType != null && (ParsedParameters == null || ParsedName != null)) { + member = ParsedBuiltinType.Type; + } else { + member = null; + } + + if (ParsedName != null || ParsedOperator.HasValue) { + TypeSpec type = null; + string member_name = null; + + if (member == null) { + if (ParsedOperator.HasValue) { + type = mc.CurrentType; + } else if (ParsedName.Left != null) { + fne = ResolveMemberName (mc, ParsedName.Left); + if (fne != null) { + var ns = fne as NamespaceExpression; + if (ns != null) { + fne = ns.LookupTypeOrNamespace (mc, ParsedName.Name, ParsedName.Arity, LookupMode.Probing, Location.Null); + if (fne != null) { + member = fne.Type; + } + } else { + type = fne.Type; + } + } + } else { + fne = ResolveMemberName (mc, ParsedName); + if (fne == null) { + type = mc.CurrentType; + } else if (ParsedParameters == null) { + member = fne.Type; + } else if (fne.Type.MemberDefinition == mc.CurrentType.MemberDefinition) { + member_name = Constructor.ConstructorName; + type = fne.Type; + } + } + } else { + type = (TypeSpec) member; + member = null; + } + + if (ParsedParameters != null) { + var old_printer = mc.Module.Compiler.Report.SetPrinter (new NullReportPrinter ()); + try { + var context = new DocumentationMemberContext (mc, ParsedName ?? MemberName.Null); + + foreach (var pp in ParsedParameters) { + pp.Resolve (context); + } + } finally { + mc.Module.Compiler.Report.SetPrinter (old_printer); + } + } + + if (type != null) { + if (member_name == null) + member_name = ParsedOperator.HasValue ? + Operator.GetMetadataName (ParsedOperator.Value) : ParsedName.Name; + + int parsed_param_count; + if (ParsedOperator == Operator.OpType.Explicit || ParsedOperator == Operator.OpType.Implicit) { + parsed_param_count = ParsedParameters.Count - 1; + } else if (ParsedParameters != null) { + parsed_param_count = ParsedParameters.Count; + } else { + parsed_param_count = 0; + } + + int parameters_match = -1; + do { + var members = MemberCache.FindMembers (type, member_name, true); + if (members != null) { + foreach (var m in members) { + if (ParsedName != null && m.Arity != ParsedName.Arity) + continue; + + if (ParsedParameters != null) { + IParametersMember pm = m as IParametersMember; + if (pm == null) + continue; + + if (m.Kind == MemberKind.Operator && !ParsedOperator.HasValue) + continue; + + var pm_params = pm.Parameters; + + int i; + for (i = 0; i < parsed_param_count; ++i) { + var pparam = ParsedParameters[i]; + + if (i >= pm_params.Count || pparam == null || pparam.TypeSpec == null || + !TypeSpecComparer.Override.IsEqual (pparam.TypeSpec, pm_params.Types[i]) || + (pparam.Modifier & Parameter.Modifier.RefOutMask) != (pm_params.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask)) { + + if (i > parameters_match) { + parameters_match = i; + } + + i = -1; + break; + } + } + + if (i < 0) + continue; + + if (ParsedOperator == Operator.OpType.Explicit || ParsedOperator == Operator.OpType.Implicit) { + if (pm.MemberType != ParsedParameters[parsed_param_count].TypeSpec) { + parameters_match = parsed_param_count + 1; + continue; + } + } else { + if (parsed_param_count != pm_params.Count) + continue; + } + } + + if (member != null) { + Report.Warning (419, 3, mc.Location, + "Ambiguous reference in cref attribute `{0}'. Assuming `{1}' but other overloads including `{2}' have also matched", + cref, member.GetSignatureForError (), m.GetSignatureForError ()); + + break; + } + + member = m; + } + } + + // Continue with parent type for nested types + if (member == null) { + type = type.DeclaringType; + } else { + type = null; + } + } while (type != null); + + if (member == null && parameters_match >= 0) { + for (int i = parameters_match; i < parsed_param_count; ++i) { + Report.Warning (1580, 1, mc.Location, "Invalid type for parameter `{0}' in XML comment cref attribute `{1}'", + (i + 1).ToString (), cref); + } + + if (parameters_match == parsed_param_count + 1) { + Report.Warning (1581, 1, mc.Location, "Invalid return type in XML comment cref attribute `{0}'", cref); + } + } + } + } + + if (member == null) { + Report.Warning (1574, 1, mc.Location, "XML comment on `{0}' has cref attribute `{1}' that could not be resolved", + mc.GetSignatureForError (), cref); + cref = "!:" + cref; + } else if (member == InternalType.Namespace) { + cref = "N:" + fne.GetSignatureForError (); + } else { + prefix = GetMemberDocHead (member); + cref = prefix + member.GetSignatureForDocumentation (); + } + + xref.SetAttribute ("cref", cref); + } + + // + // Get a prefix from member type for XML documentation (used + // to formalize cref target name). + // + static string GetMemberDocHead (MemberSpec type) + { + if (type is FieldSpec) + return "F:"; + if (type is MethodSpec) + return "M:"; + if (type is EventSpec) + return "E:"; + if (type is PropertySpec) + return "P:"; + if (type is TypeSpec) + return "T:"; + + throw new NotImplementedException (type.GetType ().ToString ()); + } + + // + // Raised (and passed an XmlElement that contains the comment) + // when GenerateDocComment is writing documentation expectedly. + // + // FIXME: with a few effort, it could be done with XmlReader, + // that means removal of DOM use. + // + void CheckParametersComments (MemberCore member, IParametersMember paramMember, XmlElement el) + { + HashSet found_tags = null; + foreach (XmlElement pelem in el.SelectNodes ("param")) { + string xname = pelem.GetAttribute ("name"); + if (xname.Length == 0) + continue; // really? but MS looks doing so + + if (found_tags == null) { + found_tags = new HashSet (); + } + + if (xname != "" && paramMember.Parameters.GetParameterIndexByName (xname) < 0) { + Report.Warning (1572, 2, member.Location, + "XML comment on `{0}' has a param tag for `{1}', but there is no parameter by that name", + member.GetSignatureForError (), xname); + continue; + } + + if (found_tags.Contains (xname)) { + Report.Warning (1571, 2, member.Location, + "XML comment on `{0}' has a duplicate param tag for `{1}'", + member.GetSignatureForError (), xname); + continue; + } + + found_tags.Add (xname); + } + + if (found_tags != null) { + foreach (Parameter p in paramMember.Parameters.FixedParameters) { + if (!found_tags.Contains (p.Name) && !(p is ArglistParameter)) + Report.Warning (1573, 4, member.Location, + "Parameter `{0}' has no matching param tag in the XML comment for `{1}'", + p.Name, member.GetSignatureForError ()); + } + } + } + + // + // Outputs XML documentation comment from tokenized comments. + // + public bool OutputDocComment (string asmfilename, string xmlFileName) + { + XmlTextWriter w = null; + try { + w = new XmlTextWriter (xmlFileName, null); + w.Indentation = 4; + w.Formatting = Formatting.Indented; + w.WriteStartDocument (); + w.WriteStartElement ("doc"); + w.WriteStartElement ("assembly"); + w.WriteStartElement ("name"); + w.WriteString (Path.GetFileNameWithoutExtension (asmfilename)); + w.WriteEndElement (); // name + w.WriteEndElement (); // assembly + w.WriteStartElement ("members"); + XmlCommentOutput = w; + module.GenerateDocComment (this); + w.WriteFullEndElement (); // members + w.WriteEndElement (); + w.WriteWhitespace (Environment.NewLine); + w.WriteEndDocument (); + return true; + } catch (Exception ex) { + Report.Error (1569, "Error generating XML documentation file `{0}' (`{1}')", xmlFileName, ex.Message); + return false; + } finally { + if (w != null) + w.Close (); + } + } + } + + // + // Type lookup of documentation references uses context of type where + // the reference is used but type parameters from cref value + // + sealed class DocumentationMemberContext : IMemberContext + { + readonly MemberCore host; + MemberName contextName; + + public DocumentationMemberContext (MemberCore host, MemberName contextName) + { + this.host = host; + this.contextName = contextName; + } + + public TypeSpec CurrentType { + get { + return host.CurrentType; + } + } + + public TypeParameters CurrentTypeParameters { + get { + return contextName.TypeParameters; + } + } + + public MemberCore CurrentMemberDefinition { + get { + return host.CurrentMemberDefinition; + } + } + + public bool IsObsolete { + get { + return false; + } + } + + public bool IsUnsafe { + get { + return host.IsStatic; + } + } + + public bool IsStatic { + get { + return host.IsStatic; + } + } + + public ModuleContainer Module { + get { + return host.Module; + } + } + + public string GetSignatureForError () + { + return host.GetSignatureForError (); + } + + public ExtensionMethodCandidates LookupExtensionMethod (TypeSpec extensionType, string name, int arity) + { + return null; + } + + public FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc) + { + if (arity == 0) { + var tp = CurrentTypeParameters; + if (tp != null) { + for (int i = 0; i < tp.Count; ++i) { + var t = tp[i]; + if (t.Name == name) { + t.Type.DeclaredPosition = i; + return new TypeParameterExpr (t, loc); + } + } + } + } + + return host.Parent.LookupNamespaceOrType (name, arity, mode, loc); + } + + public FullNamedExpression LookupNamespaceAlias (string name) + { + throw new NotImplementedException (); + } + } + + class DocumentationParameter + { + public readonly Parameter.Modifier Modifier; + public FullNamedExpression Type; + TypeSpec type; + + public DocumentationParameter (Parameter.Modifier modifier, FullNamedExpression type) + : this (type) + { + this.Modifier = modifier; + } + + public DocumentationParameter (FullNamedExpression type) + { + this.Type = type; + } + + public TypeSpec TypeSpec { + get { + return type; + } + } + + public void Resolve (IMemberContext context) + { + type = Type.ResolveAsType (context); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/driver.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/driver.cs new file mode 100644 index 000000000..6ef6859e8 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/driver.cs @@ -0,0 +1,475 @@ +// +// driver.cs: The compiler command line driver. +// +// Authors: +// Miguel de Icaza (miguel@gnu.org) +// Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com) +// Copyright 2004, 2005, 2006, 2007, 2008 Novell, Inc +// Copyright 2011 Xamarin Inc +// + +using System; +using System.Reflection; +using System.Reflection.Emit; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Globalization; +using System.Diagnostics; +using System.Threading; + +namespace Mono.CSharp +{ + /// + /// The compiler driver. + /// + class Driver + { + readonly CompilerContext ctx; + + public Driver (CompilerContext ctx) + { + this.ctx = ctx; + } + + Report Report { + get { + return ctx.Report; + } + } + + void tokenize_file (SourceFile sourceFile, ModuleContainer module, ParserSession session) + { + Stream input; + + try { + input = File.OpenRead (sourceFile.Name); + } catch { + Report.Error (2001, "Source file `" + sourceFile.Name + "' could not be found"); + return; + } + + using (input){ + SeekableStreamReader reader = new SeekableStreamReader (input, ctx.Settings.Encoding); + var file = new CompilationSourceFile (module, sourceFile); + + Tokenizer lexer = new Tokenizer (reader, file, session, ctx.Report); + int token, tokens = 0, errors = 0; + + while ((token = lexer.token ()) != Token.EOF){ + tokens++; + if (token == Token.ERROR) + errors++; + } + Console.WriteLine ("Tokenized: " + tokens + " found " + errors + " errors"); + } + + return; + } + + void Parse (ModuleContainer module) + { + bool tokenize_only = module.Compiler.Settings.TokenizeOnly; + var sources = module.Compiler.SourceFiles; + + Location.Initialize (sources); + + var session = new ParserSession { + UseJayGlobalArrays = true, + LocatedTokens = new LocatedToken[15000] + }; + + for (int i = 0; i < sources.Count; ++i) { + if (tokenize_only) { + tokenize_file (sources[i], module, session); + } else { + Parse (sources[i], module, session, Report); + } + } + } + +#if false + void ParseParallel (ModuleContainer module) + { + var sources = module.Compiler.SourceFiles; + + Location.Initialize (sources); + + var pcount = Environment.ProcessorCount; + var threads = new Thread[System.Math.Max (2, pcount - 1)]; + + for (int i = 0; i < threads.Length; ++i) { + var t = new Thread (l => { + var session = new ParserSession () { + //UseJayGlobalArrays = true, + }; + + var report = new Report (ctx, Report.Printer); // TODO: Implement flush at once printer + + for (int ii = (int) l; ii < sources.Count; ii += threads.Length) { + Parse (sources[ii], module, session, report); + } + + // TODO: Merge warning regions + }); + + t.Start (i); + threads[i] = t; + } + + for (int t = 0; t < threads.Length; ++t) { + threads[t].Join (); + } + } +#endif + + public void Parse (SourceFile file, ModuleContainer module, ParserSession session, Report report) + { + Stream input; + + try { + input = File.OpenRead (file.Name); + } catch { + report.Error (2001, "Source file `{0}' could not be found", file.Name); + return; + } + + // Check 'MZ' header + if (input.ReadByte () == 77 && input.ReadByte () == 90) { + + report.Error (2015, "Source file `{0}' is a binary file and not a text file", file.Name); + input.Close (); + return; + } + + input.Position = 0; + SeekableStreamReader reader = new SeekableStreamReader (input, ctx.Settings.Encoding, session.StreamReaderBuffer); + + Parse (reader, file, module, session, report); + + if (ctx.Settings.GenerateDebugInfo && report.Errors == 0 && !file.HasChecksum) { + input.Position = 0; + var checksum = session.GetChecksumAlgorithm (); + file.SetChecksum (checksum.ComputeHash (input)); + } + + reader.Dispose (); + input.Close (); + } + + public static CSharpParser Parse (SeekableStreamReader reader, SourceFile sourceFile, ModuleContainer module, ParserSession session, Report report, int lineModifier = 0, int colModifier = 0) + { + var file = new CompilationSourceFile (module, sourceFile); + module.AddTypeContainer(file); + + CSharpParser parser = new CSharpParser (reader, file, report, session); + parser.Lexer.Line += lineModifier; + parser.Lexer.Column += colModifier; + parser.Lexer.sbag = new SpecialsBag (); + parser.parse (); + return parser; + } + + public static int Main (string[] args) + { + Location.InEmacs = Environment.GetEnvironmentVariable ("EMACS") == "t"; + + CommandLineParser cmd = new CommandLineParser (Console.Out); + var settings = cmd.ParseArguments (args); + if (settings == null) + return 1; + + if (cmd.HasBeenStopped) + return 0; + + Driver d = new Driver (new CompilerContext (settings, new ConsoleReportPrinter ())); + + if (d.Compile () && d.Report.Errors == 0) { + if (d.Report.Warnings > 0) { + Console.WriteLine ("Compilation succeeded - {0} warning(s)", d.Report.Warnings); + } + Environment.Exit (0); + return 0; + } + + + Console.WriteLine("Compilation failed: {0} error(s), {1} warnings", + d.Report.Errors, d.Report.Warnings); + Environment.Exit (1); + return 1; + } + + public static string GetPackageFlags (string packages, Report report) + { + ProcessStartInfo pi = new ProcessStartInfo (); + pi.FileName = "pkg-config"; + pi.RedirectStandardOutput = true; + pi.UseShellExecute = false; + pi.Arguments = "--libs " + packages; + Process p = null; + try { + p = Process.Start (pi); + } catch (Exception e) { + if (report == null) + throw; + + report.Error (-27, "Couldn't run pkg-config: " + e.Message); + return null; + } + + if (p.StandardOutput == null) { + if (report == null) + throw new ApplicationException ("Specified package did not return any information"); + + report.Warning (-27, 1, "Specified package did not return any information"); + p.Close (); + return null; + } + + string pkgout = p.StandardOutput.ReadToEnd (); + p.WaitForExit (); + if (p.ExitCode != 0) { + if (report == null) + throw new ApplicationException (pkgout); + + report.Error (-27, "Error running pkg-config. Check the above output."); + p.Close (); + return null; + } + + p.Close (); + return pkgout; + } + + // + // Main compilation method + // + public bool Compile () + { + var settings = ctx.Settings; + + // + // If we are an exe, require a source file for the entry point or + // if there is nothing to put in the assembly, and we are not a library + // + if (settings.FirstSourceFile == null && + ((settings.Target == Target.Exe || settings.Target == Target.WinExe || settings.Target == Target.Module) || + settings.Resources == null)) { + Report.Error (2008, "No files to compile were specified"); + return false; + } + + if (settings.Platform == Platform.AnyCPU32Preferred && (settings.Target == Target.Library || settings.Target == Target.Module)) { + Report.Error (4023, "Platform option `anycpu32bitpreferred' is valid only for executables"); + return false; + } + + TimeReporter tr = new TimeReporter (settings.Timestamps); + ctx.TimeReporter = tr; + tr.StartTotal (); + + var module = new ModuleContainer (ctx); + RootContext.ToplevelTypes = module; + + tr.Start (TimeReporter.TimerType.ParseTotal); + Parse (module); + tr.Stop (TimeReporter.TimerType.ParseTotal); + + if (Report.Errors > 0) + return false; + + if (settings.TokenizeOnly || settings.ParseOnly) { + tr.StopTotal (); + tr.ShowStats (); + return true; + } + + var output_file = settings.OutputFile; + string output_file_name; + if (output_file == null) { + var source_file = settings.FirstSourceFile; + + if (source_file == null) { + Report.Error (1562, "If no source files are specified you must specify the output file with -out:"); + return false; + } + + output_file_name = source_file.Name; + int pos = output_file_name.LastIndexOf ('.'); + + if (pos > 0) + output_file_name = output_file_name.Substring (0, pos); + + output_file_name += settings.TargetExt; + output_file = output_file_name; + } else { + output_file_name = Path.GetFileName (output_file); + + if (string.IsNullOrEmpty (Path.GetFileNameWithoutExtension (output_file_name)) || + output_file_name.IndexOfAny (Path.GetInvalidFileNameChars ()) >= 0) { + Report.Error (2021, "Output file name is not valid"); + return false; + } + } + +#if STATIC + var importer = new StaticImporter (module); + var references_loader = new StaticLoader (importer, ctx); + + tr.Start (TimeReporter.TimerType.AssemblyBuilderSetup); + var assembly = new AssemblyDefinitionStatic (module, references_loader, output_file_name, output_file); + assembly.Create (references_loader.Domain); + tr.Stop (TimeReporter.TimerType.AssemblyBuilderSetup); + + // Create compiler types first even before any referenced + // assembly is loaded to allow forward referenced types from + // loaded assembly into compiled builder to be resolved + // correctly + tr.Start (TimeReporter.TimerType.CreateTypeTotal); + module.CreateContainer (); + importer.AddCompiledAssembly (assembly); + references_loader.CompiledAssembly = assembly; + tr.Stop (TimeReporter.TimerType.CreateTypeTotal); + + references_loader.LoadReferences (module); + + tr.Start (TimeReporter.TimerType.PredefinedTypesInit); + if (!ctx.BuiltinTypes.CheckDefinitions (module)) + return false; + + tr.Stop (TimeReporter.TimerType.PredefinedTypesInit); + + references_loader.LoadModules (assembly, module.GlobalRootNamespace); +#else + var assembly = new AssemblyDefinitionDynamic (module, output_file_name, output_file); + module.SetDeclaringAssembly (assembly); + + var importer = new ReflectionImporter (module, ctx.BuiltinTypes); + assembly.Importer = importer; + + var loader = new DynamicLoader (importer, ctx); + loader.LoadReferences (module); + + if (!ctx.BuiltinTypes.CheckDefinitions (module)) + return false; + + if (!assembly.Create (AppDomain.CurrentDomain, AssemblyBuilderAccess.Save)) + return false; + + module.CreateContainer (); + + loader.LoadModules (assembly, module.GlobalRootNamespace); +#endif + module.InitializePredefinedTypes (); + + tr.Start (TimeReporter.TimerType.ModuleDefinitionTotal); + module.Define (); + tr.Stop (TimeReporter.TimerType.ModuleDefinitionTotal); + + if (Report.Errors > 0) + return false; + + if (settings.DocumentationFile != null) { + var doc = new DocumentationBuilder (module); + doc.OutputDocComment (output_file, settings.DocumentationFile); + } + + assembly.Resolve (); + + if (Report.Errors > 0) + return false; + + + tr.Start (TimeReporter.TimerType.EmitTotal); + assembly.Emit (); + tr.Stop (TimeReporter.TimerType.EmitTotal); + + if (Report.Errors > 0){ + return false; + } + + tr.Start (TimeReporter.TimerType.CloseTypes); + module.CloseContainer (); + tr.Stop (TimeReporter.TimerType.CloseTypes); + + tr.Start (TimeReporter.TimerType.Resouces); + if (!settings.WriteMetadataOnly) + assembly.EmbedResources (); + tr.Stop (TimeReporter.TimerType.Resouces); + + if (Report.Errors > 0) + return false; + + assembly.Save (); + +#if STATIC + references_loader.Dispose (); +#endif + tr.StopTotal (); + tr.ShowStats (); + + return Report.Errors == 0; + } + } + + public class CompilerCompilationUnit { + public ModuleContainer ModuleCompiled { get; set; } + public LocationsBag LocationsBag { get; set; } + public SpecialsBag SpecialsBag { get; set; } + public IDictionary Conditionals { get; set; } + public object LastYYValue { get; set; } + } + + // + // This is the only public entry point + // + public class CompilerCallableEntryPoint : MarshalByRefObject + { + public static bool InvokeCompiler (string [] args, TextWriter error) + { + try { + CommandLineParser cmd = new CommandLineParser (error); + var setting = cmd.ParseArguments (args); + if (setting == null) + return false; + + var d = new Driver (new CompilerContext (setting, new StreamReportPrinter (error))); + return d.Compile (); + } finally { + Reset (); + } + } + + public static int[] AllWarningNumbers { + get { + return Report.AllWarnings; + } + } + + public static void Reset () + { + Reset (true); + } + + public static void PartialReset () + { + Reset (false); + } + + public static void Reset (bool full_flag) + { + Location.Reset (); + + if (!full_flag) + return; + + Linq.QueryBlock.TransparentParameter.Reset (); + TypeInfo.Reset (); + } + } + +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/dynamic.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/dynamic.cs new file mode 100644 index 000000000..5604a3fcd --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/dynamic.cs @@ -0,0 +1,986 @@ +// +// dynamic.cs: support for dynamic expressions +// +// Authors: Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2009 Novell, Inc +// Copyright 2011 Xamarin Inc. +// + +using System; +using System.Linq; +using SLE = System.Linq.Expressions; + +#if NET_4_0 || MOBILE_DYNAMIC +using System.Dynamic; +#endif + +namespace Mono.CSharp +{ + // + // A copy of Microsoft.CSharp/Microsoft.CSharp.RuntimeBinder/CSharpBinderFlags.cs + // has to be kept in sync + // + [Flags] + public enum CSharpBinderFlags + { + None = 0, + CheckedContext = 1, + InvokeSimpleName = 1 << 1, + InvokeSpecialName = 1 << 2, + BinaryOperationLogical = 1 << 3, + ConvertExplicit = 1 << 4, + ConvertArrayIndex = 1 << 5, + ResultIndexed = 1 << 6, + ValueFromCompoundAssignment = 1 << 7, + ResultDiscarded = 1 << 8 + } + + // + // Type expression with internal dynamic type symbol + // + class DynamicTypeExpr : TypeExpr + { + public DynamicTypeExpr (Location loc) + { + this.loc = loc; + } + + public override TypeSpec ResolveAsType (IMemberContext ec, bool allowUnboundTypeArguments) + { + eclass = ExprClass.Type; + type = ec.Module.Compiler.BuiltinTypes.Dynamic; + return type; + } + } + + #region Dynamic runtime binder expressions + + // + // Expression created from runtime dynamic object value by dynamic binder + // + public class RuntimeValueExpression : Expression, IDynamicAssign, IMemoryLocation + { +#if !NET_4_0 && !MOBILE_DYNAMIC + public class DynamicMetaObject + { + public TypeSpec RuntimeType; + public TypeSpec LimitType; + public SLE.Expression Expression; + } +#endif + + readonly DynamicMetaObject obj; + + public RuntimeValueExpression (DynamicMetaObject obj, TypeSpec type) + { + this.obj = obj; + this.type = type; + this.eclass = ExprClass.Variable; + } + + #region Properties + + public bool IsSuggestionOnly { get; set; } + + public DynamicMetaObject MetaObject { + get { return obj; } + } + + #endregion + + public void AddressOf (EmitContext ec, AddressOp mode) + { + throw new NotImplementedException (); + } + + public override bool ContainsEmitWithAwait () + { + throw new NotSupportedException (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + throw new NotSupportedException (); + } + + protected override Expression DoResolve (ResolveContext ec) + { + return this; + } + + public override Expression DoResolveLValue (ResolveContext ec, Expression right_side) + { + return this; + } + + public override void Emit (EmitContext ec) + { + throw new NotImplementedException (); + } + + #region IAssignMethod Members + + public void Emit (EmitContext ec, bool leave_copy) + { + throw new NotImplementedException (); + } + + public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound) + { + throw new NotImplementedException (); + } + + #endregion + + public SLE.Expression MakeAssignExpression (BuilderContext ctx, Expression source) + { + return obj.Expression; + } + + public override SLE.Expression MakeExpression (BuilderContext ctx) + { +#if STATIC + return base.MakeExpression (ctx); +#else + +#if NET_4_0 || MOBILE_DYNAMIC + if (type.IsStruct && !obj.Expression.Type.IsValueType) + return SLE.Expression.Unbox (obj.Expression, type.GetMetaInfo ()); + + if (obj.Expression.NodeType == SLE.ExpressionType.Parameter) { + if (((SLE.ParameterExpression) obj.Expression).IsByRef) + return obj.Expression; + } + #endif + + return SLE.Expression.Convert (obj.Expression, type.GetMetaInfo ()); +#endif + } + } + + // + // Wraps runtime dynamic expression into expected type. Needed + // to satify expected type check by dynamic binder and no conversion + // is required (ResultDiscarded). + // + public class DynamicResultCast : ShimExpression + { + public DynamicResultCast (TypeSpec type, Expression expr) + : base (expr) + { + this.type = type; + } + + protected override Expression DoResolve (ResolveContext ec) + { + expr = expr.Resolve (ec); + eclass = ExprClass.Value; + return this; + } + +#if NET_4_0 || MOBILE_DYNAMIC + public override SLE.Expression MakeExpression (BuilderContext ctx) + { +#if STATIC + return base.MakeExpression (ctx); +#else + return SLE.Expression.Block (expr.MakeExpression (ctx), SLE.Expression.Default (type.GetMetaInfo ())); +#endif + } +#endif + } + + #endregion + + // + // Creates dynamic binder expression + // + interface IDynamicBinder + { + Expression CreateCallSiteBinder (ResolveContext ec, Arguments args); + } + + // + // Extends standard assignment interface for expressions + // supported by dynamic resolver + // + interface IDynamicAssign : IAssignMethod + { + SLE.Expression MakeAssignExpression (BuilderContext ctx, Expression source); + } + + // + // Base dynamic expression statement creator + // + class DynamicExpressionStatement : ExpressionStatement + { + // + // Binder flag dynamic constant, the value is combination of + // flags known at resolve stage and flags known only at emit + // stage + // + protected class BinderFlags : EnumConstant + { + readonly DynamicExpressionStatement statement; + readonly CSharpBinderFlags flags; + + public BinderFlags (CSharpBinderFlags flags, DynamicExpressionStatement statement) + : base (statement.loc) + { + this.flags = flags; + this.statement = statement; + eclass = 0; + } + + protected override Expression DoResolve (ResolveContext ec) + { + Child = new IntConstant (ec.BuiltinTypes, (int) (flags | statement.flags), statement.loc); + + type = ec.Module.PredefinedTypes.BinderFlags.Resolve (); + eclass = Child.eclass; + return this; + } + } + + readonly Arguments arguments; + protected IDynamicBinder binder; + protected Expression binder_expr; + + // Used by BinderFlags + protected CSharpBinderFlags flags; + + TypeSpec binder_type; + TypeParameters context_mvars; + + public DynamicExpressionStatement (IDynamicBinder binder, Arguments args, Location loc) + { + this.binder = binder; + this.arguments = args; + this.loc = loc; + } + + public Arguments Arguments { + get { + return arguments; + } + } + + public override bool ContainsEmitWithAwait () + { + return arguments.ContainsEmitWithAwait (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + ec.Report.Error (1963, loc, "An expression tree cannot contain a dynamic operation"); + return null; + } + + protected override Expression DoResolve (ResolveContext rc) + { + if (DoResolveCore (rc)) + binder_expr = binder.CreateCallSiteBinder (rc, arguments); + + return this; + } + + protected bool DoResolveCore (ResolveContext rc) + { + if (rc.CurrentTypeParameters != null && rc.CurrentTypeParameters[0].IsMethodTypeParameter) + context_mvars = rc.CurrentTypeParameters; + + int errors = rc.Report.Errors; + var pt = rc.Module.PredefinedTypes; + + binder_type = pt.Binder.Resolve (); + pt.CallSite.Resolve (); + pt.CallSiteGeneric.Resolve (); + + eclass = ExprClass.Value; + + if (type == null) + type = rc.BuiltinTypes.Dynamic; + + if (rc.Report.Errors == errors) + return true; + + rc.Report.Error (1969, loc, + "Dynamic operation cannot be compiled without `Microsoft.CSharp.dll' assembly reference"); + return false; + } + + public override void Emit (EmitContext ec) + { + EmitCall (ec, binder_expr, arguments, false); + } + + public override void EmitStatement (EmitContext ec) + { + EmitCall (ec, binder_expr, arguments, true); + } + + protected void EmitCall (EmitContext ec, Expression binder, Arguments arguments, bool isStatement) + { + // + // This method generates all internal infrastructure for a dynamic call. The + // reason why it's quite complicated is the mixture of dynamic and anonymous + // methods. Dynamic itself requires a temporary class (ContainerX) and anonymous + // methods can generate temporary storey as well (AnonStorey). Handling MVAR + // type parameters rewrite is non-trivial in such case as there are various + // combinations possible therefore the mutator is not straightforward. Secondly + // we need to keep both MVAR(possibly VAR for anon storey) and type VAR to emit + // correct Site field type and its access from EmitContext. + // + + int dyn_args_count = arguments == null ? 0 : arguments.Count; + int default_args = isStatement ? 1 : 2; + var module = ec.Module; + + bool has_ref_out_argument = false; + var targs = new TypeExpression[dyn_args_count + default_args]; + targs[0] = new TypeExpression (module.PredefinedTypes.CallSite.TypeSpec, loc); + + TypeExpression[] targs_for_instance = null; + TypeParameterMutator mutator; + + var site_container = ec.CreateDynamicSite (); + + if (context_mvars != null) { + TypeParameters tparam; + TypeContainer sc = site_container; + do { + tparam = sc.CurrentTypeParameters; + sc = sc.Parent; + } while (tparam == null); + + mutator = new TypeParameterMutator (context_mvars, tparam); + + if (!ec.IsAnonymousStoreyMutateRequired) { + targs_for_instance = new TypeExpression[targs.Length]; + targs_for_instance[0] = targs[0]; + } + } else { + mutator = null; + } + + for (int i = 0; i < dyn_args_count; ++i) { + Argument a = arguments[i]; + if (a.ArgType == Argument.AType.Out || a.ArgType == Argument.AType.Ref) + has_ref_out_argument = true; + + var t = a.Type; + + // Convert any internal type like dynamic or null to object + if (t.Kind == MemberKind.InternalCompilerType) + t = ec.BuiltinTypes.Object; + + if (targs_for_instance != null) + targs_for_instance[i + 1] = new TypeExpression (t, loc); + + if (mutator != null) + t = t.Mutate (mutator); + + targs[i + 1] = new TypeExpression (t, loc); + } + + TypeExpr del_type = null; + TypeExpr del_type_instance_access = null; + if (!has_ref_out_argument) { + string d_name = isStatement ? "Action" : "Func"; + + TypeSpec te = null; + Namespace type_ns = module.GlobalRootNamespace.GetNamespace ("System", true); + if (type_ns != null) { + te = type_ns.LookupType (module, d_name, dyn_args_count + default_args, LookupMode.Normal, loc); + } + + if (te != null) { + if (!isStatement) { + var t = type; + if (t.Kind == MemberKind.InternalCompilerType) + t = ec.BuiltinTypes.Object; + + if (targs_for_instance != null) + targs_for_instance[targs_for_instance.Length - 1] = new TypeExpression (t, loc); + + if (mutator != null) + t = t.Mutate (mutator); + + targs[targs.Length - 1] = new TypeExpression (t, loc); + } + + del_type = new GenericTypeExpr (te, new TypeArguments (targs), loc); + if (targs_for_instance != null) + del_type_instance_access = new GenericTypeExpr (te, new TypeArguments (targs_for_instance), loc); + else + del_type_instance_access = del_type; + } + } + + // + // Create custom delegate when no appropriate predefined delegate has been found + // + Delegate d; + if (del_type == null) { + TypeSpec rt = isStatement ? ec.BuiltinTypes.Void : type; + Parameter[] p = new Parameter[dyn_args_count + 1]; + p[0] = new Parameter (targs[0], "p0", Parameter.Modifier.NONE, null, loc); + + var site = ec.CreateDynamicSite (); + int index = site.Containers == null ? 0 : site.Containers.Count; + + if (mutator != null) + rt = mutator.Mutate (rt); + + for (int i = 1; i < dyn_args_count + 1; ++i) { + p[i] = new Parameter (targs[i], "p" + i.ToString ("X"), arguments[i - 1].Modifier, null, loc); + } + + d = new Delegate (site, new TypeExpression (rt, loc), + Modifiers.INTERNAL | Modifiers.COMPILER_GENERATED, + new MemberName ("Container" + index.ToString ("X")), + new ParametersCompiled (p), null); + + d.CreateContainer (); + d.DefineContainer (); + d.Define (); + d.PrepareEmit (); + + site.AddTypeContainer (d); + + // + // Add new container to inflated site container when the + // member cache already exists + // + if (site.CurrentType is InflatedTypeSpec && index > 0) + site.CurrentType.MemberCache.AddMember (d.CurrentType); + + del_type = new TypeExpression (d.CurrentType, loc); + if (targs_for_instance != null) { + del_type_instance_access = null; + } else { + del_type_instance_access = del_type; + } + } else { + d = null; + } + + var site_type_decl = new GenericTypeExpr (module.PredefinedTypes.CallSiteGeneric.TypeSpec, new TypeArguments (del_type), loc); + var field = site_container.CreateCallSiteField (site_type_decl, loc); + if (field == null) + return; + + if (del_type_instance_access == null) { + var dt = d.CurrentType.DeclaringType.MakeGenericType (module, context_mvars.Types); + del_type_instance_access = new TypeExpression (MemberCache.GetMember (dt, d.CurrentType), loc); + } + + var instanceAccessExprType = new GenericTypeExpr (module.PredefinedTypes.CallSiteGeneric.TypeSpec, + new TypeArguments (del_type_instance_access), loc); + + if (instanceAccessExprType.ResolveAsType (ec.MemberContext) == null) + return; + + bool inflate_using_mvar = context_mvars != null && ec.IsAnonymousStoreyMutateRequired; + + TypeSpec gt; + if (inflate_using_mvar || context_mvars == null) { + gt = site_container.CurrentType; + } else { + gt = site_container.CurrentType.MakeGenericType (module, context_mvars.Types); + } + + // When site container already exists the inflated version has to be + // updated manually to contain newly created field + if (gt is InflatedTypeSpec && site_container.AnonymousMethodsCounter > 1) { + var tparams = gt.MemberDefinition.TypeParametersCount > 0 ? gt.MemberDefinition.TypeParameters : TypeParameterSpec.EmptyTypes; + var inflator = new TypeParameterInflator (module, gt, tparams, gt.TypeArguments); + gt.MemberCache.AddMember (field.InflateMember (inflator)); + } + + FieldExpr site_field_expr = new FieldExpr (MemberCache.GetMember (gt, field), loc); + + BlockContext bc = new BlockContext (ec.MemberContext, null, ec.BuiltinTypes.Void); + + Arguments args = new Arguments (1); + args.Add (new Argument (binder)); + StatementExpression s = new StatementExpression (new SimpleAssign (site_field_expr, new Invocation (new MemberAccess (instanceAccessExprType, "Create"), args))); + + using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) { + if (s.Resolve (bc)) { + Statement init = new If (new Binary (Binary.Operator.Equality, site_field_expr, new NullLiteral (loc)), s, loc); + init.Emit (ec); + } + + args = new Arguments (1 + dyn_args_count); + args.Add (new Argument (site_field_expr)); + if (arguments != null) { + int arg_pos = 1; + foreach (Argument a in arguments) { + if (a is NamedArgument) { + // Name is not valid in this context + args.Add (new Argument (a.Expr, a.ArgType)); + } else { + args.Add (a); + } + + if (inflate_using_mvar && a.Type != targs[arg_pos].Type) + a.Expr.Type = targs[arg_pos].Type; + + ++arg_pos; + } + } + + Expression target = new DelegateInvocation (new MemberAccess (site_field_expr, "Target", loc).Resolve (bc), args, false, loc).Resolve (bc); + if (target != null) + target.Emit (ec); + } + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + arguments.FlowAnalysis (fc); + } + + public static MemberAccess GetBinderNamespace (Location loc) + { + return new MemberAccess (new MemberAccess ( + new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "Microsoft", loc), "CSharp", loc), "RuntimeBinder", loc); + } + + protected MemberAccess GetBinder (string name, Location loc) + { + return new MemberAccess (new TypeExpression (binder_type, loc), name, loc); + } + } + + // + // Dynamic member access compound assignment for events + // + class DynamicEventCompoundAssign : ExpressionStatement + { + class IsEvent : DynamicExpressionStatement, IDynamicBinder + { + string name; + + public IsEvent (string name, Arguments args, Location loc) + : base (null, args, loc) + { + this.name = name; + binder = this; + } + + public Expression CreateCallSiteBinder (ResolveContext ec, Arguments args) + { + type = ec.BuiltinTypes.Bool; + + Arguments binder_args = new Arguments (3); + + binder_args.Add (new Argument (new BinderFlags (0, this))); + binder_args.Add (new Argument (new StringLiteral (ec.BuiltinTypes, name, loc))); + binder_args.Add (new Argument (new TypeOf (ec.CurrentType, loc))); + + return new Invocation (GetBinder ("IsEvent", loc), binder_args); + } + } + + Expression condition; + ExpressionStatement invoke, assign; + + public DynamicEventCompoundAssign (string name, Arguments args, ExpressionStatement assignment, ExpressionStatement invoke, Location loc) + { + condition = new IsEvent (name, args, loc); + this.invoke = invoke; + this.assign = assignment; + this.loc = loc; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + return condition.CreateExpressionTree (ec); + } + + protected override Expression DoResolve (ResolveContext rc) + { + type = rc.BuiltinTypes.Dynamic; + eclass = ExprClass.Value; + condition = condition.Resolve (rc); + return this; + } + + public override void Emit (EmitContext ec) + { + var rc = new ResolveContext (ec.MemberContext); + var expr = new Conditional (new BooleanExpression (condition), invoke, assign, loc).Resolve (rc); + expr.Emit (ec); + } + + public override void EmitStatement (EmitContext ec) + { + var stmt = new If (condition, new StatementExpression (invoke), new StatementExpression (assign), loc); + using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) { + stmt.Emit (ec); + } + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + invoke.FlowAnalysis (fc); + } + } + + class DynamicConversion : DynamicExpressionStatement, IDynamicBinder + { + public DynamicConversion (TypeSpec targetType, CSharpBinderFlags flags, Arguments args, Location loc) + : base (null, args, loc) + { + type = targetType; + base.flags = flags; + base.binder = this; + } + + public Expression CreateCallSiteBinder (ResolveContext ec, Arguments args) + { + Arguments binder_args = new Arguments (3); + + flags |= ec.HasSet (ResolveContext.Options.CheckedScope) ? CSharpBinderFlags.CheckedContext : 0; + + binder_args.Add (new Argument (new BinderFlags (flags, this))); + binder_args.Add (new Argument (new TypeOf (type, loc))); + binder_args.Add (new Argument (new TypeOf (ec.CurrentType, loc))); + return new Invocation (GetBinder ("Convert", loc), binder_args); + } + } + + class DynamicConstructorBinder : DynamicExpressionStatement, IDynamicBinder + { + public DynamicConstructorBinder (TypeSpec type, Arguments args, Location loc) + : base (null, args, loc) + { + this.type = type; + base.binder = this; + } + + public Expression CreateCallSiteBinder (ResolveContext ec, Arguments args) + { + Arguments binder_args = new Arguments (3); + + binder_args.Add (new Argument (new BinderFlags (0, this))); + binder_args.Add (new Argument (new TypeOf (ec.CurrentType, loc))); + binder_args.Add (new Argument (new ImplicitlyTypedArrayCreation (args.CreateDynamicBinderArguments (ec), loc))); + + return new Invocation (GetBinder ("InvokeConstructor", loc), binder_args); + } + } + + class DynamicIndexBinder : DynamicMemberAssignable + { + bool can_be_mutator; + + public DynamicIndexBinder (Arguments args, Location loc) + : base (args, loc) + { + } + + public DynamicIndexBinder (CSharpBinderFlags flags, Arguments args, Location loc) + : this (args, loc) + { + base.flags = flags; + } + + protected override Expression DoResolve (ResolveContext ec) + { + can_be_mutator = true; + return base.DoResolve (ec); + } + + protected override Expression CreateCallSiteBinder (ResolveContext ec, Arguments args, bool isSet) + { + Arguments binder_args = new Arguments (3); + + binder_args.Add (new Argument (new BinderFlags (flags, this))); + binder_args.Add (new Argument (new TypeOf (ec.CurrentType, loc))); + binder_args.Add (new Argument (new ImplicitlyTypedArrayCreation (args.CreateDynamicBinderArguments (ec), loc))); + + isSet |= (flags & CSharpBinderFlags.ValueFromCompoundAssignment) != 0; + return new Invocation (GetBinder (isSet ? "SetIndex" : "GetIndex", loc), binder_args); + } + + protected override Arguments CreateSetterArguments (ResolveContext rc, Expression rhs) + { + // + // Indexer has arguments which complicates things as the setter and getter + // are called in two steps when unary mutator is used. We have to make a + // copy of all variable arguments to not duplicate any side effect. + // + // ++d[++arg, Foo ()] + // + + if (!can_be_mutator) + return base.CreateSetterArguments (rc, rhs); + + var setter_args = new Arguments (Arguments.Count + 1); + for (int i = 0; i < Arguments.Count; ++i) { + var expr = Arguments[i].Expr; + + if (expr is Constant || expr is VariableReference || expr is This) { + setter_args.Add (Arguments [i]); + continue; + } + + LocalVariable temp = LocalVariable.CreateCompilerGenerated (expr.Type, rc.CurrentBlock, loc); + expr = new SimpleAssign (temp.CreateReferenceExpression (rc, expr.Location), expr).Resolve (rc); + Arguments[i].Expr = temp.CreateReferenceExpression (rc, expr.Location).Resolve (rc); + setter_args.Add (Arguments [i].Clone (expr)); + } + + setter_args.Add (new Argument (rhs)); + return setter_args; + } + } + + class DynamicInvocation : DynamicExpressionStatement, IDynamicBinder + { + readonly ATypeNameExpression member; + + public DynamicInvocation (ATypeNameExpression member, Arguments args, Location loc) + : base (null, args, loc) + { + base.binder = this; + this.member = member; + } + + public static DynamicInvocation CreateSpecialNameInvoke (ATypeNameExpression member, Arguments args, Location loc) + { + return new DynamicInvocation (member, args, loc) { + flags = CSharpBinderFlags.InvokeSpecialName + }; + } + + public Expression CreateCallSiteBinder (ResolveContext ec, Arguments args) + { + Arguments binder_args = new Arguments (member != null ? 5 : 3); + bool is_member_access = member is MemberAccess; + + CSharpBinderFlags call_flags; + if (!is_member_access && member is SimpleName) { + call_flags = CSharpBinderFlags.InvokeSimpleName; + is_member_access = true; + } else { + call_flags = 0; + } + + binder_args.Add (new Argument (new BinderFlags (call_flags, this))); + + if (is_member_access) + binder_args.Add (new Argument (new StringLiteral (ec.BuiltinTypes, member.Name, member.Location))); + + if (member != null && member.HasTypeArguments) { + TypeArguments ta = member.TypeArguments; + if (ta.Resolve (ec)) { + var targs = new ArrayInitializer (ta.Count, loc); + foreach (TypeSpec t in ta.Arguments) + targs.Add (new TypeOf (t, loc)); + + binder_args.Add (new Argument (new ImplicitlyTypedArrayCreation (targs, loc))); + } + } else if (is_member_access) { + binder_args.Add (new Argument (new NullLiteral (loc))); + } + + binder_args.Add (new Argument (new TypeOf (ec.CurrentType, loc))); + + Expression real_args; + if (args == null) { + // Cannot be null because .NET trips over + real_args = new ArrayCreation ( + new MemberAccess (GetBinderNamespace (loc), "CSharpArgumentInfo", loc), + new ArrayInitializer (0, loc), loc); + } else { + real_args = new ImplicitlyTypedArrayCreation (args.CreateDynamicBinderArguments (ec), loc); + } + + binder_args.Add (new Argument (real_args)); + + return new Invocation (GetBinder (is_member_access ? "InvokeMember" : "Invoke", loc), binder_args); + } + + public override void EmitStatement (EmitContext ec) + { + flags |= CSharpBinderFlags.ResultDiscarded; + base.EmitStatement (ec); + } + } + + class DynamicMemberBinder : DynamicMemberAssignable + { + readonly string name; + + public DynamicMemberBinder (string name, Arguments args, Location loc) + : base (args, loc) + { + this.name = name; + } + + public DynamicMemberBinder (string name, CSharpBinderFlags flags, Arguments args, Location loc) + : this (name, args, loc) + { + base.flags = flags; + } + + protected override Expression CreateCallSiteBinder (ResolveContext ec, Arguments args, bool isSet) + { + Arguments binder_args = new Arguments (4); + + binder_args.Add (new Argument (new BinderFlags (flags, this))); + binder_args.Add (new Argument (new StringLiteral (ec.BuiltinTypes, name, loc))); + binder_args.Add (new Argument (new TypeOf (ec.CurrentType, loc))); + binder_args.Add (new Argument (new ImplicitlyTypedArrayCreation (args.CreateDynamicBinderArguments (ec), loc))); + + isSet |= (flags & CSharpBinderFlags.ValueFromCompoundAssignment) != 0; + return new Invocation (GetBinder (isSet ? "SetMember" : "GetMember", loc), binder_args); + } + } + + // + // Any member binder which can be source and target of assignment + // + abstract class DynamicMemberAssignable : DynamicExpressionStatement, IDynamicBinder, IAssignMethod + { + Expression setter; + Arguments setter_args; + + protected DynamicMemberAssignable (Arguments args, Location loc) + : base (null, args, loc) + { + base.binder = this; + } + + public Expression CreateCallSiteBinder (ResolveContext ec, Arguments args) + { + // + // DoResolve always uses getter + // + return CreateCallSiteBinder (ec, args, false); + } + + protected abstract Expression CreateCallSiteBinder (ResolveContext ec, Arguments args, bool isSet); + + protected virtual Arguments CreateSetterArguments (ResolveContext rc, Expression rhs) + { + var setter_args = new Arguments (Arguments.Count + 1); + setter_args.AddRange (Arguments); + setter_args.Add (new Argument (rhs)); + return setter_args; + } + + public override Expression DoResolveLValue (ResolveContext rc, Expression right_side) + { + if (right_side == EmptyExpression.OutAccess) { + right_side.DoResolveLValue (rc, this); + return null; + } + + if (DoResolveCore (rc)) { + setter_args = CreateSetterArguments (rc, right_side); + setter = CreateCallSiteBinder (rc, setter_args, true); + } + + eclass = ExprClass.Variable; + return this; + } + + public override void Emit (EmitContext ec) + { + // It's null for ResolveLValue used without assignment + if (binder_expr == null) + EmitCall (ec, setter, Arguments, false); + else + base.Emit (ec); + } + + public override void EmitStatement (EmitContext ec) + { + // It's null for ResolveLValue used without assignment + if (binder_expr == null) + EmitCall (ec, setter, Arguments, true); + else + base.EmitStatement (ec); + } + + #region IAssignMethod Members + + public void Emit (EmitContext ec, bool leave_copy) + { + throw new NotImplementedException (); + } + + public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound) + { + EmitCall (ec, setter, setter_args, !leave_copy); + } + + #endregion + } + + class DynamicUnaryConversion : DynamicExpressionStatement, IDynamicBinder + { + readonly string name; + + public DynamicUnaryConversion (string name, Arguments args, Location loc) + : base (null, args, loc) + { + this.name = name; + base.binder = this; + } + + public static DynamicUnaryConversion CreateIsTrue (ResolveContext rc, Arguments args, Location loc) + { + return new DynamicUnaryConversion ("IsTrue", args, loc) { type = rc.BuiltinTypes.Bool }; + } + + public static DynamicUnaryConversion CreateIsFalse (ResolveContext rc, Arguments args, Location loc) + { + return new DynamicUnaryConversion ("IsFalse", args, loc) { type = rc.BuiltinTypes.Bool }; + } + + public Expression CreateCallSiteBinder (ResolveContext ec, Arguments args) + { + Arguments binder_args = new Arguments (4); + + MemberAccess sle = new MemberAccess (new MemberAccess ( + new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Linq", loc), "Expressions", loc); + + var flags = ec.HasSet (ResolveContext.Options.CheckedScope) ? CSharpBinderFlags.CheckedContext : 0; + + binder_args.Add (new Argument (new BinderFlags (flags, this))); + binder_args.Add (new Argument (new MemberAccess (new MemberAccess (sle, "ExpressionType", loc), name, loc))); + binder_args.Add (new Argument (new TypeOf (ec.CurrentType, loc))); + binder_args.Add (new Argument (new ImplicitlyTypedArrayCreation (args.CreateDynamicBinderArguments (ec), loc))); + + return new Invocation (GetBinder ("UnaryOperation", loc), binder_args); + } + } + + sealed class DynamicSiteClass : HoistedStoreyClass + { + public DynamicSiteClass (TypeDefinition parent, MemberBase host, TypeParameters tparams) + : base (parent, MakeMemberName (host, "DynamicSite", parent.DynamicSitesCounter, tparams, Location.Null), tparams, Modifiers.STATIC, MemberKind.Class) + { + parent.DynamicSitesCounter++; + } + + public FieldSpec CreateCallSiteField (FullNamedExpression type, Location loc) + { + int index = AnonymousMethodsCounter++; + Field f = new HoistedField (this, type, Modifiers.PUBLIC | Modifiers.STATIC, "Site" + index.ToString ("X"), null, loc); + f.Define (); + + AddField (f); + return f.Spec; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/ecore.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/ecore.cs new file mode 100644 index 000000000..d1e2c3b84 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/ecore.cs @@ -0,0 +1,7426 @@ +// +// ecore.cs: Core of the Expression representation for the intermediate tree. +// +// Author: +// Miguel de Icaza (miguel@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// Copyright 2001, 2002, 2003 Ximian, Inc. +// Copyright 2003-2008 Novell, Inc. +// Copyright 2011-2012 Xamarin Inc. +// +// + +using System; +using System.Collections.Generic; +using System.Text; +using SLE = System.Linq.Expressions; +using System.Linq; + +#if STATIC +using IKVM.Reflection; +using IKVM.Reflection.Emit; +#else +using System.Reflection; +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp { + + /// + /// The ExprClass class contains the is used to pass the + /// classification of an expression (value, variable, namespace, + /// type, method group, property access, event access, indexer access, + /// nothing). + /// + public enum ExprClass : byte { + Unresolved = 0, + + Value, + Variable, + Namespace, + Type, + TypeParameter, + MethodGroup, + PropertyAccess, + EventAccess, + IndexerAccess, + Nothing, + } + + /// + /// This is used to tell Resolve in which types of expressions we're + /// interested. + /// + [Flags] + public enum ResolveFlags { + // Returns Value, Variable, PropertyAccess, EventAccess or IndexerAccess. + VariableOrValue = 1, + + // Returns a type expression. + Type = 1 << 1, + + // Returns a method group. + MethodGroup = 1 << 2, + + TypeParameter = 1 << 3, + + // Mask of all the expression class flags. + MaskExprClass = VariableOrValue | Type | MethodGroup | TypeParameter, + } + + // + // This is just as a hint to AddressOf of what will be done with the + // address. + [Flags] + public enum AddressOp { + Store = 1, + Load = 2, + LoadStore = 3 + }; + + /// + /// This interface is implemented by variables + /// + public interface IMemoryLocation { + /// + /// The AddressOf method should generate code that loads + /// the address of the object and leaves it on the stack. + /// + /// The `mode' argument is used to notify the expression + /// of whether this will be used to read from the address or + /// write to the address. + /// + /// This is just a hint that can be used to provide good error + /// reporting, and should have no other side effects. + /// + void AddressOf (EmitContext ec, AddressOp mode); + } + + // + // An expressions resolved as a direct variable reference + // + public interface IVariableReference : IFixedExpression + { + bool IsHoisted { get; } + string Name { get; } + VariableInfo VariableInfo { get; } + + void SetHasAddressTaken (); + } + + // + // Implemented by an expression which could be or is always + // fixed + // + public interface IFixedExpression + { + bool IsFixed { get; } + } + + public interface IExpressionCleanup + { + void EmitCleanup (EmitContext ec); + } + + /// + /// Base class for expressions + /// + public abstract class Expression { + public ExprClass eclass; + protected TypeSpec type; + protected Location loc; + + public TypeSpec Type { + get { return type; } + set { type = value; } + } + + public virtual bool IsSideEffectFree { + get { + return false; + } + } + + public Location Location { + get { return loc; } + } + + public virtual bool IsNull { + get { + return false; + } + } + + // + // Used to workaround parser limitation where we cannot get + // start of statement expression location + // + public virtual Location StartLocation { + get { + return loc; + } + } + + public virtual MethodGroupExpr CanReduceLambda (AnonymousMethodBody body) + { + // + // Return method-group expression when the expression can be used as + // lambda replacement. A good example is array sorting where instead of + // code like + // + // Array.Sort (s, (a, b) => String.Compare (a, b)); + // + // we can use method group directly + // + // Array.Sort (s, String.Compare); + // + // Correct overload will be used because we do the reduction after + // best candidate was found. + // + return null; + } + + // + // Returns true when the expression during Emit phase breaks stack + // by using await expression + // + public virtual bool ContainsEmitWithAwait () + { + return false; + } + + /// + /// Performs semantic analysis on the Expression + /// + /// + /// + /// The Resolve method is invoked to perform the semantic analysis + /// on the node. + /// + /// The return value is an expression (it can be the + /// same expression in some cases) or a new + /// expression that better represents this node. + /// + /// For example, optimizations of Unary (LiteralInt) + /// would return a new LiteralInt with a negated + /// value. + /// + /// If there is an error during semantic analysis, + /// then an error should be reported (using Report) + /// and a null value should be returned. + /// + /// There are two side effects expected from calling + /// Resolve(): the the field variable "eclass" should + /// be set to any value of the enumeration + /// `ExprClass' and the type variable should be set + /// to a valid type (this is the type of the + /// expression). + /// + protected abstract Expression DoResolve (ResolveContext rc); + + public virtual Expression DoResolveLValue (ResolveContext rc, Expression right_side) + { + return null; + } + + // + // This is used if the expression should be resolved as a type or namespace name. + // the default implementation fails. + // + public virtual TypeSpec ResolveAsType (IMemberContext mc, bool allowUnboundTypeArguments = false) + { + ResolveContext ec = new ResolveContext (mc); + Expression e = Resolve (ec); + if (e != null) + e.Error_UnexpectedKind (ec, ResolveFlags.Type, loc); + + return null; + } + + public static void ErrorIsInaccesible (IMemberContext rc, string member, Location loc) + { + rc.Module.Compiler.Report.Error (122, loc, "`{0}' is inaccessible due to its protection level", member); + } + + public void Error_ExpressionMustBeConstant (ResolveContext rc, Location loc, string e_name) + { + rc.Report.Error (133, loc, "The expression being assigned to `{0}' must be constant", e_name); + } + + public void Error_ConstantCanBeInitializedWithNullOnly (ResolveContext rc, TypeSpec type, Location loc, string name) + { + rc.Report.Error (134, loc, "A constant `{0}' of reference type `{1}' can only be initialized with null", + name, type.GetSignatureForError ()); + } + + protected virtual void Error_InvalidExpressionStatement (Report report, Location loc) + { + report.Error (201, loc, "Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement"); + } + + public void Error_InvalidExpressionStatement (BlockContext bc) + { + Error_InvalidExpressionStatement (bc.Report, loc); + } + + public void Error_InvalidExpressionStatement (Report report) + { + Error_InvalidExpressionStatement (report, loc); + } + + public static void Error_VoidInvalidInTheContext (Location loc, Report Report) + { + Report.Error (1547, loc, "Keyword `void' cannot be used in this context"); + } + + public virtual void Error_ValueCannotBeConverted (ResolveContext ec, TypeSpec target, bool expl) + { + Error_ValueCannotBeConvertedCore (ec, loc, target, expl); + } + + protected void Error_ValueCannotBeConvertedCore (ResolveContext ec, Location loc, TypeSpec target, bool expl) + { + // The error was already reported as CS1660 + if (type == InternalType.AnonymousMethod) + return; + + if (type == InternalType.ErrorType || target == InternalType.ErrorType) + return; + + string from_type = type.GetSignatureForError (); + string to_type = target.GetSignatureForError (); + if (from_type == to_type) { + from_type = type.GetSignatureForErrorIncludingAssemblyName (); + to_type = target.GetSignatureForErrorIncludingAssemblyName (); + } + + if (expl) { + ec.Report.Error (30, loc, "Cannot convert type `{0}' to `{1}'", + from_type, to_type); + return; + } + + ec.Report.DisableReporting (); + bool expl_exists = Convert.ExplicitConversion (ec, this, target, Location.Null) != null; + ec.Report.EnableReporting (); + + if (expl_exists) { + ec.Report.Error (266, loc, + "Cannot implicitly convert type `{0}' to `{1}'. An explicit conversion exists (are you missing a cast?)", + from_type, to_type); + } else { + ec.Report.Error (29, loc, "Cannot implicitly convert type `{0}' to `{1}'", + from_type, to_type); + } + } + + public void Error_TypeArgumentsCannotBeUsed (IMemberContext context, MemberSpec member, Location loc) + { + // Better message for possible generic expressions + if (member != null && (member.Kind & MemberKind.GenericMask) != 0) { + var report = context.Module.Compiler.Report; + report.SymbolRelatedToPreviousError (member); + if (member is TypeSpec) + member = ((TypeSpec) member).GetDefinition (); + else + member = ((MethodSpec) member).GetGenericMethodDefinition (); + + string name = member.Kind == MemberKind.Method ? "method" : "type"; + if (member.IsGeneric) { + report.Error (305, loc, "Using the generic {0} `{1}' requires `{2}' type argument(s)", + name, member.GetSignatureForError (), member.Arity.ToString ()); + } else { + report.Error (308, loc, "The non-generic {0} `{1}' cannot be used with the type arguments", + name, member.GetSignatureForError ()); + } + } else { + Error_TypeArgumentsCannotBeUsed (context, ExprClassName, GetSignatureForError (), loc); + } + } + + public static void Error_TypeArgumentsCannotBeUsed (IMemberContext context, string exprType, string name, Location loc) + { + context.Module.Compiler.Report.Error (307, loc, "The {0} `{1}' cannot be used with type arguments", + exprType, name); + } + + protected virtual void Error_TypeDoesNotContainDefinition (ResolveContext ec, TypeSpec type, string name) + { + Error_TypeDoesNotContainDefinition (ec, loc, type, name); + } + + public static void Error_TypeDoesNotContainDefinition (ResolveContext ec, Location loc, TypeSpec type, string name) + { + ec.Report.SymbolRelatedToPreviousError (type); + ec.Report.Error (117, loc, "`{0}' does not contain a definition for `{1}'", + type.GetSignatureForError (), name); + } + + public virtual void Error_ValueAssignment (ResolveContext rc, Expression rhs) + { + if (rhs == EmptyExpression.LValueMemberAccess || rhs == EmptyExpression.LValueMemberOutAccess) { + // Already reported as CS1612 + } else if (rhs == EmptyExpression.OutAccess) { + rc.Report.Error (1510, loc, "A ref or out argument must be an assignable variable"); + } else { + rc.Report.Error (131, loc, "The left-hand side of an assignment must be a variable, a property or an indexer"); + } + } + + protected void Error_VoidPointerOperation (ResolveContext rc) + { + rc.Report.Error (242, loc, "The operation in question is undefined on void pointers"); + } + + public static void Warning_UnreachableExpression (ResolveContext rc, Location loc) + { + rc.Report.Warning (429, 4, loc, "Unreachable expression code detected"); + } + + public ResolveFlags ExprClassToResolveFlags { + get { + switch (eclass) { + case ExprClass.Type: + case ExprClass.Namespace: + return ResolveFlags.Type; + + case ExprClass.MethodGroup: + return ResolveFlags.MethodGroup; + + case ExprClass.TypeParameter: + return ResolveFlags.TypeParameter; + + case ExprClass.Value: + case ExprClass.Variable: + case ExprClass.PropertyAccess: + case ExprClass.EventAccess: + case ExprClass.IndexerAccess: + return ResolveFlags.VariableOrValue; + + default: + throw new InternalErrorException (loc.ToString () + " " + GetType () + " ExprClass is Invalid after resolve"); + } + } + } + + // + // Implements identical simple name and type-name resolution + // + public Expression ProbeIdenticalTypeName (ResolveContext rc, Expression left, SimpleName name) + { + var t = left.Type; + if (t.Kind == MemberKind.InternalCompilerType || t is ElementTypeSpec || t.Arity > 0) + return left; + + // In a member access of the form E.I, if E is a single identifier, and if the meaning of E as a simple-name is + // a constant, field, property, local variable, or parameter with the same type as the meaning of E as a type-name + + if (left is MemberExpr || left is VariableReference) { + var identical_type = rc.LookupNamespaceOrType (name.Name, 0, LookupMode.Probing, loc) as TypeExpr; + if (identical_type != null && identical_type.Type == left.Type) + return identical_type; + } + + return left; + } + + public virtual string GetSignatureForError () + { + return type.GetDefinition ().GetSignatureForError (); + } + + public static bool IsNeverNull (Expression expr) + { + if (expr is This || expr is New || expr is ArrayCreation || expr is DelegateCreation || expr is ConditionalMemberAccess) + return true; + + var c = expr as Constant; + if (c != null) + return !c.IsNull; + + var tc = expr as TypeCast; + if (tc != null) + return IsNeverNull (tc.Child); + + return false; + } + + protected static bool IsNullPropagatingValid (TypeSpec type) + { + switch (type.Kind) { + case MemberKind.Struct: + return type.IsNullableType; + case MemberKind.Enum: + case MemberKind.Void: + case MemberKind.PointerType: + return false; + case MemberKind.InternalCompilerType: + return type.BuiltinType == BuiltinTypeSpec.Type.Dynamic; + default: + return true; + } + } + + public virtual bool HasConditionalAccess () + { + return false; + } + + protected static TypeSpec LiftMemberType (ResolveContext rc, TypeSpec type) + { + return TypeSpec.IsValueType (type) && !type.IsNullableType ? + Nullable.NullableInfo.MakeType (rc.Module, type) : + type; + } + + /// + /// Resolves an expression and performs semantic analysis on it. + /// + /// + /// + /// Currently Resolve wraps DoResolve to perform sanity + /// checking and assertion checking on what we expect from Resolve. + /// + public Expression Resolve (ResolveContext ec, ResolveFlags flags) + { + if (eclass != ExprClass.Unresolved) { + if ((flags & ExprClassToResolveFlags) == 0) { + Error_UnexpectedKind (ec, flags, loc); + return null; + } + + return this; + } + + Expression e; + try { + e = DoResolve (ec); + + if (e == null) + return null; + + if ((flags & e.ExprClassToResolveFlags) == 0) { + e.Error_UnexpectedKind (ec, flags, loc); + return null; + } + + if (e.type == null) + throw new InternalErrorException ("Expression `{0}' didn't set its type in DoResolve", e.GetType ()); + + return e; + } catch (Exception ex) { + if (loc.IsNull || ec.Module.Compiler.Settings.BreakOnInternalError || ex is CompletionResult || ec.Report.IsDisabled || ex is FatalException || + ec.Report.Printer is NullReportPrinter) + throw; + + ec.Report.Error (584, loc, "Internal compiler error: {0}", ex.Message); + return ErrorExpression.Instance; // TODO: Add location + } + } + + /// + /// Resolves an expression and performs semantic analysis on it. + /// + public Expression Resolve (ResolveContext rc) + { + return Resolve (rc, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup); + } + + /// + /// Resolves an expression for LValue assignment + /// + /// + /// + /// Currently ResolveLValue wraps DoResolveLValue to perform sanity + /// checking and assertion checking on what we expect from Resolve + /// + public Expression ResolveLValue (ResolveContext ec, Expression right_side) + { + int errors = ec.Report.Errors; + bool out_access = right_side == EmptyExpression.OutAccess; + + Expression e = DoResolveLValue (ec, right_side); + + if (e != null && out_access && !(e is IMemoryLocation)) { + // FIXME: There's no problem with correctness, the 'Expr = null' handles that. + // Enabling this 'throw' will "only" result in deleting useless code elsewhere, + + //throw new InternalErrorException ("ResolveLValue didn't return an IMemoryLocation: " + + // e.GetType () + " " + e.GetSignatureForError ()); + e = null; + } + + if (e == null) { + if (errors == ec.Report.Errors) { + Error_ValueAssignment (ec, right_side); + } + return null; + } + + if (e.eclass == ExprClass.Unresolved) + throw new Exception ("Expression " + e + " ExprClass is Invalid after resolve"); + + if ((e.type == null) && !(e is GenericTypeExpr)) + throw new Exception ("Expression " + e + " did not set its type after Resolve"); + + return e; + } + + public Constant ResolveLabelConstant (ResolveContext rc) + { + var expr = Resolve (rc); + if (expr == null) + return null; + + Constant c = expr as Constant; + if (c == null) { + if (expr.type != InternalType.ErrorType) + rc.Report.Error (150, expr.StartLocation, "A constant value is expected"); + + return null; + } + + return c; + } + + public virtual void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) + { + if (Attribute.IsValidArgumentType (parameterType)) { + rc.Module.Compiler.Report.Error (182, loc, + "An attribute argument must be a constant expression, typeof expression or array creation expression"); + } else { + rc.Module.Compiler.Report.Error (181, loc, + "Attribute constructor parameter has type `{0}', which is not a valid attribute parameter type", + targetType.GetSignatureForError ()); + } + } + + /// + /// Emits the code for the expression + /// + /// + /// + /// The Emit method is invoked to generate the code + /// for the expression. + /// + public abstract void Emit (EmitContext ec); + + + // Emit code to branch to @target if this expression is equivalent to @on_true. + // The default implementation is to emit the value, and then emit a brtrue or brfalse. + // Subclasses can provide more efficient implementations, but those MUST be equivalent, + // including the use of conditional branches. Note also that a branch MUST be emitted + public virtual void EmitBranchable (EmitContext ec, Label target, bool on_true) + { + Emit (ec); + ec.Emit (on_true ? OpCodes.Brtrue : OpCodes.Brfalse, target); + } + + // Emit this expression for its side effects, not for its value. + // The default implementation is to emit the value, and then throw it away. + // Subclasses can provide more efficient implementations, but those MUST be equivalent + public virtual void EmitSideEffect (EmitContext ec) + { + Emit (ec); + ec.Emit (OpCodes.Pop); + } + + // + // Emits the expression into temporary field variable. The method + // should be used for await expressions only + // + public virtual Expression EmitToField (EmitContext ec) + { + // + // This is the await prepare Emit method. When emitting code like + // a + b we emit code like + // + // a.Emit () + // b.Emit () + // Opcodes.Add + // + // For await a + await b we have to interfere the flow to keep the + // stack clean because await yields from the expression. The emit + // then changes to + // + // a = a.EmitToField () // a is changed to temporary field access + // b = b.EmitToField () + // a.Emit () + // b.Emit () + // Opcodes.Add + // + // + // The idea is to emit expression and leave the stack empty with + // result value still available. + // + // Expressions should override this default implementation when + // optimized version can be provided (e.g. FieldExpr) + // + // + // We can optimize for side-effect free expressions, they can be + // emitted out of order + // + if (IsSideEffectFree) + return this; + + bool needs_temporary = ContainsEmitWithAwait (); + if (!needs_temporary) + ec.EmitThis (); + + // Emit original code + var field = EmitToFieldSource (ec); + if (field == null) { + // + // Store the result to temporary field when we + // cannot load `this' directly + // + field = ec.GetTemporaryField (type); + if (needs_temporary) { + // + // Create temporary local (we cannot load `this' before Emit) + // + var temp = ec.GetTemporaryLocal (type); + ec.Emit (OpCodes.Stloc, temp); + + ec.EmitThis (); + ec.Emit (OpCodes.Ldloc, temp); + field.EmitAssignFromStack (ec); + + ec.FreeTemporaryLocal (temp, type); + } else { + field.EmitAssignFromStack (ec); + } + } + + return field; + } + + protected virtual FieldExpr EmitToFieldSource (EmitContext ec) + { + // + // Default implementation calls Emit method + // + Emit (ec); + return null; + } + + protected static void EmitExpressionsList (EmitContext ec, List expressions) + { + if (ec.HasSet (BuilderContext.Options.AsyncBody)) { + bool contains_await = false; + + for (int i = 1; i < expressions.Count; ++i) { + if (expressions[i].ContainsEmitWithAwait ()) { + contains_await = true; + break; + } + } + + if (contains_await) { + for (int i = 0; i < expressions.Count; ++i) { + expressions[i] = expressions[i].EmitToField (ec); + } + } + } + + for (int i = 0; i < expressions.Count; ++i) { + expressions[i].Emit (ec); + } + } + + /// + /// Protected constructor. Only derivate types should + /// be able to be created + /// + + protected Expression () + { + } + + /// + /// Returns a fully formed expression after a MemberLookup + /// + /// + static Expression ExprClassFromMemberInfo (MemberSpec spec, Location loc) + { + if (spec is EventSpec) + return new EventExpr ((EventSpec) spec, loc); + if (spec is ConstSpec) + return new ConstantExpr ((ConstSpec) spec, loc); + if (spec is FieldSpec) + return new FieldExpr ((FieldSpec) spec, loc); + if (spec is PropertySpec) + return new PropertyExpr ((PropertySpec) spec, loc); + if (spec is TypeSpec) + return new TypeExpression (((TypeSpec) spec), loc); + + return null; + } + + public static MethodSpec ConstructorLookup (ResolveContext rc, TypeSpec type, ref Arguments args, Location loc) + { + var ctors = MemberCache.FindMembers (type, Constructor.ConstructorName, true); + if (ctors == null) { + switch (type.Kind) { + case MemberKind.Struct: + rc.Report.SymbolRelatedToPreviousError (type); + // Report meaningful error for struct as they always have default ctor in C# context + OverloadResolver.Error_ConstructorMismatch (rc, type, args == null ? 0 : args.Count, loc); + break; + case MemberKind.MissingType: + case MemberKind.InternalCompilerType: +// LAMESPEC: dynamic is not really object +// if (type.BuiltinType == BuiltinTypeSpec.Type.Object) +// goto default; + break; + default: + rc.Report.SymbolRelatedToPreviousError (type); + rc.Report.Error (143, loc, "The class `{0}' has no constructors defined", + type.GetSignatureForError ()); + break; + } + + return null; + } + + var r = new OverloadResolver (ctors, OverloadResolver.Restrictions.NoBaseMembers, loc); + if (!rc.HasSet (ResolveContext.Options.BaseInitializer)) { + r.InstanceQualifier = new ConstructorInstanceQualifier (type); + } + + return r.ResolveMember (rc, ref args); + } + + [Flags] + public enum MemberLookupRestrictions + { + None = 0, + InvocableOnly = 1, + ExactArity = 1 << 2, + ReadAccess = 1 << 3, + EmptyArguments = 1 << 4, + IgnoreArity = 1 << 5, + IgnoreAmbiguity = 1 << 6 + } + + // + // Lookup type `queried_type' for code in class `container_type' with a qualifier of + // `qualifier_type' or null to lookup members in the current class. + // + public static Expression MemberLookup (IMemberContext rc, bool errorMode, TypeSpec queried_type, string name, int arity, MemberLookupRestrictions restrictions, Location loc) + { + var members = MemberCache.FindMembers (queried_type, name, false); + if (members == null) + return null; + + MemberSpec non_method = null; + MemberSpec ambig_non_method = null; + do { + for (int i = 0; i < members.Count; ++i) { + var member = members[i]; + + // HACK: for events because +=/-= can appear at same class only, should use OverrideToBase there + if ((member.Modifiers & Modifiers.OVERRIDE) != 0 && member.Kind != MemberKind.Event) + continue; + + if ((member.Modifiers & Modifiers.BACKING_FIELD) != 0 || member.Kind == MemberKind.Operator) + continue; + + if ((arity > 0 || (restrictions & MemberLookupRestrictions.ExactArity) != 0) && member.Arity != arity) + continue; + + if (!errorMode) { + if (!member.IsAccessible (rc)) + continue; + + // + // With runtime binder we can have a situation where queried type is inaccessible + // because it came via dynamic object, the check about inconsisted accessibility + // had no effect as the type was unknown during compilation + // + // class A { + // private class N { } + // + // public dynamic Foo () + // { + // return new N (); + // } + // } + // + if (rc.Module.Compiler.IsRuntimeBinder && !member.DeclaringType.IsAccessible (rc)) + continue; + } + + if ((restrictions & MemberLookupRestrictions.InvocableOnly) != 0) { + if (member is MethodSpec) { + // + // Interface members that are hidden by class members are removed from the set. This + // step only has an effect if T is a type parameter and T has both an effective base + // class other than object and a non-empty effective interface set + // + var tps = queried_type as TypeParameterSpec; + if (tps != null && tps.HasTypeConstraint) + members = RemoveHiddenTypeParameterMethods (members); + + return new MethodGroupExpr (members, queried_type, loc); + } + + if (!Invocation.IsMemberInvocable (member)) + continue; + } + + if (non_method == null || member is MethodSpec || non_method.IsNotCSharpCompatible) { + non_method = member; + } else if (!errorMode && !member.IsNotCSharpCompatible) { + // + // Interface members that are hidden by class members are removed from the set when T is a type parameter and + // T has both an effective base class other than object and a non-empty effective interface set. + // + // The spec has more complex rules but we simply remove all members declared in an interface declaration. + // + var tps = queried_type as TypeParameterSpec; + if (tps != null && tps.HasTypeConstraint) { + if (non_method.DeclaringType.IsClass && member.DeclaringType.IsInterface) + continue; + + if (non_method.DeclaringType.IsInterface && member.DeclaringType.IsInterface) { + non_method = member; + continue; + } + } + + ambig_non_method = member; + } + } + + if (non_method != null) { + if (ambig_non_method != null && rc != null && (restrictions & MemberLookupRestrictions.IgnoreAmbiguity) == 0) { + var report = rc.Module.Compiler.Report; + report.SymbolRelatedToPreviousError (non_method); + report.SymbolRelatedToPreviousError (ambig_non_method); + report.Error (229, loc, "Ambiguity between `{0}' and `{1}'", + non_method.GetSignatureForError (), ambig_non_method.GetSignatureForError ()); + } + + if (non_method is MethodSpec) + return new MethodGroupExpr (members, queried_type, loc); + + return ExprClassFromMemberInfo (non_method, loc); + } + + if (members[0].DeclaringType.BaseType == null) + members = null; + else + members = MemberCache.FindMembers (members[0].DeclaringType.BaseType, name, false); + + } while (members != null); + + return null; + } + + static IList RemoveHiddenTypeParameterMethods (IList members) + { + if (members.Count < 2) + return members; + + // + // If M is a method, then all non-method members declared in an interface declaration + // are removed from the set, and all methods with the same signature as M declared in + // an interface declaration are removed from the set + // + + bool copied = false; + for (int i = 0; i < members.Count; ++i) { + var method = members[i] as MethodSpec; + if (method == null) { + if (!copied) { + copied = true; + members = new List (members); + } + + members.RemoveAt (i--); + continue; + } + + if (!method.DeclaringType.IsInterface) + continue; + + for (int ii = 0; ii < members.Count; ++ii) { + var candidate = members[ii] as MethodSpec; + if (candidate == null || !candidate.DeclaringType.IsClass) + continue; + + if (!TypeSpecComparer.Override.IsEqual (candidate.Parameters, method.Parameters)) + continue; + + if (!copied) { + copied = true; + members = new List (members); + } + + members.RemoveAt (i--); + break; + } + } + + return members; + } + + protected virtual void Error_NegativeArrayIndex (ResolveContext ec, Location loc) + { + throw new NotImplementedException (); + } + + public virtual void Error_OperatorCannotBeApplied (ResolveContext rc, Location loc, string oper, TypeSpec t) + { + if (t == InternalType.ErrorType) + return; + + rc.Report.Error (23, loc, "The `{0}' operator cannot be applied to operand of type `{1}'", + oper, t.GetSignatureForError ()); + } + + protected void Error_PointerInsideExpressionTree (ResolveContext ec) + { + ec.Report.Error (1944, loc, "An expression tree cannot contain an unsafe pointer operation"); + } + + protected void Error_NullShortCircuitInsideExpressionTree (ResolveContext rc) + { + rc.Report.Error (8072, loc, "An expression tree cannot contain a null propagating operator"); + } + + public virtual void FlowAnalysis (FlowAnalysisContext fc) + { + } + + // + // Special version of flow analysis for expressions which can return different + // on-true and on-false result. Used by &&, ||, ?: expressions + // + public virtual void FlowAnalysisConditional (FlowAnalysisContext fc) + { + FlowAnalysis (fc); + fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = fc.DefiniteAssignment; + } + + /// + /// Returns an expression that can be used to invoke operator true + /// on the expression if it exists. + /// + protected static Expression GetOperatorTrue (ResolveContext ec, Expression e, Location loc) + { + return GetOperatorTrueOrFalse (ec, e, true, loc); + } + + /// + /// Returns an expression that can be used to invoke operator false + /// on the expression if it exists. + /// + protected static Expression GetOperatorFalse (ResolveContext ec, Expression e, Location loc) + { + return GetOperatorTrueOrFalse (ec, e, false, loc); + } + + static Expression GetOperatorTrueOrFalse (ResolveContext ec, Expression e, bool is_true, Location loc) + { + var op = is_true ? Operator.OpType.True : Operator.OpType.False; + var methods = MemberCache.GetUserOperator (e.type, op, false); + if (methods == null) + return null; + + Arguments arguments = new Arguments (1); + arguments.Add (new Argument (e)); + + var res = new OverloadResolver (methods, OverloadResolver.Restrictions.BaseMembersIncluded | OverloadResolver.Restrictions.NoBaseMembers, loc); + var oper = res.ResolveOperator (ec, ref arguments); + + if (oper == null) + return null; + + return new UserOperatorCall (oper, arguments, null, loc); + } + + public virtual string ExprClassName + { + get { + switch (eclass){ + case ExprClass.Unresolved: + return "Unresolved"; + case ExprClass.Value: + return "value"; + case ExprClass.Variable: + return "variable"; + case ExprClass.Namespace: + return "namespace"; + case ExprClass.Type: + return "type"; + case ExprClass.MethodGroup: + return "method group"; + case ExprClass.PropertyAccess: + return "property access"; + case ExprClass.EventAccess: + return "event access"; + case ExprClass.IndexerAccess: + return "indexer access"; + case ExprClass.Nothing: + return "null"; + case ExprClass.TypeParameter: + return "type parameter"; + } + throw new Exception ("Should not happen"); + } + } + + /// + /// Reports that we were expecting `expr' to be of class `expected' + /// + public static void Error_UnexpectedKind (IMemberContext ctx, Expression memberExpr, string expected, string was, Location loc) + { + var name = memberExpr.GetSignatureForError (); + + ctx.Module.Compiler.Report.Error (118, loc, "`{0}' is a `{1}' but a `{2}' was expected", name, was, expected); + } + + public virtual void Error_UnexpectedKind (ResolveContext ec, ResolveFlags flags, Location loc) + { + string [] valid = new string [4]; + int count = 0; + + if ((flags & ResolveFlags.VariableOrValue) != 0) { + valid [count++] = "variable"; + valid [count++] = "value"; + } + + if ((flags & ResolveFlags.Type) != 0) + valid [count++] = "type"; + + if ((flags & ResolveFlags.MethodGroup) != 0) + valid [count++] = "method group"; + + if (count == 0) + valid [count++] = "unknown"; + + StringBuilder sb = new StringBuilder (valid [0]); + for (int i = 1; i < count - 1; i++) { + sb.Append ("', `"); + sb.Append (valid [i]); + } + if (count > 1) { + sb.Append ("' or `"); + sb.Append (valid [count - 1]); + } + + ec.Report.Error (119, loc, + "Expression denotes a `{0}', where a `{1}' was expected", ExprClassName, sb.ToString ()); + } + + public static void UnsafeError (ResolveContext ec, Location loc) + { + UnsafeError (ec.Report, loc); + } + + public static void UnsafeError (Report Report, Location loc) + { + Report.Error (214, loc, "Pointers and fixed size buffers may only be used in an unsafe context"); + } + + // + // Converts `source' to an int, uint, long or ulong. + // + protected Expression ConvertExpressionToArrayIndex (ResolveContext ec, Expression source, bool pointerArray = false) + { + var btypes = ec.BuiltinTypes; + + if (source.type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + Arguments args = new Arguments (1); + args.Add (new Argument (source)); + return new DynamicConversion (btypes.Int, CSharpBinderFlags.ConvertArrayIndex, args, loc).Resolve (ec); + } + + Expression converted; + + using (ec.Set (ResolveContext.Options.CheckedScope)) { + converted = Convert.ImplicitConversion (ec, source, btypes.Int, source.loc); + if (converted == null) + converted = Convert.ImplicitConversion (ec, source, btypes.UInt, source.loc); + if (converted == null) + converted = Convert.ImplicitConversion (ec, source, btypes.Long, source.loc); + if (converted == null) + converted = Convert.ImplicitConversion (ec, source, btypes.ULong, source.loc); + + if (converted == null) { + source.Error_ValueCannotBeConverted (ec, btypes.Int, false); + return null; + } + } + + if (pointerArray) + return converted; + + // + // Only positive constants are allowed at compile time + // + Constant c = converted as Constant; + if (c != null && c.IsNegative) + Error_NegativeArrayIndex (ec, source.loc); + + // No conversion needed to array index + if (converted.Type.BuiltinType == BuiltinTypeSpec.Type.Int) + return converted; + + return new ArrayIndexCast (converted, btypes.Int).Resolve (ec); + } + + // + // Derived classes implement this method by cloning the fields that + // could become altered during the Resolve stage + // + // Only expressions that are created for the parser need to implement + // this. + // + protected virtual void CloneTo (CloneContext clonectx, Expression target) + { + throw new NotImplementedException ( + String.Format ( + "CloneTo not implemented for expression {0}", this.GetType ())); + } + + // + // Clones an expression created by the parser. + // + // We only support expressions created by the parser so far, not + // expressions that have been resolved (many more classes would need + // to implement CloneTo). + // + // This infrastructure is here merely for Lambda expressions which + // compile the same code using different type values for the same + // arguments to find the correct overload + // + public virtual Expression Clone (CloneContext clonectx) + { + Expression cloned = (Expression) MemberwiseClone (); + CloneTo (clonectx, cloned); + + return cloned; + } + + // + // Implementation of expression to expression tree conversion + // + public abstract Expression CreateExpressionTree (ResolveContext ec); + + protected Expression CreateExpressionFactoryCall (ResolveContext ec, string name, Arguments args) + { + return CreateExpressionFactoryCall (ec, name, null, args, loc); + } + + protected Expression CreateExpressionFactoryCall (ResolveContext ec, string name, TypeArguments typeArguments, Arguments args) + { + return CreateExpressionFactoryCall (ec, name, typeArguments, args, loc); + } + + public static Expression CreateExpressionFactoryCall (ResolveContext ec, string name, TypeArguments typeArguments, Arguments args, Location loc) + { + return new Invocation (new MemberAccess (CreateExpressionTypeExpression (ec, loc), name, typeArguments, loc), args); + } + + protected static TypeExpr CreateExpressionTypeExpression (ResolveContext ec, Location loc) + { + var t = ec.Module.PredefinedTypes.Expression.Resolve (); + if (t == null) + return null; + + return new TypeExpression (t, loc); + } + + // + // Implemented by all expressions which support conversion from + // compiler expression to invokable runtime expression. Used by + // dynamic C# binder. + // + public virtual SLE.Expression MakeExpression (BuilderContext ctx) + { + throw new NotImplementedException ("MakeExpression for " + GetType ()); + } + + public virtual object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + /// + /// This is just a base class for expressions that can + /// appear on statements (invocations, object creation, + /// assignments, post/pre increment and decrement). The idea + /// being that they would support an extra Emition interface that + /// does not leave a result on the stack. + /// + public abstract class ExpressionStatement : Expression + { + public virtual void MarkReachable (Reachability rc) + { + } + + public ExpressionStatement ResolveStatement (BlockContext ec) + { + Expression e = Resolve (ec); + if (e == null) + return null; + + ExpressionStatement es = e as ExpressionStatement; + if (es == null || e is AnonymousMethodBody) + Error_InvalidExpressionStatement (ec); + + // + // This is quite expensive warning, try to limit the damage + // + if (MemberAccess.IsValidDotExpression (e.Type) && !(e is Assign || e is Await)) { + WarningAsyncWithoutWait (ec, e); + } + + return es; + } + + static void WarningAsyncWithoutWait (BlockContext bc, Expression e) + { + if (bc.CurrentAnonymousMethod is AsyncInitializer) { + var awaiter = new AwaitStatement.AwaitableMemberAccess (e) { + ProbingMode = true + }; + + // + // Need to do full resolve because GetAwaiter can be extension method + // available only in this context + // + var mg = awaiter.Resolve (bc) as MethodGroupExpr; + if (mg == null) + return; + + var arguments = new Arguments (0); + mg = mg.OverloadResolve (bc, ref arguments, null, OverloadResolver.Restrictions.ProbingOnly); + if (mg == null) + return; + + // + // Use same check rules as for real await + // + var awaiter_definition = bc.Module.GetAwaiter (mg.BestCandidateReturnType); + if (!awaiter_definition.IsValidPattern || !awaiter_definition.INotifyCompletion) + return; + + bc.Report.Warning (4014, 1, e.Location, + "The statement is not awaited and execution of current method continues before the call is completed. Consider using `await' operator"); + return; + } + + var inv = e as Invocation; + if (inv != null && inv.MethodGroup != null && inv.MethodGroup.BestCandidate.IsAsync) { + // The warning won't be reported for imported methods to maintain warning compatiblity with csc + bc.Report.Warning (4014, 1, e.Location, + "The statement is not awaited and execution of current method continues before the call is completed. Consider using `await' operator or calling `Wait' method"); + return; + } + } + + /// + /// Requests the expression to be emitted in a `statement' + /// context. This means that no new value is left on the + /// stack after invoking this method (constrasted with + /// Emit that will always leave a value on the stack). + /// + public abstract void EmitStatement (EmitContext ec); + + public override void EmitSideEffect (EmitContext ec) + { + EmitStatement (ec); + } + } + + /// + /// This kind of cast is used to encapsulate the child + /// whose type is child.Type into an expression that is + /// reported to return "return_type". This is used to encapsulate + /// expressions which have compatible types, but need to be dealt + /// at higher levels with. + /// + /// For example, a "byte" expression could be encapsulated in one + /// of these as an "unsigned int". The type for the expression + /// would be "unsigned int". + /// + /// + public abstract class TypeCast : Expression + { + protected readonly Expression child; + + protected TypeCast (Expression child, TypeSpec return_type) + { + eclass = child.eclass; + loc = child.Location; + type = return_type; + this.child = child; + } + + public Expression Child { + get { + return child; + } + } + + public override bool ContainsEmitWithAwait () + { + return child.ContainsEmitWithAwait (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Arguments args = new Arguments (2); + args.Add (new Argument (child.CreateExpressionTree (ec))); + args.Add (new Argument (new TypeOf (type, loc))); + + if (type.IsPointer || child.Type.IsPointer) + Error_PointerInsideExpressionTree (ec); + + return CreateExpressionFactoryCall (ec, ec.HasSet (ResolveContext.Options.CheckedScope) ? "ConvertChecked" : "Convert", args); + } + + protected override Expression DoResolve (ResolveContext ec) + { + // This should never be invoked, we are born in fully + // initialized state. + + return this; + } + + public override void Emit (EmitContext ec) + { + child.Emit (ec); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + child.FlowAnalysis (fc); + } + + public override SLE.Expression MakeExpression (BuilderContext ctx) + { +#if STATIC + return base.MakeExpression (ctx); +#else + return ctx.HasSet (BuilderContext.Options.CheckedScope) ? + SLE.Expression.ConvertChecked (child.MakeExpression (ctx), type.GetMetaInfo ()) : + SLE.Expression.Convert (child.MakeExpression (ctx), type.GetMetaInfo ()); +#endif + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + // Nothing to clone + } + + public override bool IsNull { + get { return child.IsNull; } + } + } + + public class EmptyCast : TypeCast { + EmptyCast (Expression child, TypeSpec target_type) + : base (child, target_type) + { + } + + public static Expression Create (Expression child, TypeSpec type) + { + Constant c = child as Constant; + if (c != null) { + var enum_constant = c as EnumConstant; + if (enum_constant != null) + c = enum_constant.Child; + + if (!(c is ReducedExpression.ReducedConstantExpression)) { + if (c.Type == type) + return c; + + var res = c.ConvertImplicitly (type); + if (res != null) + return res; + } + } + + EmptyCast e = child as EmptyCast; + if (e != null) + return new EmptyCast (e.child, type); + + return new EmptyCast (child, type); + } + + public override void EmitBranchable (EmitContext ec, Label label, bool on_true) + { + child.EmitBranchable (ec, label, on_true); + } + + public override void EmitSideEffect (EmitContext ec) + { + child.EmitSideEffect (ec); + } + } + + // + // Used for predefined type user operator (no obsolete check, etc.) + // + public class OperatorCast : TypeCast + { + readonly MethodSpec conversion_operator; + + public OperatorCast (Expression expr, TypeSpec target_type) + : this (expr, target_type, target_type, false) + { + } + + public OperatorCast (Expression expr, TypeSpec target_type, bool find_explicit) + : this (expr, target_type, target_type, find_explicit) + { + } + + public OperatorCast (Expression expr, TypeSpec declaringType, TypeSpec returnType, bool isExplicit) + : base (expr, returnType) + { + var op = isExplicit ? Operator.OpType.Explicit : Operator.OpType.Implicit; + var mi = MemberCache.GetUserOperator (declaringType, op, true); + + if (mi != null) { + foreach (MethodSpec oper in mi) { + if (oper.ReturnType != returnType) + continue; + + if (oper.Parameters.Types[0] == expr.Type) { + conversion_operator = oper; + return; + } + } + } + + throw new InternalErrorException ("Missing predefined user operator between `{0}' and `{1}'", + returnType.GetSignatureForError (), expr.Type.GetSignatureForError ()); + } + + public override void Emit (EmitContext ec) + { + child.Emit (ec); + ec.Emit (OpCodes.Call, conversion_operator); + } + } + + // + // Constant specialization of EmptyCast. + // We need to special case this since an empty cast of + // a constant is still a constant. + // + public class EmptyConstantCast : Constant + { + public readonly Constant child; + + public EmptyConstantCast (Constant child, TypeSpec type) + : base (child.Location) + { + if (child == null) + throw new ArgumentNullException ("child"); + + this.child = child; + this.eclass = child.eclass; + this.type = type; + } + + public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) + { + if (child.Type == target_type) + return child; + + // FIXME: check that 'type' can be converted to 'target_type' first + return child.ConvertExplicitly (in_checked_context, target_type); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Arguments args = Arguments.CreateForExpressionTree (ec, null, + child.CreateExpressionTree (ec), + new TypeOf (type, loc)); + + if (type.IsPointer) + Error_PointerInsideExpressionTree (ec); + + return CreateExpressionFactoryCall (ec, "Convert", args); + } + + public override bool IsDefaultValue { + get { return child.IsDefaultValue; } + } + + public override bool IsNegative { + get { return child.IsNegative; } + } + + public override bool IsNull { + get { return child.IsNull; } + } + + public override bool IsOneInteger { + get { return child.IsOneInteger; } + } + + public override bool IsSideEffectFree { + get { + return child.IsSideEffectFree; + } + } + + public override bool IsZeroInteger { + get { return child.IsZeroInteger; } + } + + public override void Emit (EmitContext ec) + { + child.Emit (ec); + } + + public override void EmitBranchable (EmitContext ec, Label label, bool on_true) + { + child.EmitBranchable (ec, label, on_true); + + // Only to make verifier happy + if (TypeManager.IsGenericParameter (type) && child.IsNull) + ec.Emit (OpCodes.Unbox_Any, type); + } + + public override void EmitSideEffect (EmitContext ec) + { + child.EmitSideEffect (ec); + } + + public override object GetValue () + { + return child.GetValue (); + } + + public override string GetValueAsLiteral () + { + return child.GetValueAsLiteral (); + } + + public override long GetValueAsLong () + { + return child.GetValueAsLong (); + } + + public override Constant ConvertImplicitly (TypeSpec target_type) + { + if (type == target_type) + return this; + + // FIXME: Do we need to check user conversions? + if (!Convert.ImplicitStandardConversionExists (this, target_type)) + return null; + + return child.ConvertImplicitly (target_type); + } + } + + /// + /// This class is used to wrap literals which belong inside Enums + /// + public class EnumConstant : Constant + { + public Constant Child; + + public EnumConstant (Constant child, TypeSpec enum_type) + : base (child.Location) + { + this.Child = child; + + this.eclass = ExprClass.Value; + this.type = enum_type; + } + + protected EnumConstant (Location loc) + : base (loc) + { + } + + public override void Emit (EmitContext ec) + { + Child.Emit (ec); + } + + public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) + { + Child.EncodeAttributeValue (rc, enc, Child.Type, parameterType); + } + + public override void EmitBranchable (EmitContext ec, Label label, bool on_true) + { + Child.EmitBranchable (ec, label, on_true); + } + + public override void EmitSideEffect (EmitContext ec) + { + Child.EmitSideEffect (ec); + } + + public override string GetSignatureForError() + { + return Type.GetSignatureForError (); + } + + public override object GetValue () + { + return Child.GetValue (); + } + +#if !STATIC + public override object GetTypedValue () + { + // + // The method can be used in dynamic context only (on closed types) + // + // System.Enum.ToObject cannot be called on dynamic types + // EnumBuilder has to be used, but we cannot use EnumBuilder + // because it does not properly support generics + // + return System.Enum.ToObject (type.GetMetaInfo (), Child.GetValue ()); + } +#endif + + public override string GetValueAsLiteral () + { + return Child.GetValueAsLiteral (); + } + + public override long GetValueAsLong () + { + return Child.GetValueAsLong (); + } + + public EnumConstant Increment() + { + return new EnumConstant (((IntegralConstant) Child).Increment (), type); + } + + public override bool IsDefaultValue { + get { + return Child.IsDefaultValue; + } + } + + public override bool IsSideEffectFree { + get { + return Child.IsSideEffectFree; + } + } + + public override bool IsZeroInteger { + get { return Child.IsZeroInteger; } + } + + public override bool IsNegative { + get { + return Child.IsNegative; + } + } + + public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) + { + if (Child.Type == target_type) + return Child; + + return Child.ConvertExplicitly (in_checked_context, target_type); + } + + public override Constant ConvertImplicitly (TypeSpec type) + { + if (this.type == type) { + return this; + } + + if (!Convert.ImplicitStandardConversionExists (this, type)){ + return null; + } + + return Child.ConvertImplicitly (type); + } + } + + /// + /// This kind of cast is used to encapsulate Value Types in objects. + /// + /// The effect of it is to box the value type emitted by the previous + /// operation. + /// + public class BoxedCast : TypeCast { + + public BoxedCast (Expression expr, TypeSpec target_type) + : base (expr, target_type) + { + eclass = ExprClass.Value; + } + + protected override Expression DoResolve (ResolveContext ec) + { + // This should never be invoked, we are born in fully + // initialized state. + + return this; + } + + public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) + { + // Only boxing to object type is supported + if (targetType.BuiltinType != BuiltinTypeSpec.Type.Object) { + base.EncodeAttributeValue (rc, enc, targetType, parameterType); + return; + } + + enc.Encode (child.Type); + child.EncodeAttributeValue (rc, enc, child.Type, parameterType); + } + + public override void Emit (EmitContext ec) + { + base.Emit (ec); + + ec.Emit (OpCodes.Box, child.Type); + } + + public override void EmitSideEffect (EmitContext ec) + { + // boxing is side-effectful, since it involves runtime checks, except when boxing to Object or ValueType + // so, we need to emit the box+pop instructions in most cases + if (child.Type.IsStruct && + (type.BuiltinType == BuiltinTypeSpec.Type.Object || type.BuiltinType == BuiltinTypeSpec.Type.ValueType)) + child.EmitSideEffect (ec); + else + base.EmitSideEffect (ec); + } + } + + public class UnboxCast : TypeCast { + public UnboxCast (Expression expr, TypeSpec return_type) + : base (expr, return_type) + { + } + + protected override Expression DoResolve (ResolveContext ec) + { + // This should never be invoked, we are born in fully + // initialized state. + + return this; + } + + public override void Emit (EmitContext ec) + { + base.Emit (ec); + + ec.Emit (OpCodes.Unbox_Any, type); + } + } + + /// + /// This is used to perform explicit numeric conversions. + /// + /// Explicit numeric conversions might trigger exceptions in a checked + /// context, so they should generate the conv.ovf opcodes instead of + /// conv opcodes. + /// + public class ConvCast : TypeCast { + public enum Mode : byte { + I1_U1, I1_U2, I1_U4, I1_U8, I1_CH, + U1_I1, U1_CH, + I2_I1, I2_U1, I2_U2, I2_U4, I2_U8, I2_CH, + U2_I1, U2_U1, U2_I2, U2_CH, + I4_I1, I4_U1, I4_I2, I4_U2, I4_U4, I4_U8, I4_CH, + U4_I1, U4_U1, U4_I2, U4_U2, U4_I4, U4_CH, + I8_I1, I8_U1, I8_I2, I8_U2, I8_I4, I8_U4, I8_U8, I8_CH, I8_I, + U8_I1, U8_U1, U8_I2, U8_U2, U8_I4, U8_U4, U8_I8, U8_CH, U8_I, + CH_I1, CH_U1, CH_I2, + R4_I1, R4_U1, R4_I2, R4_U2, R4_I4, R4_U4, R4_I8, R4_U8, R4_CH, + R8_I1, R8_U1, R8_I2, R8_U2, R8_I4, R8_U4, R8_I8, R8_U8, R8_CH, R8_R4, + I_I8, + } + + Mode mode; + + public ConvCast (Expression child, TypeSpec return_type, Mode m) + : base (child, return_type) + { + mode = m; + } + + protected override Expression DoResolve (ResolveContext ec) + { + // This should never be invoked, we are born in fully + // initialized state. + + return this; + } + + public override string ToString () + { + return String.Format ("ConvCast ({0}, {1})", mode, child); + } + + public override void Emit (EmitContext ec) + { + base.Emit (ec); + Emit (ec, mode); + } + + public static void Emit (EmitContext ec, Mode mode) + { + if (ec.HasSet (EmitContext.Options.CheckedScope)) { + switch (mode){ + case Mode.I1_U1: ec.Emit (OpCodes.Conv_Ovf_U1); break; + case Mode.I1_U2: ec.Emit (OpCodes.Conv_Ovf_U2); break; + case Mode.I1_U4: ec.Emit (OpCodes.Conv_Ovf_U4); break; + case Mode.I1_U8: ec.Emit (OpCodes.Conv_Ovf_U8); break; + case Mode.I1_CH: ec.Emit (OpCodes.Conv_Ovf_U2); break; + + case Mode.U1_I1: ec.Emit (OpCodes.Conv_Ovf_I1_Un); break; + case Mode.U1_CH: /* nothing */ break; + + case Mode.I2_I1: ec.Emit (OpCodes.Conv_Ovf_I1); break; + case Mode.I2_U1: ec.Emit (OpCodes.Conv_Ovf_U1); break; + case Mode.I2_U2: ec.Emit (OpCodes.Conv_Ovf_U2); break; + case Mode.I2_U4: ec.Emit (OpCodes.Conv_Ovf_U4); break; + case Mode.I2_U8: ec.Emit (OpCodes.Conv_Ovf_U8); break; + case Mode.I2_CH: ec.Emit (OpCodes.Conv_Ovf_U2); break; + + case Mode.U2_I1: ec.Emit (OpCodes.Conv_Ovf_I1_Un); break; + case Mode.U2_U1: ec.Emit (OpCodes.Conv_Ovf_U1_Un); break; + case Mode.U2_I2: ec.Emit (OpCodes.Conv_Ovf_I2_Un); break; + case Mode.U2_CH: /* nothing */ break; + + case Mode.I4_I1: ec.Emit (OpCodes.Conv_Ovf_I1); break; + case Mode.I4_U1: ec.Emit (OpCodes.Conv_Ovf_U1); break; + case Mode.I4_I2: ec.Emit (OpCodes.Conv_Ovf_I2); break; + case Mode.I4_U4: ec.Emit (OpCodes.Conv_Ovf_U4); break; + case Mode.I4_U2: ec.Emit (OpCodes.Conv_Ovf_U2); break; + case Mode.I4_U8: ec.Emit (OpCodes.Conv_Ovf_U8); break; + case Mode.I4_CH: ec.Emit (OpCodes.Conv_Ovf_U2); break; + + case Mode.U4_I1: ec.Emit (OpCodes.Conv_Ovf_I1_Un); break; + case Mode.U4_U1: ec.Emit (OpCodes.Conv_Ovf_U1_Un); break; + case Mode.U4_I2: ec.Emit (OpCodes.Conv_Ovf_I2_Un); break; + case Mode.U4_U2: ec.Emit (OpCodes.Conv_Ovf_U2_Un); break; + case Mode.U4_I4: ec.Emit (OpCodes.Conv_Ovf_I4_Un); break; + case Mode.U4_CH: ec.Emit (OpCodes.Conv_Ovf_U2_Un); break; + + case Mode.I8_I1: ec.Emit (OpCodes.Conv_Ovf_I1); break; + case Mode.I8_U1: ec.Emit (OpCodes.Conv_Ovf_U1); break; + case Mode.I8_I2: ec.Emit (OpCodes.Conv_Ovf_I2); break; + case Mode.I8_U2: ec.Emit (OpCodes.Conv_Ovf_U2); break; + case Mode.I8_I4: ec.Emit (OpCodes.Conv_Ovf_I4); break; + case Mode.I8_U4: ec.Emit (OpCodes.Conv_Ovf_U4); break; + case Mode.I8_U8: ec.Emit (OpCodes.Conv_Ovf_U8); break; + case Mode.I8_CH: ec.Emit (OpCodes.Conv_Ovf_U2); break; + case Mode.I8_I: ec.Emit (OpCodes.Conv_Ovf_U); break; + + case Mode.U8_I1: ec.Emit (OpCodes.Conv_Ovf_I1_Un); break; + case Mode.U8_U1: ec.Emit (OpCodes.Conv_Ovf_U1_Un); break; + case Mode.U8_I2: ec.Emit (OpCodes.Conv_Ovf_I2_Un); break; + case Mode.U8_U2: ec.Emit (OpCodes.Conv_Ovf_U2_Un); break; + case Mode.U8_I4: ec.Emit (OpCodes.Conv_Ovf_I4_Un); break; + case Mode.U8_U4: ec.Emit (OpCodes.Conv_Ovf_U4_Un); break; + case Mode.U8_I8: ec.Emit (OpCodes.Conv_Ovf_I8_Un); break; + case Mode.U8_CH: ec.Emit (OpCodes.Conv_Ovf_U2_Un); break; + case Mode.U8_I: ec.Emit (OpCodes.Conv_Ovf_U_Un); break; + + case Mode.CH_I1: ec.Emit (OpCodes.Conv_Ovf_I1_Un); break; + case Mode.CH_U1: ec.Emit (OpCodes.Conv_Ovf_U1_Un); break; + case Mode.CH_I2: ec.Emit (OpCodes.Conv_Ovf_I2_Un); break; + + case Mode.R4_I1: ec.Emit (OpCodes.Conv_Ovf_I1); break; + case Mode.R4_U1: ec.Emit (OpCodes.Conv_Ovf_U1); break; + case Mode.R4_I2: ec.Emit (OpCodes.Conv_Ovf_I2); break; + case Mode.R4_U2: ec.Emit (OpCodes.Conv_Ovf_U2); break; + case Mode.R4_I4: ec.Emit (OpCodes.Conv_Ovf_I4); break; + case Mode.R4_U4: ec.Emit (OpCodes.Conv_Ovf_U4); break; + case Mode.R4_I8: ec.Emit (OpCodes.Conv_Ovf_I8); break; + case Mode.R4_U8: ec.Emit (OpCodes.Conv_Ovf_U8); break; + case Mode.R4_CH: ec.Emit (OpCodes.Conv_Ovf_U2); break; + + case Mode.R8_I1: ec.Emit (OpCodes.Conv_Ovf_I1); break; + case Mode.R8_U1: ec.Emit (OpCodes.Conv_Ovf_U1); break; + case Mode.R8_I2: ec.Emit (OpCodes.Conv_Ovf_I2); break; + case Mode.R8_U2: ec.Emit (OpCodes.Conv_Ovf_U2); break; + case Mode.R8_I4: ec.Emit (OpCodes.Conv_Ovf_I4); break; + case Mode.R8_U4: ec.Emit (OpCodes.Conv_Ovf_U4); break; + case Mode.R8_I8: ec.Emit (OpCodes.Conv_Ovf_I8); break; + case Mode.R8_U8: ec.Emit (OpCodes.Conv_Ovf_U8); break; + case Mode.R8_CH: ec.Emit (OpCodes.Conv_Ovf_U2); break; + case Mode.R8_R4: ec.Emit (OpCodes.Conv_R4); break; + + case Mode.I_I8: ec.Emit (OpCodes.Conv_Ovf_I8_Un); break; + } + } else { + switch (mode){ + case Mode.I1_U1: ec.Emit (OpCodes.Conv_U1); break; + case Mode.I1_U2: ec.Emit (OpCodes.Conv_U2); break; + case Mode.I1_U4: ec.Emit (OpCodes.Conv_U4); break; + case Mode.I1_U8: ec.Emit (OpCodes.Conv_I8); break; + case Mode.I1_CH: ec.Emit (OpCodes.Conv_U2); break; + + case Mode.U1_I1: ec.Emit (OpCodes.Conv_I1); break; + case Mode.U1_CH: ec.Emit (OpCodes.Conv_U2); break; + + case Mode.I2_I1: ec.Emit (OpCodes.Conv_I1); break; + case Mode.I2_U1: ec.Emit (OpCodes.Conv_U1); break; + case Mode.I2_U2: ec.Emit (OpCodes.Conv_U2); break; + case Mode.I2_U4: ec.Emit (OpCodes.Conv_U4); break; + case Mode.I2_U8: ec.Emit (OpCodes.Conv_I8); break; + case Mode.I2_CH: ec.Emit (OpCodes.Conv_U2); break; + + case Mode.U2_I1: ec.Emit (OpCodes.Conv_I1); break; + case Mode.U2_U1: ec.Emit (OpCodes.Conv_U1); break; + case Mode.U2_I2: ec.Emit (OpCodes.Conv_I2); break; + case Mode.U2_CH: /* nothing */ break; + + case Mode.I4_I1: ec.Emit (OpCodes.Conv_I1); break; + case Mode.I4_U1: ec.Emit (OpCodes.Conv_U1); break; + case Mode.I4_I2: ec.Emit (OpCodes.Conv_I2); break; + case Mode.I4_U4: /* nothing */ break; + case Mode.I4_U2: ec.Emit (OpCodes.Conv_U2); break; + case Mode.I4_U8: ec.Emit (OpCodes.Conv_I8); break; + case Mode.I4_CH: ec.Emit (OpCodes.Conv_U2); break; + + case Mode.U4_I1: ec.Emit (OpCodes.Conv_I1); break; + case Mode.U4_U1: ec.Emit (OpCodes.Conv_U1); break; + case Mode.U4_I2: ec.Emit (OpCodes.Conv_I2); break; + case Mode.U4_U2: ec.Emit (OpCodes.Conv_U2); break; + case Mode.U4_I4: /* nothing */ break; + case Mode.U4_CH: ec.Emit (OpCodes.Conv_U2); break; + + case Mode.I8_I1: ec.Emit (OpCodes.Conv_I1); break; + case Mode.I8_U1: ec.Emit (OpCodes.Conv_U1); break; + case Mode.I8_I2: ec.Emit (OpCodes.Conv_I2); break; + case Mode.I8_U2: ec.Emit (OpCodes.Conv_U2); break; + case Mode.I8_I4: ec.Emit (OpCodes.Conv_I4); break; + case Mode.I8_U4: ec.Emit (OpCodes.Conv_U4); break; + case Mode.I8_U8: /* nothing */ break; + case Mode.I8_CH: ec.Emit (OpCodes.Conv_U2); break; + case Mode.I8_I: ec.Emit (OpCodes.Conv_U); break; + + case Mode.U8_I1: ec.Emit (OpCodes.Conv_I1); break; + case Mode.U8_U1: ec.Emit (OpCodes.Conv_U1); break; + case Mode.U8_I2: ec.Emit (OpCodes.Conv_I2); break; + case Mode.U8_U2: ec.Emit (OpCodes.Conv_U2); break; + case Mode.U8_I4: ec.Emit (OpCodes.Conv_I4); break; + case Mode.U8_U4: ec.Emit (OpCodes.Conv_U4); break; + case Mode.U8_I8: /* nothing */ break; + case Mode.U8_CH: ec.Emit (OpCodes.Conv_U2); break; + case Mode.U8_I: ec.Emit (OpCodes.Conv_U); break; + + case Mode.CH_I1: ec.Emit (OpCodes.Conv_I1); break; + case Mode.CH_U1: ec.Emit (OpCodes.Conv_U1); break; + case Mode.CH_I2: ec.Emit (OpCodes.Conv_I2); break; + + case Mode.R4_I1: ec.Emit (OpCodes.Conv_I1); break; + case Mode.R4_U1: ec.Emit (OpCodes.Conv_U1); break; + case Mode.R4_I2: ec.Emit (OpCodes.Conv_I2); break; + case Mode.R4_U2: ec.Emit (OpCodes.Conv_U2); break; + case Mode.R4_I4: ec.Emit (OpCodes.Conv_I4); break; + case Mode.R4_U4: ec.Emit (OpCodes.Conv_U4); break; + case Mode.R4_I8: ec.Emit (OpCodes.Conv_I8); break; + case Mode.R4_U8: ec.Emit (OpCodes.Conv_U8); break; + case Mode.R4_CH: ec.Emit (OpCodes.Conv_U2); break; + + case Mode.R8_I1: ec.Emit (OpCodes.Conv_I1); break; + case Mode.R8_U1: ec.Emit (OpCodes.Conv_U1); break; + case Mode.R8_I2: ec.Emit (OpCodes.Conv_I2); break; + case Mode.R8_U2: ec.Emit (OpCodes.Conv_U2); break; + case Mode.R8_I4: ec.Emit (OpCodes.Conv_I4); break; + case Mode.R8_U4: ec.Emit (OpCodes.Conv_U4); break; + case Mode.R8_I8: ec.Emit (OpCodes.Conv_I8); break; + case Mode.R8_U8: ec.Emit (OpCodes.Conv_U8); break; + case Mode.R8_CH: ec.Emit (OpCodes.Conv_U2); break; + case Mode.R8_R4: ec.Emit (OpCodes.Conv_R4); break; + + case Mode.I_I8: ec.Emit (OpCodes.Conv_U8); break; + } + } + } + } + + class OpcodeCast : TypeCast + { + readonly OpCode op; + + public OpcodeCast (Expression child, TypeSpec return_type, OpCode op) + : base (child, return_type) + { + this.op = op; + } + + protected override Expression DoResolve (ResolveContext ec) + { + // This should never be invoked, we are born in fully + // initialized state. + + return this; + } + + public override void Emit (EmitContext ec) + { + base.Emit (ec); + ec.Emit (op); + } + + public TypeSpec UnderlyingType { + get { return child.Type; } + } + } + + // + // Opcode casts expression with 2 opcodes but only + // single expression tree node + // + class OpcodeCastDuplex : OpcodeCast + { + readonly OpCode second; + + public OpcodeCastDuplex (Expression child, TypeSpec returnType, OpCode first, OpCode second) + : base (child, returnType, first) + { + this.second = second; + } + + public override void Emit (EmitContext ec) + { + base.Emit (ec); + ec.Emit (second); + } + } + + /// + /// This kind of cast is used to encapsulate a child and cast it + /// to the class requested + /// + public sealed class ClassCast : TypeCast { + readonly bool forced; + + public ClassCast (Expression child, TypeSpec return_type) + : base (child, return_type) + { + } + + public ClassCast (Expression child, TypeSpec return_type, bool forced) + : base (child, return_type) + { + this.forced = forced; + } + + public override void Emit (EmitContext ec) + { + base.Emit (ec); + + bool gen = TypeManager.IsGenericParameter (child.Type); + if (gen) + ec.Emit (OpCodes.Box, child.Type); + + if (type.IsGenericParameter) { + ec.Emit (OpCodes.Unbox_Any, type); + return; + } + + if (gen && !forced) + return; + + ec.Emit (OpCodes.Castclass, type); + } + } + + // + // Created during resolving pahse when an expression is wrapped or constantified + // and original expression can be used later (e.g. for expression trees) + // + public class ReducedExpression : Expression + { + public sealed class ReducedConstantExpression : EmptyConstantCast + { + readonly Expression orig_expr; + + public ReducedConstantExpression (Constant expr, Expression orig_expr) + : base (expr, expr.Type) + { + this.orig_expr = orig_expr; + } + + public Expression OriginalExpression { + get { + return orig_expr; + } + } + + public override Constant ConvertImplicitly (TypeSpec target_type) + { + Constant c = base.ConvertImplicitly (target_type); + if (c != null) + c = new ReducedConstantExpression (c, orig_expr); + + return c; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + return orig_expr.CreateExpressionTree (ec); + } + + public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) + { + Constant c = base.ConvertExplicitly (in_checked_context, target_type); + if (c != null) + c = new ReducedConstantExpression (c, orig_expr); + return c; + } + + public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) + { + // + // LAMESPEC: Reduced conditional expression is allowed as an attribute argument + // + if (orig_expr is Conditional) + child.EncodeAttributeValue (rc, enc, targetType,parameterType); + else + base.EncodeAttributeValue (rc, enc, targetType, parameterType); + } + } + + sealed class ReducedExpressionStatement : ExpressionStatement + { + readonly Expression orig_expr; + readonly ExpressionStatement stm; + + public ReducedExpressionStatement (ExpressionStatement stm, Expression orig) + { + this.orig_expr = orig; + this.stm = stm; + this.eclass = stm.eclass; + this.type = stm.Type; + + this.loc = orig.Location; + } + + public override bool ContainsEmitWithAwait () + { + return stm.ContainsEmitWithAwait (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + return orig_expr.CreateExpressionTree (ec); + } + + protected override Expression DoResolve (ResolveContext ec) + { + return this; + } + + public override void Emit (EmitContext ec) + { + stm.Emit (ec); + } + + public override void EmitStatement (EmitContext ec) + { + stm.EmitStatement (ec); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + stm.FlowAnalysis (fc); + } + } + + readonly Expression expr, orig_expr; + + private ReducedExpression (Expression expr, Expression orig_expr) + { + this.expr = expr; + this.eclass = expr.eclass; + this.type = expr.Type; + this.orig_expr = orig_expr; + this.loc = orig_expr.Location; + } + + #region Properties + + public override bool IsSideEffectFree { + get { + return expr.IsSideEffectFree; + } + } + + public Expression OriginalExpression { + get { + return orig_expr; + } + } + + #endregion + + public override bool ContainsEmitWithAwait () + { + return expr.ContainsEmitWithAwait (); + } + + // + // Creates fully resolved expression switcher + // + public static Constant Create (Constant expr, Expression original_expr) + { + if (expr.eclass == ExprClass.Unresolved) + throw new ArgumentException ("Unresolved expression"); + + return new ReducedConstantExpression (expr, original_expr); + } + + public static ExpressionStatement Create (ExpressionStatement s, Expression orig) + { + return new ReducedExpressionStatement (s, orig); + } + + public static Expression Create (Expression expr, Expression original_expr) + { + return Create (expr, original_expr, true); + } + + // + // Creates unresolved reduce expression. The original expression has to be + // already resolved. Created expression is constant based based on `expr' + // value unless canBeConstant is used + // + public static Expression Create (Expression expr, Expression original_expr, bool canBeConstant) + { + if (canBeConstant) { + Constant c = expr as Constant; + if (c != null) + return Create (c, original_expr); + } + + ExpressionStatement s = expr as ExpressionStatement; + if (s != null) + return Create (s, original_expr); + + if (expr.eclass == ExprClass.Unresolved) + throw new ArgumentException ("Unresolved expression"); + + return new ReducedExpression (expr, original_expr); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + return orig_expr.CreateExpressionTree (ec); + } + + protected override Expression DoResolve (ResolveContext ec) + { + return this; + } + + public override void Emit (EmitContext ec) + { + expr.Emit (ec); + } + + public override Expression EmitToField (EmitContext ec) + { + return expr.EmitToField(ec); + } + + public override void EmitBranchable (EmitContext ec, Label target, bool on_true) + { + expr.EmitBranchable (ec, target, on_true); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + expr.FlowAnalysis (fc); + } + + public override SLE.Expression MakeExpression (BuilderContext ctx) + { + return orig_expr.MakeExpression (ctx); + } + } + + // + // Standard composite pattern + // + public abstract class CompositeExpression : Expression + { + protected Expression expr; + + protected CompositeExpression (Expression expr) + { + this.expr = expr; + this.loc = expr.Location; + } + + public override bool ContainsEmitWithAwait () + { + return expr.ContainsEmitWithAwait (); + } + + public override Expression CreateExpressionTree (ResolveContext rc) + { + return expr.CreateExpressionTree (rc); + } + + public Expression Child { + get { return expr; } + } + + protected override Expression DoResolve (ResolveContext rc) + { + expr = expr.Resolve (rc); + if (expr == null) + return null; + + type = expr.Type; + eclass = expr.eclass; + return this; + } + + public override void Emit (EmitContext ec) + { + expr.Emit (ec); + } + + public override bool IsNull { + get { return expr.IsNull; } + } + } + + // + // Base of expressions used only to narrow resolve flow + // + public abstract class ShimExpression : Expression + { + protected Expression expr; + + protected ShimExpression (Expression expr) + { + this.expr = expr; + } + + public Expression Expr { + get { + return expr; + } + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + if (expr == null) + return; + + ShimExpression target = (ShimExpression) t; + target.expr = expr.Clone (clonectx); + } + + public override bool ContainsEmitWithAwait () + { + return expr.ContainsEmitWithAwait (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + throw new NotSupportedException ("ET"); + } + + public override void Emit (EmitContext ec) + { + throw new InternalErrorException ("Missing Resolve call"); + } + } + + public class UnreachableExpression : Expression + { + public UnreachableExpression (Expression expr) + { + this.loc = expr.Location; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + // TODO: is it ok + throw new NotImplementedException (); + } + + protected override Expression DoResolve (ResolveContext rc) + { + throw new NotSupportedException (); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + fc.Report.Warning (429, 4, loc, "Unreachable expression code detected"); + } + + public override void Emit (EmitContext ec) + { + } + + public override void EmitBranchable (EmitContext ec, Label target, bool on_true) + { + } + } + + // + // Unresolved type name expressions + // + public abstract class ATypeNameExpression : FullNamedExpression + { + string name; + protected TypeArguments targs; + + protected ATypeNameExpression (string name, Location l) + { + this.name = name; + loc = l; + } + + protected ATypeNameExpression (string name, TypeArguments targs, Location l) + { + this.name = name; + this.targs = targs; + loc = l; + } + + protected ATypeNameExpression (string name, int arity, Location l) + : this (name, new UnboundTypeArguments (arity), l) + { + } + + #region Properties + + protected int Arity { + get { + return targs == null ? 0 : targs.Count; + } + } + + public bool HasTypeArguments { + get { + return targs != null && !targs.IsEmpty; + } + } + + public string Name { + get { + return name; + } + set { + name = value; + } + } + + public TypeArguments TypeArguments { + get { + return targs; + } + } + + #endregion + + public override bool Equals (object obj) + { + ATypeNameExpression atne = obj as ATypeNameExpression; + return atne != null && atne.Name == Name && + (targs == null || targs.Equals (atne.targs)); + } + + protected void Error_OpenGenericTypeIsNotAllowed (IMemberContext mc) + { + mc.Module.Compiler.Report.Error (7003, Location, "Unbound generic name is not valid in this context"); + } + + public override int GetHashCode () + { + return Name.GetHashCode (); + } + + // TODO: Move it to MemberCore + public static string GetMemberType (MemberCore mc) + { + if (mc is Property) + return "property"; + if (mc is Indexer) + return "indexer"; + if (mc is FieldBase) + return "field"; + if (mc is MethodCore) + return "method"; + if (mc is EnumMember) + return "enum"; + if (mc is Event) + return "event"; + + return "type"; + } + + public override string GetSignatureForError () + { + if (targs != null) { + return Name + "<" + targs.GetSignatureForError () + ">"; + } + + return Name; + } + + public abstract Expression LookupNameExpression (ResolveContext rc, MemberLookupRestrictions restriction); + } + + /// + /// SimpleName expressions are formed of a single word and only happen at the beginning + /// of a dotted-name. + /// + public class SimpleName : ATypeNameExpression + { + public SimpleName (string name, Location l) + : base (name, l) + { + } + + public SimpleName (string name, TypeArguments args, Location l) + : base (name, args, l) + { + } + + public SimpleName (string name, int arity, Location l) + : base (name, arity, l) + { + } + + public SimpleName GetMethodGroup () + { + return new SimpleName (Name, targs, loc); + } + + protected override Expression DoResolve (ResolveContext rc) + { + return SimpleNameResolve (rc, null); + } + + public override Expression DoResolveLValue (ResolveContext ec, Expression right_side) + { + return SimpleNameResolve (ec, right_side); + } + + public void Error_NameDoesNotExist (ResolveContext rc) + { + rc.Report.Error (103, loc, "The name `{0}' does not exist in the current context", Name); + } + + protected virtual void Error_TypeOrNamespaceNotFound (IMemberContext ctx) + { + if (ctx.CurrentType != null) { + var member = MemberLookup (ctx, false, ctx.CurrentType, Name, 0, MemberLookupRestrictions.ExactArity, loc) as MemberExpr; + if (member != null) { + Error_UnexpectedKind (ctx, member, "type", member.KindName, loc); + return; + } + } + + var report = ctx.Module.Compiler.Report; + + var retval = ctx.LookupNamespaceOrType (Name, Arity, LookupMode.IgnoreAccessibility, loc); + if (retval != null) { + report.SymbolRelatedToPreviousError (retval.Type); + ErrorIsInaccesible (ctx, retval.GetSignatureForError (), loc); + return; + } + + retval = ctx.LookupNamespaceOrType (Name, -System.Math.Max (1, Arity), LookupMode.Probing, loc); + if (retval != null) { + Error_TypeArgumentsCannotBeUsed (ctx, retval.Type, loc); + return; + } + + var ns_candidates = ctx.Module.GlobalRootNamespace.FindTypeNamespaces (ctx, Name, Arity); + if (ns_candidates != null) { + if (ctx is UsingAliasNamespace.AliasContext) { + report.Error (246, loc, + "The type or namespace name `{1}' could not be found. Consider using fully qualified name `{0}.{1}'", + ns_candidates[0], Name); + } else { + string usings = string.Join ("' or `", ns_candidates.ToArray ()); + report.Error (246, loc, + "The type or namespace name `{0}' could not be found. Are you missing `{1}' using directive?", + Name, usings); + } + } else { + report.Error (246, loc, + "The type or namespace name `{0}' could not be found. Are you missing an assembly reference?", + Name); + } + } + + public override FullNamedExpression ResolveAsTypeOrNamespace (IMemberContext mc, bool allowUnboundTypeArguments) + { + FullNamedExpression fne = mc.LookupNamespaceOrType (Name, Arity, LookupMode.Normal, loc); + + if (fne != null) { + if (fne.Type != null && Arity > 0) { + if (HasTypeArguments) { + GenericTypeExpr ct = new GenericTypeExpr (fne.Type, targs, loc); + if (ct.ResolveAsType (mc) == null) + return null; + + return ct; + } + + if (!allowUnboundTypeArguments) + Error_OpenGenericTypeIsNotAllowed (mc); + + return new GenericOpenTypeExpr (fne.Type, loc); + } + + // + // dynamic namespace is ignored when dynamic is allowed (does not apply to types) + // + if (!(fne is NamespaceExpression)) + return fne; + } + + if (Arity == 0 && Name == "dynamic" && mc.Module.Compiler.Settings.Version > LanguageVersion.V_3) { + if (!mc.Module.PredefinedAttributes.Dynamic.IsDefined) { + mc.Module.Compiler.Report.Error (1980, Location, + "Dynamic keyword requires `{0}' to be defined. Are you missing System.Core.dll assembly reference?", + mc.Module.PredefinedAttributes.Dynamic.GetSignatureForError ()); + } + + fne = new DynamicTypeExpr (loc); + fne.ResolveAsType (mc); + } + + if (fne != null) + return fne; + + Error_TypeOrNamespaceNotFound (mc); + return null; + } + + public bool IsPossibleTypeOrNamespace (IMemberContext mc) + { + return mc.LookupNamespaceOrType (Name, Arity, LookupMode.Probing, loc) != null; + } + + public override Expression LookupNameExpression (ResolveContext rc, MemberLookupRestrictions restrictions) + { + int lookup_arity = Arity; + bool errorMode = false; + Expression e; + Block current_block = rc.CurrentBlock; + INamedBlockVariable variable = null; + bool variable_found = false; + + while (true) { + // + // Stage 1: binding to local variables or parameters + // + // LAMESPEC: It should take invocableOnly into account but that would break csc compatibility + // + if (current_block != null && lookup_arity == 0) { + if (current_block.ParametersBlock.TopBlock.GetLocalName (Name, current_block.Original, ref variable)) { + if (!variable.IsDeclared) { + // We found local name in accessible block but it's not + // initialized yet, maybe the user wanted to bind to something else + errorMode = true; + variable_found = true; + } else { + e = variable.CreateReferenceExpression (rc, loc); + if (e != null) { + if (Arity > 0) + Error_TypeArgumentsCannotBeUsed (rc, "variable", Name, loc); + + return e; + } + } + } + } + + // + // Stage 2: Lookup members if we are inside a type up to top level type for nested types + // + TypeSpec member_type = rc.CurrentType; + for (; member_type != null; member_type = member_type.DeclaringType) { + e = MemberLookup (rc, errorMode, member_type, Name, lookup_arity, restrictions, loc); + if (e == null) + continue; + + var me = e as MemberExpr; + if (me == null) { + // The name matches a type, defer to ResolveAsTypeStep + if (e is TypeExpr) + break; + + continue; + } + + if (errorMode) { + if (variable != null) { + if (me is FieldExpr || me is ConstantExpr || me is EventExpr || me is PropertyExpr) { + rc.Report.Error (844, loc, + "A local variable `{0}' cannot be used before it is declared. Consider renaming the local variable when it hides the member `{1}'", + Name, me.GetSignatureForError ()); + } else { + break; + } + } else if (me is MethodGroupExpr || me is PropertyExpr || me is IndexerExpr) { + // Leave it to overload resolution to report correct error + } else { + // TODO: rc.Report.SymbolRelatedToPreviousError () + ErrorIsInaccesible (rc, me.GetSignatureForError (), loc); + } + } else { + // LAMESPEC: again, ignores InvocableOnly + if (variable != null) { + rc.Report.SymbolRelatedToPreviousError (variable.Location, Name); + rc.Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", Name); + } + + // + // MemberLookup does not check accessors availability, this is actually needed for properties only + // + var pe = me as PropertyExpr; + if (pe != null) { + + // Break as there is no other overload available anyway + if ((restrictions & MemberLookupRestrictions.ReadAccess) != 0) { + if (!pe.PropertyInfo.HasGet || !pe.PropertyInfo.Get.IsAccessible (rc)) + break; + + pe.Getter = pe.PropertyInfo.Get; + } else { + if (!pe.PropertyInfo.HasSet || !pe.PropertyInfo.Set.IsAccessible (rc)) + break; + + pe.Setter = pe.PropertyInfo.Set; + } + } + } + + // TODO: It's used by EventExpr -> FieldExpr transformation only + // TODO: Should go to MemberAccess + me = me.ResolveMemberAccess (rc, null, null); + + if (Arity > 0) { + targs.Resolve (rc); + me.SetTypeArguments (rc, targs); + } + + return me; + } + + // + // Stage 3: Lookup nested types, namespaces and type parameters in the context + // + if ((restrictions & MemberLookupRestrictions.InvocableOnly) == 0 && !variable_found) { + if (IsPossibleTypeOrNamespace (rc)) { + if (variable != null) { + rc.Report.SymbolRelatedToPreviousError (variable.Location, Name); + rc.Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", Name); + } + + return ResolveAsTypeOrNamespace (rc, false); + } + } + + var mg = NamespaceContainer.LookupStaticUsings (rc, Name, Arity, loc); + if (mg != null) { + if (Arity > 0) { + targs.Resolve (rc); + mg.SetTypeArguments (rc, targs); + } + return mg; + } + + if (Name == "nameof") + return new NameOf (this); + + if (errorMode) { + if (variable_found) { + rc.Report.Error (841, loc, "A local variable `{0}' cannot be used before it is declared", Name); + } else { + if (Arity > 0) { + var tparams = rc.CurrentTypeParameters; + if (tparams != null) { + if (tparams.Find (Name) != null) { + Error_TypeArgumentsCannotBeUsed (rc, "type parameter", Name, loc); + return null; + } + } + + var ct = rc.CurrentType; + do { + if (ct.MemberDefinition.TypeParametersCount > 0) { + foreach (var ctp in ct.MemberDefinition.TypeParameters) { + if (ctp.Name == Name) { + Error_TypeArgumentsCannotBeUsed (rc, "type parameter", Name, loc); + return null; + } + } + } + + ct = ct.DeclaringType; + } while (ct != null); + } + + if ((restrictions & MemberLookupRestrictions.InvocableOnly) == 0) { + e = rc.LookupNamespaceOrType (Name, Arity, LookupMode.IgnoreAccessibility, loc); + if (e != null) { + rc.Report.SymbolRelatedToPreviousError (e.Type); + ErrorIsInaccesible (rc, e.GetSignatureForError (), loc); + return e; + } + } else { + var me = MemberLookup (rc, false, rc.CurrentType, Name, Arity, restrictions & ~MemberLookupRestrictions.InvocableOnly, loc) as MemberExpr; + if (me != null) { + Error_UnexpectedKind (rc, me, "method group", me.KindName, loc); + return ErrorExpression.Instance; + } + } + + e = rc.LookupNamespaceOrType (Name, -System.Math.Max (1, Arity), LookupMode.Probing, loc); + if (e != null) { + if (e.Type.Arity != Arity && (restrictions & MemberLookupRestrictions.IgnoreArity) == 0) { + Error_TypeArgumentsCannotBeUsed (rc, e.Type, loc); + return e; + } + + if (e is TypeExpr) { + // TypeExpression does not have correct location + if (e is TypeExpression) + e = new TypeExpression (e.Type, loc); + + return e; + } + } + + Error_NameDoesNotExist (rc); + } + + return ErrorExpression.Instance; + } + + if (rc.Module.Evaluator != null) { + var fi = rc.Module.Evaluator.LookupField (Name); + if (fi != null) + return new FieldExpr (fi.Item1, loc); + } + + lookup_arity = 0; + errorMode = true; + } + } + + Expression SimpleNameResolve (ResolveContext ec, Expression right_side) + { + Expression e = LookupNameExpression (ec, right_side == null ? MemberLookupRestrictions.ReadAccess : MemberLookupRestrictions.None); + + if (e == null) + return null; + + if (e is FullNamedExpression && e.eclass != ExprClass.Unresolved) { + Error_UnexpectedKind (ec, e, "variable", e.ExprClassName, loc); + return e; + } + + if (right_side != null) { + e = e.ResolveLValue (ec, right_side); + } else { + e = e.Resolve (ec); + } + + return e; + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + /// + /// Represents a namespace or a type. The name of the class was inspired by + /// section 10.8.1 (Fully Qualified Names). + /// + public abstract class FullNamedExpression : Expression + { + protected override void CloneTo (CloneContext clonectx, Expression target) + { + // Do nothing, most unresolved type expressions cannot be + // resolved to different type + } + + public override bool ContainsEmitWithAwait () + { + return false; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + throw new NotSupportedException ("ET"); + } + + public abstract FullNamedExpression ResolveAsTypeOrNamespace (IMemberContext mc, bool allowUnboundTypeArguments); + + // + // This is used to resolve the expression as a type, a null + // value will be returned if the expression is not a type + // reference + // + public override TypeSpec ResolveAsType (IMemberContext mc, bool allowUnboundTypeArguments = false) + { + FullNamedExpression fne = ResolveAsTypeOrNamespace (mc, allowUnboundTypeArguments); + + if (fne == null) + return null; + + TypeExpr te = fne as TypeExpr; + if (te == null) { + Error_UnexpectedKind (mc, fne, "type", fne.ExprClassName, loc); + return null; + } + + te.loc = loc; + + type = te.Type; + + var dep = type.GetMissingDependencies (); + if (dep != null) { + ImportedTypeDefinition.Error_MissingDependency (mc, dep, loc); + } + + if (type.Kind == MemberKind.Void) { + mc.Module.Compiler.Report.Error (673, loc, "System.Void cannot be used from C#. Consider using `void'"); + } + + // + // Obsolete checks cannot be done when resolving base context as they + // require type dependencies to be set but we are in process of resolving them + // + if (!(mc is TypeDefinition.BaseContext) && !(mc is UsingAliasNamespace.AliasContext)) { + ObsoleteAttribute obsolete_attr = type.GetAttributeObsolete (); + if (obsolete_attr != null && !mc.IsObsolete) { + AttributeTester.Report_ObsoleteMessage (obsolete_attr, te.GetSignatureForError (), Location, mc.Module.Compiler.Report); + } + } + + return type; + } + + + public override void Emit (EmitContext ec) + { + throw new InternalErrorException ("FullNamedExpression `{0}' found in resolved tree", + GetSignatureForError ()); + } + } + + /// + /// Expression that evaluates to a type + /// + public abstract class TypeExpr : FullNamedExpression + { + public sealed override FullNamedExpression ResolveAsTypeOrNamespace (IMemberContext mc, bool allowUnboundTypeArguments) + { + ResolveAsType (mc); + return this; + } + + protected sealed override Expression DoResolve (ResolveContext ec) + { + ResolveAsType (ec); + return this; + } + + public override bool Equals (object obj) + { + TypeExpr tobj = obj as TypeExpr; + if (tobj == null) + return false; + + return Type == tobj.Type; + } + + public override int GetHashCode () + { + return Type.GetHashCode (); + } + } + + /// + /// Fully resolved Expression that already evaluated to a type + /// + public class TypeExpression : TypeExpr + { + public TypeExpression (TypeSpec t, Location l) + { + Type = t; + eclass = ExprClass.Type; + loc = l; + } + + public sealed override TypeSpec ResolveAsType (IMemberContext mc, bool allowUnboundTypeArguments = false) + { + return type; + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class NamespaceExpression : FullNamedExpression + { + readonly Namespace ns; + + public NamespaceExpression (Namespace ns, Location loc) + { + this.ns = ns; + this.Type = InternalType.Namespace; + this.eclass = ExprClass.Namespace; + this.loc = loc; + } + + public Namespace Namespace { + get { + return ns; + } + } + + protected override Expression DoResolve (ResolveContext rc) + { + throw new NotImplementedException (); + } + + public override FullNamedExpression ResolveAsTypeOrNamespace (IMemberContext mc, bool allowUnboundTypeArguments) + { + return this; + } + + public void Error_NamespaceDoesNotExist (IMemberContext ctx, string name, int arity) + { + var retval = Namespace.LookupType (ctx, name, arity, LookupMode.IgnoreAccessibility, loc); + if (retval != null) { +// ctx.Module.Compiler.Report.SymbolRelatedToPreviousError (retval.MemberDefinition); + ErrorIsInaccesible (ctx, retval.GetSignatureForError (), loc); + return; + } + + retval = Namespace.LookupType (ctx, name, -System.Math.Max (1, arity), LookupMode.Probing, loc); + if (retval != null) { + Error_TypeArgumentsCannotBeUsed (ctx, retval, loc); + return; + } + + Namespace ns; + if (arity > 0 && Namespace.TryGetNamespace (name, out ns)) { + Error_TypeArgumentsCannotBeUsed (ctx, ExprClassName, ns.GetSignatureForError (), loc); + return; + } + + string assembly = null; + string possible_name = Namespace.GetSignatureForError () + "." + name; + + // Only assembly unique name should be added + switch (possible_name) { + case "System.Drawing": + case "System.Web.Services": + case "System.Web": + case "System.Data": + case "System.Configuration": + case "System.Data.Services": + case "System.DirectoryServices": + case "System.Json": + case "System.Net.Http": + case "System.Numerics": + case "System.Runtime.Caching": + case "System.ServiceModel": + case "System.Transactions": + case "System.Web.Routing": + case "System.Xml.Linq": + case "System.Xml": + assembly = possible_name; + break; + + case "System.Linq": + case "System.Linq.Expressions": + assembly = "System.Core"; + break; + + case "System.Windows.Forms": + case "System.Windows.Forms.Layout": + assembly = "System.Windows.Forms"; + break; + } + + assembly = assembly == null ? "an" : "`" + assembly + "'"; + + if (Namespace is GlobalRootNamespace) { + ctx.Module.Compiler.Report.Error (400, loc, + "The type or namespace name `{0}' could not be found in the global namespace. Are you missing {1} assembly reference?", + name, assembly); + } else { + ctx.Module.Compiler.Report.Error (234, loc, + "The type or namespace name `{0}' does not exist in the namespace `{1}'. Are you missing {2} assembly reference?", + name, GetSignatureForError (), assembly); + } + } + + public override string GetSignatureForError () + { + return ns.GetSignatureForError (); + } + + public FullNamedExpression LookupTypeOrNamespace (IMemberContext ctx, string name, int arity, LookupMode mode, Location loc) + { + return ns.LookupTypeOrNamespace (ctx, name, arity, mode, loc); + } + } + + /// + /// This class denotes an expression which evaluates to a member + /// of a struct or a class. + /// + public abstract class MemberExpr : Expression, OverloadResolver.IInstanceQualifier + { + protected bool conditional_access_receiver; + + // + // An instance expression associated with this member, if it's a + // non-static member + // + public Expression InstanceExpression; + + /// + /// The name of this member. + /// + public abstract string Name { + get; + } + + // + // When base.member is used + // + public bool IsBase { + get { return InstanceExpression is BaseThis; } + } + + /// + /// Whether this is an instance member. + /// + public abstract bool IsInstance { + get; + } + + /// + /// Whether this is a static member. + /// + public abstract bool IsStatic { + get; + } + + public abstract string KindName { + get; + } + + public bool ConditionalAccess { get; set; } + + protected abstract TypeSpec DeclaringType { + get; + } + + TypeSpec OverloadResolver.IInstanceQualifier.InstanceType { + get { + return InstanceExpression.Type; + } + } + + // + // Converts best base candidate for virtual method starting from QueriedBaseType + // + protected MethodSpec CandidateToBaseOverride (ResolveContext rc, MethodSpec method) + { + // + // Only when base.member is used and method is virtual + // + if (!IsBase) + return method; + + // + // Overload resulution works on virtual or non-virtual members only (no overrides). That + // means for base.member access we have to find the closest match after we found best candidate + // + if ((method.Modifiers & (Modifiers.ABSTRACT | Modifiers.VIRTUAL | Modifiers.OVERRIDE)) != 0) { + // + // The method could already be what we are looking for + // + TypeSpec[] targs = null; + if (method.DeclaringType != InstanceExpression.Type) { + // + // Candidate can have inflated MVAR parameters and we need to find + // base match for original definition not inflated parameter types + // + var parameters = method.Parameters; + if (method.Arity > 0) { + parameters = ((IParametersMember) method.MemberDefinition).Parameters; + var inflated = method.DeclaringType as InflatedTypeSpec; + if (inflated != null) { + parameters = parameters.Inflate (inflated.CreateLocalInflator (rc)); + } + } + + var filter = new MemberFilter (method.Name, method.Arity, MemberKind.Method, parameters, null); + var base_override = MemberCache.FindMember (InstanceExpression.Type, filter, BindingRestriction.InstanceOnly | BindingRestriction.OverrideOnly) as MethodSpec; + if (base_override != null && base_override.DeclaringType != method.DeclaringType) { + if (base_override.IsGeneric) + targs = method.TypeArguments; + + method = base_override; + } + } + + // + // When base access is used inside anonymous method/iterator/etc we need to + // get back to the context of original type. We do it by emiting proxy + // method in original class and rewriting base call to this compiler + // generated method call which does the actual base invocation. This may + // introduce redundant storey but with `this' only but it's tricky to avoid + // at this stage as we don't know what expressions follow base + // + if (rc.CurrentAnonymousMethod != null) { + if (targs == null && method.IsGeneric) { + targs = method.TypeArguments; + method = method.GetGenericMethodDefinition (); + } + + if (method.Parameters.HasArglist) + throw new NotImplementedException ("__arglist base call proxy"); + + method = rc.CurrentMemberDefinition.Parent.PartialContainer.CreateHoistedBaseCallProxy (rc, method); + + // Ideally this should apply to any proxy rewrite but in the case of unary mutators on + // get/set member expressions second call would fail to proxy because left expression + // would be of 'this' and not 'base' because we share InstanceExpression for get/set + // FIXME: The async check is another hack but will probably fail with mutators + if (rc.CurrentType.IsStruct || rc.CurrentAnonymousMethod.Storey is AsyncTaskStorey) + InstanceExpression = new This (loc).Resolve (rc); + } + + if (targs != null) + method = method.MakeGenericMethod (rc, targs); + } + + // + // Only base will allow this invocation to happen. + // + if (method.IsAbstract) { + rc.Report.SymbolRelatedToPreviousError (method); + Error_CannotCallAbstractBase (rc, method.GetSignatureForError ()); + } + + return method; + } + + protected void CheckProtectedMemberAccess (ResolveContext rc, MemberSpec member) + { + if (InstanceExpression == null) + return; + + if ((member.Modifiers & Modifiers.PROTECTED) != 0 && !(InstanceExpression is This)) { + if (!CheckProtectedMemberAccess (rc, member, InstanceExpression.Type)) { + Error_ProtectedMemberAccess (rc, member, InstanceExpression.Type, loc); + } + } + } + + bool OverloadResolver.IInstanceQualifier.CheckProtectedMemberAccess (ResolveContext rc, MemberSpec member) + { + if (InstanceExpression == null) + return true; + + return InstanceExpression is This || CheckProtectedMemberAccess (rc, member, InstanceExpression.Type); + } + + public static bool CheckProtectedMemberAccess (ResolveContext rc, T member, TypeSpec qualifier) where T : MemberSpec + { + var ct = rc.CurrentType; + if (ct == qualifier) + return true; + + if ((member.Modifiers & Modifiers.INTERNAL) != 0 && member.DeclaringType.MemberDefinition.IsInternalAsPublic (ct.MemberDefinition.DeclaringAssembly)) + return true; + + qualifier = qualifier.GetDefinition (); + if (ct != qualifier && !IsSameOrBaseQualifier (ct, qualifier)) { + return false; + } + + return true; + } + + public override bool ContainsEmitWithAwait () + { + return InstanceExpression != null && InstanceExpression.ContainsEmitWithAwait (); + } + + public override bool HasConditionalAccess () + { + return ConditionalAccess || (InstanceExpression != null && InstanceExpression.HasConditionalAccess ()); + } + + static bool IsSameOrBaseQualifier (TypeSpec type, TypeSpec qtype) + { + do { + type = type.GetDefinition (); + + if (type == qtype || TypeManager.IsFamilyAccessible (qtype, type)) + return true; + + type = type.DeclaringType; + } while (type != null); + + return false; + } + + protected void DoBestMemberChecks (ResolveContext rc, T member) where T : MemberSpec, IInterfaceMemberSpec + { + if (InstanceExpression != null) { + InstanceExpression = InstanceExpression.Resolve (rc); + CheckProtectedMemberAccess (rc, member); + } + + if (member.MemberType.IsPointer && !rc.IsUnsafe) { + UnsafeError (rc, loc); + } + + var dep = member.GetMissingDependencies (); + if (dep != null) { + ImportedTypeDefinition.Error_MissingDependency (rc, dep, loc); + } + + if (!rc.IsObsolete) { + ObsoleteAttribute oa = member.GetAttributeObsolete (); + if (oa != null) + AttributeTester.Report_ObsoleteMessage (oa, member.GetSignatureForError (), loc, rc.Report); + } + + if (!(member is FieldSpec)) + member.MemberDefinition.SetIsUsed (); + } + + protected virtual void Error_CannotCallAbstractBase (ResolveContext rc, string name) + { + rc.Report.Error (205, loc, "Cannot call an abstract base member `{0}'", name); + } + + public static void Error_ProtectedMemberAccess (ResolveContext rc, MemberSpec member, TypeSpec qualifier, Location loc) + { + rc.Report.SymbolRelatedToPreviousError (member); + rc.Report.Error (1540, loc, + "Cannot access protected member `{0}' via a qualifier of type `{1}'. The qualifier must be of type `{2}' or derived from it", + member.GetSignatureForError (), qualifier.GetSignatureForError (), rc.CurrentType.GetSignatureForError ()); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + if (InstanceExpression != null) { + InstanceExpression.FlowAnalysis (fc); + + if (ConditionalAccess) { + fc.BranchConditionalAccessDefiniteAssignment (); + } + } + } + + protected void ResolveConditionalAccessReceiver (ResolveContext rc) + { + if (!rc.HasSet (ResolveContext.Options.ConditionalAccessReceiver)) { + if (HasConditionalAccess ()) { + conditional_access_receiver = true; + rc.Set (ResolveContext.Options.ConditionalAccessReceiver); + } + } + } + + public bool ResolveInstanceExpression (ResolveContext rc, Expression rhs) + { + if (!ResolveInstanceExpressionCore (rc, rhs)) + return false; + + // + // Check intermediate value modification which won't have any effect + // + if (rhs != null && TypeSpec.IsValueType (InstanceExpression.Type)) { + var fexpr = InstanceExpression as FieldExpr; + if (fexpr != null) { + if (!fexpr.Spec.IsReadOnly || rc.HasAny (ResolveContext.Options.FieldInitializerScope | ResolveContext.Options.ConstructorScope)) + return true; + + if (fexpr.IsStatic) { + rc.Report.Error (1650, loc, "Fields of static readonly field `{0}' cannot be assigned to (except in a static constructor or a variable initializer)", + fexpr.GetSignatureForError ()); + } else { + rc.Report.Error (1648, loc, "Members of readonly field `{0}' cannot be modified (except in a constructor or a variable initializer)", + fexpr.GetSignatureForError ()); + } + + return true; + } + + if (InstanceExpression is PropertyExpr || InstanceExpression is IndexerExpr || InstanceExpression is Invocation) { + if (rc.CurrentInitializerVariable != null) { + rc.Report.Error (1918, loc, "Members of value type `{0}' cannot be assigned using a property `{1}' object initializer", + InstanceExpression.Type.GetSignatureForError (), InstanceExpression.GetSignatureForError ()); + } else { + rc.Report.Error (1612, loc, + "Cannot modify a value type return value of `{0}'. Consider storing the value in a temporary variable", + InstanceExpression.GetSignatureForError ()); + } + + return true; + } + + var lvr = InstanceExpression as LocalVariableReference; + if (lvr != null) { + + if (!lvr.local_info.IsReadonly) + return true; + + rc.Report.Error (1654, loc, "Cannot assign to members of `{0}' because it is a `{1}'", + InstanceExpression.GetSignatureForError (), lvr.local_info.GetReadOnlyContext ()); + } + } + + return true; + } + + bool ResolveInstanceExpressionCore (ResolveContext rc, Expression rhs) + { + if (IsStatic) { + if (InstanceExpression != null) { + if (InstanceExpression is TypeExpr) { + var t = InstanceExpression.Type; + do { + ObsoleteAttribute oa = t.GetAttributeObsolete (); + if (oa != null && !rc.IsObsolete) { + AttributeTester.Report_ObsoleteMessage (oa, t.GetSignatureForError (), loc, rc.Report); + } + + t = t.DeclaringType; + } while (t != null); + } else { + var runtime_expr = InstanceExpression as RuntimeValueExpression; + if (runtime_expr == null || !runtime_expr.IsSuggestionOnly) { + rc.Report.Error (176, loc, + "Static member `{0}' cannot be accessed with an instance reference, qualify it with a type name instead", + GetSignatureForError ()); + } + } + + InstanceExpression = null; + } + + return false; + } + + if (InstanceExpression == null || InstanceExpression is TypeExpr) { + if (InstanceExpression != null || !This.IsThisAvailable (rc, true)) { + if (rc.HasSet (ResolveContext.Options.FieldInitializerScope)) { + rc.Report.Error (236, loc, + "A field initializer cannot reference the nonstatic field, method, or property `{0}'", + GetSignatureForError ()); + } else { + var fe = this as FieldExpr; + if (fe != null && fe.Spec.MemberDefinition is PrimaryConstructorField) { + if (rc.HasSet (ResolveContext.Options.BaseInitializer)) { + rc.Report.Error (9005, loc, "Constructor initializer cannot access primary constructor parameters"); + } else { + rc.Report.Error (9006, loc, "An object reference is required to access primary constructor parameter `{0}'", + fe.Name); + } + } else { + rc.Report.Error (120, loc, + "An object reference is required to access non-static member `{0}'", + GetSignatureForError ()); + } + } + + InstanceExpression = new CompilerGeneratedThis (rc.CurrentType, loc).Resolve (rc); + return false; + } + + if (!TypeManager.IsFamilyAccessible (rc.CurrentType, DeclaringType)) { + rc.Report.Error (38, loc, + "Cannot access a nonstatic member of outer type `{0}' via nested type `{1}'", + DeclaringType.GetSignatureForError (), rc.CurrentType.GetSignatureForError ()); + } + + InstanceExpression = new This (loc).Resolve (rc); + return false; + } + + var me = InstanceExpression as MemberExpr; + if (me != null) { + me.ResolveInstanceExpressionCore (rc, rhs); + + var fe = me as FieldExpr; + if (fe != null && fe.IsMarshalByRefAccess (rc)) { + rc.Report.SymbolRelatedToPreviousError (me.DeclaringType); + rc.Report.Warning (1690, 1, loc, + "Cannot call methods, properties, or indexers on `{0}' because it is a value type member of a marshal-by-reference class", + me.GetSignatureForError ()); + } + + return true; + } + + // + // Additional checks for l-value member access + // + if (rhs != null) { + if (InstanceExpression is UnboxCast) { + rc.Report.Error (445, InstanceExpression.Location, "Cannot modify the result of an unboxing conversion"); + } + } + + return true; + } + + public virtual MemberExpr ResolveMemberAccess (ResolveContext ec, Expression left, SimpleName original) + { + if (left != null && !ConditionalAccess && left.IsNull && TypeSpec.IsReferenceType (left.Type)) { + ec.Report.Warning (1720, 1, left.Location, + "Expression will always cause a `{0}'", "System.NullReferenceException"); + } + + InstanceExpression = left; + return this; + } + + protected void EmitInstance (EmitContext ec, bool prepare_for_load) + { + var inst = new InstanceEmitter (InstanceExpression, TypeSpec.IsValueType (InstanceExpression.Type)); + inst.Emit (ec, ConditionalAccess); + + if (prepare_for_load) + ec.Emit (OpCodes.Dup); + } + + public abstract void SetTypeArguments (ResolveContext ec, TypeArguments ta); + } + + public class ExtensionMethodCandidates + { + readonly NamespaceContainer container; + readonly IList methods; + readonly int index; + readonly IMemberContext context; + + public ExtensionMethodCandidates (IMemberContext context, IList methods, NamespaceContainer nsContainer, int lookupIndex) + { + this.context = context; + this.methods = methods; + this.container = nsContainer; + this.index = lookupIndex; + } + + public NamespaceContainer Container { + get { + return container; + } + } + + public IMemberContext Context { + get { + return context; + } + } + + public int LookupIndex { + get { + return index; + } + } + + public IList Methods { + get { + return methods; + } + } + } + + // + // Represents a group of extension method candidates for whole namespace + // + class ExtensionMethodGroupExpr : MethodGroupExpr, OverloadResolver.IErrorHandler + { + ExtensionMethodCandidates candidates; + public Expression ExtensionExpression; + + public ExtensionMethodGroupExpr (ExtensionMethodCandidates candidates, Expression extensionExpr, Location loc) + : base (candidates.Methods.Cast().ToList (), extensionExpr.Type, loc) + { + this.candidates = candidates; + this.ExtensionExpression = extensionExpr; + } + + public override bool IsStatic { + get { return true; } + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + if (ConditionalAccess) { + fc.BranchConditionalAccessDefiniteAssignment (); + } + } + + // + // For extension methodgroup we are not looking for base members but parent + // namespace extension methods + // + public override IList GetBaseMembers (TypeSpec baseType) + { + // TODO: candidates are null only when doing error reporting, that's + // incorrect. We have to discover same extension methods in error mode + if (candidates == null) + return null; + + int arity = type_arguments == null ? 0 : type_arguments.Count; + + candidates = candidates.Container.LookupExtensionMethod (candidates.Context, ExtensionExpression.Type, Name, arity, candidates.LookupIndex); + if (candidates == null) + return null; + + return candidates.Methods.Cast ().ToList (); + } + + public override MethodGroupExpr LookupExtensionMethod (ResolveContext rc) + { + // We are already here + return null; + } + + public override MethodGroupExpr OverloadResolve (ResolveContext ec, ref Arguments arguments, OverloadResolver.IErrorHandler ehandler, OverloadResolver.Restrictions restr) + { + if (arguments == null) + arguments = new Arguments (1); + + ExtensionExpression = ExtensionExpression.Resolve (ec); + if (ExtensionExpression == null) + return null; + + var cand = candidates; + var atype = ConditionalAccess ? Argument.AType.ExtensionTypeConditionalAccess : Argument.AType.ExtensionType; + arguments.Insert (0, new Argument (ExtensionExpression, atype)); + var res = base.OverloadResolve (ec, ref arguments, ehandler ?? this, restr); + + // Restore candidates in case we are running in probing mode + candidates = cand; + + // Store resolved argument and restore original arguments + if (res == null) { + // Clean-up modified arguments for error reporting + arguments.RemoveAt (0); + return null; + } + + var me = ExtensionExpression as MemberExpr; + if (me != null) { + me.ResolveInstanceExpression (ec, null); + var fe = me as FieldExpr; + if (fe != null) + fe.Spec.MemberDefinition.SetIsUsed (); + } + + InstanceExpression = null; + return this; + } + + #region IErrorHandler Members + + bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext rc, MemberSpec best, MemberSpec ambiguous) + { + return false; + } + + bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index) + { + rc.Report.SymbolRelatedToPreviousError (best); + rc.Report.Error (1928, loc, + "Type `{0}' does not contain a member `{1}' and the best extension method overload `{2}' has some invalid arguments", + queried_type.GetSignatureForError (), Name, best.GetSignatureForError ()); + + if (index == 0) { + rc.Report.Error (1929, loc, + "Extension method instance type `{0}' cannot be converted to `{1}'", + arg.Type.GetSignatureForError (), ((MethodSpec)best).Parameters.ExtensionMethodType.GetSignatureForError ()); + } + + return true; + } + + bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best) + { + return false; + } + + bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best) + { + return false; + } + + #endregion + } + + /// + /// MethodGroupExpr represents a group of method candidates which + /// can be resolved to the best method overload + /// + public class MethodGroupExpr : MemberExpr, OverloadResolver.IBaseMembersProvider + { + static readonly MemberSpec[] Excluded = new MemberSpec[0]; + + protected IList Methods; + MethodSpec best_candidate; + TypeSpec best_candidate_return; + protected TypeArguments type_arguments; + + SimpleName simple_name; + protected TypeSpec queried_type; + + public MethodGroupExpr (IList mi, TypeSpec type, Location loc) + { + Methods = mi; + this.loc = loc; + this.type = InternalType.MethodGroup; + + eclass = ExprClass.MethodGroup; + queried_type = type; + } + + public MethodGroupExpr (MethodSpec m, TypeSpec type, Location loc) + : this (new MemberSpec[] { m }, type, loc) + { + } + + #region Properties + + public MethodSpec BestCandidate { + get { + return best_candidate; + } + } + + public TypeSpec BestCandidateReturnType { + get { + return best_candidate_return; + } + } + + public IList Candidates { + get { + return Methods; + } + } + + protected override TypeSpec DeclaringType { + get { + return queried_type; + } + } + + public bool IsConditionallyExcluded { + get { + return Methods == Excluded; + } + } + + public override bool IsInstance { + get { + if (best_candidate != null) + return !best_candidate.IsStatic; + + return false; + } + } + + public override bool IsSideEffectFree { + get { + return InstanceExpression == null || InstanceExpression.IsSideEffectFree; + } + } + + public override bool IsStatic { + get { + if (best_candidate != null) + return best_candidate.IsStatic; + + return false; + } + } + + public override string KindName { + get { return "method"; } + } + + public override string Name { + get { + if (best_candidate != null) + return best_candidate.Name; + + // TODO: throw ? + return Methods.First ().Name; + } + } + + #endregion + + // + // When best candidate is already know this factory can be used + // to avoid expensive overload resolution to be called + // + // NOTE: InstanceExpression has to be set manually + // + public static MethodGroupExpr CreatePredefined (MethodSpec best, TypeSpec queriedType, Location loc) + { + return new MethodGroupExpr (best, queriedType, loc) { + best_candidate = best, + best_candidate_return = best.ReturnType + }; + } + + public override string GetSignatureForError () + { + if (best_candidate != null) + return best_candidate.GetSignatureForError (); + + return Methods.First ().GetSignatureForError (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + if (best_candidate == null) { + ec.Report.Error (1953, loc, "An expression tree cannot contain an expression with method group"); + return null; + } + + if (IsConditionallyExcluded) + ec.Report.Error (765, loc, + "Partial methods with only a defining declaration or removed conditional methods cannot be used in an expression tree"); + + if (ConditionalAccess) + Error_NullShortCircuitInsideExpressionTree (ec); + + return new TypeOfMethod (best_candidate, loc); + } + + protected override Expression DoResolve (ResolveContext ec) + { + this.eclass = ExprClass.MethodGroup; + + if (InstanceExpression != null) { + InstanceExpression = InstanceExpression.Resolve (ec); + if (InstanceExpression == null) + return null; + } + + return this; + } + + public override void Emit (EmitContext ec) + { + throw new NotSupportedException (); + } + + public void EmitCall (EmitContext ec, Arguments arguments, bool statement) + { + var call = new CallEmitter (); + call.InstanceExpression = InstanceExpression; + call.ConditionalAccess = ConditionalAccess; + + if (statement) + call.EmitStatement (ec, best_candidate, arguments, loc); + else + call.Emit (ec, best_candidate, arguments, loc); + } + + public void EmitCall (EmitContext ec, Arguments arguments, TypeSpec conditionalAccessReceiver, bool statement) + { + ec.ConditionalAccess = new ConditionalAccessContext (conditionalAccessReceiver, ec.DefineLabel ()) { + Statement = statement + }; + + EmitCall (ec, arguments, statement); + + ec.CloseConditionalAccess (!statement && best_candidate_return != conditionalAccessReceiver && conditionalAccessReceiver.IsNullableType ? conditionalAccessReceiver : null); + } + + public override void Error_ValueCannotBeConverted (ResolveContext ec, TypeSpec target, bool expl) + { + ec.Report.Error (428, loc, "Cannot convert method group `{0}' to non-delegate type `{1}'. Consider using parentheses to invoke the method", + Name, target.GetSignatureForError ()); + } + + public static bool IsExtensionMethodArgument (Expression expr) + { + // + // LAMESPEC: No details about which expressions are not allowed + // + return !(expr is TypeExpr) && !(expr is BaseThis); + } + + /// + /// Find the Applicable Function Members (7.4.2.1) + /// + /// me: Method Group expression with the members to select. + /// it might contain constructors or methods (or anything + /// that maps to a method). + /// + /// Arguments: ArrayList containing resolved Argument objects. + /// + /// loc: The location if we want an error to be reported, or a Null + /// location for "probing" purposes. + /// + /// Returns: The MethodBase (either a ConstructorInfo or a MethodInfo) + /// that is the best match of me on Arguments. + /// + /// + public virtual MethodGroupExpr OverloadResolve (ResolveContext ec, ref Arguments args, OverloadResolver.IErrorHandler cerrors, OverloadResolver.Restrictions restr) + { + // TODO: causes issues with probing mode, remove explicit Kind check + if (best_candidate != null && best_candidate.Kind == MemberKind.Destructor) + return this; + + var r = new OverloadResolver (Methods, type_arguments, restr, loc); + if ((restr & OverloadResolver.Restrictions.NoBaseMembers) == 0) { + r.BaseMembersProvider = this; + r.InstanceQualifier = this; + } + + if (cerrors != null) + r.CustomErrors = cerrors; + + // TODO: When in probing mode do IsApplicable only and when called again do VerifyArguments for full error reporting + best_candidate = r.ResolveMember (ec, ref args); + if (best_candidate == null) { + if (!r.BestCandidateIsDynamic) + return null; + + if (simple_name != null && ec.IsStatic) + InstanceExpression = ProbeIdenticalTypeName (ec, InstanceExpression, simple_name); + + return this; + } + + // Overload resolver had to create a new method group, all checks bellow have already been executed + if (r.BestCandidateNewMethodGroup != null) + return r.BestCandidateNewMethodGroup; + + if (best_candidate.Kind == MemberKind.Method && (restr & OverloadResolver.Restrictions.ProbingOnly) == 0) { + if (InstanceExpression != null) { + if (best_candidate.IsExtensionMethod && args[0].Expr == InstanceExpression) { + InstanceExpression = null; + } else { + if (simple_name != null && best_candidate.IsStatic) { + InstanceExpression = ProbeIdenticalTypeName (ec, InstanceExpression, simple_name); + } + + InstanceExpression.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup | ResolveFlags.Type); + } + } + + ResolveInstanceExpression (ec, null); + } + + var base_override = CandidateToBaseOverride (ec, best_candidate); + if (base_override == best_candidate) { + best_candidate_return = r.BestCandidateReturnType; + } else { + best_candidate = base_override; + best_candidate_return = best_candidate.ReturnType; + } + + if (best_candidate.IsGeneric && (restr & OverloadResolver.Restrictions.ProbingOnly) == 0 && TypeParameterSpec.HasAnyTypeParameterConstrained (best_candidate.GenericDefinition)) { + ConstraintChecker cc = new ConstraintChecker (ec); + cc.CheckAll (best_candidate.GetGenericMethodDefinition (), best_candidate.TypeArguments, best_candidate.Constraints, loc); + } + + // + // Additional check for possible imported base override method which + // could not be done during IsOverrideMethodBaseTypeAccessible + // + if (best_candidate.IsVirtual && (best_candidate.DeclaringType.Modifiers & Modifiers.PROTECTED) != 0 && + best_candidate.MemberDefinition.IsImported && !best_candidate.DeclaringType.IsAccessible (ec)) { + ec.Report.SymbolRelatedToPreviousError (best_candidate); + ErrorIsInaccesible (ec, best_candidate.GetSignatureForError (), loc); + } + + // Speed up the check by not doing it on disallowed targets + if (best_candidate_return.Kind == MemberKind.Void && best_candidate.IsConditionallyExcluded (ec)) + Methods = Excluded; + + return this; + } + + public override MemberExpr ResolveMemberAccess (ResolveContext ec, Expression left, SimpleName original) + { + var fe = left as FieldExpr; + if (fe != null) { + // + // Using method-group on struct fields makes the struct assigned. I am not sure + // why but that's what .net does + // + fe.Spec.MemberDefinition.SetIsAssigned (); + } + + simple_name = original; + return base.ResolveMemberAccess (ec, left, original); + } + + public override void SetTypeArguments (ResolveContext ec, TypeArguments ta) + { + type_arguments = ta; + } + + #region IBaseMembersProvider Members + + public virtual IList GetBaseMembers (TypeSpec baseType) + { + return baseType == null ? null : MemberCache.FindMembers (baseType, Methods [0].Name, false); + } + + public IParametersMember GetOverrideMemberParameters (MemberSpec member) + { + if (queried_type == member.DeclaringType) + return null; + + return MemberCache.FindMember (queried_type, new MemberFilter ((MethodSpec) member), + BindingRestriction.InstanceOnly | BindingRestriction.OverrideOnly) as IParametersMember; + } + + // + // Extension methods lookup after ordinary methods candidates failed to apply + // + public virtual MethodGroupExpr LookupExtensionMethod (ResolveContext rc) + { + if (InstanceExpression == null || InstanceExpression.eclass == ExprClass.Type) + return null; + + if (!IsExtensionMethodArgument (InstanceExpression)) + return null; + + int arity = type_arguments == null ? 0 : type_arguments.Count; + var methods = rc.LookupExtensionMethod (InstanceExpression.Type, Methods[0].Name, arity); + if (methods == null) + return null; + + var emg = new ExtensionMethodGroupExpr (methods, InstanceExpression, loc); + emg.SetTypeArguments (rc, type_arguments); + return emg; + } + + #endregion + } + + struct ConstructorInstanceQualifier : OverloadResolver.IInstanceQualifier + { + public ConstructorInstanceQualifier (TypeSpec type) + : this () + { + InstanceType = type; + } + + public TypeSpec InstanceType { get; private set; } + + public bool CheckProtectedMemberAccess (ResolveContext rc, MemberSpec member) + { + return MemberExpr.CheckProtectedMemberAccess (rc, member, InstanceType); + } + } + + public struct OverloadResolver + { + [Flags] + public enum Restrictions + { + None = 0, + DelegateInvoke = 1, + ProbingOnly = 1 << 1, + CovariantDelegate = 1 << 2, + NoBaseMembers = 1 << 3, + BaseMembersIncluded = 1 << 4, + GetEnumeratorLookup = 1 << 5 + } + + public interface IBaseMembersProvider + { + IList GetBaseMembers (TypeSpec baseType); + IParametersMember GetOverrideMemberParameters (MemberSpec member); + MethodGroupExpr LookupExtensionMethod (ResolveContext rc); + } + + public interface IErrorHandler + { + bool AmbiguousCandidates (ResolveContext rc, MemberSpec best, MemberSpec ambiguous); + bool ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument a, int index); + bool NoArgumentMatch (ResolveContext rc, MemberSpec best); + bool TypeInferenceFailed (ResolveContext rc, MemberSpec best); + } + + public interface IInstanceQualifier + { + TypeSpec InstanceType { get; } + bool CheckProtectedMemberAccess (ResolveContext rc, MemberSpec member); + } + + sealed class NoBaseMembers : IBaseMembersProvider + { + public static readonly IBaseMembersProvider Instance = new NoBaseMembers (); + + public IList GetBaseMembers (TypeSpec baseType) + { + return null; + } + + public IParametersMember GetOverrideMemberParameters (MemberSpec member) + { + return null; + } + + public MethodGroupExpr LookupExtensionMethod (ResolveContext rc) + { + return null; + } + } + + struct AmbiguousCandidate + { + public readonly MemberSpec Member; + public readonly bool Expanded; + public readonly AParametersCollection Parameters; + + public AmbiguousCandidate (MemberSpec member, AParametersCollection parameters, bool expanded) + { + Member = member; + Parameters = parameters; + Expanded = expanded; + } + } + + Location loc; + IList members; + TypeArguments type_arguments; + IBaseMembersProvider base_provider; + IErrorHandler custom_errors; + IInstanceQualifier instance_qualifier; + Restrictions restrictions; + MethodGroupExpr best_candidate_extension_group; + TypeSpec best_candidate_return_type; + + SessionReportPrinter lambda_conv_msgs; + + public OverloadResolver (IList members, Restrictions restrictions, Location loc) + : this (members, null, restrictions, loc) + { + } + + public OverloadResolver (IList members, TypeArguments targs, Restrictions restrictions, Location loc) + : this () + { + if (members == null || members.Count == 0) + throw new ArgumentException ("empty members set"); + + this.members = members; + this.loc = loc; + type_arguments = targs; + this.restrictions = restrictions; + if (IsDelegateInvoke) + this.restrictions |= Restrictions.NoBaseMembers; + + base_provider = NoBaseMembers.Instance; + } + + #region Properties + + public IBaseMembersProvider BaseMembersProvider { + get { + return base_provider; + } + set { + base_provider = value; + } + } + + public bool BestCandidateIsDynamic { get; set; } + + // + // Best candidate was found in newly created MethodGroupExpr, used by extension methods + // + public MethodGroupExpr BestCandidateNewMethodGroup { + get { + return best_candidate_extension_group; + } + } + + // + // Return type can be different between best candidate and closest override + // + public TypeSpec BestCandidateReturnType { + get { + return best_candidate_return_type; + } + } + + public IErrorHandler CustomErrors { + get { + return custom_errors; + } + set { + custom_errors = value; + } + } + + TypeSpec DelegateType { + get { + if ((restrictions & Restrictions.DelegateInvoke) == 0) + throw new InternalErrorException ("Not running in delegate mode", loc); + + return members [0].DeclaringType; + } + } + + public IInstanceQualifier InstanceQualifier { + get { + return instance_qualifier; + } + set { + instance_qualifier = value; + } + } + + bool IsProbingOnly { + get { + return (restrictions & Restrictions.ProbingOnly) != 0; + } + } + + bool IsDelegateInvoke { + get { + return (restrictions & Restrictions.DelegateInvoke) != 0; + } + } + + #endregion + + // + // 7.4.3.3 Better conversion from expression + // Returns : 1 if a->p is better, + // 2 if a->q is better, + // 0 if neither is better + // + static int BetterExpressionConversion (ResolveContext ec, Argument a, TypeSpec p, TypeSpec q) + { + TypeSpec argument_type = a.Type; + + // + // If argument is an anonymous function + // + if (argument_type == InternalType.AnonymousMethod && ec.Module.Compiler.Settings.Version > LanguageVersion.ISO_2) { + // + // p and q are delegate types or expression tree types + // + if (p.IsExpressionTreeType || q.IsExpressionTreeType) { + if (q.MemberDefinition != p.MemberDefinition) { + return 0; + } + + // + // Uwrap delegate from Expression + // + q = TypeManager.GetTypeArguments (q)[0]; + p = TypeManager.GetTypeArguments (p)[0]; + } + + var p_m = Delegate.GetInvokeMethod (p); + var q_m = Delegate.GetInvokeMethod (q); + + // + // With identical parameter lists + // + if (!TypeSpecComparer.Equals (p_m.Parameters.Types, q_m.Parameters.Types)) + return 0; + + p = p_m.ReturnType; + var orig_q = q; + q = q_m.ReturnType; + + // + // if p is void returning, and q has a return type Y, then C2 is the better conversion. + // + if (p.Kind == MemberKind.Void) { + return q.Kind != MemberKind.Void ? 2 : 0; + } + + // + // if p has a return type Y, and q is void returning, then C1 is the better conversion. + // + if (q.Kind == MemberKind.Void) { + return p.Kind != MemberKind.Void ? 1: 0; + } + + var am = (AnonymousMethodExpression) a.Expr; + + // + // When anonymous method is an asynchronous, and P has a return type Task, and Q has a return type Task + // better conversion is performed between underlying types Y1 and Y2 + // + if (p.IsGenericTask || q.IsGenericTask) { + if (am.Block.IsAsync && p.IsGenericTask && q.IsGenericTask) { + q = q.TypeArguments[0]; + p = p.TypeArguments[0]; + } + } + + if (q != p) { + // + // An inferred return type X exists for E in the context of that parameter list, and + // the conversion from X to Y1 is better than the conversion from X to Y2 + // + argument_type = am.InferReturnType (ec, null, orig_q); + if (argument_type == null) { + // TODO: Can this be hit? + return 1; + } + + if (argument_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) + argument_type = ec.BuiltinTypes.Object; + } + } + + if (argument_type == p) + return 1; + + if (argument_type == q) + return 2; + + // + // The parameters are identicial and return type is not void, use better type conversion + // on return type to determine better one + // + return BetterTypeConversion (ec, p, q); + } + + // + // 7.4.3.4 Better conversion from type + // + public static int BetterTypeConversion (ResolveContext ec, TypeSpec p, TypeSpec q) + { + if (p == null || q == null) + throw new InternalErrorException ("BetterTypeConversion got a null conversion"); + + switch (p.BuiltinType) { + case BuiltinTypeSpec.Type.Int: + if (q.BuiltinType == BuiltinTypeSpec.Type.UInt || q.BuiltinType == BuiltinTypeSpec.Type.ULong) + return 1; + break; + case BuiltinTypeSpec.Type.Long: + if (q.BuiltinType == BuiltinTypeSpec.Type.ULong) + return 1; + break; + case BuiltinTypeSpec.Type.SByte: + switch (q.BuiltinType) { + case BuiltinTypeSpec.Type.Byte: + case BuiltinTypeSpec.Type.UShort: + case BuiltinTypeSpec.Type.UInt: + case BuiltinTypeSpec.Type.ULong: + return 1; + } + break; + case BuiltinTypeSpec.Type.Short: + switch (q.BuiltinType) { + case BuiltinTypeSpec.Type.UShort: + case BuiltinTypeSpec.Type.UInt: + case BuiltinTypeSpec.Type.ULong: + return 1; + } + break; + case BuiltinTypeSpec.Type.Dynamic: + // Dynamic is never better + return 2; + } + + switch (q.BuiltinType) { + case BuiltinTypeSpec.Type.Int: + if (p.BuiltinType == BuiltinTypeSpec.Type.UInt || p.BuiltinType == BuiltinTypeSpec.Type.ULong) + return 2; + break; + case BuiltinTypeSpec.Type.Long: + if (p.BuiltinType == BuiltinTypeSpec.Type.ULong) + return 2; + break; + case BuiltinTypeSpec.Type.SByte: + switch (p.BuiltinType) { + case BuiltinTypeSpec.Type.Byte: + case BuiltinTypeSpec.Type.UShort: + case BuiltinTypeSpec.Type.UInt: + case BuiltinTypeSpec.Type.ULong: + return 2; + } + break; + case BuiltinTypeSpec.Type.Short: + switch (p.BuiltinType) { + case BuiltinTypeSpec.Type.UShort: + case BuiltinTypeSpec.Type.UInt: + case BuiltinTypeSpec.Type.ULong: + return 2; + } + break; + case BuiltinTypeSpec.Type.Dynamic: + // Dynamic is never better + return 1; + } + + // FIXME: handle lifted operators + + // TODO: this is expensive + Expression p_tmp = new EmptyExpression (p); + Expression q_tmp = new EmptyExpression (q); + + bool p_to_q = Convert.ImplicitConversionExists (ec, p_tmp, q); + bool q_to_p = Convert.ImplicitConversionExists (ec, q_tmp, p); + + if (p_to_q && !q_to_p) + return 1; + + if (q_to_p && !p_to_q) + return 2; + + return 0; + } + + /// + /// Determines "Better function" between candidate + /// and the current best match + /// + /// + /// Returns a boolean indicating : + /// false if candidate ain't better + /// true if candidate is better than the current best match + /// + static bool BetterFunction (ResolveContext ec, Arguments args, MemberSpec candidate, AParametersCollection cparam, bool candidate_params, + MemberSpec best, AParametersCollection bparam, bool best_params) + { + AParametersCollection candidate_pd = ((IParametersMember) candidate).Parameters; + AParametersCollection best_pd = ((IParametersMember) best).Parameters; + + bool better_at_least_one = false; + bool are_equivalent = true; + int args_count = args == null ? 0 : args.Count; + int j = 0; + Argument a = null; + TypeSpec ct, bt; + for (int c_idx = 0, b_idx = 0; j < args_count; ++j, ++c_idx, ++b_idx) { + a = args[j]; + + // Default arguments are ignored for better decision + if (a.IsDefaultArgument) + break; + + // + // When comparing named argument the parameter type index has to be looked up + // in original parameter set (override version for virtual members) + // + NamedArgument na = a as NamedArgument; + if (na != null) { + int idx = cparam.GetParameterIndexByName (na.Name); + ct = candidate_pd.Types[idx]; + if (candidate_params && candidate_pd.FixedParameters[idx].ModFlags == Parameter.Modifier.PARAMS) + ct = TypeManager.GetElementType (ct); + + idx = bparam.GetParameterIndexByName (na.Name); + bt = best_pd.Types[idx]; + if (best_params && best_pd.FixedParameters[idx].ModFlags == Parameter.Modifier.PARAMS) + bt = TypeManager.GetElementType (bt); + } else { + ct = candidate_pd.Types[c_idx]; + bt = best_pd.Types[b_idx]; + + if (candidate_params && candidate_pd.FixedParameters[c_idx].ModFlags == Parameter.Modifier.PARAMS) { + ct = TypeManager.GetElementType (ct); + --c_idx; + } + + if (best_params && best_pd.FixedParameters[b_idx].ModFlags == Parameter.Modifier.PARAMS) { + bt = TypeManager.GetElementType (bt); + --b_idx; + } + } + + if (TypeSpecComparer.IsEqual (ct, bt)) + continue; + + are_equivalent = false; + int result = BetterExpressionConversion (ec, a, ct, bt); + + // for each argument, the conversion to 'ct' should be no worse than + // the conversion to 'bt'. + if (result == 2) + return false; + + // for at least one argument, the conversion to 'ct' should be better than + // the conversion to 'bt'. + if (result != 0) + better_at_least_one = true; + } + + if (better_at_least_one) + return true; + + // + // Tie-breaking rules are applied only for equivalent parameter types + // + if (!are_equivalent) + return false; + + // + // If candidate is applicable in its normal form and best has a params array and is applicable + // only in its expanded form, then candidate is better + // + if (candidate_params != best_params) + return !candidate_params; + + // + // We have not reached end of parameters list due to params or used default parameters + // + while (j < candidate_pd.Count && j < best_pd.Count) { + var cand_param = candidate_pd.FixedParameters [j]; + var best_param = best_pd.FixedParameters [j]; + + if (candidate_pd.Count == best_pd.Count) { + // + // LAMESPEC: + // + // void Foo (int i = 0) is better than void Foo (params int[]) for Foo () + // void Foo (string[] s, string value = null) is better than Foo (string s, params string[]) for Foo (null) or Foo () + // + if (cand_param.HasDefaultValue != best_param.HasDefaultValue) + return cand_param.HasDefaultValue; + + if (cand_param.HasDefaultValue) { + ++j; + continue; + } + } else { + // + // Neither is better when not all arguments are provided + // + // void Foo (string s, int i = 0) <-> Foo (string s, int i = 0, int i2 = 0) + // void Foo (string s, int i = 0) <-> Foo (string s, byte i = 0) + // void Foo (string s, params int[]) <-> Foo (string s, params byte[]) + // + if (cand_param.HasDefaultValue && best_param.HasDefaultValue) + return false; + } + + break; + } + + if (candidate_pd.Count != best_pd.Count) + return candidate_pd.Count < best_pd.Count; + + // + // One is a non-generic method and second is a generic method, then non-generic is better + // + if (best.IsGeneric != candidate.IsGeneric) + return best.IsGeneric; + + // + // Both methods have the same number of parameters, and the parameters have equal types + // Pick the "more specific" signature using rules over original (non-inflated) types + // + var candidate_def_pd = ((IParametersMember) candidate.MemberDefinition).Parameters; + var best_def_pd = ((IParametersMember) best.MemberDefinition).Parameters; + + bool specific_at_least_once = false; + for (j = 0; j < args_count; ++j) { + NamedArgument na = args_count == 0 ? null : args [j] as NamedArgument; + if (na != null) { + ct = candidate_def_pd.Types[cparam.GetParameterIndexByName (na.Name)]; + bt = best_def_pd.Types[bparam.GetParameterIndexByName (na.Name)]; + } else { + ct = candidate_def_pd.Types[j]; + bt = best_def_pd.Types[j]; + } + + if (ct == bt) + continue; + TypeSpec specific = MoreSpecific (ct, bt); + if (specific == bt) + return false; + if (specific == ct) + specific_at_least_once = true; + } + + if (specific_at_least_once) + return true; + + return false; + } + + static bool CheckInflatedArguments (MethodSpec ms) + { + if (!TypeParameterSpec.HasAnyTypeParameterTypeConstrained (ms.GenericDefinition)) + return true; + + // Setup constraint checker for probing only + ConstraintChecker cc = new ConstraintChecker (null); + + var mp = ms.Parameters.Types; + for (int i = 0; i < mp.Length; ++i) { + var type = mp[i] as InflatedTypeSpec; + if (type == null) + continue; + + var targs = type.TypeArguments; + if (targs.Length == 0) + continue; + + // TODO: Checking inflated MVAR arguments should be enough + if (!cc.CheckAll (type.GetDefinition (), targs, type.Constraints, Location.Null)) + return false; + } + + return true; + } + + public static void Error_ConstructorMismatch (ResolveContext rc, TypeSpec type, int argCount, Location loc) + { + rc.Report.Error (1729, loc, + "The type `{0}' does not contain a constructor that takes `{1}' arguments", + type.GetSignatureForError (), argCount.ToString ()); + } + + // + // Determines if the candidate method is applicable to the given set of arguments + // There could be two different set of parameters for same candidate where one + // is the closest override for default values and named arguments checks and second + // one being the virtual base for the parameter types and modifiers. + // + // A return value rates candidate method compatibility, + // 0 = the best, int.MaxValue = the worst + // -1 = fatal error + // + int IsApplicable (ResolveContext ec, ref Arguments arguments, int arg_count, ref MemberSpec candidate, IParametersMember pm, ref bool params_expanded_form, ref bool dynamicArgument, ref TypeSpec returnType, bool errorMode) + { + // Parameters of most-derived type used mainly for named and optional parameters + var pd = pm.Parameters; + + // Used for params modifier only, that's legacy of C# 1.0 which uses base type for + // params modifier instead of most-derived type + var cpd = ((IParametersMember) candidate).Parameters; + int param_count = pd.Count; + int optional_count = 0; + int score; + Arguments orig_args = arguments; + + if (arg_count != param_count) { + // + // No arguments expansion when doing exact match for delegates + // + if ((restrictions & Restrictions.CovariantDelegate) == 0) { + for (int i = 0; i < pd.Count; ++i) { + if (pd.FixedParameters[i].HasDefaultValue) { + optional_count = pd.Count - i; + break; + } + } + } + + if (optional_count != 0) { + // Readjust expected number when params used + if (cpd.HasParams) { + optional_count--; + if (arg_count < param_count) + param_count--; + } else if (arg_count > param_count) { + int args_gap = System.Math.Abs (arg_count - param_count); + return int.MaxValue - 10000 + args_gap; + } else if (arg_count < param_count - optional_count) { + int args_gap = System.Math.Abs (param_count - optional_count - arg_count); + return int.MaxValue - 10000 + args_gap; + } + } else if (arg_count != param_count) { + int args_gap = System.Math.Abs (arg_count - param_count); + if (!cpd.HasParams) + return int.MaxValue - 10000 + args_gap; + if (arg_count < param_count - 1) + return int.MaxValue - 10000 + args_gap; + } + + // Resize to fit optional arguments + if (optional_count != 0) { + if (arguments == null) { + arguments = new Arguments (optional_count); + } else { + // Have to create a new container, so the next run can do same + var resized = new Arguments (param_count); + resized.AddRange (arguments); + arguments = resized; + } + + for (int i = arg_count; i < param_count; ++i) + arguments.Add (null); + } + } + + if (arg_count > 0) { + // + // Shuffle named arguments to the right positions if there are any + // + if (arguments[arg_count - 1] is NamedArgument) { + arg_count = arguments.Count; + + for (int i = 0; i < arg_count; ++i) { + bool arg_moved = false; + while (true) { + NamedArgument na = arguments[i] as NamedArgument; + if (na == null) + break; + + int index = pd.GetParameterIndexByName (na.Name); + + // Named parameter not found + if (index < 0) + return (i + 1) * 3; + + // already reordered + if (index == i) + break; + + Argument temp; + if (index >= param_count) { + // When using parameters which should not be available to the user + if ((cpd.FixedParameters[index].ModFlags & Parameter.Modifier.PARAMS) == 0) + break; + + arguments.Add (null); + ++arg_count; + temp = null; + } else { + if (index == arg_count) + return (i + 1) * 3; + + temp = arguments [index]; + + // The slot has been taken by positional argument + if (temp != null && !(temp is NamedArgument)) + break; + } + + if (!arg_moved) { + arguments = arguments.MarkOrderedArgument (na); + arg_moved = true; + } + + if (arguments == orig_args) { + arguments = new Arguments (orig_args.Count); + arguments.AddRange (orig_args); + } + + arguments[index] = arguments[i]; + arguments[i] = temp; + + if (temp == null) + break; + } + } + } else { + arg_count = arguments.Count; + } + } else if (arguments != null) { + arg_count = arguments.Count; + } + + // + // Don't do any expensive checks when the candidate cannot succeed + // + if (arg_count != param_count && !cpd.HasParams) + return (param_count - arg_count) * 2 + 1; + + var dep = candidate.GetMissingDependencies (); + if (dep != null) { + ImportedTypeDefinition.Error_MissingDependency (ec, dep, loc); + return -1; + } + + // + // 1. Handle generic method using type arguments when specified or type inference + // + TypeSpec[] ptypes; + var ms = candidate as MethodSpec; + if (ms != null && ms.IsGeneric) { + if (type_arguments != null) { + var g_args_count = ms.Arity; + if (g_args_count != type_arguments.Count) + return int.MaxValue - 20000 + System.Math.Abs (type_arguments.Count - g_args_count); + + if (type_arguments.Arguments != null) + ms = ms.MakeGenericMethod (ec, type_arguments.Arguments); + } else { + // + // Deploy custom error reporting for infered anonymous expression or lambda methods. When + // probing lambda methods keep all errors reported in separate set and once we are done and no best + // candidate was found use the set to report more details about what was wrong with lambda body. + // The general idea is to distinguish between code errors and errors caused by + // trial-and-error type inference + // + if (lambda_conv_msgs == null) { + for (int i = 0; i < arg_count; i++) { + Argument a = arguments[i]; + if (a == null) + continue; + + var am = a.Expr as AnonymousMethodExpression; + if (am != null) { + if (lambda_conv_msgs == null) + lambda_conv_msgs = new SessionReportPrinter (); + + am.TypeInferenceReportPrinter = lambda_conv_msgs; + } + } + } + + var ti = new TypeInference (arguments); + TypeSpec[] i_args = ti.InferMethodArguments (ec, ms); + + if (i_args == null) + return ti.InferenceScore - 20000; + + // + // Clear any error messages when the result was success + // + if (lambda_conv_msgs != null) + lambda_conv_msgs.ClearSession (); + + if (i_args.Length != 0) { + if (!errorMode) { + foreach (var ta in i_args) { + if (!ta.IsAccessible (ec)) + return ti.InferenceScore - 10000; + } + } + + ms = ms.MakeGenericMethod (ec, i_args); + } + } + + // + // Type arguments constraints have to match for the method to be applicable + // + if (!CheckInflatedArguments (ms)) { + candidate = ms; + return int.MaxValue - 25000; + } + + // + // We have a generic return type and at same time the method is override which + // means we have to also inflate override return type in case the candidate is + // best candidate and override return type is different to base return type. + // + // virtual Foo with override Foo + // + if (candidate != pm) { + MethodSpec override_ms = (MethodSpec) pm; + var inflator = new TypeParameterInflator (ec, ms.DeclaringType, override_ms.GenericDefinition.TypeParameters, ms.TypeArguments); + returnType = inflator.Inflate (returnType); + } else { + returnType = ms.ReturnType; + } + + candidate = ms; + pd = ms.Parameters; + ptypes = pd.Types; + } else { + if (type_arguments != null) + return int.MaxValue - 15000; + + ptypes = cpd.Types; + } + + // + // 2. Each argument has to be implicitly convertible to method parameter + // + Parameter.Modifier p_mod = 0; + TypeSpec pt = null; + + for (int i = 0; i < arg_count; i++) { + Argument a = arguments[i]; + if (a == null) { + var fp = pd.FixedParameters[i]; + if (!fp.HasDefaultValue) { + arguments = orig_args; + return arg_count * 2 + 2; + } + + // + // Get the default value expression, we can use the same expression + // if the type matches + // + Expression e = fp.DefaultValue; + if (e != null) { + e = ResolveDefaultValueArgument (ec, ptypes[i], e, loc); + if (e == null) { + // Restore for possible error reporting + for (int ii = i; ii < arg_count; ++ii) + arguments.RemoveAt (i); + + return (arg_count - i) * 2 + 1; + } + } + + if ((fp.ModFlags & Parameter.Modifier.CallerMask) != 0) { + // + // LAMESPEC: Attributes can be mixed together with build-in priority + // + if ((fp.ModFlags & Parameter.Modifier.CallerLineNumber) != 0) { + e = new IntLiteral (ec.BuiltinTypes, loc.Row, loc); + } else if ((fp.ModFlags & Parameter.Modifier.CallerFilePath) != 0) { + e = new StringLiteral (ec.BuiltinTypes, loc.NameFullPath, loc); + } else if (ec.MemberContext.CurrentMemberDefinition != null) { + e = new StringLiteral (ec.BuiltinTypes, ec.MemberContext.CurrentMemberDefinition.GetCallerMemberName (), loc); + } + } + + arguments[i] = new Argument (e, Argument.AType.Default); + continue; + } + + if (p_mod != Parameter.Modifier.PARAMS) { + p_mod = (pd.FixedParameters[i].ModFlags & ~Parameter.Modifier.PARAMS) | (cpd.FixedParameters[i].ModFlags & Parameter.Modifier.PARAMS); + pt = ptypes [i]; + } else if (!params_expanded_form) { + params_expanded_form = true; + pt = ((ElementTypeSpec) pt).Element; + i -= 2; + continue; + } + + score = 1; + if (!params_expanded_form) { + if (a.IsExtensionType) { + // + // Indentity, implicit reference or boxing conversion must exist for the extension parameter + // + // LAMESPEC: or implicit type parameter conversion + // + var at = a.Type; + if (at == pt || TypeSpecComparer.IsEqual (at, pt) || + Convert.ImplicitReferenceConversionExists (at, pt, false) || + Convert.ImplicitBoxingConversion (null, at, pt) != null) { + score = 0; + continue; + } + } else { + score = IsArgumentCompatible (ec, a, p_mod, pt); + + if (score < 0) + dynamicArgument = true; + } + } + + // + // It can be applicable in expanded form (when not doing exact match like for delegates) + // + if (score != 0 && (p_mod & Parameter.Modifier.PARAMS) != 0 && (restrictions & Restrictions.CovariantDelegate) == 0) { + if (!params_expanded_form) { + pt = ((ElementTypeSpec) pt).Element; + } + + if (score > 0) + score = IsArgumentCompatible (ec, a, Parameter.Modifier.NONE, pt); + + if (score < 0) { + params_expanded_form = true; + dynamicArgument = true; + } else if (score == 0 || arg_count > pd.Count) { + params_expanded_form = true; + } + } + + if (score > 0) { + if (params_expanded_form) + ++score; + return (arg_count - i) * 2 + score; + } + } + + // + // Restore original arguments for dynamic binder to keep the intention of original source code + // + if (dynamicArgument) + arguments = orig_args; + + return 0; + } + + public static Expression ResolveDefaultValueArgument (ResolveContext ec, TypeSpec ptype, Expression e, Location loc) + { + if (e is Constant && e.Type == ptype) + return e; + + // + // LAMESPEC: No idea what the exact rules are for System.Reflection.Missing.Value instead of null + // + if (e == EmptyExpression.MissingValue && ptype.BuiltinType == BuiltinTypeSpec.Type.Object || ptype.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + e = new MemberAccess (new MemberAccess (new MemberAccess ( + new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Reflection", loc), "Missing", loc), "Value", loc); + } else if (e is Constant) { + // + // Handles int to int? conversions, DefaultParameterValue check + // + e = Convert.ImplicitConversionStandard (ec, e, ptype, loc); + if (e == null) + return null; + } else { + e = new DefaultValueExpression (new TypeExpression (ptype, loc), loc); + } + + return e.Resolve (ec); + } + + // + // Tests argument compatibility with the parameter + // The possible return values are + // 0 - success + // 1 - modifier mismatch + // 2 - type mismatch + // -1 - dynamic binding required + // + int IsArgumentCompatible (ResolveContext ec, Argument argument, Parameter.Modifier param_mod, TypeSpec parameter) + { + // + // Types have to be identical when ref or out modifer + // is used and argument is not of dynamic type + // + if (((argument.Modifier | param_mod) & Parameter.Modifier.RefOutMask) != 0) { + if (argument.Type != parameter) { + // + // Do full equality check after quick path + // + if (!TypeSpecComparer.IsEqual (argument.Type, parameter)) { + // + // Using dynamic for ref/out parameter can still succeed at runtime + // + if (argument.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic && (argument.Modifier & Parameter.Modifier.RefOutMask) == 0 && (restrictions & Restrictions.CovariantDelegate) == 0) + return -1; + + return 2; + } + } + + if ((argument.Modifier & Parameter.Modifier.RefOutMask) != (param_mod & Parameter.Modifier.RefOutMask)) { + // + // Using dynamic for ref/out parameter can still succeed at runtime + // + if (argument.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic && (argument.Modifier & Parameter.Modifier.RefOutMask) == 0 && (restrictions & Restrictions.CovariantDelegate) == 0) + return -1; + + return 1; + } + + } else { + if (argument.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic && (restrictions & Restrictions.CovariantDelegate) == 0) + return -1; + + // + // Use implicit conversion in all modes to return same candidates when the expression + // is used as argument or delegate conversion + // + if (!Convert.ImplicitConversionExists (ec, argument.Expr, parameter)) { + return parameter.IsDelegate && argument.Expr is AnonymousMethodExpression ? 2 : 3; + } + } + + return 0; + } + + static TypeSpec MoreSpecific (TypeSpec p, TypeSpec q) + { + if (TypeManager.IsGenericParameter (p) && !TypeManager.IsGenericParameter (q)) + return q; + if (!TypeManager.IsGenericParameter (p) && TypeManager.IsGenericParameter (q)) + return p; + + var ac_p = p as ArrayContainer; + if (ac_p != null) { + var ac_q = q as ArrayContainer; + if (ac_q == null) + return null; + + TypeSpec specific = MoreSpecific (ac_p.Element, ac_q.Element); + if (specific == ac_p.Element) + return p; + if (specific == ac_q.Element) + return q; + } else if (p.IsGeneric && q.IsGeneric) { + var pargs = TypeManager.GetTypeArguments (p); + var qargs = TypeManager.GetTypeArguments (q); + + bool p_specific_at_least_once = false; + bool q_specific_at_least_once = false; + + for (int i = 0; i < pargs.Length; i++) { + TypeSpec specific = MoreSpecific (pargs[i], qargs[i]); + if (specific == pargs[i]) + p_specific_at_least_once = true; + if (specific == qargs[i]) + q_specific_at_least_once = true; + } + + if (p_specific_at_least_once && !q_specific_at_least_once) + return p; + if (!p_specific_at_least_once && q_specific_at_least_once) + return q; + } + + return null; + } + + // + // Find the best method from candidate list + // + public T ResolveMember (ResolveContext rc, ref Arguments args) where T : MemberSpec, IParametersMember + { + List ambiguous_candidates = null; + + MemberSpec best_candidate; + Arguments best_candidate_args = null; + bool best_candidate_params = false; + bool best_candidate_dynamic = false; + int best_candidate_rate; + IParametersMember best_parameter_member = null; + + int args_count = args != null ? args.Count : 0; + + Arguments candidate_args = args; + bool error_mode = false; + MemberSpec invocable_member = null; + + while (true) { + best_candidate = null; + best_candidate_rate = int.MaxValue; + + var type_members = members; + do { + for (int i = 0; i < type_members.Count; ++i) { + var member = type_members[i]; + + // + // Methods in a base class are not candidates if any method in a derived + // class is applicable + // + if ((member.Modifiers & Modifiers.OVERRIDE) != 0) + continue; + + if (!error_mode) { + if (!member.IsAccessible (rc)) + continue; + + if (rc.IsRuntimeBinder && !member.DeclaringType.IsAccessible (rc)) + continue; + + if ((member.Modifiers & (Modifiers.PROTECTED | Modifiers.STATIC)) == Modifiers.PROTECTED && + instance_qualifier != null && !instance_qualifier.CheckProtectedMemberAccess (rc, member)) { + continue; + } + } + + IParametersMember pm = member as IParametersMember; + if (pm == null) { + // + // Will use it later to report ambiguity between best method and invocable member + // + if (Invocation.IsMemberInvocable (member)) + invocable_member = member; + + continue; + } + + // + // Overload resolution is looking for base member but using parameter names + // and default values from the closest member. That means to do expensive lookup + // for the closest override for virtual or abstract members + // + if ((member.Modifiers & (Modifiers.VIRTUAL | Modifiers.ABSTRACT)) != 0) { + var override_params = base_provider.GetOverrideMemberParameters (member); + if (override_params != null) + pm = override_params; + } + + // + // Check if the member candidate is applicable + // + bool params_expanded_form = false; + bool dynamic_argument = false; + TypeSpec rt = pm.MemberType; + int candidate_rate = IsApplicable (rc, ref candidate_args, args_count, ref member, pm, ref params_expanded_form, ref dynamic_argument, ref rt, error_mode); + + if (lambda_conv_msgs != null) + lambda_conv_msgs.EndSession (); + + // + // How does it score compare to others + // + if (candidate_rate < best_candidate_rate) { + + // Fatal error (missing dependency), cannot continue + if (candidate_rate < 0) + return null; + + if ((restrictions & Restrictions.GetEnumeratorLookup) != 0 && candidate_args.Count != 0) { + // Only parameterless methods are considered + } else { + best_candidate_rate = candidate_rate; + best_candidate = member; + best_candidate_args = candidate_args; + best_candidate_params = params_expanded_form; + best_candidate_dynamic = dynamic_argument; + best_parameter_member = pm; + best_candidate_return_type = rt; + } + } else if (candidate_rate == 0) { + // + // The member look is done per type for most operations but sometimes + // it's not possible like for binary operators overload because they + // are unioned between 2 sides + // + if ((restrictions & Restrictions.BaseMembersIncluded) != 0) { + if (TypeSpec.IsBaseClass (best_candidate.DeclaringType, member.DeclaringType, true)) + continue; + } + + bool is_better; + if (best_candidate.DeclaringType.IsInterface && member.DeclaringType.ImplementsInterface (best_candidate.DeclaringType, false)) { + // + // We pack all interface members into top level type which makes the overload resolution + // more complicated for interfaces. We compensate it by removing methods with same + // signature when building the cache hence this path should not really be hit often + // + // Example: + // interface IA { void Foo (int arg); } + // interface IB : IA { void Foo (params int[] args); } + // + // IB::Foo is the best overload when calling IB.Foo (1) + // + is_better = true; + if (ambiguous_candidates != null) { + foreach (var amb_cand in ambiguous_candidates) { + if (member.DeclaringType.ImplementsInterface (best_candidate.DeclaringType, false)) { + continue; + } + + is_better = false; + break; + } + + if (is_better) + ambiguous_candidates = null; + } + } else { + // Is the new candidate better + is_better = BetterFunction (rc, candidate_args, member, pm.Parameters, params_expanded_form, best_candidate, best_parameter_member.Parameters, best_candidate_params); + } + + if (is_better) { + best_candidate = member; + best_candidate_args = candidate_args; + best_candidate_params = params_expanded_form; + best_candidate_dynamic = dynamic_argument; + best_parameter_member = pm; + best_candidate_return_type = rt; + } else { + // It's not better but any other found later could be but we are not sure yet + if (ambiguous_candidates == null) + ambiguous_candidates = new List (); + + ambiguous_candidates.Add (new AmbiguousCandidate (member, pm.Parameters, params_expanded_form)); + } + } + + // Restore expanded arguments + candidate_args = args; + } + } while (best_candidate_rate != 0 && (type_members = base_provider.GetBaseMembers (type_members[0].DeclaringType.BaseType)) != null); + + // + // We've found exact match + // + if (best_candidate_rate == 0) + break; + + // + // Try extension methods lookup when no ordinary method match was found and provider enables it + // + if (!error_mode) { + var emg = base_provider.LookupExtensionMethod (rc); + if (emg != null) { + emg = emg.OverloadResolve (rc, ref args, null, restrictions); + if (emg != null) { + best_candidate_extension_group = emg; + return (T) (MemberSpec) emg.BestCandidate; + } + } + } + + // Don't run expensive error reporting mode for probing + if (IsProbingOnly) + return null; + + if (error_mode) + break; + + if (lambda_conv_msgs != null && !lambda_conv_msgs.IsEmpty) + break; + + lambda_conv_msgs = null; + error_mode = true; + } + + // + // No best member match found, report an error + // + if (best_candidate_rate != 0 || error_mode) { + ReportOverloadError (rc, best_candidate, best_parameter_member, best_candidate_args, best_candidate_params); + return null; + } + + if (best_candidate_dynamic) { + if (args[0].IsExtensionType) { + rc.Report.Error (1973, loc, + "Type `{0}' does not contain a member `{1}' and the best extension method overload `{2}' cannot be dynamically dispatched. Consider calling the method without the extension method syntax", + args [0].Type.GetSignatureForError (), best_candidate.Name, best_candidate.GetSignatureForError ()); + } + + // + // Check type constraints only when explicit type arguments are used + // + if (best_candidate.IsGeneric && type_arguments != null) { + MethodSpec bc = best_candidate as MethodSpec; + if (bc != null && TypeParameterSpec.HasAnyTypeParameterConstrained (bc.GenericDefinition)) { + ConstraintChecker cc = new ConstraintChecker (rc); + cc.CheckAll (bc.GetGenericMethodDefinition (), bc.TypeArguments, bc.Constraints, loc); + } + } + + BestCandidateIsDynamic = true; + return null; + } + + // + // These flags indicates we are running delegate probing conversion. No need to + // do more expensive checks + // + if ((restrictions & (Restrictions.ProbingOnly | Restrictions.CovariantDelegate)) == (Restrictions.CovariantDelegate | Restrictions.ProbingOnly)) + return (T) best_candidate; + + if (ambiguous_candidates != null) { + // + // Now check that there are no ambiguities i.e the selected method + // should be better than all the others + // + for (int ix = 0; ix < ambiguous_candidates.Count; ix++) { + var candidate = ambiguous_candidates [ix]; + + if (!BetterFunction (rc, best_candidate_args, best_candidate, best_parameter_member.Parameters, best_candidate_params, candidate.Member, candidate.Parameters, candidate.Expanded)) { + var ambiguous = candidate.Member; + if (custom_errors == null || !custom_errors.AmbiguousCandidates (rc, best_candidate, ambiguous)) { + rc.Report.SymbolRelatedToPreviousError (best_candidate); + rc.Report.SymbolRelatedToPreviousError (ambiguous); + rc.Report.Error (121, loc, "The call is ambiguous between the following methods or properties: `{0}' and `{1}'", + best_candidate.GetSignatureForError (), ambiguous.GetSignatureForError ()); + } + + return (T) best_candidate; + } + } + } + + if (invocable_member != null && !IsProbingOnly) { + rc.Report.SymbolRelatedToPreviousError (best_candidate); + rc.Report.SymbolRelatedToPreviousError (invocable_member); + rc.Report.Warning (467, 2, loc, "Ambiguity between method `{0}' and invocable non-method `{1}'. Using method group", + best_candidate.GetSignatureForError (), invocable_member.GetSignatureForError ()); + } + + // + // And now check if the arguments are all + // compatible, perform conversions if + // necessary etc. and return if everything is + // all right + // + if (!VerifyArguments (rc, ref best_candidate_args, best_candidate, best_parameter_member, best_candidate_params)) + return null; + + if (best_candidate == null) + return null; + + // + // Don't run possibly expensive checks in probing mode + // + if (!IsProbingOnly && !rc.IsInProbingMode) { + // + // Check ObsoleteAttribute on the best method + // + ObsoleteAttribute oa = best_candidate.GetAttributeObsolete (); + if (oa != null && !rc.IsObsolete) + AttributeTester.Report_ObsoleteMessage (oa, best_candidate.GetSignatureForError (), loc, rc.Report); + + best_candidate.MemberDefinition.SetIsUsed (); + } + + args = best_candidate_args; + return (T) best_candidate; + } + + public MethodSpec ResolveOperator (ResolveContext rc, ref Arguments args) + { + return ResolveMember (rc, ref args); + } + + void ReportArgumentMismatch (ResolveContext ec, int idx, MemberSpec method, + Argument a, AParametersCollection expected_par, TypeSpec paramType) + { + if (custom_errors != null && custom_errors.ArgumentMismatch (ec, method, a, idx)) + return; + + if (a.Type == InternalType.ErrorType) + return; + + if (a is CollectionElementInitializer.ElementInitializerArgument) { + ec.Report.SymbolRelatedToPreviousError (method); + if ((expected_par.FixedParameters[idx].ModFlags & Parameter.Modifier.RefOutMask) != 0) { + ec.Report.Error (1954, loc, "The best overloaded collection initalizer method `{0}' cannot have `ref' or `out' modifier", + TypeManager.CSharpSignature (method)); + return; + } + ec.Report.Error (1950, loc, "The best overloaded collection initalizer method `{0}' has some invalid arguments", + TypeManager.CSharpSignature (method)); + } else if (IsDelegateInvoke) { + ec.Report.Error (1594, loc, "Delegate `{0}' has some invalid arguments", + DelegateType.GetSignatureForError ()); + } else { + ec.Report.SymbolRelatedToPreviousError (method); + ec.Report.Error (1502, loc, "The best overloaded method match for `{0}' has some invalid arguments", + method.GetSignatureForError ()); + } + + Parameter.Modifier mod = idx >= expected_par.Count ? 0 : expected_par.FixedParameters[idx].ModFlags; + + string index = (idx + 1).ToString (); + if (((mod & Parameter.Modifier.RefOutMask) ^ (a.Modifier & Parameter.Modifier.RefOutMask)) != 0) { + if ((mod & Parameter.Modifier.RefOutMask) == 0) + ec.Report.Error (1615, a.Expr.Location, "Argument `#{0}' does not require `{1}' modifier. Consider removing `{1}' modifier", + index, Parameter.GetModifierSignature (a.Modifier)); + else + ec.Report.Error (1620, a.Expr.Location, "Argument `#{0}' is missing `{1}' modifier", + index, Parameter.GetModifierSignature (mod)); + } else { + string p1 = a.GetSignatureForError (); + string p2 = paramType.GetSignatureForError (); + + if (p1 == p2) { + p1 = a.Type.GetSignatureForErrorIncludingAssemblyName (); + p2 = paramType.GetSignatureForErrorIncludingAssemblyName (); + } + + if ((mod & Parameter.Modifier.RefOutMask) != 0) { + p1 = Parameter.GetModifierSignature (a.Modifier) + " " + p1; + p2 = Parameter.GetModifierSignature (a.Modifier) + " " + p2; + } + + ec.Report.Error (1503, a.Expr.Location, + "Argument `#{0}' cannot convert `{1}' expression to type `{2}'", index, p1, p2); + } + } + + // + // We have failed to find exact match so we return error info about the closest match + // + void ReportOverloadError (ResolveContext rc, MemberSpec best_candidate, IParametersMember pm, Arguments args, bool params_expanded) + { + int ta_count = type_arguments == null ? 0 : type_arguments.Count; + int arg_count = args == null ? 0 : args.Count; + + if (ta_count != best_candidate.Arity && (ta_count > 0 || ((IParametersMember) best_candidate).Parameters.IsEmpty)) { + var mg = new MethodGroupExpr (new [] { best_candidate }, best_candidate.DeclaringType, loc); + mg.Error_TypeArgumentsCannotBeUsed (rc, best_candidate, loc); + return; + } + + if (lambda_conv_msgs != null && lambda_conv_msgs.Merge (rc.Report.Printer)) { + return; + } + + + if ((best_candidate.Modifiers & (Modifiers.PROTECTED | Modifiers.STATIC)) == Modifiers.PROTECTED && + InstanceQualifier != null && !InstanceQualifier.CheckProtectedMemberAccess (rc, best_candidate)) { + MemberExpr.Error_ProtectedMemberAccess (rc, best_candidate, InstanceQualifier.InstanceType, loc); + } + + // + // For candidates which match on parameters count report more details about incorrect arguments + // + if (pm != null) { + if (pm.Parameters.Count == arg_count || params_expanded || HasUnfilledParams (best_candidate, pm, args)) { + // Reject any inaccessible member + if (!best_candidate.IsAccessible (rc) || !best_candidate.DeclaringType.IsAccessible (rc)) { + rc.Report.SymbolRelatedToPreviousError (best_candidate); + Expression.ErrorIsInaccesible (rc, best_candidate.GetSignatureForError (), loc); + return; + } + + var ms = best_candidate as MethodSpec; + if (ms != null && ms.IsGeneric) { + bool constr_ok = true; + if (ms.TypeArguments != null) + constr_ok = new ConstraintChecker (rc.MemberContext).CheckAll (ms.GetGenericMethodDefinition (), ms.TypeArguments, ms.Constraints, loc); + + if (ta_count == 0 && ms.TypeArguments == null) { + if (custom_errors != null && custom_errors.TypeInferenceFailed (rc, best_candidate)) + return; + + if (constr_ok) { + rc.Report.Error (411, loc, + "The type arguments for method `{0}' cannot be inferred from the usage. Try specifying the type arguments explicitly", + ms.GetGenericMethodDefinition ().GetSignatureForError ()); + } + + return; + } + } + + VerifyArguments (rc, ref args, best_candidate, pm, params_expanded); + return; + } + } + + // + // We failed to find any method with correct argument count, report best candidate + // + if (custom_errors != null && custom_errors.NoArgumentMatch (rc, best_candidate)) + return; + + if (best_candidate.Kind == MemberKind.Constructor) { + rc.Report.SymbolRelatedToPreviousError (best_candidate); + Error_ConstructorMismatch (rc, best_candidate.DeclaringType, arg_count, loc); + } else if (IsDelegateInvoke) { + rc.Report.SymbolRelatedToPreviousError (DelegateType); + rc.Report.Error (1593, loc, "Delegate `{0}' does not take `{1}' arguments", + DelegateType.GetSignatureForError (), arg_count.ToString ()); + } else { + string name = best_candidate.Kind == MemberKind.Indexer ? "this" : best_candidate.Name; + rc.Report.SymbolRelatedToPreviousError (best_candidate); + rc.Report.Error (1501, loc, "No overload for method `{0}' takes `{1}' arguments", + name, arg_count.ToString ()); + } + } + + static bool HasUnfilledParams (MemberSpec best_candidate, IParametersMember pm, Arguments args) + { + var p = ((IParametersMember)best_candidate).Parameters; + if (!p.HasParams) + return false; + + string name = null; + for (int i = p.Count - 1; i != 0; --i) { + var fp = p.FixedParameters [i]; + if ((fp.ModFlags & Parameter.Modifier.PARAMS) == 0) + continue; + + name = fp.Name; + break; + } + + if (args == null) + return false; + + foreach (var arg in args) { + var na = arg as NamedArgument; + if (na == null) + continue; + + if (na.Name == name) { + name = null; + break; + } + } + + if (name == null) + return false; + + return args.Count + 1 == pm.Parameters.Count; + } + + bool VerifyArguments (ResolveContext ec, ref Arguments args, MemberSpec member, IParametersMember pm, bool chose_params_expanded) + { + var pd = pm.Parameters; + var cpd = ((IParametersMember) member).Parameters; + var ptypes = cpd.Types; + + Parameter.Modifier p_mod = 0; + TypeSpec pt = null; + int a_idx = 0, a_pos = 0; + Argument a = null; + ArrayInitializer params_initializers = null; + bool has_unsafe_arg = pm.MemberType.IsPointer; + int arg_count = args == null ? 0 : args.Count; + + for (; a_idx < arg_count; a_idx++, ++a_pos) { + a = args[a_idx]; + if (a == null) + continue; + + if (p_mod != Parameter.Modifier.PARAMS) { + p_mod = cpd.FixedParameters [a_idx].ModFlags; + pt = ptypes[a_idx]; + has_unsafe_arg |= pt.IsPointer; + + if (p_mod == Parameter.Modifier.PARAMS) { + if (chose_params_expanded) { + params_initializers = new ArrayInitializer (arg_count - a_idx, a.Expr.Location); + pt = TypeManager.GetElementType (pt); + } + } + } + + // + // Types have to be identical when ref or out modifer is used + // + if (((a.Modifier | p_mod) & Parameter.Modifier.RefOutMask) != 0) { + if ((a.Modifier & Parameter.Modifier.RefOutMask) != (p_mod & Parameter.Modifier.RefOutMask)) + break; + + if (a.Expr.Type == pt || TypeSpecComparer.IsEqual (a.Expr.Type, pt)) + continue; + + break; + } + + NamedArgument na = a as NamedArgument; + if (na != null) { + int name_index = pd.GetParameterIndexByName (na.Name); + if (name_index < 0 || name_index >= pd.Count) { + if (IsDelegateInvoke) { + ec.Report.SymbolRelatedToPreviousError (DelegateType); + ec.Report.Error (1746, na.Location, + "The delegate `{0}' does not contain a parameter named `{1}'", + DelegateType.GetSignatureForError (), na.Name); + } else { + ec.Report.SymbolRelatedToPreviousError (member); + ec.Report.Error (1739, na.Location, + "The best overloaded method match for `{0}' does not contain a parameter named `{1}'", + TypeManager.CSharpSignature (member), na.Name); + } + } else if (args[name_index] != a && args[name_index] != null) { + if (IsDelegateInvoke) + ec.Report.SymbolRelatedToPreviousError (DelegateType); + else + ec.Report.SymbolRelatedToPreviousError (member); + + ec.Report.Error (1744, na.Location, + "Named argument `{0}' cannot be used for a parameter which has positional argument specified", + na.Name); + } + } + + if (a.Expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) + continue; + + if ((restrictions & Restrictions.CovariantDelegate) != 0 && !Delegate.IsTypeCovariant (ec, a.Expr.Type, pt)) { + custom_errors.NoArgumentMatch (ec, member); + return false; + } + + Expression conv; + if (a.IsExtensionType) { + if (a.Expr.Type == pt || TypeSpecComparer.IsEqual (a.Expr.Type, pt)) { + conv = a.Expr; + } else { + conv = Convert.ImplicitReferenceConversion (a.Expr, pt, false); + if (conv == null) + conv = Convert.ImplicitBoxingConversion (a.Expr, a.Expr.Type, pt); + } + } else { + conv = Convert.ImplicitConversion (ec, a.Expr, pt, loc); + } + + if (conv == null) + break; + + // + // Convert params arguments to an array initializer + // + if (params_initializers != null) { + // we choose to use 'a.Expr' rather than 'conv' so that + // we don't hide the kind of expression we have (esp. CompoundAssign.Helper) + params_initializers.Add (a.Expr); + args.RemoveAt (a_idx--); + --arg_count; + a.Expr = conv; + continue; + } + + // Update the argument with the implicit conversion + a.Expr = conv; + } + + if (a_idx != arg_count) { + ReportArgumentMismatch (ec, a_pos, member, a, pd, pt); + return false; + } + + // + // Fill not provided arguments required by params modifier + // + if (params_initializers == null && arg_count + 1 == pd.Count) { + if (args == null) + args = new Arguments (1); + + pt = ptypes[pd.Count - 1]; + pt = TypeManager.GetElementType (pt); + has_unsafe_arg |= pt.IsPointer; + params_initializers = new ArrayInitializer (0, loc); + } + + // + // Append an array argument with all params arguments + // + if (params_initializers != null) { + args.Add (new Argument ( + new ArrayCreation (new TypeExpression (pt, loc), params_initializers, loc).Resolve (ec))); + arg_count++; + } + + if (has_unsafe_arg && !ec.IsUnsafe) { + Expression.UnsafeError (ec, loc); + } + + // + // We could infer inaccesible type arguments + // + if (type_arguments == null && member.IsGeneric) { + var ms = (MethodSpec) member; + foreach (var ta in ms.TypeArguments) { + if (!ta.IsAccessible (ec)) { + ec.Report.SymbolRelatedToPreviousError (ta); + Expression.ErrorIsInaccesible (ec, member.GetSignatureForError (), loc); + break; + } + } + } + + return true; + } + } + + public class ConstantExpr : MemberExpr + { + readonly ConstSpec constant; + + public ConstantExpr (ConstSpec constant, Location loc) + { + this.constant = constant; + this.loc = loc; + } + + public override string Name { + get { throw new NotImplementedException (); } + } + + public override string KindName { + get { return "constant"; } + } + + public override bool IsInstance { + get { return !IsStatic; } + } + + public override bool IsStatic { + get { return true; } + } + + protected override TypeSpec DeclaringType { + get { return constant.DeclaringType; } + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + throw new NotSupportedException ("ET"); + } + + protected override Expression DoResolve (ResolveContext rc) + { + ResolveInstanceExpression (rc, null); + DoBestMemberChecks (rc, constant); + + var c = constant.GetConstant (rc); + + // Creates reference expression to the constant value + return Constant.CreateConstantFromValue (constant.MemberType, c.GetValue (), loc); + } + + public override void Emit (EmitContext ec) + { + throw new NotSupportedException (); + } + + public override string GetSignatureForError () + { + return constant.GetSignatureForError (); + } + + public override void SetTypeArguments (ResolveContext ec, TypeArguments ta) + { + Error_TypeArgumentsCannotBeUsed (ec, "constant", GetSignatureForError (), loc); + } + } + + // + // Fully resolved expression that references a Field + // + public class FieldExpr : MemberExpr, IDynamicAssign, IMemoryLocation, IVariableReference + { + protected FieldSpec spec; + VariableInfo variable_info; + + LocalTemporary temp; + bool prepared; + + protected FieldExpr (Location l) + { + loc = l; + } + + public FieldExpr (FieldSpec spec, Location loc) + { + this.spec = spec; + this.loc = loc; + + type = spec.MemberType; + } + + public FieldExpr (FieldBase fi, Location l) + : this (fi.Spec, l) + { + } + + #region Properties + + public override string Name { + get { + return spec.Name; + } + } + + public bool IsHoisted { + get { + IVariableReference hv = InstanceExpression as IVariableReference; + return hv != null && hv.IsHoisted; + } + } + + public override bool IsInstance { + get { + return !spec.IsStatic; + } + } + + public override bool IsStatic { + get { + return spec.IsStatic; + } + } + + public override string KindName { + get { return "field"; } + } + + public FieldSpec Spec { + get { + return spec; + } + } + + protected override TypeSpec DeclaringType { + get { + return spec.DeclaringType; + } + } + + public VariableInfo VariableInfo { + get { + return variable_info; + } + } + +#endregion + + public override string GetSignatureForError () + { + return spec.GetSignatureForError (); + } + + public bool IsMarshalByRefAccess (ResolveContext rc) + { + // Checks possible ldflda of field access expression + return !spec.IsStatic && TypeSpec.IsValueType (spec.MemberType) && !(InstanceExpression is This) && + rc.Module.PredefinedTypes.MarshalByRefObject.Define () && + TypeSpec.IsBaseClass (spec.DeclaringType, rc.Module.PredefinedTypes.MarshalByRefObject.TypeSpec, false); + } + + public void SetHasAddressTaken () + { + IVariableReference vr = InstanceExpression as IVariableReference; + if (vr != null) { + vr.SetHasAddressTaken (); + } + } + + protected override void CloneTo (CloneContext clonectx, Expression target) + { + var t = (FieldExpr) target; + + if (InstanceExpression != null) + t.InstanceExpression = InstanceExpression.Clone (clonectx); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + if (ConditionalAccess) { + Error_NullShortCircuitInsideExpressionTree (ec); + } + + return CreateExpressionTree (ec, true); + } + + public Expression CreateExpressionTree (ResolveContext ec, bool convertInstance) + { + Arguments args; + Expression instance; + + if (InstanceExpression == null) { + instance = new NullLiteral (loc); + } else if (convertInstance) { + instance = InstanceExpression.CreateExpressionTree (ec); + } else { + args = new Arguments (1); + args.Add (new Argument (InstanceExpression)); + instance = CreateExpressionFactoryCall (ec, "Constant", args); + } + + args = Arguments.CreateForExpressionTree (ec, null, + instance, + CreateTypeOfExpression ()); + + return CreateExpressionFactoryCall (ec, "Field", args); + } + + public Expression CreateTypeOfExpression () + { + return new TypeOfField (spec, loc); + } + + protected override Expression DoResolve (ResolveContext ec) + { + spec.MemberDefinition.SetIsUsed (); + + return DoResolve (ec, null); + } + + Expression DoResolve (ResolveContext ec, Expression rhs) + { + bool lvalue_instance = rhs != null && IsInstance && spec.DeclaringType.IsStruct; + + if (rhs != this) { + ResolveConditionalAccessReceiver (ec); + + if (ResolveInstanceExpression (ec, rhs)) { + // Resolve the field's instance expression while flow analysis is turned + // off: when accessing a field "a.b", we must check whether the field + // "a.b" is initialized, not whether the whole struct "a" is initialized. + + if (lvalue_instance) { + bool out_access = rhs == EmptyExpression.OutAccess || rhs == EmptyExpression.LValueMemberOutAccess; + + Expression right_side = + out_access ? EmptyExpression.LValueMemberOutAccess : EmptyExpression.LValueMemberAccess; + + InstanceExpression = InstanceExpression.ResolveLValue (ec, right_side); + } else { + InstanceExpression = InstanceExpression.Resolve (ec, ResolveFlags.VariableOrValue); + } + + if (InstanceExpression == null) + return null; + } + + DoBestMemberChecks (ec, spec); + + if (conditional_access_receiver) + ec.With (ResolveContext.Options.ConditionalAccessReceiver, false); + } + + var fb = spec as FixedFieldSpec; + IVariableReference var = InstanceExpression as IVariableReference; + + if (fb != null) { + IFixedExpression fe = InstanceExpression as IFixedExpression; + if (!ec.HasSet (ResolveContext.Options.FixedInitializerScope) && (fe == null || !fe.IsFixed)) { + ec.Report.Error (1666, loc, "You cannot use fixed size buffers contained in unfixed expressions. Try using the fixed statement"); + } + + if (InstanceExpression.eclass != ExprClass.Variable) { + ec.Report.SymbolRelatedToPreviousError (spec); + ec.Report.Error (1708, loc, "`{0}': Fixed size buffers can only be accessed through locals or fields", + TypeManager.GetFullNameSignature (spec)); + } else if (var != null && var.IsHoisted) { + AnonymousMethodExpression.Error_AddressOfCapturedVar (ec, var, loc); + } + + return new FixedBufferPtr (this, fb.ElementType, loc).Resolve (ec); + } + + // + // Set flow-analysis variable info for struct member access. It will be check later + // for precise error reporting + // + if (var != null && var.VariableInfo != null && InstanceExpression.Type.IsStruct) { + variable_info = var.VariableInfo.GetStructFieldInfo (Name); + } + + if (ConditionalAccess) { + if (conditional_access_receiver) + type = LiftMemberType (ec, type); + + if (InstanceExpression.IsNull) + return Constant.CreateConstantFromValue (type, null, loc); + } + + eclass = ExprClass.Variable; + return this; + } + + public void SetFieldAssigned (FlowAnalysisContext fc) + { + if (!IsInstance) + return; + + bool lvalue_instance = spec.DeclaringType.IsStruct; + if (lvalue_instance) { + var var = InstanceExpression as IVariableReference; + if (var != null && var.VariableInfo != null) { + fc.SetStructFieldAssigned (var.VariableInfo, Name); + } + } + + var fe = InstanceExpression as FieldExpr; + if (fe != null) { + Expression instance; + + do { + instance = fe.InstanceExpression; + var fe_instance = instance as FieldExpr; + if ((fe_instance != null && !fe_instance.IsStatic) || instance is LocalVariableReference) { + if (TypeSpec.IsReferenceType (fe.Type) && instance.Type.IsStruct) { + var var = InstanceExpression as IVariableReference; + if (var != null && var.VariableInfo == null) { + var var_inst = instance as IVariableReference; + if (var_inst == null || (var_inst.VariableInfo != null && !fc.IsDefinitelyAssigned (var_inst.VariableInfo))) + fc.Report.Warning (1060, 1, fe.loc, "Use of possibly unassigned field `{0}'", fe.Name); + } + } + + if (fe_instance != null) { + fe = fe_instance; + continue; + } + } + + break; + } while (true); + + if (instance != null && TypeSpec.IsReferenceType (instance.Type)) + instance.FlowAnalysis (fc); + } else { + if (TypeSpec.IsReferenceType (InstanceExpression.Type)) + InstanceExpression.FlowAnalysis (fc); + } + } + + Expression Error_AssignToReadonly (ResolveContext rc, Expression right_side) + { + // The return value is always null. Returning a value simplifies calling code. + + if (right_side == EmptyExpression.OutAccess) { + if (IsStatic) { + rc.Report.Error (199, loc, "A static readonly field `{0}' cannot be passed ref or out (except in a static constructor)", + GetSignatureForError ()); + } else { + rc.Report.Error (192, loc, "A readonly field `{0}' cannot be passed ref or out (except in a constructor)", + GetSignatureForError ()); + } + + return null; + } + + if (right_side == EmptyExpression.LValueMemberAccess) { + // Already reported as CS1648/CS1650 + return null; + } + + if (right_side == EmptyExpression.LValueMemberOutAccess) { + if (IsStatic) { + rc.Report.Error (1651, loc, "Fields of static readonly field `{0}' cannot be passed ref or out (except in a static constructor)", + GetSignatureForError ()); + } else { + rc.Report.Error (1649, loc, "Members of readonly field `{0}' cannot be passed ref or out (except in a constructor)", + GetSignatureForError ()); + } + return null; + } + + if (IsStatic) { + rc.Report.Error (198, loc, "A static readonly field `{0}' cannot be assigned to (except in a static constructor or a variable initializer)", + GetSignatureForError ()); + } else { + rc.Report.Error (191, loc, "A readonly field `{0}' cannot be assigned to (except in a constructor or a variable initializer)", + GetSignatureForError ()); + } + + return null; + } + + public override Expression DoResolveLValue (ResolveContext ec, Expression right_side) + { + if (ConditionalAccess) + throw new NotSupportedException ("null propagating operator assignment"); + + if (spec is FixedFieldSpec) { + // It could be much better error message but we want to be error compatible + Error_ValueAssignment (ec, right_side); + } + + Expression e = DoResolve (ec, right_side); + + if (e == null) + return null; + + spec.MemberDefinition.SetIsAssigned (); + + if ((right_side == EmptyExpression.UnaryAddress || right_side == EmptyExpression.OutAccess) && + (spec.Modifiers & Modifiers.VOLATILE) != 0) { + ec.Report.Warning (420, 1, loc, + "`{0}': A volatile field references will not be treated as volatile", + spec.GetSignatureForError ()); + } + + if (spec.IsReadOnly) { + // InitOnly fields can only be assigned in constructors or initializers + if (!ec.HasAny (ResolveContext.Options.FieldInitializerScope | ResolveContext.Options.ConstructorScope)) + return Error_AssignToReadonly (ec, right_side); + + if (ec.HasSet (ResolveContext.Options.ConstructorScope)) { + + // InitOnly fields cannot be assigned-to in a different constructor from their declaring type + if (ec.CurrentMemberDefinition.Parent.PartialContainer.Definition != spec.DeclaringType.GetDefinition ()) + return Error_AssignToReadonly (ec, right_side); + // static InitOnly fields cannot be assigned-to in an instance constructor + if (IsStatic && !ec.IsStatic) + return Error_AssignToReadonly (ec, right_side); + // instance constructors can't modify InitOnly fields of other instances of the same type + if (!IsStatic && !(InstanceExpression is This)) + return Error_AssignToReadonly (ec, right_side); + } + } + + if (right_side == EmptyExpression.OutAccess && IsMarshalByRefAccess (ec)) { + ec.Report.SymbolRelatedToPreviousError (spec.DeclaringType); + ec.Report.Warning (197, 1, loc, + "Passing `{0}' as ref or out or taking its address may cause a runtime exception because it is a field of a marshal-by-reference class", + GetSignatureForError ()); + } + + eclass = ExprClass.Variable; + return this; + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + var var = InstanceExpression as IVariableReference; + if (var != null) { + var vi = var.VariableInfo; + if (vi != null && !fc.IsStructFieldDefinitelyAssigned (vi, Name)) { + fc.Report.Error (170, loc, "Use of possibly unassigned field `{0}'", Name); + return; + } + + if (TypeSpec.IsValueType (InstanceExpression.Type) && InstanceExpression is VariableReference) + return; + } + + base.FlowAnalysis (fc); + + if (conditional_access_receiver) + fc.ConditionalAccessEnd (); + } + + public override int GetHashCode () + { + return spec.GetHashCode (); + } + + public bool IsFixed { + get { + // + // A variable of the form V.I is fixed when V is a fixed variable of a struct type + // + IVariableReference variable = InstanceExpression as IVariableReference; + if (variable != null) + return InstanceExpression.Type.IsStruct && variable.IsFixed; + + IFixedExpression fe = InstanceExpression as IFixedExpression; + return fe != null && fe.IsFixed; + } + } + + public override bool Equals (object obj) + { + FieldExpr fe = obj as FieldExpr; + if (fe == null) + return false; + + if (spec != fe.spec) + return false; + + if (InstanceExpression == null || fe.InstanceExpression == null) + return true; + + return InstanceExpression.Equals (fe.InstanceExpression); + } + + public void Emit (EmitContext ec, bool leave_copy) + { + bool is_volatile = (spec.Modifiers & Modifiers.VOLATILE) != 0; + + if (IsStatic){ + if (is_volatile) + ec.Emit (OpCodes.Volatile); + + ec.Emit (OpCodes.Ldsfld, spec); + } else { + if (!prepared) { + if (conditional_access_receiver) + ec.ConditionalAccess = new ConditionalAccessContext (type, ec.DefineLabel ()); + + EmitInstance (ec, false); + } + + // Optimization for build-in types + if (type.IsStruct && type == ec.CurrentType && InstanceExpression.Type == type) { + ec.EmitLoadFromPtr (type); + } else { + var ff = spec as FixedFieldSpec; + if (ff != null) { + ec.Emit (OpCodes.Ldflda, spec); + ec.Emit (OpCodes.Ldflda, ff.Element); + } else { + if (is_volatile) + ec.Emit (OpCodes.Volatile); + + ec.Emit (OpCodes.Ldfld, spec); + } + } + + if (conditional_access_receiver) { + ec.CloseConditionalAccess (type.IsNullableType && type != spec.MemberType ? type : null); + } + } + + if (leave_copy) { + ec.Emit (OpCodes.Dup); + if (!IsStatic) { + temp = new LocalTemporary (this.Type); + temp.Store (ec); + } + } + } + + public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound) + { + bool has_await_source = ec.HasSet (BuilderContext.Options.AsyncBody) && source.ContainsEmitWithAwait (); + if (isCompound && !(source is DynamicExpressionStatement) && !has_await_source) { + prepared = true; + } + + if (IsInstance) { + if (ConditionalAccess) + throw new NotImplementedException ("null operator assignment"); + + if (has_await_source) + source = source.EmitToField (ec); + + EmitInstance (ec, prepared); + } + + source.Emit (ec); + + if (leave_copy || ec.NotifyEvaluatorOnStore) { + ec.Emit (OpCodes.Dup); + if (!IsStatic) { + temp = new LocalTemporary (this.Type); + temp.Store (ec); + } + } + + if ((spec.Modifiers & Modifiers.VOLATILE) != 0) + ec.Emit (OpCodes.Volatile); + + spec.MemberDefinition.SetIsAssigned (); + + if (IsStatic) + ec.Emit (OpCodes.Stsfld, spec); + else + ec.Emit (OpCodes.Stfld, spec); + + if (ec.NotifyEvaluatorOnStore) { + if (!IsStatic) + throw new NotImplementedException ("instance field write"); + + if (leave_copy) + ec.Emit (OpCodes.Dup); + + ec.Module.Evaluator.EmitValueChangedCallback (ec, Name, type, loc); + } + + if (temp != null) { + temp.Emit (ec); + temp.Release (ec); + temp = null; + } + } + + // + // Emits store to field with prepared values on stack + // + public void EmitAssignFromStack (EmitContext ec) + { + if (IsStatic) { + ec.Emit (OpCodes.Stsfld, spec); + } else { + ec.Emit (OpCodes.Stfld, spec); + } + } + + public override void Emit (EmitContext ec) + { + Emit (ec, false); + } + + public override void EmitSideEffect (EmitContext ec) + { + bool is_volatile = (spec.Modifiers & Modifiers.VOLATILE) != 0; + + if (is_volatile) // || is_marshal_by_ref ()) + base.EmitSideEffect (ec); + } + + public virtual void AddressOf (EmitContext ec, AddressOp mode) + { + if ((mode & AddressOp.Store) != 0) + spec.MemberDefinition.SetIsAssigned (); + if ((mode & AddressOp.Load) != 0) + spec.MemberDefinition.SetIsUsed (); + + // + // Handle initonly fields specially: make a copy and then + // get the address of the copy. + // + bool need_copy; + if (spec.IsReadOnly){ + need_copy = true; + if (ec.HasSet (EmitContext.Options.ConstructorScope) && spec.DeclaringType == ec.CurrentType) { + if (IsStatic){ + if (ec.IsStatic) + need_copy = false; + } else + need_copy = false; + } + } else + need_copy = false; + + if (need_copy) { + Emit (ec); + var temp = ec.GetTemporaryLocal (type); + ec.Emit (OpCodes.Stloc, temp); + ec.Emit (OpCodes.Ldloca, temp); + return; + } + + + if (IsStatic){ + ec.Emit (OpCodes.Ldsflda, spec); + } else { + if (!prepared) + EmitInstance (ec, false); + ec.Emit (OpCodes.Ldflda, spec); + } + } + + public SLE.Expression MakeAssignExpression (BuilderContext ctx, Expression source) + { + return MakeExpression (ctx); + } + + public override SLE.Expression MakeExpression (BuilderContext ctx) + { +#if STATIC + return base.MakeExpression (ctx); +#else + return SLE.Expression.Field ( + IsStatic ? null : InstanceExpression.MakeExpression (ctx), + spec.GetMetaInfo ()); +#endif + } + + public override void SetTypeArguments (ResolveContext ec, TypeArguments ta) + { + Error_TypeArgumentsCannotBeUsed (ec, "field", GetSignatureForError (), loc); + } + } + + + // + // Expression that evaluates to a Property. + // + // This is not an LValue because we need to re-write the expression. We + // can not take data from the stack and store it. + // + sealed class PropertyExpr : PropertyOrIndexerExpr + { + Arguments arguments; + + public PropertyExpr (PropertySpec spec, Location l) + : base (l) + { + best_candidate = spec; + type = spec.MemberType; + } + + #region Properties + + protected override Arguments Arguments { + get { + return arguments; + } + set { + arguments = value; + } + } + + protected override TypeSpec DeclaringType { + get { + return best_candidate.DeclaringType; + } + } + + public override string Name { + get { + return best_candidate.Name; + } + } + + public override bool IsInstance { + get { + return !IsStatic; + } + } + + public override bool IsStatic { + get { + return best_candidate.IsStatic; + } + } + + public override string KindName { + get { return "property"; } + } + + public PropertySpec PropertyInfo { + get { + return best_candidate; + } + } + + #endregion + + public override MethodGroupExpr CanReduceLambda (AnonymousMethodBody body) + { + if (best_candidate == null || !(best_candidate.IsStatic || InstanceExpression is This)) + return null; + + var args_count = arguments == null ? 0 : arguments.Count; + if (args_count != body.Parameters.Count && args_count == 0) + return null; + + var mg = MethodGroupExpr.CreatePredefined (best_candidate.Get, DeclaringType, loc); + mg.InstanceExpression = InstanceExpression; + + return mg; + } + + public static PropertyExpr CreatePredefined (PropertySpec spec, Location loc) + { + return new PropertyExpr (spec, loc) { + Getter = spec.Get, + Setter = spec.Set + }; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + if (ConditionalAccess) { + Error_NullShortCircuitInsideExpressionTree (ec); + } + + Arguments args; + if (IsSingleDimensionalArrayLength ()) { + args = new Arguments (1); + args.Add (new Argument (InstanceExpression.CreateExpressionTree (ec))); + return CreateExpressionFactoryCall (ec, "ArrayLength", args); + } + + args = new Arguments (2); + if (InstanceExpression == null) + args.Add (new Argument (new NullLiteral (loc))); + else + args.Add (new Argument (InstanceExpression.CreateExpressionTree (ec))); + args.Add (new Argument (new TypeOfMethod (Getter, loc))); + return CreateExpressionFactoryCall (ec, "Property", args); + } + + public Expression CreateSetterTypeOfExpression (ResolveContext rc) + { + DoResolveLValue (rc, null); + return new TypeOfMethod (Setter, loc); + } + + public override string GetSignatureForError () + { + return best_candidate.GetSignatureForError (); + } + + public override SLE.Expression MakeAssignExpression (BuilderContext ctx, Expression source) + { +#if STATIC + return base.MakeExpression (ctx); +#else + return SLE.Expression.Property (InstanceExpression.MakeExpression (ctx), (MethodInfo) Setter.GetMetaInfo ()); +#endif + } + + public override SLE.Expression MakeExpression (BuilderContext ctx) + { +#if STATIC + return base.MakeExpression (ctx); +#else + return SLE.Expression.Property (InstanceExpression.MakeExpression (ctx), (MethodInfo) Getter.GetMetaInfo ()); +#endif + } + + void Error_PropertyNotValid (ResolveContext ec) + { + ec.Report.SymbolRelatedToPreviousError (best_candidate); + ec.Report.Error (1546, loc, "Property or event `{0}' is not supported by the C# language", + GetSignatureForError ()); + } + + bool IsSingleDimensionalArrayLength () + { + if (best_candidate.DeclaringType.BuiltinType != BuiltinTypeSpec.Type.Array || !best_candidate.HasGet || Name != "Length") + return false; + + ArrayContainer ac = InstanceExpression.Type as ArrayContainer; + return ac != null && ac.Rank == 1; + } + + public override void Emit (EmitContext ec, bool leave_copy) + { + // + // Special case: length of single dimension array property is turned into ldlen + // + if (IsSingleDimensionalArrayLength ()) { + if (conditional_access_receiver) { + ec.ConditionalAccess = new ConditionalAccessContext (type, ec.DefineLabel ()); + } + + EmitInstance (ec, false); + + ec.Emit (OpCodes.Ldlen); + ec.Emit (OpCodes.Conv_I4); + + if (conditional_access_receiver) { + ec.CloseConditionalAccess (type); + } + + return; + } + + base.Emit (ec, leave_copy); + } + + public override void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound) + { + Arguments args; + LocalTemporary await_source_arg = null; + + if (isCompound && !(source is DynamicExpressionStatement)) { + emitting_compound_assignment = true; + source.Emit (ec); + + if (has_await_arguments) { + await_source_arg = new LocalTemporary (Type); + await_source_arg.Store (ec); + + args = new Arguments (1); + args.Add (new Argument (await_source_arg)); + + if (leave_copy) { + temp = await_source_arg; + } + + has_await_arguments = false; + } else { + args = null; + + if (leave_copy) { + ec.Emit (OpCodes.Dup); + temp = new LocalTemporary (this.Type); + temp.Store (ec); + } + } + } else { + args = arguments ?? new Arguments (1); + + if (leave_copy) { + source.Emit (ec); + temp = new LocalTemporary (this.Type); + temp.Store (ec); + args.Add (new Argument (temp)); + } else { + args.Add (new Argument (source)); + } + } + + emitting_compound_assignment = false; + + var call = new CallEmitter (); + call.InstanceExpression = InstanceExpression; + if (args == null) + call.InstanceExpressionOnStack = true; + + if (ConditionalAccess) { + call.ConditionalAccess = true; + } + + if (leave_copy) + call.Emit (ec, Setter, args, loc); + else + call.EmitStatement (ec, Setter, args, loc); + + if (temp != null) { + temp.Emit (ec); + temp.Release (ec); + } + + if (await_source_arg != null) { + await_source_arg.Release (ec); + } + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + base.FlowAnalysis (fc); + + if (conditional_access_receiver) + fc.ConditionalAccessEnd (); + } + + protected override Expression OverloadResolve (ResolveContext rc, Expression right_side) + { + eclass = ExprClass.PropertyAccess; + + if (best_candidate.IsNotCSharpCompatible) { + Error_PropertyNotValid (rc); + } + + ResolveInstanceExpression (rc, right_side); + + if ((best_candidate.Modifiers & (Modifiers.ABSTRACT | Modifiers.VIRTUAL)) != 0 && best_candidate.DeclaringType != InstanceExpression.Type) { + var filter = new MemberFilter (best_candidate.Name, 0, MemberKind.Property, null, null); + var p = MemberCache.FindMember (InstanceExpression.Type, filter, BindingRestriction.InstanceOnly | BindingRestriction.OverrideOnly) as PropertySpec; + if (p != null) { + type = p.MemberType; + } + } + + DoBestMemberChecks (rc, best_candidate); + + // Handling of com-imported properties with any number of default property parameters + if (best_candidate.HasGet && !best_candidate.Get.Parameters.IsEmpty) { + var p = best_candidate.Get.Parameters; + arguments = new Arguments (p.Count); + for (int i = 0; i < p.Count; ++i) { + arguments.Add (new Argument (OverloadResolver.ResolveDefaultValueArgument (rc, p.Types [i], p.FixedParameters [i].DefaultValue, loc))); + } + } else if (best_candidate.HasSet && best_candidate.Set.Parameters.Count > 1) { + var p = best_candidate.Set.Parameters; + arguments = new Arguments (p.Count - 1); + for (int i = 0; i < p.Count - 1; ++i) { + arguments.Add (new Argument (OverloadResolver.ResolveDefaultValueArgument (rc, p.Types [i], p.FixedParameters [i].DefaultValue, loc))); + } + } + + return this; + } + + public override void SetTypeArguments (ResolveContext ec, TypeArguments ta) + { + Error_TypeArgumentsCannotBeUsed (ec, "property", GetSignatureForError (), loc); + } + } + + abstract class PropertyOrIndexerExpr : MemberExpr, IDynamicAssign where T : PropertySpec + { + // getter and setter can be different for base calls + MethodSpec getter, setter; + protected T best_candidate; + + protected LocalTemporary temp; + protected bool emitting_compound_assignment; + protected bool has_await_arguments; + + protected PropertyOrIndexerExpr (Location l) + { + loc = l; + } + + #region Properties + + protected abstract Arguments Arguments { get; set; } + + public MethodSpec Getter { + get { + return getter; + } + set { + getter = value; + } + } + + public MethodSpec Setter { + get { + return setter; + } + set { + setter = value; + } + } + + #endregion + + protected override Expression DoResolve (ResolveContext ec) + { + if (eclass == ExprClass.Unresolved) { + ResolveConditionalAccessReceiver (ec); + + var expr = OverloadResolve (ec, null); + if (expr == null) + return null; + + if (expr != this) + return expr.Resolve (ec); + + if (conditional_access_receiver) { + type = LiftMemberType (ec, type); + ec.With (ResolveContext.Options.ConditionalAccessReceiver, false); + } + } + + if (!ResolveGetter (ec)) + return null; + + return this; + } + + public override Expression DoResolveLValue (ResolveContext ec, Expression right_side) + { + if (ConditionalAccess) + throw new NotSupportedException ("null propagating operator assignment"); + + if (right_side == EmptyExpression.OutAccess) { + // TODO: best_candidate can be null at this point + INamedBlockVariable variable = null; + if (best_candidate != null && ec.CurrentBlock.ParametersBlock.TopBlock.GetLocalName (best_candidate.Name, ec.CurrentBlock, ref variable) && variable is Linq.RangeVariable) { + ec.Report.Error (1939, loc, "A range variable `{0}' may not be passes as `ref' or `out' parameter", + best_candidate.Name); + } else { + right_side.DoResolveLValue (ec, this); + } + return null; + } + + if (eclass == ExprClass.Unresolved) { + var expr = OverloadResolve (ec, right_side); + if (expr == null) + return null; + + if (expr != this) + return expr.ResolveLValue (ec, right_side); + } else { + ResolveInstanceExpression (ec, right_side); + } + + if (!ResolveSetter (ec)) + return null; + + return this; + } + + void EmitConditionalAccess (EmitContext ec, ref CallEmitter call, MethodSpec method, Arguments arguments) + { + ec.ConditionalAccess = new ConditionalAccessContext (type, ec.DefineLabel ()); + + call.Emit (ec, method, arguments, loc); + + ec.CloseConditionalAccess (method.ReturnType != type && type.IsNullableType ? type : null); + } + + // + // Implements the IAssignMethod interface for assignments + // + public virtual void Emit (EmitContext ec, bool leave_copy) + { + var call = new CallEmitter (); + call.ConditionalAccess = ConditionalAccess; + call.InstanceExpression = InstanceExpression; + if (has_await_arguments) + call.HasAwaitArguments = true; + else + call.DuplicateArguments = emitting_compound_assignment; + + if (conditional_access_receiver) + EmitConditionalAccess (ec, ref call, Getter, Arguments); + else + call.Emit (ec, Getter, Arguments, loc); + + if (call.HasAwaitArguments) { + InstanceExpression = call.InstanceExpression; + Arguments = call.EmittedArguments; + has_await_arguments = true; + } + + if (leave_copy) { + ec.Emit (OpCodes.Dup); + temp = new LocalTemporary (Type); + temp.Store (ec); + } + } + + public abstract void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound); + + public override void Emit (EmitContext ec) + { + Emit (ec, false); + } + + protected override FieldExpr EmitToFieldSource (EmitContext ec) + { + has_await_arguments = true; + Emit (ec, false); + return null; + } + + public abstract SLE.Expression MakeAssignExpression (BuilderContext ctx, Expression source); + + protected abstract Expression OverloadResolve (ResolveContext rc, Expression right_side); + + bool ResolveGetter (ResolveContext rc) + { + if (!best_candidate.HasGet) { + if (InstanceExpression != EmptyExpression.Null) { + rc.Report.SymbolRelatedToPreviousError (best_candidate); + rc.Report.Error (154, loc, "The property or indexer `{0}' cannot be used in this context because it lacks the `get' accessor", + best_candidate.GetSignatureForError ()); + return false; + } + } else if (!best_candidate.Get.IsAccessible (rc) || !best_candidate.Get.DeclaringType.IsAccessible (rc)) { + if (best_candidate.HasDifferentAccessibility) { + rc.Report.SymbolRelatedToPreviousError (best_candidate.Get); + rc.Report.Error (271, loc, "The property or indexer `{0}' cannot be used in this context because the get accessor is inaccessible", + TypeManager.CSharpSignature (best_candidate)); + } else { + rc.Report.SymbolRelatedToPreviousError (best_candidate.Get); + ErrorIsInaccesible (rc, best_candidate.Get.GetSignatureForError (), loc); + } + } + + if (best_candidate.HasDifferentAccessibility) { + CheckProtectedMemberAccess (rc, best_candidate.Get); + } + + getter = CandidateToBaseOverride (rc, best_candidate.Get); + return true; + } + + bool ResolveSetter (ResolveContext rc) + { + if (!best_candidate.HasSet) { + rc.Report.Error (200, loc, "Property or indexer `{0}' cannot be assigned to (it is read-only)", + GetSignatureForError ()); + return false; + } + + if (!best_candidate.Set.IsAccessible (rc) || !best_candidate.Set.DeclaringType.IsAccessible (rc)) { + if (best_candidate.HasDifferentAccessibility) { + rc.Report.SymbolRelatedToPreviousError (best_candidate.Set); + rc.Report.Error (272, loc, "The property or indexer `{0}' cannot be used in this context because the set accessor is inaccessible", + GetSignatureForError ()); + } else { + rc.Report.SymbolRelatedToPreviousError (best_candidate.Set); + ErrorIsInaccesible (rc, best_candidate.GetSignatureForError (), loc); + } + } + + if (best_candidate.HasDifferentAccessibility) + CheckProtectedMemberAccess (rc, best_candidate.Set); + + setter = CandidateToBaseOverride (rc, best_candidate.Set); + return true; + } + } + + /// + /// Fully resolved expression that evaluates to an Event + /// + public class EventExpr : MemberExpr, IAssignMethod + { + readonly EventSpec spec; + MethodSpec op; + + public EventExpr (EventSpec spec, Location loc) + { + this.spec = spec; + this.loc = loc; + } + + #region Properties + + protected override TypeSpec DeclaringType { + get { + return spec.DeclaringType; + } + } + + public override string Name { + get { + return spec.Name; + } + } + + public override bool IsInstance { + get { + return !spec.IsStatic; + } + } + + public override bool IsStatic { + get { + return spec.IsStatic; + } + } + + public override string KindName { + get { return "event"; } + } + + public MethodSpec Operator { + get { + return op; + } + } + + #endregion + + public override MemberExpr ResolveMemberAccess (ResolveContext ec, Expression left, SimpleName original) + { + // + // If the event is local to this class and we are not lhs of +=/-= we transform ourselves into a FieldExpr + // + if (!ec.HasSet (ResolveContext.Options.CompoundAssignmentScope)) { + if (spec.BackingField != null && + (spec.DeclaringType == ec.CurrentType || TypeManager.IsNestedChildOf (ec.CurrentType, spec.DeclaringType.MemberDefinition))) { + + spec.MemberDefinition.SetIsUsed (); + + if (!ec.IsObsolete) { + ObsoleteAttribute oa = spec.GetAttributeObsolete (); + if (oa != null) + AttributeTester.Report_ObsoleteMessage (oa, spec.GetSignatureForError (), loc, ec.Report); + } + + if ((spec.Modifiers & (Modifiers.ABSTRACT | Modifiers.EXTERN)) != 0) + Error_AssignmentEventOnly (ec); + + FieldExpr ml = new FieldExpr (spec.BackingField, loc); + + InstanceExpression = null; + + return ml.ResolveMemberAccess (ec, left, original); + } + } + + return base.ResolveMemberAccess (ec, left, original); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + throw new NotSupportedException ("ET"); + } + + public override Expression DoResolveLValue (ResolveContext ec, Expression right_side) + { + if (right_side == EmptyExpression.EventAddition) { + op = spec.AccessorAdd; + } else if (right_side == EmptyExpression.EventSubtraction) { + op = spec.AccessorRemove; + } + + if (op == null) { + Error_AssignmentEventOnly (ec); + return null; + } + + op = CandidateToBaseOverride (ec, op); + return this; + } + + protected override Expression DoResolve (ResolveContext ec) + { + eclass = ExprClass.EventAccess; + type = spec.MemberType; + + ResolveInstanceExpression (ec, null); + + if (!ec.HasSet (ResolveContext.Options.CompoundAssignmentScope)) { + Error_AssignmentEventOnly (ec); + } + + DoBestMemberChecks (ec, spec); + return this; + } + + public override void Emit (EmitContext ec) + { + throw new NotSupportedException (); + //Error_CannotAssign (); + } + + #region IAssignMethod Members + + public void Emit (EmitContext ec, bool leave_copy) + { + throw new NotImplementedException (); + } + + public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound) + { + if (leave_copy || !isCompound) + throw new NotImplementedException ("EventExpr::EmitAssign"); + + Arguments args = new Arguments (1); + args.Add (new Argument (source)); + + // TODO: Wrong, needs receiver +// if (NullShortCircuit) { +// ec.ConditionalAccess = new ConditionalAccessContext (type, ec.DefineLabel ()); +// } + + var call = new CallEmitter (); + call.InstanceExpression = InstanceExpression; + call.ConditionalAccess = ConditionalAccess; + call.EmitStatement (ec, op, args, loc); + +// if (NullShortCircuit) +// ec.CloseConditionalAccess (null); + } + + #endregion + + void Error_AssignmentEventOnly (ResolveContext ec) + { + if (spec.DeclaringType == ec.CurrentType || TypeManager.IsNestedChildOf (ec.CurrentType, spec.DeclaringType.MemberDefinition)) { + ec.Report.Error (79, loc, + "The event `{0}' can only appear on the left hand side of `+=' or `-=' operator", + GetSignatureForError ()); + } else { + ec.Report.Error (70, loc, + "The event `{0}' can only appear on the left hand side of += or -= when used outside of the type `{1}'", + GetSignatureForError (), spec.DeclaringType.GetSignatureForError ()); + } + } + + protected override void Error_CannotCallAbstractBase (ResolveContext rc, string name) + { + name = name.Substring (0, name.LastIndexOf ('.')); + base.Error_CannotCallAbstractBase (rc, name); + } + + public override string GetSignatureForError () + { + return TypeManager.CSharpSignature (spec); + } + + public override void SetTypeArguments (ResolveContext ec, TypeArguments ta) + { + Error_TypeArgumentsCannotBeUsed (ec, "event", GetSignatureForError (), loc); + } + } + + public class TemporaryVariableReference : VariableReference + { + public class Declarator : Statement + { + TemporaryVariableReference variable; + + public Declarator (TemporaryVariableReference variable) + { + this.variable = variable; + loc = variable.loc; + } + + protected override void DoEmit (EmitContext ec) + { + variable.li.CreateBuilder (ec); + } + + public override void Emit (EmitContext ec) + { + // Don't create sequence point + DoEmit (ec); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + return false; + } + + protected override void CloneTo (CloneContext clonectx, Statement target) + { + // Nothing + } + } + + LocalVariable li; + + public TemporaryVariableReference (LocalVariable li, Location loc) + { + this.li = li; + this.type = li.Type; + this.loc = loc; + } + + public override bool IsLockedByStatement { + get { + return false; + } + set { + } + } + + public LocalVariable LocalInfo { + get { + return li; + } + } + + public static TemporaryVariableReference Create (TypeSpec type, Block block, Location loc) + { + var li = LocalVariable.CreateCompilerGenerated (type, block, loc); + return new TemporaryVariableReference (li, loc); + } + + protected override Expression DoResolve (ResolveContext ec) + { + eclass = ExprClass.Variable; + + // + // Don't capture temporary variables except when using + // state machine redirection and block yields + // + if (ec.CurrentAnonymousMethod is StateMachineInitializer && + (ec.CurrentBlock.Explicit.HasYield || ec.CurrentBlock.Explicit.HasAwait) && + ec.IsVariableCapturingRequired) { + AnonymousMethodStorey storey = li.Block.Explicit.CreateAnonymousMethodStorey (ec); + storey.CaptureLocalVariable (ec, li); + } + + return this; + } + + public override Expression DoResolveLValue (ResolveContext ec, Expression right_side) + { + return Resolve (ec); + } + + public override void Emit (EmitContext ec) + { + li.CreateBuilder (ec); + + Emit (ec, false); + } + + public void EmitAssign (EmitContext ec, Expression source) + { + li.CreateBuilder (ec); + + EmitAssign (ec, source, false, false); + } + + public override HoistedVariable GetHoistedVariable (AnonymousExpression ae) + { + return li.HoistedVariant; + } + + public override bool IsFixed { + get { return true; } + } + + public override bool IsRef { + get { return false; } + } + + public override string Name { + get { throw new NotImplementedException (); } + } + + public override void SetHasAddressTaken () + { + throw new NotImplementedException (); + } + + protected override ILocalVariable Variable { + get { return li; } + } + + public override VariableInfo VariableInfo { + get { return null; } + } + } + + /// + /// Handles `var' contextual keyword; var becomes a keyword only + /// if no type called var exists in a variable scope + /// + class VarExpr : SimpleName + { + public VarExpr (Location loc) + : base ("var", loc) + { + } + + public bool InferType (ResolveContext ec, Expression right_side) + { + if (type != null) + throw new InternalErrorException ("An implicitly typed local variable could not be redefined"); + + type = right_side.Type; + if (type == InternalType.NullLiteral || type.Kind == MemberKind.Void || type == InternalType.AnonymousMethod || type == InternalType.MethodGroup) { + ec.Report.Error (815, loc, + "An implicitly typed local variable declaration cannot be initialized with `{0}'", + type.GetSignatureForError ()); + return false; + } + + eclass = ExprClass.Variable; + return true; + } + + protected override void Error_TypeOrNamespaceNotFound (IMemberContext ec) + { + if (ec.Module.Compiler.Settings.Version < LanguageVersion.V_3) + base.Error_TypeOrNamespaceNotFound (ec); + else + ec.Module.Compiler.Report.Error (825, loc, "The contextual keyword `var' may only appear within a local variable declaration"); + } + } + + public class InvalidStatementExpression : Statement + { + public Expression Expression { + get; + private set; + } + + public InvalidStatementExpression (Expression expr) + { + this.Expression = expr; + } + + public override void Emit (EmitContext ec) + { + // nothing + } + + protected override void DoEmit (EmitContext ec) + { + // nothing + } + + protected override void CloneTo (CloneContext clonectx, Statement target) + { + // nothing + } + + public override Mono.CSharp.Expression CreateExpressionTree (ResolveContext ec) + { + return null; + } + + public override object Accept (Mono.CSharp.StructuralVisitor visitor) + { + return visitor.Visit (this); + } + + protected override bool DoFlowAnalysis(FlowAnalysisContext fc) + { + return false; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/enum.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/enum.cs new file mode 100644 index 000000000..96385f622 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/enum.cs @@ -0,0 +1,339 @@ +// +// enum.cs: Enum handling. +// +// Author: Miguel de Icaza (miguel@gnu.org) +// Ravi Pratap (ravi@ximian.com) +// Marek Safar (marek.safar@seznam.cz) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2001 Ximian, Inc (http://www.ximian.com) +// Copyright 2003-2003 Novell, Inc (http://www.novell.com) +// Copyright 2011 Xamarin Inc +// + +using System; + +#if STATIC +using MetaType = IKVM.Reflection.Type; +using IKVM.Reflection; +#else +using MetaType = System.Type; +using System.Reflection; +#endif + +namespace Mono.CSharp { + + public class EnumMember : Const + { + class EnumTypeExpr : TypeExpr + { + public override TypeSpec ResolveAsType (IMemberContext ec, bool allowUnboundTypeArguments) + { + type = ec.CurrentType; + eclass = ExprClass.Type; + return type; + } + } + + public EnumMember (Enum parent, MemberName name, Attributes attrs) + : base (parent, new EnumTypeExpr (), Modifiers.PUBLIC, name, attrs) + { + } + + static bool IsValidEnumType (TypeSpec t) + { + switch (t.BuiltinType) { + case BuiltinTypeSpec.Type.Int: + case BuiltinTypeSpec.Type.UInt: + case BuiltinTypeSpec.Type.Long: + case BuiltinTypeSpec.Type.Byte: + case BuiltinTypeSpec.Type.SByte: + case BuiltinTypeSpec.Type.Short: + case BuiltinTypeSpec.Type.UShort: + case BuiltinTypeSpec.Type.ULong: + case BuiltinTypeSpec.Type.Char: + return true; + default: + return t.IsEnum; + } + } + + public override Constant ConvertInitializer (ResolveContext rc, Constant expr) + { + if (expr is EnumConstant) + expr = ((EnumConstant) expr).Child; + + var en = (Enum)Parent; + var underlying = en.UnderlyingType; + if (expr != null) { + expr = expr.ImplicitConversionRequired (rc, underlying); + if (expr != null && !IsValidEnumType (expr.Type)) { + en.Error_UnderlyingType (Location); + expr = null; + } + } + + if (expr == null) + expr = New.Constantify (underlying, Location); + + return new EnumConstant (expr, MemberType); + } + + public override bool Define () + { + if (!ResolveMemberType ()) + return false; + + const FieldAttributes attr = FieldAttributes.Public | FieldAttributes.Static | FieldAttributes.Literal; + FieldBuilder = Parent.TypeBuilder.DefineField (Name, MemberType.GetMetaInfo (), attr); + spec = new ConstSpec (Parent.Definition, this, MemberType, FieldBuilder, ModFlags, initializer); + + Parent.MemberCache.AddMember (spec); + return true; + } + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + + } + + /// + /// Enumeration container + /// + public class Enum : TypeDefinition + { + // + // Implicit enum member initializer, used when no constant value is provided + // + sealed class ImplicitInitializer : Expression + { + readonly EnumMember prev; + readonly EnumMember current; + + public ImplicitInitializer (EnumMember current, EnumMember prev) + { + this.current = current; + this.prev = prev; + } + + public override bool ContainsEmitWithAwait () + { + return false; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + throw new NotSupportedException ("Missing Resolve call"); + } + + protected override Expression DoResolve (ResolveContext rc) + { + // We are the first member + if (prev == null) { + return New.Constantify (current.Parent.Definition, Location); + } + + var c = ((ConstSpec) prev.Spec).GetConstant (rc) as EnumConstant; + try { + return c.Increment (); + } catch (OverflowException) { + rc.Report.Error (543, current.Location, + "The enumerator value `{0}' is outside the range of enumerator underlying type `{1}'", + current.GetSignatureForError (), ((Enum) current.Parent).UnderlyingType.GetSignatureForError ()); + + return New.Constantify (current.Parent.Definition, current.Location); + } + } + + public override void Emit (EmitContext ec) + { + throw new NotSupportedException ("Missing Resolve call"); + } + } + + public static readonly string UnderlyingValueField = "value__"; + + const Modifiers AllowedModifiers = + Modifiers.NEW | + Modifiers.PUBLIC | + Modifiers.PROTECTED | + Modifiers.INTERNAL | + Modifiers.PRIVATE; + + readonly FullNamedExpression underlying_type_expr; + + public Enum (TypeContainer parent, FullNamedExpression type, Modifiers mod_flags, MemberName name, Attributes attrs) + : base (parent, name, attrs, MemberKind.Enum) + { + underlying_type_expr = type; + var accmods = IsTopLevel ? Modifiers.INTERNAL : Modifiers.PRIVATE; + ModFlags = ModifiersExtensions.Check (AllowedModifiers, mod_flags, accmods, Location, Report); + spec = new EnumSpec (null, this, null, null, ModFlags); + } + + #region Properties + + public override AttributeTargets AttributeTargets { + get { + return AttributeTargets.Enum; + } + } + + public FullNamedExpression BaseTypeExpression { + get { + return underlying_type_expr; + } + } + + protected override TypeAttributes TypeAttr { + get { + return base.TypeAttr | TypeAttributes.Class | TypeAttributes.Sealed; + } + } + + public TypeSpec UnderlyingType { + get { + return ((EnumSpec) spec).UnderlyingType; + } + } + + #endregion + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + + public void AddEnumMember (EnumMember em) + { + if (em.Name == UnderlyingValueField) { + Report.Error (76, em.Location, "An item in an enumeration cannot have an identifier `{0}'", + UnderlyingValueField); + return; + } + + AddMember (em); + } + + public void Error_UnderlyingType (Location loc) + { + Report.Error (1008, loc, + "Type byte, sbyte, short, ushort, int, uint, long or ulong expected"); + } + + protected override void DoDefineContainer () + { + TypeSpec ut; + if (underlying_type_expr != null) { + ut = underlying_type_expr.ResolveAsType (this); + if (!EnumSpec.IsValidUnderlyingType (ut)) { + Error_UnderlyingType (underlying_type_expr.Location); + ut = null; + } + } else { + ut = null; + } + + if (ut == null) + ut = Compiler.BuiltinTypes.Int; + + ((EnumSpec) spec).UnderlyingType = ut; + + TypeBuilder.DefineField (UnderlyingValueField, UnderlyingType.GetMetaInfo (), + FieldAttributes.Public | FieldAttributes.SpecialName | FieldAttributes.RTSpecialName); + + DefineBaseTypes (); + } + + protected override bool DoDefineMembers () + { + for (int i = 0; i < Members.Count; ++i) { + EnumMember em = (EnumMember) Members[i]; + if (em.Initializer == null) { + em.Initializer = new ImplicitInitializer (em, i == 0 ? null : (EnumMember) Members[i - 1]); + } + + em.Define (); + } + + return true; + } + + public override bool IsUnmanagedType () + { + return true; + } + + protected override TypeSpec[] ResolveBaseTypes (out FullNamedExpression base_class) + { + base_type = Compiler.BuiltinTypes.Enum; + base_class = null; + return null; + } + + protected override bool VerifyClsCompliance () + { + if (!base.VerifyClsCompliance ()) + return false; + + switch (UnderlyingType.BuiltinType) { + case BuiltinTypeSpec.Type.UInt: + case BuiltinTypeSpec.Type.ULong: + case BuiltinTypeSpec.Type.UShort: + Report.Warning (3009, 1, Location, "`{0}': base type `{1}' is not CLS-compliant", + GetSignatureForError (), UnderlyingType.GetSignatureForError ()); + break; + } + + return true; + } + } + + class EnumSpec : TypeSpec + { + TypeSpec underlying; + + public EnumSpec (TypeSpec declaringType, ITypeDefinition definition, TypeSpec underlyingType, MetaType info, Modifiers modifiers) + : base (MemberKind.Enum, declaringType, definition, info, modifiers | Modifiers.SEALED) + { + this.underlying = underlyingType; + } + + public TypeSpec UnderlyingType { + get { + return underlying; + } + set { + if (underlying != null) + throw new InternalErrorException ("UnderlyingType reset"); + + underlying = value; + } + } + + public static TypeSpec GetUnderlyingType (TypeSpec t) + { + return ((EnumSpec) t.GetDefinition ()).UnderlyingType; + } + + public static bool IsValidUnderlyingType (TypeSpec type) + { + switch (type.BuiltinType) { + case BuiltinTypeSpec.Type.Int: + case BuiltinTypeSpec.Type.UInt: + case BuiltinTypeSpec.Type.Long: + case BuiltinTypeSpec.Type.Byte: + case BuiltinTypeSpec.Type.SByte: + case BuiltinTypeSpec.Type.Short: + case BuiltinTypeSpec.Type.UShort: + case BuiltinTypeSpec.Type.ULong: + return true; + } + + return false; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/eval.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/eval.cs new file mode 100644 index 000000000..aac77eee6 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/eval.cs @@ -0,0 +1,1333 @@ +// +// eval.cs: Evaluation and Hosting API for the C# compiler +// +// Authors: +// Miguel de Icaza (miguel@gnome.org) +// Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com) +// Copyright 2004-2011 Novell, Inc +// Copyright 2011 Xamarin Inc +// + +using System; +using System.Threading; +using System.Collections.Generic; +using System.Reflection; +using System.Reflection.Emit; +using System.IO; +using System.Text; +using System.Linq; + +namespace Mono.CSharp +{ + + /// + /// Experimental! + /// + public delegate void ValueModificationHandler (string variableName, int row, int column, object value); + + /// + /// Evaluator: provides an API to evaluate C# statements and + /// expressions dynamically. + /// + /// + /// This class exposes static methods to evaluate expressions in the + /// current program. + /// + /// To initialize the evaluator with a number of compiler + /// options call the Init(string[]args) method with a set of + /// command line options that the compiler recognizes. + /// + /// To interrupt execution of a statement, you can invoke the + /// Evaluator.Interrupt method. + /// + public class Evaluator { + + enum ParseMode { + // Parse silently, do not output any error messages + Silent, + + // Report errors during parse + ReportErrors, + + // Auto-complete, means that the tokenizer will start producing + // GETCOMPLETIONS tokens when it reaches a certain point. + GetCompletions + } + + static object evaluator_lock = new object (); + static volatile bool invoking; + +#if !STATIC + static int count; +#endif + static Thread invoke_thread; + + readonly Dictionary> fields; + + Type base_class; + bool inited; + int startup_files; + + readonly CompilerContext ctx; + readonly ModuleContainer module; + readonly ReflectionImporter importer; + readonly CompilationSourceFile source_file; + + int? listener_id; + + public Evaluator (CompilerContext ctx) + { + this.ctx = ctx; + + module = new ModuleContainer (ctx); + module.Evaluator = this; + + source_file = new CompilationSourceFile (module, null); + module.AddTypeContainer (source_file); + + startup_files = ctx.SourceFiles.Count; + + // FIXME: Importer needs this assembly for internalsvisibleto + module.SetDeclaringAssembly (new AssemblyDefinitionDynamic (module, "evaluator")); + importer = new ReflectionImporter (module, ctx.BuiltinTypes); + + InteractiveBaseClass = typeof (InteractiveBase); + fields = new Dictionary> (); + } + + void Init () + { + var loader = new DynamicLoader (importer, ctx); + + RootContext.ToplevelTypes = module; + + //var startup_files = new List (); + //foreach (CompilationUnit file in Location.SourceFiles) + // startup_files.Add (file.Path); + + loader.LoadReferences (module); + ctx.BuiltinTypes.CheckDefinitions (module); + module.InitializePredefinedTypes (); + + inited = true; + } + + void ParseStartupFiles () + { + Driver d = new Driver (ctx); + + Location.Initialize (ctx.SourceFiles); + + var parser_session = new ParserSession (); + for (int i = 0; i < startup_files; ++i) { + var sf = ctx.SourceFiles [i]; + d.Parse (sf, module, parser_session, ctx.Report); + } + } + + void Reset () + { + Location.Reset (); + Location.Initialize (ctx.SourceFiles); + } + + /// + /// When set evaluator will automatically wait on Task of async methods. When not + /// set it's called responsibility to handle Task execution + /// + public bool WaitOnTask { get; set; } + + /// + /// If true, turns type expressions into valid expressions + /// and calls the describe method on it + /// + public bool DescribeTypeExpressions; + + /// + /// Whether the evaluator will use terse syntax, and the semicolons at the end are optional + /// + public bool Terse = true; + + /// + /// The base class for the classes that host the user generated code + /// + /// + /// + /// This is the base class that will host the code + /// executed by the Evaluator. By default + /// this is the Mono.CSharp.InteractiveBase class + /// which is useful for interactive use. + /// + /// By changing this property you can control the + /// base class and the static members that are + /// available to your evaluated code. + /// + public Type InteractiveBaseClass { + get { + return base_class; + } + set { + base_class = value; + + if (value != null && typeof (InteractiveBase).IsAssignableFrom (value)) + InteractiveBase.Evaluator = this; + } + } + + /// + /// Interrupts the evaluation of an expression executing in Evaluate. + /// + /// + /// Use this method to interrupt long-running invocations. + /// + public void Interrupt () + { + if (!inited || !invoking) + return; + + if (invoke_thread != null) + invoke_thread.Abort (); + } + + /// + /// Compiles the input string and returns a delegate that represents the compiled code. + /// + /// + /// + /// Compiles the input string as a C# expression or + /// statement, unlike the Evaluate method, the + /// resulting delegate can be invoked multiple times + /// without incurring in the compilation overhead. + /// + /// If the return value of this function is null, + /// this indicates that the parsing was complete. + /// If the return value is a string it indicates + /// that the input string was partial and that the + /// invoking code should provide more code before + /// the code can be successfully compiled. + /// + /// If you know that you will always get full expressions or + /// statements and do not care about partial input, you can use + /// the other Compile overload. + /// + /// On success, in addition to returning null, the + /// compiled parameter will be set to the delegate + /// that can be invoked to execute the code. + /// + /// + public string Compile (string input, out CompiledMethod compiled) + { + if (input == null || input.Length == 0){ + compiled = null; + return null; + } + + lock (evaluator_lock){ + if (!inited) { + Init (); + ParseStartupFiles (); + } else { + ctx.Report.Printer.Reset (); + } + + bool partial_input; + CSharpParser parser = ParseString (ParseMode.Silent, input, out partial_input); + + // Terse mode, try to provide the trailing semicolon automatically. + if (parser == null && Terse && partial_input){ + bool ignore; + + // check if the source would compile with a block, if so, we should not + // add the semicolon. + var needs_block = ParseString (ParseMode.Silent, input + "{}", out ignore) != null; + if (!needs_block) + parser = ParseString (ParseMode.Silent, input + ";", out ignore); + } + if (parser == null){ + compiled = null; + if (partial_input) + return input; + + ParseString (ParseMode.ReportErrors, input, out partial_input); + return null; + } + + Class parser_result = parser.InteractiveResult; + compiled = CompileBlock (parser_result, parser.undo, ctx.Report); + return null; + } + } + + /// + /// Compiles the input string and returns a delegate that represents the compiled code. + /// + /// + /// + /// Compiles the input string as a C# expression or + /// statement, unlike the Evaluate method, the + /// resulting delegate can be invoked multiple times + /// without incurring in the compilation overhead. + /// + /// This method can only deal with fully formed input + /// strings and does not provide a completion mechanism. + /// If you must deal with partial input (for example for + /// interactive use) use the other overload. + /// + /// On success, a delegate is returned that can be used + /// to invoke the method. + /// + /// + public CompiledMethod Compile (string input) + { + CompiledMethod compiled; + + // Ignore partial inputs + if (Compile (input, out compiled) != null){ + // Error, the input was partial. + return null; + } + + // Either null (on error) or the compiled method. + return compiled; + } + + static MethodInfo listener_proxy_value; + internal void EmitValueChangedCallback (EmitContext ec, string name, TypeSpec type, Location loc) + { + if (listener_id == null) + listener_id = ListenerProxy.Register (ModificationListener); + + if (listener_proxy_value == null) + listener_proxy_value = typeof (ListenerProxy).GetMethod ("ValueChanged"); + +#if STATIC + throw new NotSupportedException (); +#else + // object value, int row, int col, string name, int listenerId + if (type.IsStructOrEnum) + ec.Emit (OpCodes.Box, type); + + ec.EmitInt (loc.Row); + ec.EmitInt (loc.Column); + ec.Emit (OpCodes.Ldstr, name); + ec.EmitInt (listener_id.Value); + ec.Emit (OpCodes.Call, listener_proxy_value); +#endif + } + + /// + /// Evaluates and expression or statement and returns any result values. + /// + /// + /// Evaluates the input string as a C# expression or + /// statement. If the input string is an expression + /// the result will be stored in the result variable + /// and the result_set variable will be set to true. + /// + /// It is necessary to use the result/result_set + /// pair to identify when a result was set (for + /// example, execution of user-provided input can be + /// an expression, a statement or others, and + /// result_set would only be set if the input was an + /// expression. + /// + /// If the return value of this function is null, + /// this indicates that the parsing was complete. + /// If the return value is a string, it indicates + /// that the input is partial and that the user + /// should provide an updated string. + /// + public string Evaluate (string input, out object result, out bool result_set) + { + CompiledMethod compiled; + + result_set = false; + result = null; + + input = Compile (input, out compiled); + if (input != null) + return input; + + if (compiled == null) + return null; + + // + // The code execution does not need to keep the compiler lock + // + object retval = typeof (QuitValue); + + try { + invoke_thread = System.Threading.Thread.CurrentThread; + invoking = true; + compiled (ref retval); + } catch (ThreadAbortException e){ + Thread.ResetAbort (); + Console.WriteLine ("Interrupted!\n{0}", e); + } finally { + invoking = false; + + if (listener_id != null) { + ListenerProxy.Unregister (listener_id.Value); + listener_id = null; + } + } + + // + // We use a reference to a compiler type, in this case + // Driver as a flag to indicate that this was a statement + // + if (!ReferenceEquals (retval, typeof (QuitValue))) { + result_set = true; + result = retval; + } + + return null; + } + + public string [] GetCompletions (string input, out string prefix) + { + prefix = ""; + if (input == null || input.Length == 0) + return null; + + lock (evaluator_lock){ + if (!inited) + Init (); + + bool partial_input; + CSharpParser parser = ParseString (ParseMode.GetCompletions, input, out partial_input); + if (parser == null){ + return null; + } + + Class host = parser.InteractiveResult; + + var base_class_imported = importer.ImportType (base_class); + var baseclass_list = new List (1) { + new TypeExpression (base_class_imported, host.Location) + }; + host.SetBaseTypes (baseclass_list); + +#if NET_4_0 + var access = AssemblyBuilderAccess.RunAndCollect; +#else + var access = AssemblyBuilderAccess.Run; +#endif + var a = new AssemblyDefinitionDynamic (module, "completions"); + a.Create (AppDomain.CurrentDomain, access); + module.SetDeclaringAssembly (a); + + // Need to setup MemberCache + host.CreateContainer (); + // Need to setup base type + host.DefineContainer (); + + var method = host.Members[0] as Method; + BlockContext bc = new BlockContext (method, method.Block, ctx.BuiltinTypes.Void); + + try { + method.Block.Resolve (bc, method); + } catch (CompletionResult cr) { + prefix = cr.BaseText; + return cr.Result; + } + } + return null; + } + + /// + /// Executes the given expression or statement. + /// + /// + /// Executes the provided statement, returns true + /// on success, false on parsing errors. Exceptions + /// might be thrown by the called code. + /// + public bool Run (string statement) + { + object result; + bool result_set; + + return Evaluate (statement, out result, out result_set) == null; + } + + /// + /// Evaluates and expression or statement and returns the result. + /// + /// + /// Evaluates the input string as a C# expression or + /// statement and returns the value. + /// + /// This method will throw an exception if there is a syntax error, + /// of if the provided input is not an expression but a statement. + /// + public object Evaluate (string input) + { + object result; + bool result_set; + + string r = Evaluate (input, out result, out result_set); + + if (r != null) + throw new ArgumentException ("Syntax error on input: partial input"); + + if (result_set == false) + throw new ArgumentException ("The expression failed to resolve"); + + return result; + } + + /// + /// Experimental! + /// + public ValueModificationHandler ModificationListener { get; set; } + + enum InputKind { + EOF, + StatementOrExpression, + CompilationUnit, + Error + } + + // + // Deambiguates the input string to determine if we + // want to process a statement or if we want to + // process a compilation unit. + // + // This is done using a top-down predictive parser, + // since the yacc/jay parser can not deambiguage this + // without more than one lookahead token. There are very + // few ambiguities. + // + InputKind ToplevelOrStatement (SeekableStreamReader seekable) + { + Tokenizer tokenizer = new Tokenizer (seekable, source_file, new ParserSession (), ctx.Report); + + // Prefer contextual block keywords over identifiers + tokenizer.parsing_block++; + + int t = tokenizer.token (); + switch (t){ + case Token.EOF: + return InputKind.EOF; + + // These are toplevels + case Token.EXTERN: + case Token.OPEN_BRACKET: + case Token.ABSTRACT: + case Token.CLASS: + case Token.ENUM: + case Token.INTERFACE: + case Token.INTERNAL: + case Token.NAMESPACE: + case Token.PRIVATE: + case Token.PROTECTED: + case Token.PUBLIC: + case Token.SEALED: + case Token.STATIC: + case Token.STRUCT: + return InputKind.CompilationUnit; + + // Definitely expression + case Token.FIXED: + case Token.BOOL: + case Token.BYTE: + case Token.CHAR: + case Token.DECIMAL: + case Token.DOUBLE: + case Token.FLOAT: + case Token.INT: + case Token.LONG: + case Token.NEW: + case Token.OBJECT: + case Token.SBYTE: + case Token.SHORT: + case Token.STRING: + case Token.UINT: + case Token.ULONG: + return InputKind.StatementOrExpression; + + // These need deambiguation help + case Token.USING: + t = tokenizer.token (); + if (t == Token.EOF) + return InputKind.EOF; + + if (t == Token.IDENTIFIER) + return InputKind.CompilationUnit; + return InputKind.StatementOrExpression; + + + // Distinguish between: + // delegate opt_anonymous_method_signature block + // delegate type + case Token.DELEGATE: + t = tokenizer.token (); + if (t == Token.EOF) + return InputKind.EOF; + if (t == Token.OPEN_PARENS || t == Token.OPEN_BRACE) + return InputKind.StatementOrExpression; + return InputKind.CompilationUnit; + + // Distinguih between: + // unsafe block + // unsafe as modifier of a type declaration + case Token.UNSAFE: + t = tokenizer.token (); + if (t == Token.EOF) + return InputKind.EOF; + if (t == Token.OPEN_PARENS) + return InputKind.StatementOrExpression; + return InputKind.CompilationUnit; + + // These are errors: we list explicitly what we had + // from the grammar, ERROR and then everything else + + case Token.READONLY: + case Token.OVERRIDE: + case Token.ERROR: + return InputKind.Error; + + // This catches everything else allowed by + // expressions. We could add one-by-one use cases + // if needed. + default: + return InputKind.StatementOrExpression; + } + } + + // + // Parses the string @input and returns a CSharpParser if succeeful. + // + // if @silent is set to true then no errors are + // reported to the user. This is used to do various calls to the + // parser and check if the expression is parsable. + // + // @partial_input: if @silent is true, then it returns whether the + // parsed expression was partial, and more data is needed + // + CSharpParser ParseString (ParseMode mode, string input, out bool partial_input) + { + partial_input = false; + Reset (); + + var enc = ctx.Settings.Encoding; + var s = new MemoryStream (enc.GetBytes (input)); + SeekableStreamReader seekable = new SeekableStreamReader (s, enc); + + InputKind kind = ToplevelOrStatement (seekable); + if (kind == InputKind.Error){ + if (mode == ParseMode.ReportErrors) + ctx.Report.Error (-25, "Detection Parsing Error"); + partial_input = false; + return null; + } + + if (kind == InputKind.EOF){ + if (mode == ParseMode.ReportErrors) + Console.Error.WriteLine ("Internal error: EOF condition should have been detected in a previous call with silent=true"); + partial_input = true; + return null; + + } + seekable.Position = 0; + + source_file.DeclarationFound = false; + CSharpParser parser = new CSharpParser (seekable, source_file, new ParserSession ()); + + if (kind == InputKind.StatementOrExpression){ + parser.Lexer.putback_char = Tokenizer.EvalStatementParserCharacter; + parser.Lexer.parsing_block++; + ctx.Settings.StatementMode = true; + } else { + parser.Lexer.putback_char = Tokenizer.EvalCompilationUnitParserCharacter; + ctx.Settings.StatementMode = false; + } + + if (mode == ParseMode.GetCompletions) + parser.Lexer.CompleteOnEOF = true; + + ReportPrinter old_printer = null; + if ((mode == ParseMode.Silent || mode == ParseMode.GetCompletions)) + old_printer = ctx.Report.SetPrinter (new StreamReportPrinter (TextWriter.Null)); + + try { + parser.parse (); + } finally { + if (ctx.Report.Errors != 0){ + if (mode != ParseMode.ReportErrors && parser.UnexpectedEOF) + partial_input = true; + + if (parser.undo != null) + parser.undo.ExecuteUndo (); + + parser = null; + } + + if (old_printer != null) + ctx.Report.SetPrinter (old_printer); + } + return parser; + } + + CompiledMethod CompileBlock (Class host, Undo undo, Report Report) + { +#if STATIC + throw new NotSupportedException (); +#else + string current_debug_name = "eval-" + count + ".dll"; + ++count; + + AssemblyDefinitionDynamic assembly; + AssemblyBuilderAccess access; + + if (Environment.GetEnvironmentVariable ("SAVE") != null) { + access = AssemblyBuilderAccess.RunAndSave; + assembly = new AssemblyDefinitionDynamic (module, current_debug_name, current_debug_name); + assembly.Importer = importer; + } else { +#if NET_4_0 + access = AssemblyBuilderAccess.RunAndCollect; +#else + access = AssemblyBuilderAccess.Run; +#endif + assembly = new AssemblyDefinitionDynamic (module, current_debug_name); + } + + assembly.Create (AppDomain.CurrentDomain, access); + + Method expression_method; + if (host != null) { + var base_class_imported = importer.ImportType (base_class); + var baseclass_list = new List (1) { + new TypeExpression (base_class_imported, host.Location) + }; + + host.SetBaseTypes (baseclass_list); + + expression_method = (Method) host.Members[0]; + + if ((expression_method.ModFlags & Modifiers.ASYNC) != 0) { + // + // Host method is async. When WaitOnTask is set we wrap it with wait + // + // void AsyncWait (ref object $retval) { + // $retval = Host(); + // ((Task)$retval).Wait(); // When WaitOnTask is set + // } + // + var p = new ParametersCompiled ( + new Parameter (new TypeExpression (module.Compiler.BuiltinTypes.Object, Location.Null), "$retval", Parameter.Modifier.REF, null, Location.Null) + ); + + var method = new Method(host, new TypeExpression(module.Compiler.BuiltinTypes.Void, Location.Null), + Modifiers.PUBLIC | Modifiers.STATIC, new MemberName("AsyncWait"), p, null); + + method.Block = new ToplevelBlock(method.Compiler, p, Location.Null); + method.Block.AddStatement(new StatementExpression (new SimpleAssign( + new SimpleName(p [0].Name, Location.Null), + new Invocation(new SimpleName(expression_method.MemberName.Name, Location.Null), new Arguments(0)), + Location.Null), Location.Null)); + + if (WaitOnTask) { + var task = new Cast (expression_method.TypeExpression, new SimpleName (p [0].Name, Location.Null), Location.Null); + + method.Block.AddStatement (new StatementExpression (new Invocation ( + new MemberAccess (task, "Wait", Location.Null), + new Arguments (0)), Location.Null)); + } + + host.AddMember(method); + + expression_method = method; + } + + host.CreateContainer(); + host.DefineContainer(); + host.Define(); + + } else { + expression_method = null; + } + + module.CreateContainer (); + + // Disable module and source file re-definition checks + module.EnableRedefinition (); + source_file.EnableRedefinition (); + + module.Define (); + + if (Report.Errors != 0){ + if (undo != null) + undo.ExecuteUndo (); + + return null; + } + + if (host != null){ + host.PrepareEmit (); + host.EmitContainer (); + } + + module.EmitContainer (); + + if (Report.Errors != 0){ + if (undo != null) + undo.ExecuteUndo (); + return null; + } + + module.CloseContainer (); + if (host != null) + host.CloseContainer (); + + if (access == AssemblyBuilderAccess.RunAndSave) + assembly.Save (); + + if (host == null) + return null; + + // + // Unlike Mono, .NET requires that the MethodInfo is fetched, it cant + // work from MethodBuilders. Retarded, I know. + // + var tt = assembly.Builder.GetType (host.TypeBuilder.Name); + var mi = tt.GetMethod (expression_method.MemberName.Name); + + // + // We need to then go from FieldBuilder to FieldInfo + // or reflection gets confused (it basically gets confused, and variables override each + // other). + // + foreach (var member in host.Members) { + var field = member as Field; + if (field == null) + continue; + + var fi = tt.GetField (field.Name); + + Tuple old; + + // If a previous value was set, nullify it, so that we do + // not leak memory + if (fields.TryGetValue (field.Name, out old)) { + if (old.Item1.MemberType.IsStruct) { + // + // TODO: Clear fields for structs + // + } else { + try { + old.Item2.SetValue (null, null); + } catch { + } + } + } + + fields[field.Name] = Tuple.Create (field.Spec, fi); + } + + return (CompiledMethod) System.Delegate.CreateDelegate (typeof (CompiledMethod), mi); +#endif + } + + /// + /// A sentinel value used to indicate that no value was + /// was set by the compiled function. This is used to + /// differentiate between a function not returning a + /// value and null. + /// + internal static class QuitValue { } + + internal Tuple LookupField (string name) + { + Tuple fi; + fields.TryGetValue (name, out fi); + return fi; + } + + static string Quote (string s) + { + if (s.IndexOf ('"') != -1) + s = s.Replace ("\"", "\\\""); + + return "\"" + s + "\""; + } + + public string GetUsing () + { + StringBuilder sb = new StringBuilder (); + // TODO: + //foreach (object x in ns.using_alias_list) + // sb.AppendFormat ("using {0};\n", x); + + foreach (var ue in source_file.Usings) { + sb.AppendFormat ("using {0};", ue.ToString ()); + sb.Append (Environment.NewLine); + } + + return sb.ToString (); + } + + internal List GetUsingList () + { + var res = new List (); + + foreach (var ue in source_file.Usings) { + if (ue.Alias != null || ue.ResolvedExpression == null) + continue; + + res.Add (ue.NamespaceExpression.Name); + } + + return res; + } + + internal string [] GetVarNames () + { + lock (evaluator_lock){ + return new List (fields.Keys).ToArray (); + } + } + + public string GetVars () + { + lock (evaluator_lock){ + StringBuilder sb = new StringBuilder (); + + foreach (var de in fields){ + var fi = LookupField (de.Key); + object value; + try { + value = fi.Item2.GetValue (null); + if (value is string) + value = Quote ((string)value); + } catch { + value = ""; + } + + sb.AppendFormat ("{0} {1} = {2}", fi.Item1.MemberType.GetSignatureForError (), de.Key, value); + sb.AppendLine (); + } + + return sb.ToString (); + } + } + + /// + /// Loads the given assembly and exposes the API to the user. + /// + public void LoadAssembly (string file) + { + var loader = new DynamicLoader (importer, ctx); + var assembly = loader.LoadAssemblyFile (file, false); + if (assembly == null) + return; + + lock (evaluator_lock){ + importer.ImportAssembly (assembly, module.GlobalRootNamespace); + } + } + + /// + /// Exposes the API of the given assembly to the Evaluator + /// + public void ReferenceAssembly (Assembly a) + { + lock (evaluator_lock){ + importer.ImportAssembly (a, module.GlobalRootNamespace); + } + } + } + + + /// + /// A delegate that can be used to invoke the + /// compiled expression or statement. + /// + /// + /// Since the Compile methods will compile + /// statements and expressions into the same + /// delegate, you can tell if a value was returned + /// by checking whether the returned value is of type + /// NoValueSet. + /// + + public delegate void CompiledMethod (ref object retvalue); + + /// + /// The default base class for every interaction line + /// + /// + /// The expressions and statements behave as if they were + /// a static method of this class. The InteractiveBase class + /// contains a number of useful methods, but can be overwritten + /// by setting the InteractiveBaseType property in the Evaluator + /// + public class InteractiveBase { + /// + /// Determines where the standard output of methods in this class will go. + /// + public static TextWriter Output = Console.Out; + + /// + /// Determines where the standard error of methods in this class will go. + /// + public static TextWriter Error = Console.Error; + + /// + /// The primary prompt used for interactive use. + /// + public static string Prompt = "csharp> "; + + /// + /// The secondary prompt used for interactive use (used when + /// an expression is incomplete). + /// + public static string ContinuationPrompt = " > "; + + /// + /// Used to signal that the user has invoked the `quit' statement. + /// + public static bool QuitRequested; + + public static Evaluator Evaluator; + + /// + /// Shows all the variables defined so far. + /// + static public void ShowVars () + { + Output.Write (Evaluator.GetVars ()); + Output.Flush (); + } + + /// + /// Displays the using statements in effect at this point. + /// + static public void ShowUsing () + { + Output.Write (Evaluator.GetUsing ()); + Output.Flush (); + } + + /// + /// Times the execution of the given delegate + /// + static public TimeSpan Time (Action a) + { + DateTime start = DateTime.Now; + a (); + return DateTime.Now - start; + } + + /// + /// Loads the assemblies from a package + /// + /// + /// Loads the assemblies from a package. This is equivalent + /// to passing the -pkg: command line flag to the C# compiler + /// on the command line. + /// + static public void LoadPackage (string pkg) + { + if (pkg == null){ + Error.WriteLine ("Invalid package specified"); + return; + } + + string pkgout = Driver.GetPackageFlags (pkg, null); + + string [] xargs = pkgout.Trim (new Char [] {' ', '\n', '\r', '\t'}). + Split (new Char [] { ' ', '\t'}); + + foreach (string s in xargs){ + if (s.StartsWith ("-r:") || s.StartsWith ("/r:") || s.StartsWith ("/reference:")){ + string lib = s.Substring (s.IndexOf (':')+1); + + Evaluator.LoadAssembly (lib); + continue; + } + } + } + + /// + /// Loads the assembly + /// + /// + /// Loads the specified assembly and makes its types + /// available to the evaluator. This is equivalent + /// to passing the -pkg: command line flag to the C# + /// compiler on the command line. + /// + static public void LoadAssembly (string assembly) + { + Evaluator.LoadAssembly (assembly); + } + + static public void print (object obj) + { + Output.WriteLine (obj); + } + + static public void print (string fmt, params object [] args) + { + Output.WriteLine (fmt, args); + } + + /// + /// Returns a list of available static methods. + /// + static public string help { + get { + return "Static methods:\n" + + " Describe (object); - Describes the object's type\n" + + " LoadPackage (package); - Loads the given Package (like -pkg:FILE)\n" + + " LoadAssembly (assembly); - Loads the given assembly (like -r:ASSEMBLY)\n" + + " ShowVars (); - Shows defined local variables.\n" + + " ShowUsing (); - Show active using declarations.\n" + + " Prompt - The prompt used by the C# shell\n" + + " ContinuationPrompt - The prompt for partial input\n" + + " Time (() => { }); - Times the specified code\n" + + " print (obj); - Shorthand for Console.WriteLine\n" + + " quit; - You'll never believe it - this quits the repl!\n" + + " help; - This help text\n"; + } + } + + /// + /// Indicates to the read-eval-print-loop that the interaction should be finished. + /// + static public object quit { + get { + QuitRequested = true; + + // To avoid print null at the exit + return typeof (Evaluator.QuitValue); + } + } + + /// + /// Same as quit - useful in script scenerios + /// + static public void Quit () { + QuitRequested = true; + } + +#if !NET_2_1 + /// + /// Describes an object or a type. + /// + /// + /// This method will show a textual representation + /// of the object's type. If the object is a + /// System.Type it renders the type directly, + /// otherwise it renders the type returned by + /// invoking GetType on the object. + /// + static public string Describe (object x) + { + if (x == null) + return ""; + + var type = x as Type ?? x.GetType (); + + StringWriter sw = new StringWriter (); + new Outline (type, sw, true, false, false).OutlineType (); + return sw.ToString (); + } +#endif + } + + class InteractiveMethod : Method + { + public InteractiveMethod(TypeDefinition parent, FullNamedExpression returnType, Modifiers mod, ParametersCompiled parameters) + : base(parent, returnType, mod, new MemberName("Host"), parameters, null) + { + } + + public void ChangeToAsync () + { + ModFlags |= Modifiers.ASYNC; + ModFlags &= ~Modifiers.UNSAFE; + type_expr = new TypeExpression(Module.PredefinedTypes.Task.TypeSpec, Location); + parameters = ParametersCompiled.EmptyReadOnlyParameters; + } + + public override string GetSignatureForError() + { + return "InteractiveHost"; + } + } + + class HoistedEvaluatorVariable : HoistedVariable + { + public HoistedEvaluatorVariable (Field field) + : base (null, field) + { + } + + protected override FieldExpr GetFieldExpression (EmitContext ec) + { + return new FieldExpr (field, field.Location); + } + } + + /// + /// A class used to assign values if the source expression is not void + /// + /// Used by the interactive shell to allow it to call this code to set + /// the return value for an invocation. + /// + class OptionalAssign : SimpleAssign { + public OptionalAssign (Expression s, Location loc) + : base (null, s, loc) + { + } + + public override Location StartLocation { + get { + return Location.Null; + } + } + + protected override Expression DoResolve (ResolveContext ec) + { + Expression clone = source.Clone (new CloneContext ()); + + clone = clone.Resolve (ec); + if (clone == null) + return null; + + // + // A useful feature for the REPL: if we can resolve the expression + // as a type, Describe the type; + // + if (ec.Module.Evaluator.DescribeTypeExpressions && !(ec.CurrentAnonymousMethod is AsyncInitializer)) { + var old_printer = ec.Report.SetPrinter (new SessionReportPrinter ()); + Expression tclone; + try { + // Note: clone context cannot be shared otherwise block mapping would leak + tclone = source.Clone (new CloneContext ()); + tclone = tclone.Resolve (ec, ResolveFlags.Type); + if (ec.Report.Errors > 0) + tclone = null; + } finally { + ec.Report.SetPrinter (old_printer); + } + + if (tclone is TypeExpr) { + Arguments args = new Arguments (1); + args.Add (new Argument (new TypeOf ((TypeExpr) clone, Location))); + return new Invocation (new SimpleName ("Describe", Location), args).Resolve (ec); + } + } + + // This means its really a statement. + if (clone.Type.Kind == MemberKind.Void || clone is DynamicInvocation || clone is Assign) { + return clone; + } + + source = clone; + + var host = (Method) ec.MemberContext.CurrentMemberDefinition; + + if (host.ParameterInfo.IsEmpty) { + eclass = ExprClass.Value; + type = InternalType.FakeInternalType; + return this; + } + + target = new SimpleName (host.ParameterInfo[0].Name, Location); + + return base.DoResolve (ec); + } + + public override void EmitStatement(EmitContext ec) + { + if (target == null) { + source.Emit (ec); + return; + } + + base.EmitStatement(ec); + } + } + + public class Undo + { + List undo_actions; + + public void AddTypeContainer (TypeContainer current_container, TypeDefinition tc) + { + if (current_container == tc){ + Console.Error.WriteLine ("Internal error: inserting container into itself"); + return; + } + + if (undo_actions == null) + undo_actions = new List (); + + if (current_container.Containers != null) + { + var existing = current_container.Containers.FirstOrDefault (l => l.Basename == tc.Basename); + if (existing != null) { + current_container.RemoveContainer (existing); + undo_actions.Add (() => current_container.AddTypeContainer (existing)); + } + } + + undo_actions.Add (() => current_container.RemoveContainer (tc)); + } + + public void ExecuteUndo () + { + if (undo_actions == null) + return; + + foreach (var p in undo_actions){ + p (); + } + + undo_actions = null; + } + } + + static class ListenerProxy + { + static readonly Dictionary listeners = new Dictionary (); + + static int counter; + + public static int Register (ValueModificationHandler listener) + { + lock (listeners) { + var id = counter++; + listeners.Add (id, listener); + return id; + } + } + + public static void Unregister (int listenerId) + { + lock (listeners) { + listeners.Remove (listenerId); + } + } + + public static void ValueChanged (object value, int row, int col, string name, int listenerId) + { + ValueModificationHandler action; + lock (listeners) { + if (!listeners.TryGetValue (listenerId, out action)) + return; + } + + action (name, row, col, value); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/expression.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/expression.cs new file mode 100644 index 000000000..3a076077d --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/expression.cs @@ -0,0 +1,11921 @@ +// +// expression.cs: Expression representation for the IL tree. +// +// Author: +// Miguel de Icaza (miguel@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// Copyright 2001, 2002, 2003 Ximian, Inc. +// Copyright 2003-2008 Novell, Inc. +// Copyright 2011 Xamarin Inc. +// + +using System; +using System.Collections.Generic; +using System.Linq; +using SLE = System.Linq.Expressions; + +#if STATIC +using MetaType = IKVM.Reflection.Type; +using IKVM.Reflection; +using IKVM.Reflection.Emit; +#else +using MetaType = System.Type; +using System.Reflection; +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp +{ + // + // This is an user operator expression, automatically created during + // resolve phase + // + public class UserOperatorCall : Expression { + protected readonly Arguments arguments; + protected readonly MethodSpec oper; + readonly Func expr_tree; + + public UserOperatorCall (MethodSpec oper, Arguments args, Func expr_tree, Location loc) + { + this.oper = oper; + this.arguments = args; + this.expr_tree = expr_tree; + + type = oper.ReturnType; + eclass = ExprClass.Value; + this.loc = loc; + } + + public override bool ContainsEmitWithAwait () + { + return arguments.ContainsEmitWithAwait (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + if (expr_tree != null) + return expr_tree (ec, new TypeOfMethod (oper, loc)); + + Arguments args = Arguments.CreateForExpressionTree (ec, arguments, + new NullLiteral (loc), + new TypeOfMethod (oper, loc)); + + return CreateExpressionFactoryCall (ec, "Call", args); + } + + protected override void CloneTo (CloneContext context, Expression target) + { + // Nothing to clone + } + + protected override Expression DoResolve (ResolveContext ec) + { + // + // We are born fully resolved + // + return this; + } + + public override void Emit (EmitContext ec) + { + var call = new CallEmitter (); + call.Emit (ec, oper, arguments, loc); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + arguments.FlowAnalysis (fc); + } + + public override SLE.Expression MakeExpression (BuilderContext ctx) + { +#if STATIC + return base.MakeExpression (ctx); +#else + return SLE.Expression.Call ((MethodInfo) oper.GetMetaInfo (), Arguments.MakeExpression (arguments, ctx)); +#endif + } + } + + public class ParenthesizedExpression : ShimExpression + { + public ParenthesizedExpression (Expression expr, Location loc) + : base (expr) + { + this.loc = loc; + } + + protected override Expression DoResolve (ResolveContext ec) + { + var res = expr.Resolve (ec); + var constant = res as Constant; + if (constant != null && constant.IsLiteral) + return Constant.CreateConstantFromValue (res.Type, constant.GetValue (), expr.Location); + + return res; + } + + public override Expression DoResolveLValue (ResolveContext ec, Expression right_side) + { + return expr.DoResolveLValue (ec, right_side); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + // + // Unary implements unary expressions. + // + public class Unary : Expression + { + public enum Operator : byte { + UnaryPlus, UnaryNegation, LogicalNot, OnesComplement, + AddressOf, TOP + } + + public readonly Operator Oper; + public Expression Expr; + ConvCast.Mode enum_conversion; + + public Unary (Operator op, Expression expr, Location loc) + { + Oper = op; + Expr = expr; + this.loc = loc; + } + + // + // This routine will attempt to simplify the unary expression when the + // argument is a constant. + // + Constant TryReduceConstant (ResolveContext ec, Constant constant) + { + var e = constant; + + while (e is EmptyConstantCast) + e = ((EmptyConstantCast) e).child; + + if (e is SideEffectConstant) { + Constant r = TryReduceConstant (ec, ((SideEffectConstant) e).value); + return r == null ? null : new SideEffectConstant (r, e, r.Location); + } + + TypeSpec expr_type = e.Type; + + switch (Oper){ + case Operator.UnaryPlus: + // Unary numeric promotions + switch (expr_type.BuiltinType) { + case BuiltinTypeSpec.Type.Byte: + return new IntConstant (ec.BuiltinTypes, ((ByteConstant) e).Value, e.Location); + case BuiltinTypeSpec.Type.SByte: + return new IntConstant (ec.BuiltinTypes, ((SByteConstant) e).Value, e.Location); + case BuiltinTypeSpec.Type.Short: + return new IntConstant (ec.BuiltinTypes, ((ShortConstant) e).Value, e.Location); + case BuiltinTypeSpec.Type.UShort: + return new IntConstant (ec.BuiltinTypes, ((UShortConstant) e).Value, e.Location); + case BuiltinTypeSpec.Type.Char: + return new IntConstant (ec.BuiltinTypes, ((CharConstant) e).Value, e.Location); + + // Predefined operators + case BuiltinTypeSpec.Type.Int: + case BuiltinTypeSpec.Type.UInt: + case BuiltinTypeSpec.Type.Long: + case BuiltinTypeSpec.Type.ULong: + case BuiltinTypeSpec.Type.Float: + case BuiltinTypeSpec.Type.Double: + case BuiltinTypeSpec.Type.Decimal: + return e; + } + + return null; + + case Operator.UnaryNegation: + // Unary numeric promotions + switch (expr_type.BuiltinType) { + case BuiltinTypeSpec.Type.Byte: + return new IntConstant (ec.BuiltinTypes, -((ByteConstant) e).Value, e.Location); + case BuiltinTypeSpec.Type.SByte: + return new IntConstant (ec.BuiltinTypes, -((SByteConstant) e).Value, e.Location); + case BuiltinTypeSpec.Type.Short: + return new IntConstant (ec.BuiltinTypes, -((ShortConstant) e).Value, e.Location); + case BuiltinTypeSpec.Type.UShort: + return new IntConstant (ec.BuiltinTypes, -((UShortConstant) e).Value, e.Location); + case BuiltinTypeSpec.Type.Char: + return new IntConstant (ec.BuiltinTypes, -((CharConstant) e).Value, e.Location); + + // Predefined operators + case BuiltinTypeSpec.Type.Int: + int ivalue = ((IntConstant) e).Value; + if (ivalue == int.MinValue) { + if (ec.ConstantCheckState) { + ConstantFold.Error_CompileTimeOverflow (ec, loc); + return null; + } + return e; + } + return new IntConstant (ec.BuiltinTypes, -ivalue, e.Location); + + case BuiltinTypeSpec.Type.Long: + long lvalue = ((LongConstant) e).Value; + if (lvalue == long.MinValue) { + if (ec.ConstantCheckState) { + ConstantFold.Error_CompileTimeOverflow (ec, loc); + return null; + } + return e; + } + return new LongConstant (ec.BuiltinTypes, -lvalue, e.Location); + + case BuiltinTypeSpec.Type.UInt: + UIntLiteral uil = constant as UIntLiteral; + if (uil != null) { + if (uil.Value == int.MaxValue + (uint) 1) + return new IntLiteral (ec.BuiltinTypes, int.MinValue, e.Location); + return new LongLiteral (ec.BuiltinTypes, -uil.Value, e.Location); + } + return new LongConstant (ec.BuiltinTypes, -((UIntConstant) e).Value, e.Location); + + + case BuiltinTypeSpec.Type.ULong: + ULongLiteral ull = constant as ULongLiteral; + if (ull != null && ull.Value == 9223372036854775808) + return new LongLiteral (ec.BuiltinTypes, long.MinValue, e.Location); + return null; + + case BuiltinTypeSpec.Type.Float: + FloatLiteral fl = constant as FloatLiteral; + // For better error reporting + if (fl != null) + return new FloatLiteral (ec.BuiltinTypes, -fl.Value, e.Location); + + return new FloatConstant (ec.BuiltinTypes, -((FloatConstant) e).Value, e.Location); + + case BuiltinTypeSpec.Type.Double: + DoubleLiteral dl = constant as DoubleLiteral; + // For better error reporting + if (dl != null) + return new DoubleLiteral (ec.BuiltinTypes, -dl.Value, e.Location); + + return new DoubleConstant (ec.BuiltinTypes, -((DoubleConstant) e).Value, e.Location); + + case BuiltinTypeSpec.Type.Decimal: + return new DecimalConstant (ec.BuiltinTypes, -((DecimalConstant) e).Value, e.Location); + } + + return null; + + case Operator.LogicalNot: + if (expr_type.BuiltinType != BuiltinTypeSpec.Type.Bool) + return null; + + bool b = (bool)e.GetValue (); + return new BoolConstant (ec.BuiltinTypes, !b, e.Location); + + case Operator.OnesComplement: + // Unary numeric promotions + switch (expr_type.BuiltinType) { + case BuiltinTypeSpec.Type.Byte: + return new IntConstant (ec.BuiltinTypes, ~((ByteConstant) e).Value, e.Location); + case BuiltinTypeSpec.Type.SByte: + return new IntConstant (ec.BuiltinTypes, ~((SByteConstant) e).Value, e.Location); + case BuiltinTypeSpec.Type.Short: + return new IntConstant (ec.BuiltinTypes, ~((ShortConstant) e).Value, e.Location); + case BuiltinTypeSpec.Type.UShort: + return new IntConstant (ec.BuiltinTypes, ~((UShortConstant) e).Value, e.Location); + case BuiltinTypeSpec.Type.Char: + return new IntConstant (ec.BuiltinTypes, ~((CharConstant) e).Value, e.Location); + + // Predefined operators + case BuiltinTypeSpec.Type.Int: + return new IntConstant (ec.BuiltinTypes, ~((IntConstant)e).Value, e.Location); + case BuiltinTypeSpec.Type.UInt: + return new UIntConstant (ec.BuiltinTypes, ~((UIntConstant) e).Value, e.Location); + case BuiltinTypeSpec.Type.Long: + return new LongConstant (ec.BuiltinTypes, ~((LongConstant) e).Value, e.Location); + case BuiltinTypeSpec.Type.ULong: + return new ULongConstant (ec.BuiltinTypes, ~((ULongConstant) e).Value, e.Location); + } + if (e is EnumConstant) { + var res = TryReduceConstant (ec, ((EnumConstant)e).Child); + if (res != null) { + // + // Numeric promotion upgraded types to int but for enum constant + // original underlying constant type is needed + // + if (res.Type.BuiltinType == BuiltinTypeSpec.Type.Int) { + int v = ((IntConstant) res).Value; + switch (((EnumConstant) e).Child.Type.BuiltinType) { + case BuiltinTypeSpec.Type.UShort: + res = new UShortConstant (ec.BuiltinTypes, (ushort) v, e.Location); + break; + case BuiltinTypeSpec.Type.Short: + res = new ShortConstant (ec.BuiltinTypes, (short) v, e.Location); + break; + case BuiltinTypeSpec.Type.Byte: + res = new ByteConstant (ec.BuiltinTypes, (byte) v, e.Location); + break; + case BuiltinTypeSpec.Type.SByte: + res = new SByteConstant (ec.BuiltinTypes, (sbyte) v, e.Location); + break; + } + } + + res = new EnumConstant (res, expr_type); + } + return res; + } + return null; + } + throw new Exception ("Can not constant fold: " + Oper.ToString()); + } + + protected virtual Expression ResolveOperator (ResolveContext ec, Expression expr) + { + eclass = ExprClass.Value; + + TypeSpec expr_type = expr.Type; + Expression best_expr; + + TypeSpec[] predefined = ec.BuiltinTypes.OperatorsUnary [(int) Oper]; + + // + // Primitive types first + // + if (BuiltinTypeSpec.IsPrimitiveType (expr_type)) { + best_expr = ResolvePrimitivePredefinedType (ec, expr, predefined); + if (best_expr == null) + return null; + + type = best_expr.Type; + Expr = best_expr; + return this; + } + + // + // E operator ~(E x); + // + if (Oper == Operator.OnesComplement && expr_type.IsEnum) + return ResolveEnumOperator (ec, expr, predefined); + + return ResolveUserType (ec, expr, predefined); + } + + protected virtual Expression ResolveEnumOperator (ResolveContext ec, Expression expr, TypeSpec[] predefined) + { + TypeSpec underlying_type = EnumSpec.GetUnderlyingType (expr.Type); + Expression best_expr = ResolvePrimitivePredefinedType (ec, EmptyCast.Create (expr, underlying_type), predefined); + if (best_expr == null) + return null; + + Expr = best_expr; + enum_conversion = Binary.GetEnumResultCast (underlying_type); + type = expr.Type; + return EmptyCast.Create (this, type); + } + + public override bool ContainsEmitWithAwait () + { + return Expr.ContainsEmitWithAwait (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + return CreateExpressionTree (ec, null); + } + + Expression CreateExpressionTree (ResolveContext ec, Expression user_op) + { + string method_name; + switch (Oper) { + case Operator.AddressOf: + Error_PointerInsideExpressionTree (ec); + return null; + case Operator.UnaryNegation: + if (ec.HasSet (ResolveContext.Options.CheckedScope) && user_op == null && !IsFloat (type)) + method_name = "NegateChecked"; + else + method_name = "Negate"; + break; + case Operator.OnesComplement: + case Operator.LogicalNot: + method_name = "Not"; + break; + case Operator.UnaryPlus: + method_name = "UnaryPlus"; + break; + default: + throw new InternalErrorException ("Unknown unary operator " + Oper.ToString ()); + } + + Arguments args = new Arguments (2); + args.Add (new Argument (Expr.CreateExpressionTree (ec))); + if (user_op != null) + args.Add (new Argument (user_op)); + + return CreateExpressionFactoryCall (ec, method_name, args); + } + + public static TypeSpec[][] CreatePredefinedOperatorsTable (BuiltinTypes types) + { + var predefined_operators = new TypeSpec[(int) Operator.TOP][]; + + // + // 7.6.1 Unary plus operator + // + predefined_operators [(int) Operator.UnaryPlus] = new TypeSpec [] { + types.Int, types.UInt, + types.Long, types.ULong, + types.Float, types.Double, + types.Decimal + }; + + // + // 7.6.2 Unary minus operator + // + predefined_operators [(int) Operator.UnaryNegation] = new TypeSpec [] { + types.Int, types.Long, + types.Float, types.Double, + types.Decimal + }; + + // + // 7.6.3 Logical negation operator + // + predefined_operators [(int) Operator.LogicalNot] = new TypeSpec [] { + types.Bool + }; + + // + // 7.6.4 Bitwise complement operator + // + predefined_operators [(int) Operator.OnesComplement] = new TypeSpec [] { + types.Int, types.UInt, + types.Long, types.ULong + }; + + return predefined_operators; + } + + // + // Unary numeric promotions + // + static Expression DoNumericPromotion (ResolveContext rc, Operator op, Expression expr) + { + TypeSpec expr_type = expr.Type; + if (op == Operator.UnaryPlus || op == Operator.UnaryNegation || op == Operator.OnesComplement) { + switch (expr_type.BuiltinType) { + case BuiltinTypeSpec.Type.Byte: + case BuiltinTypeSpec.Type.SByte: + case BuiltinTypeSpec.Type.Short: + case BuiltinTypeSpec.Type.UShort: + case BuiltinTypeSpec.Type.Char: + return Convert.ImplicitNumericConversion (expr, rc.BuiltinTypes.Int); + } + } + + if (op == Operator.UnaryNegation && expr_type.BuiltinType == BuiltinTypeSpec.Type.UInt) + return Convert.ImplicitNumericConversion (expr, rc.BuiltinTypes.Long); + + return expr; + } + + protected override Expression DoResolve (ResolveContext ec) + { + if (Oper == Operator.AddressOf) { + return ResolveAddressOf (ec); + } + + Expr = Expr.Resolve (ec); + if (Expr == null) + return null; + + if (Expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + Arguments args = new Arguments (1); + args.Add (new Argument (Expr)); + return new DynamicUnaryConversion (GetOperatorExpressionTypeName (), args, loc).Resolve (ec); + } + + if (Expr.Type.IsNullableType) + return new Nullable.LiftedUnaryOperator (Oper, Expr, loc).Resolve (ec); + + // + // Attempt to use a constant folding operation. + // + Constant cexpr = Expr as Constant; + if (cexpr != null) { + cexpr = TryReduceConstant (ec, cexpr); + if (cexpr != null) + return cexpr; + } + + Expression expr = ResolveOperator (ec, Expr); + if (expr == null) + Error_OperatorCannotBeApplied (ec, loc, OperName (Oper), Expr.Type); + + // + // Reduce unary operator on predefined types + // + if (expr == this && Oper == Operator.UnaryPlus) + return Expr; + + return expr; + } + + public override Expression DoResolveLValue (ResolveContext ec, Expression right) + { + return null; + } + + public override void Emit (EmitContext ec) + { + EmitOperator (ec, type); + } + + protected void EmitOperator (EmitContext ec, TypeSpec type) + { + switch (Oper) { + case Operator.UnaryPlus: + Expr.Emit (ec); + break; + + case Operator.UnaryNegation: + if (ec.HasSet (EmitContext.Options.CheckedScope) && !IsFloat (type)) { + if (ec.HasSet (BuilderContext.Options.AsyncBody) && Expr.ContainsEmitWithAwait ()) + Expr = Expr.EmitToField (ec); + + ec.EmitInt (0); + if (type.BuiltinType == BuiltinTypeSpec.Type.Long) + ec.Emit (OpCodes.Conv_U8); + Expr.Emit (ec); + ec.Emit (OpCodes.Sub_Ovf); + } else { + Expr.Emit (ec); + ec.Emit (OpCodes.Neg); + } + + break; + + case Operator.LogicalNot: + Expr.Emit (ec); + ec.EmitInt (0); + ec.Emit (OpCodes.Ceq); + break; + + case Operator.OnesComplement: + Expr.Emit (ec); + ec.Emit (OpCodes.Not); + break; + + case Operator.AddressOf: + ((IMemoryLocation)Expr).AddressOf (ec, AddressOp.LoadStore); + break; + + default: + throw new Exception ("This should not happen: Operator = " + + Oper.ToString ()); + } + + // + // Same trick as in Binary expression + // + if (enum_conversion != 0) { + using (ec.With (BuilderContext.Options.CheckedScope, false)) { + ConvCast.Emit (ec, enum_conversion); + } + } + } + + public override void EmitBranchable (EmitContext ec, Label target, bool on_true) + { + if (Oper == Operator.LogicalNot) + Expr.EmitBranchable (ec, target, !on_true); + else + base.EmitBranchable (ec, target, on_true); + } + + public override void EmitSideEffect (EmitContext ec) + { + Expr.EmitSideEffect (ec); + } + + public static void Error_Ambiguous (ResolveContext rc, string oper, TypeSpec type, Location loc) + { + rc.Report.Error (35, loc, "Operator `{0}' is ambiguous on an operand of type `{1}'", + oper, type.GetSignatureForError ()); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + FlowAnalysis (fc, false); + } + + public override void FlowAnalysisConditional (FlowAnalysisContext fc) + { + FlowAnalysis (fc, true); + } + + void FlowAnalysis (FlowAnalysisContext fc, bool conditional) + { + if (Oper == Operator.AddressOf) { + var vr = Expr as VariableReference; + if (vr != null && vr.VariableInfo != null) + fc.SetVariableAssigned (vr.VariableInfo); + + return; + } + + if (Oper == Operator.LogicalNot && conditional) { + Expr.FlowAnalysisConditional (fc); + + var temp = fc.DefiniteAssignmentOnTrue; + fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse; + fc.DefiniteAssignmentOnFalse = temp; + } else { + Expr.FlowAnalysis (fc); + } + } + + // + // Converts operator to System.Linq.Expressions.ExpressionType enum name + // + string GetOperatorExpressionTypeName () + { + switch (Oper) { + case Operator.OnesComplement: + return "OnesComplement"; + case Operator.LogicalNot: + return "Not"; + case Operator.UnaryNegation: + return "Negate"; + case Operator.UnaryPlus: + return "UnaryPlus"; + default: + throw new NotImplementedException ("Unknown express type operator " + Oper.ToString ()); + } + } + + static bool IsFloat (TypeSpec t) + { + return t.BuiltinType == BuiltinTypeSpec.Type.Double || t.BuiltinType == BuiltinTypeSpec.Type.Float; + } + + // + // Returns a stringified representation of the Operator + // + public static string OperName (Operator oper) + { + switch (oper) { + case Operator.UnaryPlus: + return "+"; + case Operator.UnaryNegation: + return "-"; + case Operator.LogicalNot: + return "!"; + case Operator.OnesComplement: + return "~"; + case Operator.AddressOf: + return "&"; + } + + throw new NotImplementedException (oper.ToString ()); + } + + public override SLE.Expression MakeExpression (BuilderContext ctx) + { + var expr = Expr.MakeExpression (ctx); + bool is_checked = ctx.HasSet (BuilderContext.Options.CheckedScope); + + switch (Oper) { + case Operator.UnaryNegation: + return is_checked ? SLE.Expression.NegateChecked (expr) : SLE.Expression.Negate (expr); + case Operator.LogicalNot: + return SLE.Expression.Not (expr); +#if NET_4_0 || MOBILE_DYNAMIC + case Operator.OnesComplement: + return SLE.Expression.OnesComplement (expr); +#endif + default: + throw new NotImplementedException (Oper.ToString ()); + } + } + + Expression ResolveAddressOf (ResolveContext ec) + { + if (!ec.IsUnsafe) + UnsafeError (ec, loc); + + Expr = Expr.DoResolveLValue (ec, EmptyExpression.UnaryAddress); + if (Expr == null || Expr.eclass != ExprClass.Variable) { + ec.Report.Error (211, loc, "Cannot take the address of the given expression"); + return null; + } + + if (!TypeManager.VerifyUnmanaged (ec.Module, Expr.Type, loc)) { + return null; + } + + IVariableReference vr = Expr as IVariableReference; + bool is_fixed; + if (vr != null) { + is_fixed = vr.IsFixed; + vr.SetHasAddressTaken (); + + if (vr.IsHoisted) { + AnonymousMethodExpression.Error_AddressOfCapturedVar (ec, vr, loc); + } + } else { + IFixedExpression fe = Expr as IFixedExpression; + is_fixed = fe != null && fe.IsFixed; + } + + if (!is_fixed && !ec.HasSet (ResolveContext.Options.FixedInitializerScope)) { + ec.Report.Error (212, loc, "You can only take the address of unfixed expression inside of a fixed statement initializer"); + } + + type = PointerContainer.MakeType (ec.Module, Expr.Type); + eclass = ExprClass.Value; + return this; + } + + Expression ResolvePrimitivePredefinedType (ResolveContext rc, Expression expr, TypeSpec[] predefined) + { + expr = DoNumericPromotion (rc, Oper, expr); + TypeSpec expr_type = expr.Type; + foreach (TypeSpec t in predefined) { + if (t == expr_type) + return expr; + } + return null; + } + + // + // Perform user-operator overload resolution + // + protected virtual Expression ResolveUserOperator (ResolveContext ec, Expression expr) + { + CSharp.Operator.OpType op_type; + switch (Oper) { + case Operator.LogicalNot: + op_type = CSharp.Operator.OpType.LogicalNot; break; + case Operator.OnesComplement: + op_type = CSharp.Operator.OpType.OnesComplement; break; + case Operator.UnaryNegation: + op_type = CSharp.Operator.OpType.UnaryNegation; break; + case Operator.UnaryPlus: + op_type = CSharp.Operator.OpType.UnaryPlus; break; + default: + throw new InternalErrorException (Oper.ToString ()); + } + + var methods = MemberCache.GetUserOperator (expr.Type, op_type, false); + if (methods == null) + return null; + + Arguments args = new Arguments (1); + args.Add (new Argument (expr)); + + var res = new OverloadResolver (methods, OverloadResolver.Restrictions.BaseMembersIncluded | OverloadResolver.Restrictions.NoBaseMembers, loc); + var oper = res.ResolveOperator (ec, ref args); + + if (oper == null) + return null; + + Expr = args [0].Expr; + return new UserOperatorCall (oper, args, CreateExpressionTree, expr.Location); + } + + // + // Unary user type overload resolution + // + Expression ResolveUserType (ResolveContext ec, Expression expr, TypeSpec[] predefined) + { + Expression best_expr = ResolveUserOperator (ec, expr); + if (best_expr != null) + return best_expr; + + foreach (TypeSpec t in predefined) { + Expression oper_expr = Convert.ImplicitUserConversion (ec, expr, t, expr.Location); + if (oper_expr == null) + continue; + + if (oper_expr == ErrorExpression.Instance) + return oper_expr; + + // + // decimal type is predefined but has user-operators + // + if (oper_expr.Type.BuiltinType == BuiltinTypeSpec.Type.Decimal) + oper_expr = ResolveUserType (ec, oper_expr, predefined); + else + oper_expr = ResolvePrimitivePredefinedType (ec, oper_expr, predefined); + + if (oper_expr == null) + continue; + + if (best_expr == null) { + best_expr = oper_expr; + continue; + } + + int result = OverloadResolver.BetterTypeConversion (ec, best_expr.Type, t); + if (result == 0) { + if ((oper_expr is UserOperatorCall || oper_expr is UserCast) && (best_expr is UserOperatorCall || best_expr is UserCast)) { + Error_Ambiguous (ec, OperName (Oper), expr.Type, loc); + } else { + Error_OperatorCannotBeApplied (ec, loc, OperName (Oper), expr.Type); + } + + break; + } + + if (result == 2) + best_expr = oper_expr; + } + + if (best_expr == null) + return null; + + // + // HACK: Decimal user-operator is included in standard operators + // + if (best_expr.Type.BuiltinType == BuiltinTypeSpec.Type.Decimal) + return best_expr; + + Expr = best_expr; + type = best_expr.Type; + return this; + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + Unary target = (Unary) t; + + target.Expr = Expr.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + + } + + // + // Unary operators are turned into Indirection expressions + // after semantic analysis (this is so we can take the address + // of an indirection). + // + public class Indirection : Expression, IMemoryLocation, IAssignMethod, IFixedExpression { + Expression expr; + LocalTemporary temporary; + bool prepared; + + public Indirection (Expression expr, Location l) + { + this.expr = expr; + loc = l; + } + + public Expression Expr { + get { + return expr; + } + } + + public bool IsFixed { + get { return true; } + } + + public override Location StartLocation { + get { + return expr.StartLocation; + } + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + Indirection target = (Indirection) t; + target.expr = expr.Clone (clonectx); + } + + public override bool ContainsEmitWithAwait () + { + throw new NotImplementedException (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Error_PointerInsideExpressionTree (ec); + return null; + } + + public override void Emit (EmitContext ec) + { + if (!prepared) + expr.Emit (ec); + + ec.EmitLoadFromPtr (Type); + } + + public void Emit (EmitContext ec, bool leave_copy) + { + Emit (ec); + if (leave_copy) { + ec.Emit (OpCodes.Dup); + temporary = new LocalTemporary (expr.Type); + temporary.Store (ec); + } + } + + public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound) + { + prepared = isCompound; + + expr.Emit (ec); + + if (isCompound) + ec.Emit (OpCodes.Dup); + + source.Emit (ec); + if (leave_copy) { + ec.Emit (OpCodes.Dup); + temporary = new LocalTemporary (source.Type); + temporary.Store (ec); + } + + ec.EmitStoreFromPtr (type); + + if (temporary != null) { + temporary.Emit (ec); + temporary.Release (ec); + } + } + + public void AddressOf (EmitContext ec, AddressOp Mode) + { + expr.Emit (ec); + } + + public override Expression DoResolveLValue (ResolveContext ec, Expression right_side) + { + return DoResolve (ec); + } + + protected override Expression DoResolve (ResolveContext ec) + { + expr = expr.Resolve (ec); + if (expr == null) + return null; + + if (!ec.IsUnsafe) + UnsafeError (ec, loc); + + var pc = expr.Type as PointerContainer; + + if (pc == null) { + ec.Report.Error (193, loc, "The * or -> operator must be applied to a pointer"); + return null; + } + + type = pc.Element; + + if (type.Kind == MemberKind.Void) { + Error_VoidPointerOperation (ec); + return null; + } + + eclass = ExprClass.Variable; + return this; + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + /// + /// Unary Mutator expressions (pre and post ++ and --) + /// + /// + /// + /// UnaryMutator implements ++ and -- expressions. It derives from + /// ExpressionStatement becuase the pre/post increment/decrement + /// operators can be used in a statement context. + /// + /// FIXME: Idea, we could split this up in two classes, one simpler + /// for the common case, and one with the extra fields for more complex + /// classes (indexers require temporary access; overloaded require method) + /// + /// + public class UnaryMutator : ExpressionStatement + { + class DynamicPostMutator : Expression, IAssignMethod + { + LocalTemporary temp; + Expression expr; + + public DynamicPostMutator (Expression expr) + { + this.expr = expr; + this.type = expr.Type; + this.loc = expr.Location; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + throw new NotImplementedException ("ET"); + } + + protected override Expression DoResolve (ResolveContext rc) + { + eclass = expr.eclass; + return this; + } + + public override Expression DoResolveLValue (ResolveContext ec, Expression right_side) + { + expr.DoResolveLValue (ec, right_side); + return DoResolve (ec); + } + + public override void Emit (EmitContext ec) + { + temp.Emit (ec); + } + + public void Emit (EmitContext ec, bool leave_copy) + { + throw new NotImplementedException (); + } + + // + // Emits target assignment using unmodified source value + // + public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound) + { + // + // Allocate temporary variable to keep original value before it's modified + // + temp = new LocalTemporary (type); + expr.Emit (ec); + temp.Store (ec); + + ((IAssignMethod) expr).EmitAssign (ec, source, false, isCompound); + + if (leave_copy) + Emit (ec); + + temp.Release (ec); + temp = null; + } + } + + [Flags] + public enum Mode : byte { + IsIncrement = 0, + IsDecrement = 1, + IsPre = 0, + IsPost = 2, + + PreIncrement = 0, + PreDecrement = IsDecrement, + PostIncrement = IsPost, + PostDecrement = IsPost | IsDecrement + } + + Mode mode; + bool is_expr, recurse; + + protected Expression expr; + + // Holds the real operation + Expression operation; + + public UnaryMutator (Mode m, Expression e, Location loc) + { + mode = m; + this.loc = loc; + expr = e; + } + + public Mode UnaryMutatorMode { + get { + return mode; + } + } + + public Expression Expr { + get { + return expr; + } + } + + public override Location StartLocation { + get { + return (mode & Mode.IsPost) != 0 ? expr.Location : loc; + } + } + + public override bool ContainsEmitWithAwait () + { + return expr.ContainsEmitWithAwait (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + return new SimpleAssign (this, this).CreateExpressionTree (ec); + } + + public static TypeSpec[] CreatePredefinedOperatorsTable (BuiltinTypes types) + { + // + // Predefined ++ and -- operators exist for the following types: + // sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal + // + return new TypeSpec[] { + types.Int, + types.Long, + + types.SByte, + types.Byte, + types.Short, + types.UInt, + types.ULong, + types.Char, + types.Float, + types.Double, + types.Decimal + }; + } + + protected override Expression DoResolve (ResolveContext ec) + { + expr = expr.Resolve (ec); + + if (expr == null || expr.Type == InternalType.ErrorType) + return null; + + if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + // + // Handle postfix unary operators using local + // temporary variable + // + if ((mode & Mode.IsPost) != 0) + expr = new DynamicPostMutator (expr); + + Arguments args = new Arguments (1); + args.Add (new Argument (expr)); + return new SimpleAssign (expr, new DynamicUnaryConversion (GetOperatorExpressionTypeName (), args, loc)).Resolve (ec); + } + + if (expr.Type.IsNullableType) + return new Nullable.LiftedUnaryMutator (mode, expr, loc).Resolve (ec); + + return DoResolveOperation (ec); + } + + protected Expression DoResolveOperation (ResolveContext ec) + { + eclass = ExprClass.Value; + type = expr.Type; + + if (expr is RuntimeValueExpression) { + operation = expr; + } else { + // Use itself at the top of the stack + operation = new EmptyExpression (type); + } + + // + // The operand of the prefix/postfix increment decrement operators + // should be an expression that is classified as a variable, + // a property access or an indexer access + // + // TODO: Move to parser, expr is ATypeNameExpression + if (expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.IndexerAccess || expr.eclass == ExprClass.PropertyAccess) { + expr = expr.ResolveLValue (ec, expr); + } else { + ec.Report.Error (1059, loc, "The operand of an increment or decrement operator must be a variable, property or indexer"); + } + + // + // Step 1: Try to find a user operator, it has priority over predefined ones + // + var user_op = IsDecrement ? Operator.OpType.Decrement : Operator.OpType.Increment; + var methods = MemberCache.GetUserOperator (type, user_op, false); + + if (methods != null) { + Arguments args = new Arguments (1); + args.Add (new Argument (expr)); + + var res = new OverloadResolver (methods, OverloadResolver.Restrictions.BaseMembersIncluded | OverloadResolver.Restrictions.NoBaseMembers, loc); + var method = res.ResolveOperator (ec, ref args); + if (method == null) + return null; + + args[0].Expr = operation; + operation = new UserOperatorCall (method, args, null, loc); + operation = Convert.ImplicitConversionRequired (ec, operation, type, loc); + return this; + } + + // + // Step 2: Try predefined types + // + + Expression source = null; + bool primitive_type; + + // + // Predefined without user conversion first for speed-up + // + // Predefined ++ and -- operators exist for the following types: + // sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal + // + switch (type.BuiltinType) { + case BuiltinTypeSpec.Type.Byte: + case BuiltinTypeSpec.Type.SByte: + case BuiltinTypeSpec.Type.Short: + case BuiltinTypeSpec.Type.UShort: + case BuiltinTypeSpec.Type.Int: + case BuiltinTypeSpec.Type.UInt: + case BuiltinTypeSpec.Type.Long: + case BuiltinTypeSpec.Type.ULong: + case BuiltinTypeSpec.Type.Char: + case BuiltinTypeSpec.Type.Float: + case BuiltinTypeSpec.Type.Double: + case BuiltinTypeSpec.Type.Decimal: + source = operation; + primitive_type = true; + break; + default: + primitive_type = false; + + // ++/-- on pointer variables of all types except void* + if (type.IsPointer) { + if (((PointerContainer) type).Element.Kind == MemberKind.Void) { + Error_VoidPointerOperation (ec); + return null; + } + + source = operation; + } else { + Expression best_source = null; + foreach (var t in ec.BuiltinTypes.OperatorsUnaryMutator) { + source = Convert.ImplicitUserConversion (ec, operation, t, loc); + + // LAMESPEC: It should error on ambiguous operators but that would make us incompatible + if (source == null) + continue; + + if (best_source == null) { + best_source = source; + continue; + } + + var better = OverloadResolver.BetterTypeConversion (ec, best_source.Type, source.Type); + if (better == 1) + continue; + + if (better == 2) { + best_source = source; + continue; + } + + Unary.Error_Ambiguous (ec, OperName (mode), type, loc); + break; + } + + source = best_source; + } + + // ++/-- on enum types + if (source == null && type.IsEnum) + source = operation; + + if (source == null) { + expr.Error_OperatorCannotBeApplied (ec, loc, Operator.GetName (user_op), type); + return null; + } + + break; + } + + var one = new IntConstant (ec.BuiltinTypes, 1, loc); + var op = IsDecrement ? Binary.Operator.Subtraction : Binary.Operator.Addition; + operation = new Binary (op, source, one); + operation = operation.Resolve (ec); + if (operation == null) + throw new NotImplementedException ("should not be reached"); + + if (operation.Type != type) { + if (primitive_type) + operation = Convert.ExplicitNumericConversion (ec, operation, type); + else + operation = Convert.ImplicitConversionRequired (ec, operation, type, loc); + } + + return this; + } + + void EmitCode (EmitContext ec, bool is_expr) + { + recurse = true; + this.is_expr = is_expr; + ((IAssignMethod) expr).EmitAssign (ec, this, is_expr && (mode == Mode.PreIncrement || mode == Mode.PreDecrement), true); + } + + public override void Emit (EmitContext ec) + { + // + // We use recurse to allow ourselfs to be the source + // of an assignment. This little hack prevents us from + // having to allocate another expression + // + if (recurse) { + ((IAssignMethod) expr).Emit (ec, is_expr && (mode == Mode.PostIncrement || mode == Mode.PostDecrement)); + + EmitOperation (ec); + + recurse = false; + return; + } + + EmitCode (ec, true); + } + + protected virtual void EmitOperation (EmitContext ec) + { + operation.Emit (ec); + } + + public override void EmitStatement (EmitContext ec) + { + EmitCode (ec, false); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + expr.FlowAnalysis (fc); + } + + // + // Converts operator to System.Linq.Expressions.ExpressionType enum name + // + string GetOperatorExpressionTypeName () + { + return IsDecrement ? "Decrement" : "Increment"; + } + + bool IsDecrement { + get { return (mode & Mode.IsDecrement) != 0; } + } + + +#if NET_4_0 || MOBILE_DYNAMIC + public override SLE.Expression MakeExpression (BuilderContext ctx) + { + var target = ((RuntimeValueExpression) expr).MetaObject.Expression; + var source = SLE.Expression.Convert (operation.MakeExpression (ctx), target.Type); + return SLE.Expression.Assign (target, source); + } +#endif + + public static string OperName (Mode oper) + { + return (oper & Mode.IsDecrement) != 0 ? "--" : "++"; + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + UnaryMutator target = (UnaryMutator) t; + + target.expr = expr.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + + } + + // + // Base class for the `is' and `as' operators + // + public abstract class Probe : Expression + { + public Expression ProbeType; + protected Expression expr; + protected TypeSpec probe_type_expr; + + protected Probe (Expression expr, Expression probe_type, Location l) + { + ProbeType = probe_type; + loc = l; + this.expr = expr; + } + + public Expression Expr { + get { + return expr; + } + } + + public override bool ContainsEmitWithAwait () + { + return expr.ContainsEmitWithAwait (); + } + + protected Expression ResolveCommon (ResolveContext rc) + { + expr = expr.Resolve (rc); + if (expr == null) + return null; + + ResolveProbeType (rc); + if (probe_type_expr == null) + return this; + + if (probe_type_expr.IsStatic) { + rc.Report.Error (7023, loc, "The second operand of `is' or `as' operator cannot be static type `{0}'", + probe_type_expr.GetSignatureForError ()); + return null; + } + + if (expr.Type.IsPointer || probe_type_expr.IsPointer) { + rc.Report.Error (244, loc, "The `{0}' operator cannot be applied to an operand of pointer type", + OperatorName); + return null; + } + + if (expr.Type == InternalType.AnonymousMethod || expr.Type == InternalType.MethodGroup) { + rc.Report.Error (837, loc, "The `{0}' operator cannot be applied to a lambda expression, anonymous method, or method group", + OperatorName); + return null; + } + + return this; + } + + protected virtual void ResolveProbeType (ResolveContext rc) + { + probe_type_expr = ProbeType.ResolveAsType (rc); + } + + public override void EmitSideEffect (EmitContext ec) + { + expr.EmitSideEffect (ec); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + expr.FlowAnalysis (fc); + } + + protected abstract string OperatorName { get; } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + Probe target = (Probe) t; + + target.expr = expr.Clone (clonectx); + target.ProbeType = ProbeType.Clone (clonectx); + } + + } + + /// + /// Implementation of the `is' operator. + /// + public class Is : Probe + { + Nullable.Unwrap expr_unwrap; + MethodSpec number_mg; + Arguments number_args; + + public Is (Expression expr, Expression probe_type, Location l) + : base (expr, probe_type, l) + { + } + + protected override string OperatorName { + get { return "is"; } + } + + public LocalVariable Variable { get; set; } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + if (Variable != null) + throw new NotSupportedException (); + + Arguments args = Arguments.CreateForExpressionTree (ec, null, + expr.CreateExpressionTree (ec), + new TypeOf (probe_type_expr, loc)); + + return CreateExpressionFactoryCall (ec, "TypeIs", args); + } + + Expression CreateConstantResult (ResolveContext rc, bool result) + { + if (result) + rc.Report.Warning (183, 1, loc, "The given expression is always of the provided (`{0}') type", + probe_type_expr.GetSignatureForError ()); + else + rc.Report.Warning (184, 1, loc, "The given expression is never of the provided (`{0}') type", + probe_type_expr.GetSignatureForError ()); + + var c = new BoolConstant (rc.BuiltinTypes, result, loc); + return expr.IsSideEffectFree ? + ReducedExpression.Create (c, this) : + new SideEffectConstant (c, this, loc); + } + + public override void Emit (EmitContext ec) + { + if (probe_type_expr == null) { + EmitConstantMatch (ec); + return; + } + + EmitLoad (ec); + + if (expr_unwrap == null) { + ec.EmitNull (); + ec.Emit (OpCodes.Cgt_Un); + } + } + + public override void EmitBranchable (EmitContext ec, Label target, bool on_true) + { + if (probe_type_expr == null) { + EmitConstantMatch (ec); + } else { + EmitLoad (ec); + } + + ec.Emit (on_true ? OpCodes.Brtrue : OpCodes.Brfalse, target); + } + + void EmitConstantMatch (EmitContext ec) + { + var no_match = ec.DefineLabel (); + var end = ec.DefineLabel (); + + if (expr_unwrap != null) { + expr_unwrap.EmitCheck (ec); + + if (ProbeType.IsNull) { + ec.EmitInt (0); + ec.Emit (OpCodes.Ceq); + return; + } + + ec.Emit (OpCodes.Brfalse_S, no_match); + expr_unwrap.Emit (ec); + ProbeType.Emit (ec); + ec.Emit (OpCodes.Ceq); + ec.Emit (OpCodes.Br_S, end); + ec.MarkLabel (no_match); + ec.EmitInt (0); + ec.MarkLabel (end); + return; + } + + if (number_args != null && number_args.Count == 3) { + var ce = new CallEmitter (); + ce.Emit (ec, number_mg, number_args, loc); + return; + } + + Expr.Emit (ec); + ec.Emit (OpCodes.Isinst, ProbeType.Type); + ec.Emit (OpCodes.Dup); + ec.Emit (OpCodes.Brfalse, no_match); + + if (number_mg != null) { + var ce = new CallEmitter (); + ce.Emit (ec, number_mg, number_args, loc); + } else { + ProbeType.Emit (ec); + ec.Emit (OpCodes.Ceq); + } + ec.Emit (OpCodes.Br_S, end); + ec.MarkLabel (no_match); + + ec.Emit (OpCodes.Pop); + ec.EmitInt (0); + ec.MarkLabel (end); + } + + void EmitLoad (EmitContext ec) + { + Label no_value_label = new Label (); + + if (expr_unwrap != null) { + expr_unwrap.EmitCheck (ec); + + if (Variable == null) + return; + + ec.Emit (OpCodes.Dup); + no_value_label = ec.DefineLabel (); + ec.Emit (OpCodes.Brfalse_S, no_value_label); + expr_unwrap.Emit (ec); + } else { + expr.Emit (ec); + + // Only to make verifier happy + if (probe_type_expr.IsGenericParameter && TypeSpec.IsValueType (expr.Type)) + ec.Emit (OpCodes.Box, expr.Type); + + ec.Emit (OpCodes.Isinst, probe_type_expr); + } + + if (Variable != null) { + bool value_on_stack; + if (probe_type_expr.IsGenericParameter || probe_type_expr.IsNullableType) { + ec.Emit (OpCodes.Dup); + ec.Emit (OpCodes.Unbox_Any, probe_type_expr); + value_on_stack = true; + } else { + value_on_stack = false; + } + + Variable.CreateBuilder (ec); + Variable.EmitAssign (ec); + + if (expr_unwrap != null) { + ec.MarkLabel (no_value_label); + } else if (!value_on_stack) { + Variable.Emit (ec); + } + } + } + + protected override Expression DoResolve (ResolveContext rc) + { + if (ResolveCommon (rc) == null) + return null; + + type = rc.BuiltinTypes.Bool; + eclass = ExprClass.Value; + + if (probe_type_expr == null) + return ResolveMatchingExpression (rc); + + var res = ResolveResultExpression (rc); + if (Variable != null) { + if (res is Constant) + throw new NotImplementedException ("constant in type pattern matching"); + + Variable.Type = probe_type_expr; + var bc = rc as BlockContext; + if (bc != null) + Variable.PrepareAssignmentAnalysis (bc); + } + + return res; + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + base.FlowAnalysis (fc); + + if (Variable != null) + fc.SetVariableAssigned (Variable.VariableInfo, true); + } + + protected override void ResolveProbeType (ResolveContext rc) + { + if (!(ProbeType is TypeExpr) && rc.Module.Compiler.Settings.Version == LanguageVersion.Experimental) { + ProbeType = ProbeType.Resolve (rc, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup | ResolveFlags.Type); + if (ProbeType == null) + return; + + if (ProbeType.eclass == ExprClass.Type) { + probe_type_expr = ProbeType.Type; + } + + return; + } + + base.ResolveProbeType (rc); + } + + Expression ResolveMatchingExpression (ResolveContext rc) + { + var mc = ProbeType as Constant; + if (mc != null) { + if (!Convert.ImplicitConversionExists (rc, ProbeType, Expr.Type)) { + ProbeType.Error_ValueCannotBeConverted (rc, Expr.Type, false); + return null; + } + + if (mc.IsNull) + return new Binary (Binary.Operator.Equality, Expr, mc).Resolve (rc); + + var c = Expr as Constant; + if (c != null) { + c = ConstantFold.BinaryFold (rc, Binary.Operator.Equality, c, mc, loc); + if (c != null) + return c; + } + + if (Expr.Type.IsNullableType) { + expr_unwrap = new Nullable.Unwrap (Expr); + expr_unwrap.Resolve (rc); + } else if (ProbeType.Type.IsEnum || (ProbeType.Type.BuiltinType >= BuiltinTypeSpec.Type.Byte && ProbeType.Type.BuiltinType <= BuiltinTypeSpec.Type.Decimal)) { + var helper = rc.Module.CreatePatterMatchingHelper (); + number_mg = helper.NumberMatcher.Spec; + + // + // There are actually 3 arguments but the first one is already on the stack + // + number_args = new Arguments (3); + if (!ProbeType.Type.IsEnum) + number_args.Add (new Argument (Expr)); + + number_args.Add (new Argument (Convert.ImplicitConversion (rc, ProbeType, rc.BuiltinTypes.Object, loc))); + number_args.Add (new Argument (new BoolLiteral (rc.BuiltinTypes, ProbeType.Type.IsEnum, loc))); + } + + return this; + } + + throw new NotImplementedException (); + } + + Expression ResolveResultExpression (ResolveContext ec) + { + TypeSpec d = expr.Type; + bool d_is_nullable = false; + + // + // If E is a method group or the null literal, or if the type of E is a reference + // type or a nullable type and the value of E is null, the result is false + // + if (expr.IsNull || expr.eclass == ExprClass.MethodGroup) + return CreateConstantResult (ec, false); + + if (d.IsNullableType) { + var ut = Nullable.NullableInfo.GetUnderlyingType (d); + if (!ut.IsGenericParameter) { + d = ut; + d_is_nullable = true; + } + } + + TypeSpec t = probe_type_expr; + bool t_is_nullable = false; + if (t.IsNullableType) { + var ut = Nullable.NullableInfo.GetUnderlyingType (t); + if (!ut.IsGenericParameter) { + t = ut; + t_is_nullable = true; + } + } + + if (t.IsStruct) { + if (d == t) { + // + // D and T are the same value types but D can be null + // + if (d_is_nullable && !t_is_nullable) { + expr_unwrap = Nullable.Unwrap.Create (expr, true); + return this; + } + + // + // The result is true if D and T are the same value types + // + return CreateConstantResult (ec, true); + } + + var tp = d as TypeParameterSpec; + if (tp != null) + return ResolveGenericParameter (ec, t, tp); + + // + // An unboxing conversion exists + // + if (Convert.ExplicitReferenceConversionExists (d, t)) + return this; + + // + // open generic type + // + if (d is InflatedTypeSpec && InflatedTypeSpec.ContainsTypeParameter (d)) + return this; + } else { + var tps = t as TypeParameterSpec; + if (tps != null) + return ResolveGenericParameter (ec, d, tps); + + if (t.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + ec.Report.Warning (1981, 3, loc, + "Using `{0}' to test compatibility with `{1}' is identical to testing compatibility with `object'", + OperatorName, t.GetSignatureForError ()); + } + + if (TypeManager.IsGenericParameter (d)) + return ResolveGenericParameter (ec, t, (TypeParameterSpec) d); + + if (TypeSpec.IsValueType (d)) { + if (Convert.ImplicitBoxingConversion (null, d, t) != null) { + if (d_is_nullable && !t_is_nullable) { + expr_unwrap = Nullable.Unwrap.Create (expr, false); + return this; + } + + return CreateConstantResult (ec, true); + } + } else { + if (Convert.ImplicitReferenceConversionExists (d, t)) { + var c = expr as Constant; + if (c != null) + return CreateConstantResult (ec, !c.IsNull); + + // + // Do not optimize for imported type or dynamic type + // + if (d.MemberDefinition.IsImported && d.BuiltinType != BuiltinTypeSpec.Type.None && + d.MemberDefinition.DeclaringAssembly != t.MemberDefinition.DeclaringAssembly) { + return this; + } + + if (d.BuiltinType == BuiltinTypeSpec.Type.Dynamic) + return this; + + // + // Turn is check into simple null check for implicitly convertible reference types + // + return ReducedExpression.Create ( + new Binary (Binary.Operator.Inequality, expr, new NullLiteral (loc)).Resolve (ec), + this).Resolve (ec); + } + + if (Convert.ExplicitReferenceConversionExists (d, t)) + return this; + + // + // open generic type + // + if ((d is InflatedTypeSpec || d.IsArray) && InflatedTypeSpec.ContainsTypeParameter (d)) + return this; + } + } + + return CreateConstantResult (ec, false); + } + + Expression ResolveGenericParameter (ResolveContext ec, TypeSpec d, TypeParameterSpec t) + { + if (t.IsReferenceType) { + if (d.IsStruct) + return CreateConstantResult (ec, false); + } + + if (expr.Type.IsGenericParameter) { + if (expr.Type == d && TypeSpec.IsValueType (t) && TypeSpec.IsValueType (d)) + return CreateConstantResult (ec, true); + + expr = new BoxedCast (expr, d); + } + + return this; + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + /// + /// Implementation of the `as' operator. + /// + public class As : Probe { + + public As (Expression expr, Expression probe_type, Location l) + : base (expr, probe_type, l) + { + } + + protected override string OperatorName { + get { return "as"; } + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Arguments args = Arguments.CreateForExpressionTree (ec, null, + expr.CreateExpressionTree (ec), + new TypeOf (probe_type_expr, loc)); + + return CreateExpressionFactoryCall (ec, "TypeAs", args); + } + + public override void Emit (EmitContext ec) + { + expr.Emit (ec); + + ec.Emit (OpCodes.Isinst, type); + + if (TypeManager.IsGenericParameter (type) || type.IsNullableType) + ec.Emit (OpCodes.Unbox_Any, type); + } + + protected override Expression DoResolve (ResolveContext ec) + { + if (ResolveCommon (ec) == null) + return null; + + type = probe_type_expr; + eclass = ExprClass.Value; + TypeSpec etype = expr.Type; + + if (!TypeSpec.IsReferenceType (type) && !type.IsNullableType) { + if (TypeManager.IsGenericParameter (type)) { + ec.Report.Error (413, loc, + "The `as' operator cannot be used with a non-reference type parameter `{0}'. Consider adding `class' or a reference type constraint", + probe_type_expr.GetSignatureForError ()); + } else { + ec.Report.Error (77, loc, + "The `as' operator cannot be used with a non-nullable value type `{0}'", + type.GetSignatureForError ()); + } + return null; + } + + if (expr.IsNull && type.IsNullableType) { + return Nullable.LiftedNull.CreateFromExpression (ec, this); + } + + // If the compile-time type of E is dynamic, unlike the cast operator the as operator is not dynamically bound + if (etype.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + return this; + } + + Expression e = Convert.ImplicitConversionStandard (ec, expr, type, loc); + if (e != null) { + e = EmptyCast.Create (e, type); + return ReducedExpression.Create (e, this).Resolve (ec); + } + + if (Convert.ExplicitReferenceConversionExists (etype, type)){ + if (TypeManager.IsGenericParameter (etype)) + expr = new BoxedCast (expr, etype); + + return this; + } + + if (InflatedTypeSpec.ContainsTypeParameter (etype) || InflatedTypeSpec.ContainsTypeParameter (type)) { + expr = new BoxedCast (expr, etype); + return this; + } + + if (etype != InternalType.ErrorType) { + ec.Report.Error (39, loc, "Cannot convert type `{0}' to `{1}' via a built-in conversion", + etype.GetSignatureForError (), type.GetSignatureForError ()); + } + + return null; + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + // + // This represents a typecast in the source language. + // + public class Cast : ShimExpression { + Expression target_type; + + public Cast (Expression cast_type, Expression expr, Location loc) + : base (expr) + { + this.target_type = cast_type; + this.loc = loc; + } + + public Expression TargetType { + get { return target_type; } + } + + protected override Expression DoResolve (ResolveContext ec) + { + expr = expr.Resolve (ec); + if (expr == null) + return null; + + type = target_type.ResolveAsType (ec); + if (type == null) + return null; + + if (type.IsStatic) { + ec.Report.Error (716, loc, "Cannot convert to static type `{0}'", type.GetSignatureForError ()); + return null; + } + + if (type.IsPointer && !ec.IsUnsafe) { + UnsafeError (ec, loc); + } + + eclass = ExprClass.Value; + + Constant c = expr as Constant; + if (c != null) { + c = c.Reduce (ec, type); + if (c != null) + return c; + } + + var res = Convert.ExplicitConversion (ec, expr, type, loc); + if (res == expr) + return EmptyCast.Create (res, type); + + return res; + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + Cast target = (Cast) t; + + target.target_type = target_type.Clone (clonectx); + target.expr = expr.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class ImplicitCast : ShimExpression + { + bool arrayAccess; + + public ImplicitCast (Expression expr, TypeSpec target, bool arrayAccess) + : base (expr) + { + this.loc = expr.Location; + this.type = target; + this.arrayAccess = arrayAccess; + } + + protected override Expression DoResolve (ResolveContext ec) + { + expr = expr.Resolve (ec); + if (expr == null) + return null; + + if (arrayAccess) + expr = ConvertExpressionToArrayIndex (ec, expr); + else + expr = Convert.ImplicitConversionRequired (ec, expr, type, loc); + + return expr; + } + } + + // + // C# 2.0 Default value expression + // + public class DefaultValueExpression : Expression + { + Expression expr; + + public DefaultValueExpression (Expression expr, Location loc) + { + this.expr = expr; + this.loc = loc; + } + + public Expression Expr { + get { + return this.expr; + } + } + + public override bool IsSideEffectFree { + get { + return true; + } + } + + public override bool ContainsEmitWithAwait () + { + return false; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Arguments args = new Arguments (2); + args.Add (new Argument (this)); + args.Add (new Argument (new TypeOf (type, loc))); + return CreateExpressionFactoryCall (ec, "Constant", args); + } + + protected override Expression DoResolve (ResolveContext ec) + { + type = expr.ResolveAsType (ec); + if (type == null) + return null; + + if (type.IsStatic) { + ec.Report.Error (-244, loc, "The `default value' operator cannot be applied to an operand of a static type"); + } + + if (type.IsPointer) + return new NullLiteral (Location).ConvertImplicitly (type); + + if (TypeSpec.IsReferenceType (type)) + return new NullConstant (type, loc); + + Constant c = New.Constantify (type, expr.Location); + if (c != null) + return c; + + eclass = ExprClass.Variable; + return this; + } + + public override void Emit (EmitContext ec) + { + LocalTemporary temp_storage = new LocalTemporary(type); + + temp_storage.AddressOf(ec, AddressOp.LoadStore); + ec.Emit(OpCodes.Initobj, type); + temp_storage.Emit(ec); + temp_storage.Release (ec); + } + +#if (NET_4_0 || MOBILE_DYNAMIC) && !STATIC + public override SLE.Expression MakeExpression (BuilderContext ctx) + { + return SLE.Expression.Default (type.GetMetaInfo ()); + } +#endif + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + DefaultValueExpression target = (DefaultValueExpression) t; + + target.expr = expr.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + /// + /// Binary operators + /// + public class Binary : Expression, IDynamicBinder + { + public class PredefinedOperator + { + protected readonly TypeSpec left; + protected readonly TypeSpec right; + protected readonly TypeSpec left_unwrap; + protected readonly TypeSpec right_unwrap; + public readonly Operator OperatorsMask; + public TypeSpec ReturnType; + + public PredefinedOperator (TypeSpec ltype, TypeSpec rtype, Operator op_mask) + : this (ltype, rtype, op_mask, ltype) + { + } + + public PredefinedOperator (TypeSpec type, Operator op_mask, TypeSpec return_type) + : this (type, type, op_mask, return_type) + { + } + + public PredefinedOperator (TypeSpec type, Operator op_mask) + : this (type, type, op_mask, type) + { + } + + public PredefinedOperator (TypeSpec ltype, TypeSpec rtype, Operator op_mask, TypeSpec return_type) + { + if ((op_mask & Operator.ValuesOnlyMask) != 0) + throw new InternalErrorException ("Only masked values can be used"); + + if ((op_mask & Operator.NullableMask) != 0) { + left_unwrap = Nullable.NullableInfo.GetUnderlyingType (ltype); + right_unwrap = Nullable.NullableInfo.GetUnderlyingType (rtype); + } else { + left_unwrap = ltype; + right_unwrap = rtype; + } + + this.left = ltype; + this.right = rtype; + this.OperatorsMask = op_mask; + this.ReturnType = return_type; + } + + public bool IsLifted { + get { + return (OperatorsMask & Operator.NullableMask) != 0; + } + } + + public virtual Expression ConvertResult (ResolveContext rc, Binary b) + { + Constant c; + + var left_expr = b.left; + var right_expr = b.right; + + b.type = ReturnType; + + if (IsLifted) { + if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion)) { + b.left = Convert.ImplicitConversion (rc, b.left, left, b.left.Location); + b.right = Convert.ImplicitConversion (rc, b.right, right, b.right.Location); + } + + if (right_expr.IsNull) { + if ((b.oper & Operator.EqualityMask) != 0) { + if (!left_expr.Type.IsNullableType && BuiltinTypeSpec.IsPrimitiveType (left_expr.Type)) + return b.CreateLiftedValueTypeResult (rc, left_expr.Type); + } else if ((b.oper & Operator.BitwiseMask) != 0) { + if (left_unwrap.BuiltinType != BuiltinTypeSpec.Type.Bool) + return Nullable.LiftedNull.CreateFromExpression (rc, b); + } else { + b.left = Convert.ImplicitConversion (rc, b.left, left, b.left.Location); + b.right = Convert.ImplicitConversion (rc, b.right, right, b.right.Location); + + if ((b.Oper & (Operator.ArithmeticMask | Operator.ShiftMask)) != 0) + return Nullable.LiftedNull.CreateFromExpression (rc, b); + + return b.CreateLiftedValueTypeResult (rc, left); + } + } else if (left_expr.IsNull) { + if ((b.oper & Operator.EqualityMask) != 0) { + if (!right_expr.Type.IsNullableType && BuiltinTypeSpec.IsPrimitiveType (right_expr.Type)) + return b.CreateLiftedValueTypeResult (rc, right_expr.Type); + } else if ((b.oper & Operator.BitwiseMask) != 0) { + if (right_unwrap.BuiltinType != BuiltinTypeSpec.Type.Bool) + return Nullable.LiftedNull.CreateFromExpression (rc, b); + } else { + b.left = Convert.ImplicitConversion (rc, b.left, left, b.left.Location); + b.right = Convert.ImplicitConversion (rc, b.right, right, b.right.Location); + + if ((b.Oper & (Operator.ArithmeticMask | Operator.ShiftMask)) != 0) + return Nullable.LiftedNull.CreateFromExpression (rc, b); + + return b.CreateLiftedValueTypeResult (rc, right); + } + } + } + + // + // A user operators does not support multiple user conversions, but decimal type + // is considered to be predefined type therefore we apply predefined operators rules + // and then look for decimal user-operator implementation + // + if (left.BuiltinType == BuiltinTypeSpec.Type.Decimal) { + b.left = Convert.ImplicitConversion (rc, b.left, left, b.left.Location); + b.right = Convert.ImplicitConversion (rc, b.right, right, b.right.Location); + + return b.ResolveUserOperator (rc, b.left, b.right); + } + + c = right_expr as Constant; + if (c != null) { + if (c.IsDefaultValue) { + // + // Optimizes + // + // (expr + 0) to expr + // (expr - 0) to expr + // (bool? | false) to bool? + // + if (b.oper == Operator.Addition || b.oper == Operator.Subtraction || + (b.oper == Operator.BitwiseOr && left_unwrap.BuiltinType == BuiltinTypeSpec.Type.Bool && c is BoolConstant)) { + b.left = Convert.ImplicitConversion (rc, b.left, left, b.left.Location); + return ReducedExpression.Create (b.left, b).Resolve (rc); + } + + // + // Optimizes (value &/&& 0) to 0 + // + if ((b.oper == Operator.BitwiseAnd || b.oper == Operator.LogicalAnd) && !IsLifted) { + Constant side_effect = new SideEffectConstant (c, b.left, c.Location); + return ReducedExpression.Create (side_effect, b); + } + } else { + // + // Optimizes (bool? & true) to bool? + // + if (IsLifted && left_unwrap.BuiltinType == BuiltinTypeSpec.Type.Bool && b.oper == Operator.BitwiseAnd) { + return ReducedExpression.Create (b.left, b).Resolve (rc); + } + } + + if ((b.oper == Operator.Multiply || b.oper == Operator.Division) && c.IsOneInteger) + return ReducedExpression.Create (b.left, b).Resolve (rc); + + if ((b.oper & Operator.ShiftMask) != 0 && c is IntConstant) { + b.right = new IntConstant (rc.BuiltinTypes, ((IntConstant) c).Value & GetShiftMask (left_unwrap), b.right.Location); + } + } + + c = b.left as Constant; + if (c != null) { + if (c.IsDefaultValue) { + // + // Optimizes + // + // (0 + expr) to expr + // (false | bool?) to bool? + // + if (b.oper == Operator.Addition || + (b.oper == Operator.BitwiseOr && right_unwrap.BuiltinType == BuiltinTypeSpec.Type.Bool && c is BoolConstant)) { + b.right = Convert.ImplicitConversion (rc, b.right, right, b.right.Location); + return ReducedExpression.Create (b.right, b).Resolve (rc); + } + + // + // Optimizes (false && expr) to false + // + if (b.oper == Operator.LogicalAnd && c.Type.BuiltinType == BuiltinTypeSpec.Type.Bool) { + // No rhs side-effects + Expression.Warning_UnreachableExpression (rc, b.right.StartLocation); + return ReducedExpression.Create (c, b); + } + + // + // Optimizes (0 & value) to 0 + // + if (b.oper == Operator.BitwiseAnd && !IsLifted) { + Constant side_effect = new SideEffectConstant (c, b.right, c.Location); + return ReducedExpression.Create (side_effect, b); + } + } else { + // + // Optimizes (true & bool?) to bool? + // + if (IsLifted && left_unwrap.BuiltinType == BuiltinTypeSpec.Type.Bool && b.oper == Operator.BitwiseAnd) { + return ReducedExpression.Create (b.right, b).Resolve (rc); + } + + // + // Optimizes (true || expr) to true + // + if (b.oper == Operator.LogicalOr && c.Type.BuiltinType == BuiltinTypeSpec.Type.Bool) { + // No rhs side-effects + Expression.Warning_UnreachableExpression (rc, b.right.StartLocation); + return ReducedExpression.Create (c, b); + } + } + + if (b.oper == Operator.Multiply && c.IsOneInteger) + return ReducedExpression.Create (b.right, b).Resolve (rc); + } + + if (IsLifted) { + var lifted = new Nullable.LiftedBinaryOperator (b); + + TypeSpec ltype, rtype; + if (b.left.Type.IsNullableType) { + lifted.UnwrapLeft = new Nullable.Unwrap (b.left); + ltype = left_unwrap; + } else { + ltype = left; + } + + if (b.right.Type.IsNullableType) { + lifted.UnwrapRight = new Nullable.Unwrap (b.right); + rtype = right_unwrap; + } else { + rtype = right; + } + + lifted.Left = b.left.IsNull ? + b.left : + Convert.ImplicitConversion (rc, lifted.UnwrapLeft ?? b.left, ltype, b.left.Location); + + lifted.Right = b.right.IsNull ? + b.right : + Convert.ImplicitConversion (rc, lifted.UnwrapRight ?? b.right, rtype, b.right.Location); + + return lifted.Resolve (rc); + } + + b.left = Convert.ImplicitConversion (rc, b.left, left, b.left.Location); + b.right = Convert.ImplicitConversion (rc, b.right, right, b.right.Location); + + return b; + } + + public bool IsPrimitiveApplicable (TypeSpec ltype, TypeSpec rtype) + { + // + // We are dealing with primitive types only + // + return left == ltype && ltype == rtype; + } + + public virtual bool IsApplicable (ResolveContext ec, Expression lexpr, Expression rexpr) + { + // Quick path + if (left == lexpr.Type && right == rexpr.Type) + return true; + + return Convert.ImplicitConversionExists (ec, lexpr, left) && + Convert.ImplicitConversionExists (ec, rexpr, right); + } + + public PredefinedOperator ResolveBetterOperator (ResolveContext ec, PredefinedOperator best_operator) + { + if ((OperatorsMask & Operator.DecomposedMask) != 0) + return best_operator; + + if ((best_operator.OperatorsMask & Operator.DecomposedMask) != 0) + return this; + + int result = 0; + if (left != null && best_operator.left != null) { + result = OverloadResolver.BetterTypeConversion (ec, best_operator.left_unwrap, left_unwrap); + } + + // + // When second argument is same as the first one, the result is same + // + if (right != null && (left != right || best_operator.left != best_operator.right)) { + result |= OverloadResolver.BetterTypeConversion (ec, best_operator.right_unwrap, right_unwrap); + } + + if (result == 0 || result > 2) + return null; + + return result == 1 ? best_operator : this; + } + } + + sealed class PredefinedStringOperator : PredefinedOperator + { + public PredefinedStringOperator (TypeSpec type, Operator op_mask, TypeSpec retType) + : base (type, type, op_mask, retType) + { + } + + public PredefinedStringOperator (TypeSpec ltype, TypeSpec rtype, Operator op_mask, TypeSpec retType) + : base (ltype, rtype, op_mask, retType) + { + } + + public override Expression ConvertResult (ResolveContext ec, Binary b) + { + // + // Use original expression for nullable arguments + // + Nullable.Unwrap unwrap = b.left as Nullable.Unwrap; + if (unwrap != null) + b.left = unwrap.Original; + + unwrap = b.right as Nullable.Unwrap; + if (unwrap != null) + b.right = unwrap.Original; + + b.left = Convert.ImplicitConversion (ec, b.left, left, b.left.Location); + b.right = Convert.ImplicitConversion (ec, b.right, right, b.right.Location); + + // + // Start a new concat expression using converted expression + // + return StringConcat.Create (ec, b.left, b.right, b.loc); + } + } + + sealed class PredefinedEqualityOperator : PredefinedOperator + { + MethodSpec equal_method, inequal_method; + + public PredefinedEqualityOperator (TypeSpec arg, TypeSpec retType) + : base (arg, arg, Operator.EqualityMask, retType) + { + } + + public override Expression ConvertResult (ResolveContext ec, Binary b) + { + b.type = ReturnType; + + b.left = Convert.ImplicitConversion (ec, b.left, left, b.left.Location); + b.right = Convert.ImplicitConversion (ec, b.right, right, b.right.Location); + + Arguments args = new Arguments (2); + args.Add (new Argument (b.left)); + args.Add (new Argument (b.right)); + + MethodSpec method; + if (b.oper == Operator.Equality) { + if (equal_method == null) { + if (left.BuiltinType == BuiltinTypeSpec.Type.String) + equal_method = ec.Module.PredefinedMembers.StringEqual.Resolve (b.loc); + else if (left.BuiltinType == BuiltinTypeSpec.Type.Delegate) + equal_method = ec.Module.PredefinedMembers.DelegateEqual.Resolve (b.loc); + else + throw new NotImplementedException (left.GetSignatureForError ()); + } + + method = equal_method; + } else { + if (inequal_method == null) { + if (left.BuiltinType == BuiltinTypeSpec.Type.String) + inequal_method = ec.Module.PredefinedMembers.StringInequal.Resolve (b.loc); + else if (left.BuiltinType == BuiltinTypeSpec.Type.Delegate) + inequal_method = ec.Module.PredefinedMembers.DelegateInequal.Resolve (b.loc); + else + throw new NotImplementedException (left.GetSignatureForError ()); + } + + method = inequal_method; + } + + return new UserOperatorCall (method, args, b.CreateExpressionTree, b.loc); + } + } + + class PredefinedPointerOperator : PredefinedOperator + { + public PredefinedPointerOperator (TypeSpec ltype, TypeSpec rtype, Operator op_mask) + : base (ltype, rtype, op_mask) + { + } + + public PredefinedPointerOperator (TypeSpec ltype, TypeSpec rtype, Operator op_mask, TypeSpec retType) + : base (ltype, rtype, op_mask, retType) + { + } + + public PredefinedPointerOperator (TypeSpec type, Operator op_mask, TypeSpec return_type) + : base (type, op_mask, return_type) + { + } + + public override bool IsApplicable (ResolveContext ec, Expression lexpr, Expression rexpr) + { + if (left == null) { + if (!lexpr.Type.IsPointer) + return false; + } else { + if (!Convert.ImplicitConversionExists (ec, lexpr, left)) + return false; + } + + if (right == null) { + if (!rexpr.Type.IsPointer) + return false; + } else { + if (!Convert.ImplicitConversionExists (ec, rexpr, right)) + return false; + } + + return true; + } + + public override Expression ConvertResult (ResolveContext ec, Binary b) + { + if (left != null) { + b.left = EmptyCast.Create (b.left, left); + } else if (right != null) { + b.right = EmptyCast.Create (b.right, right); + } + + TypeSpec r_type = ReturnType; + Expression left_arg, right_arg; + if (r_type == null) { + if (left == null) { + left_arg = b.left; + right_arg = b.right; + r_type = b.left.Type; + } else { + left_arg = b.right; + right_arg = b.left; + r_type = b.right.Type; + } + } else { + left_arg = b.left; + right_arg = b.right; + } + + return new PointerArithmetic (b.oper, left_arg, right_arg, r_type, b.loc).Resolve (ec); + } + } + + [Flags] + public enum Operator { + Multiply = 0 | ArithmeticMask, + Division = 1 | ArithmeticMask, + Modulus = 2 | ArithmeticMask, + Addition = 3 | ArithmeticMask | AdditionMask, + Subtraction = 4 | ArithmeticMask | SubtractionMask, + + LeftShift = 5 | ShiftMask, + RightShift = 6 | ShiftMask, + + LessThan = 7 | ComparisonMask | RelationalMask, + GreaterThan = 8 | ComparisonMask | RelationalMask, + LessThanOrEqual = 9 | ComparisonMask | RelationalMask, + GreaterThanOrEqual = 10 | ComparisonMask | RelationalMask, + Equality = 11 | ComparisonMask | EqualityMask, + Inequality = 12 | ComparisonMask | EqualityMask, + + BitwiseAnd = 13 | BitwiseMask, + ExclusiveOr = 14 | BitwiseMask, + BitwiseOr = 15 | BitwiseMask, + + LogicalAnd = 16 | LogicalMask, + LogicalOr = 17 | LogicalMask, + + // + // Operator masks + // + ValuesOnlyMask = ArithmeticMask - 1, + ArithmeticMask = 1 << 5, + ShiftMask = 1 << 6, + ComparisonMask = 1 << 7, + EqualityMask = 1 << 8, + BitwiseMask = 1 << 9, + LogicalMask = 1 << 10, + AdditionMask = 1 << 11, + SubtractionMask = 1 << 12, + RelationalMask = 1 << 13, + + DecomposedMask = 1 << 19, + NullableMask = 1 << 20, + } + + [Flags] + enum State : byte + { + None = 0, + Compound = 1 << 1, + } + + readonly Operator oper; + Expression left, right; + State state; + ConvCast.Mode enum_conversion; + + public Binary (Operator oper, Expression left, Expression right, bool isCompound) + : this (oper, left, right) + { + if (isCompound) + state |= State.Compound; + } + + public Binary (Operator oper, Expression left, Expression right) + { + this.oper = oper; + this.left = left; + this.right = right; + this.loc = left.Location; + } + + #region Properties + + public bool IsCompound { + get { + return (state & State.Compound) != 0; + } + } + + public Operator Oper { + get { + return oper; + } + } + + public Expression Left { + get { + return this.left; + } + } + + public Expression Right { + get { + return this.right; + } + } + + public override Location StartLocation { + get { + return left.StartLocation; + } + } + + #endregion + + /// + /// Returns a stringified representation of the Operator + /// + string OperName (Operator oper) + { + string s; + switch (oper){ + case Operator.Multiply: + s = "*"; + break; + case Operator.Division: + s = "/"; + break; + case Operator.Modulus: + s = "%"; + break; + case Operator.Addition: + s = "+"; + break; + case Operator.Subtraction: + s = "-"; + break; + case Operator.LeftShift: + s = "<<"; + break; + case Operator.RightShift: + s = ">>"; + break; + case Operator.LessThan: + s = "<"; + break; + case Operator.GreaterThan: + s = ">"; + break; + case Operator.LessThanOrEqual: + s = "<="; + break; + case Operator.GreaterThanOrEqual: + s = ">="; + break; + case Operator.Equality: + s = "=="; + break; + case Operator.Inequality: + s = "!="; + break; + case Operator.BitwiseAnd: + s = "&"; + break; + case Operator.BitwiseOr: + s = "|"; + break; + case Operator.ExclusiveOr: + s = "^"; + break; + case Operator.LogicalOr: + s = "||"; + break; + case Operator.LogicalAnd: + s = "&&"; + break; + default: + s = oper.ToString (); + break; + } + + if (IsCompound) + return s + "="; + + return s; + } + + public static void Error_OperatorCannotBeApplied (ResolveContext ec, Expression left, Expression right, Operator oper, Location loc) + { + new Binary (oper, left, right).Error_OperatorCannotBeApplied (ec, left, right); + } + + public static void Error_OperatorCannotBeApplied (ResolveContext ec, Expression left, Expression right, string oper, Location loc) + { + if (left.Type == InternalType.ErrorType || right.Type == InternalType.ErrorType) + return; + + string l, r; + l = left.Type.GetSignatureForError (); + r = right.Type.GetSignatureForError (); + + ec.Report.Error (19, loc, "Operator `{0}' cannot be applied to operands of type `{1}' and `{2}'", + oper, l, r); + } + + void Error_OperatorCannotBeApplied (ResolveContext ec, Expression left, Expression right) + { + Error_OperatorCannotBeApplied (ec, left, right, OperName (oper), loc); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + // + // Optimized version when on-true/on-false data are not needed + // + if ((oper & Operator.LogicalMask) == 0) { + left.FlowAnalysis (fc); + right.FlowAnalysis (fc); + return; + } + + left.FlowAnalysisConditional (fc); + var left_fc_ontrue = fc.DefiniteAssignmentOnTrue; + var left_fc_onfalse = fc.DefiniteAssignmentOnFalse; + + fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = fc.DefiniteAssignment = new DefiniteAssignmentBitSet ( + oper == Operator.LogicalOr ? left_fc_onfalse : left_fc_ontrue); + right.FlowAnalysisConditional (fc); + + if (oper == Operator.LogicalOr) + fc.DefiniteAssignment = (left_fc_onfalse | (fc.DefiniteAssignmentOnFalse & fc.DefiniteAssignmentOnTrue)) & left_fc_ontrue; + else + fc.DefiniteAssignment = (left_fc_ontrue | (fc.DefiniteAssignmentOnFalse & fc.DefiniteAssignmentOnTrue)) & left_fc_onfalse; + } + + public override void FlowAnalysisConditional (FlowAnalysisContext fc) + { + if ((oper & Operator.LogicalMask) == 0) { + base.FlowAnalysisConditional (fc); + return; + } + + left.FlowAnalysisConditional (fc); + var left_fc_ontrue = fc.DefiniteAssignmentOnTrue; + var left_fc_onfalse = fc.DefiniteAssignmentOnFalse; + + fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = fc.DefiniteAssignment = new DefiniteAssignmentBitSet ( + oper == Operator.LogicalOr ? left_fc_onfalse : left_fc_ontrue); + right.FlowAnalysisConditional (fc); + + var lc = left as Constant; + if (oper == Operator.LogicalOr) { + fc.DefiniteAssignmentOnFalse = left_fc_onfalse | fc.DefiniteAssignmentOnFalse; + if (lc != null && lc.IsDefaultValue) + fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse; + else + fc.DefiniteAssignmentOnTrue = new DefiniteAssignmentBitSet (left_fc_ontrue & (left_fc_onfalse | fc.DefiniteAssignmentOnTrue)); + } else { + fc.DefiniteAssignmentOnTrue = left_fc_ontrue | fc.DefiniteAssignmentOnTrue; + if (lc != null && !lc.IsDefaultValue) + fc.DefiniteAssignmentOnFalse = fc.DefiniteAssignmentOnTrue; + else + fc.DefiniteAssignmentOnFalse = new DefiniteAssignmentBitSet ((left_fc_ontrue | fc.DefiniteAssignmentOnFalse) & left_fc_onfalse); + } + } + + // + // Converts operator to System.Linq.Expressions.ExpressionType enum name + // + string GetOperatorExpressionTypeName () + { + switch (oper) { + case Operator.Addition: + return IsCompound ? "AddAssign" : "Add"; + case Operator.BitwiseAnd: + return IsCompound ? "AndAssign" : "And"; + case Operator.BitwiseOr: + return IsCompound ? "OrAssign" : "Or"; + case Operator.Division: + return IsCompound ? "DivideAssign" : "Divide"; + case Operator.ExclusiveOr: + return IsCompound ? "ExclusiveOrAssign" : "ExclusiveOr"; + case Operator.Equality: + return "Equal"; + case Operator.GreaterThan: + return "GreaterThan"; + case Operator.GreaterThanOrEqual: + return "GreaterThanOrEqual"; + case Operator.Inequality: + return "NotEqual"; + case Operator.LeftShift: + return IsCompound ? "LeftShiftAssign" : "LeftShift"; + case Operator.LessThan: + return "LessThan"; + case Operator.LessThanOrEqual: + return "LessThanOrEqual"; + case Operator.LogicalAnd: + return "And"; + case Operator.LogicalOr: + return "Or"; + case Operator.Modulus: + return IsCompound ? "ModuloAssign" : "Modulo"; + case Operator.Multiply: + return IsCompound ? "MultiplyAssign" : "Multiply"; + case Operator.RightShift: + return IsCompound ? "RightShiftAssign" : "RightShift"; + case Operator.Subtraction: + return IsCompound ? "SubtractAssign" : "Subtract"; + default: + throw new NotImplementedException ("Unknown expression type operator " + oper.ToString ()); + } + } + + static CSharp.Operator.OpType ConvertBinaryToUserOperator (Operator op) + { + switch (op) { + case Operator.Addition: + return CSharp.Operator.OpType.Addition; + case Operator.BitwiseAnd: + case Operator.LogicalAnd: + return CSharp.Operator.OpType.BitwiseAnd; + case Operator.BitwiseOr: + case Operator.LogicalOr: + return CSharp.Operator.OpType.BitwiseOr; + case Operator.Division: + return CSharp.Operator.OpType.Division; + case Operator.Equality: + return CSharp.Operator.OpType.Equality; + case Operator.ExclusiveOr: + return CSharp.Operator.OpType.ExclusiveOr; + case Operator.GreaterThan: + return CSharp.Operator.OpType.GreaterThan; + case Operator.GreaterThanOrEqual: + return CSharp.Operator.OpType.GreaterThanOrEqual; + case Operator.Inequality: + return CSharp.Operator.OpType.Inequality; + case Operator.LeftShift: + return CSharp.Operator.OpType.LeftShift; + case Operator.LessThan: + return CSharp.Operator.OpType.LessThan; + case Operator.LessThanOrEqual: + return CSharp.Operator.OpType.LessThanOrEqual; + case Operator.Modulus: + return CSharp.Operator.OpType.Modulus; + case Operator.Multiply: + return CSharp.Operator.OpType.Multiply; + case Operator.RightShift: + return CSharp.Operator.OpType.RightShift; + case Operator.Subtraction: + return CSharp.Operator.OpType.Subtraction; + default: + throw new InternalErrorException (op.ToString ()); + } + } + + public override bool ContainsEmitWithAwait () + { + return left.ContainsEmitWithAwait () || right.ContainsEmitWithAwait (); + } + + public static void EmitOperatorOpcode (EmitContext ec, Operator oper, TypeSpec l, Expression right) + { + OpCode opcode; + + switch (oper){ + case Operator.Multiply: + if (ec.HasSet (EmitContext.Options.CheckedScope)) { + if (l.BuiltinType == BuiltinTypeSpec.Type.Int || l.BuiltinType == BuiltinTypeSpec.Type.Long) + opcode = OpCodes.Mul_Ovf; + else if (!IsFloat (l)) + opcode = OpCodes.Mul_Ovf_Un; + else + opcode = OpCodes.Mul; + } else + opcode = OpCodes.Mul; + + break; + + case Operator.Division: + if (IsUnsigned (l)) + opcode = OpCodes.Div_Un; + else + opcode = OpCodes.Div; + break; + + case Operator.Modulus: + if (IsUnsigned (l)) + opcode = OpCodes.Rem_Un; + else + opcode = OpCodes.Rem; + break; + + case Operator.Addition: + if (ec.HasSet (EmitContext.Options.CheckedScope)) { + if (l.BuiltinType == BuiltinTypeSpec.Type.Int || l.BuiltinType == BuiltinTypeSpec.Type.Long) + opcode = OpCodes.Add_Ovf; + else if (!IsFloat (l)) + opcode = OpCodes.Add_Ovf_Un; + else + opcode = OpCodes.Add; + } else + opcode = OpCodes.Add; + break; + + case Operator.Subtraction: + if (ec.HasSet (EmitContext.Options.CheckedScope)) { + if (l.BuiltinType == BuiltinTypeSpec.Type.Int || l.BuiltinType == BuiltinTypeSpec.Type.Long) + opcode = OpCodes.Sub_Ovf; + else if (!IsFloat (l)) + opcode = OpCodes.Sub_Ovf_Un; + else + opcode = OpCodes.Sub; + } else + opcode = OpCodes.Sub; + break; + + case Operator.RightShift: + if (!(right is IntConstant)) { + ec.EmitInt (GetShiftMask (l)); + ec.Emit (OpCodes.And); + } + + if (IsUnsigned (l)) + opcode = OpCodes.Shr_Un; + else + opcode = OpCodes.Shr; + break; + + case Operator.LeftShift: + if (!(right is IntConstant)) { + ec.EmitInt (GetShiftMask (l)); + ec.Emit (OpCodes.And); + } + + opcode = OpCodes.Shl; + break; + + case Operator.Equality: + opcode = OpCodes.Ceq; + break; + + case Operator.Inequality: + ec.Emit (OpCodes.Ceq); + ec.EmitInt (0); + + opcode = OpCodes.Ceq; + break; + + case Operator.LessThan: + if (IsUnsigned (l)) + opcode = OpCodes.Clt_Un; + else + opcode = OpCodes.Clt; + break; + + case Operator.GreaterThan: + if (IsUnsigned (l)) + opcode = OpCodes.Cgt_Un; + else + opcode = OpCodes.Cgt; + break; + + case Operator.LessThanOrEqual: + if (IsUnsigned (l) || IsFloat (l)) + ec.Emit (OpCodes.Cgt_Un); + else + ec.Emit (OpCodes.Cgt); + ec.EmitInt (0); + + opcode = OpCodes.Ceq; + break; + + case Operator.GreaterThanOrEqual: + if (IsUnsigned (l) || IsFloat (l)) + ec.Emit (OpCodes.Clt_Un); + else + ec.Emit (OpCodes.Clt); + + ec.EmitInt (0); + + opcode = OpCodes.Ceq; + break; + + case Operator.BitwiseOr: + opcode = OpCodes.Or; + break; + + case Operator.BitwiseAnd: + opcode = OpCodes.And; + break; + + case Operator.ExclusiveOr: + opcode = OpCodes.Xor; + break; + + default: + throw new InternalErrorException (oper.ToString ()); + } + + ec.Emit (opcode); + } + + static int GetShiftMask (TypeSpec type) + { + return type.BuiltinType == BuiltinTypeSpec.Type.Int || type.BuiltinType == BuiltinTypeSpec.Type.UInt ? 0x1f : 0x3f; + } + + static bool IsUnsigned (TypeSpec t) + { + switch (t.BuiltinType) { + case BuiltinTypeSpec.Type.Char: + case BuiltinTypeSpec.Type.UInt: + case BuiltinTypeSpec.Type.ULong: + case BuiltinTypeSpec.Type.UShort: + case BuiltinTypeSpec.Type.Byte: + return true; + } + + return t.IsPointer; + } + + static bool IsFloat (TypeSpec t) + { + return t.BuiltinType == BuiltinTypeSpec.Type.Float || t.BuiltinType == BuiltinTypeSpec.Type.Double; + } + + public Expression ResolveOperator (ResolveContext rc) + { + eclass = ExprClass.Value; + + TypeSpec l = left.Type; + TypeSpec r = right.Type; + Expression expr; + bool primitives_only = false; + + // + // Handles predefined primitive types + // + if ((BuiltinTypeSpec.IsPrimitiveType (l) || (l.IsNullableType && BuiltinTypeSpec.IsPrimitiveType (Nullable.NullableInfo.GetUnderlyingType (l)))) && + (BuiltinTypeSpec.IsPrimitiveType (r) || (r.IsNullableType && BuiltinTypeSpec.IsPrimitiveType (Nullable.NullableInfo.GetUnderlyingType (r))))) { + if ((oper & Operator.ShiftMask) == 0) { + if (!DoBinaryOperatorPromotion (rc)) + return null; + + primitives_only = BuiltinTypeSpec.IsPrimitiveType (l) && BuiltinTypeSpec.IsPrimitiveType (r); + } + } else { + // Pointers + if (l.IsPointer || r.IsPointer) + return ResolveOperatorPointer (rc, l, r); + + // User operators + expr = ResolveUserOperator (rc, left, right); + if (expr != null) + return expr; + + + bool lenum = l.IsEnum; + bool renum = r.IsEnum; + if ((oper & (Operator.ComparisonMask | Operator.BitwiseMask)) != 0) { + // + // Enumerations + // + if (IsEnumOrNullableEnum (l) || IsEnumOrNullableEnum (r)) { + expr = ResolveSingleEnumOperators (rc, lenum, renum, l, r); + + if (expr == null) + return null; + + if ((oper & Operator.BitwiseMask) != 0) { + expr = EmptyCast.Create (expr, type); + enum_conversion = GetEnumResultCast (type); + + if (oper == Operator.BitwiseAnd && left.Type.IsEnum && right.Type.IsEnum) { + expr = OptimizeAndOperation (expr); + } + } + + left = ConvertEnumOperandToUnderlyingType (rc, left, r.IsNullableType); + right = ConvertEnumOperandToUnderlyingType (rc, right, l.IsNullableType); + return expr; + } + } else if ((oper == Operator.Addition || oper == Operator.Subtraction)) { + if (IsEnumOrNullableEnum (l) || IsEnumOrNullableEnum (r)) { + // + // Enumerations + // + expr = ResolveEnumOperators (rc, lenum, renum, l, r); + + // + // We cannot break here there is also Enum + String possible match + // which is not ambiguous with predefined enum operators + // + if (expr != null) { + left = ConvertEnumOperandToUnderlyingType (rc, left, false); + right = ConvertEnumOperandToUnderlyingType (rc, right, false); + + return expr; + } + } else if (l.IsDelegate || r.IsDelegate) { + // + // Delegates + // + expr = ResolveOperatorDelegate (rc, l, r); + + // TODO: Can this be ambiguous + if (expr != null) + return expr; + } + } + } + + // + // Equality operators are more complicated + // + if ((oper & Operator.EqualityMask) != 0) { + return ResolveEquality (rc, l, r, primitives_only); + } + + expr = ResolveOperatorPredefined (rc, rc.BuiltinTypes.OperatorsBinaryStandard, primitives_only); + if (expr != null) + return expr; + + if (primitives_only) + return null; + + // + // Lifted operators have lower priority + // + return ResolveOperatorPredefined (rc, rc.Module.OperatorsBinaryLifted, false); + } + + static bool IsEnumOrNullableEnum (TypeSpec type) + { + return type.IsEnum || (type.IsNullableType && Nullable.NullableInfo.GetUnderlyingType (type).IsEnum); + } + + + // at least one of 'left' or 'right' is an enumeration constant (EnumConstant or SideEffectConstant or ...) + // if 'left' is not an enumeration constant, create one from the type of 'right' + Constant EnumLiftUp (ResolveContext ec, Constant left, Constant right) + { + switch (oper) { + case Operator.BitwiseOr: + case Operator.BitwiseAnd: + case Operator.ExclusiveOr: + case Operator.Equality: + case Operator.Inequality: + case Operator.LessThan: + case Operator.LessThanOrEqual: + case Operator.GreaterThan: + case Operator.GreaterThanOrEqual: + if (left.Type.IsEnum) + return left; + + if (left.IsZeroInteger) + return left.Reduce (ec, right.Type); + + break; + + case Operator.Addition: + case Operator.Subtraction: + return left; + + case Operator.Multiply: + case Operator.Division: + case Operator.Modulus: + case Operator.LeftShift: + case Operator.RightShift: + if (right.Type.IsEnum || left.Type.IsEnum) + break; + return left; + } + + return null; + } + + // + // The `|' operator used on types which were extended is dangerous + // + void CheckBitwiseOrOnSignExtended (ResolveContext ec) + { + OpcodeCast lcast = left as OpcodeCast; + if (lcast != null) { + if (IsUnsigned (lcast.UnderlyingType)) + lcast = null; + } + + OpcodeCast rcast = right as OpcodeCast; + if (rcast != null) { + if (IsUnsigned (rcast.UnderlyingType)) + rcast = null; + } + + if (lcast == null && rcast == null) + return; + + // FIXME: consider constants + + var ltype = lcast != null ? lcast.UnderlyingType : rcast.UnderlyingType; + ec.Report.Warning (675, 3, loc, + "The operator `|' used on the sign-extended type `{0}'. Consider casting to a smaller unsigned type first", + ltype.GetSignatureForError ()); + } + + public static PredefinedOperator[] CreatePointerOperatorsTable (BuiltinTypes types) + { + return new PredefinedOperator[] { + // + // Pointer arithmetic: + // + // T* operator + (T* x, int y); T* operator - (T* x, int y); + // T* operator + (T* x, uint y); T* operator - (T* x, uint y); + // T* operator + (T* x, long y); T* operator - (T* x, long y); + // T* operator + (T* x, ulong y); T* operator - (T* x, ulong y); + // + new PredefinedPointerOperator (null, types.Int, Operator.AdditionMask | Operator.SubtractionMask), + new PredefinedPointerOperator (null, types.UInt, Operator.AdditionMask | Operator.SubtractionMask), + new PredefinedPointerOperator (null, types.Long, Operator.AdditionMask | Operator.SubtractionMask), + new PredefinedPointerOperator (null, types.ULong, Operator.AdditionMask | Operator.SubtractionMask), + + // + // T* operator + (int y, T* x); + // T* operator + (uint y, T *x); + // T* operator + (long y, T *x); + // T* operator + (ulong y, T *x); + // + new PredefinedPointerOperator (types.Int, null, Operator.AdditionMask, null), + new PredefinedPointerOperator (types.UInt, null, Operator.AdditionMask, null), + new PredefinedPointerOperator (types.Long, null, Operator.AdditionMask, null), + new PredefinedPointerOperator (types.ULong, null, Operator.AdditionMask, null), + + // + // long operator - (T* x, T *y) + // + new PredefinedPointerOperator (null, Operator.SubtractionMask, types.Long) + }; + } + + public static PredefinedOperator[] CreateStandardOperatorsTable (BuiltinTypes types) + { + TypeSpec bool_type = types.Bool; + + return new [] { + new PredefinedOperator (types.Int, Operator.ArithmeticMask | Operator.BitwiseMask | Operator.ShiftMask), + new PredefinedOperator (types.UInt, Operator.ArithmeticMask | Operator.BitwiseMask), + new PredefinedOperator (types.Long, Operator.ArithmeticMask | Operator.BitwiseMask), + new PredefinedOperator (types.ULong, Operator.ArithmeticMask | Operator.BitwiseMask), + new PredefinedOperator (types.Float, Operator.ArithmeticMask), + new PredefinedOperator (types.Double, Operator.ArithmeticMask), + new PredefinedOperator (types.Decimal, Operator.ArithmeticMask), + + new PredefinedOperator (types.Int, Operator.ComparisonMask, bool_type), + new PredefinedOperator (types.UInt, Operator.ComparisonMask, bool_type), + new PredefinedOperator (types.Long, Operator.ComparisonMask, bool_type), + new PredefinedOperator (types.ULong, Operator.ComparisonMask, bool_type), + new PredefinedOperator (types.Float, Operator.ComparisonMask, bool_type), + new PredefinedOperator (types.Double, Operator.ComparisonMask, bool_type), + new PredefinedOperator (types.Decimal, Operator.ComparisonMask, bool_type), + + new PredefinedStringOperator (types.String, Operator.AdditionMask, types.String), + // Remaining string operators are in lifted tables + + new PredefinedOperator (bool_type, Operator.BitwiseMask | Operator.LogicalMask | Operator.EqualityMask, bool_type), + + new PredefinedOperator (types.UInt, types.Int, Operator.ShiftMask), + new PredefinedOperator (types.Long, types.Int, Operator.ShiftMask), + new PredefinedOperator (types.ULong, types.Int, Operator.ShiftMask) + }; + + } + public static PredefinedOperator[] CreateStandardLiftedOperatorsTable (ModuleContainer module) + { + var types = module.Compiler.BuiltinTypes; + + // + // Not strictly lifted but need to be in second group otherwise expressions like + // int + null would resolve to +(object, string) instead of +(int?, int?) + // + var string_operators = new [] { + new PredefinedStringOperator (types.String, types.Object, Operator.AdditionMask, types.String), + new PredefinedStringOperator (types.Object, types.String, Operator.AdditionMask, types.String), + }; + + var nullable = module.PredefinedTypes.Nullable.TypeSpec; + if (nullable == null) + return string_operators; + + var bool_type = types.Bool; + + var nullable_bool = nullable.MakeGenericType (module, new[] { bool_type }); + var nullable_int = nullable.MakeGenericType (module, new[] { types.Int }); + var nullable_uint = nullable.MakeGenericType (module, new[] { types.UInt }); + var nullable_long = nullable.MakeGenericType (module, new[] { types.Long }); + var nullable_ulong = nullable.MakeGenericType (module, new[] { types.ULong }); + var nullable_float = nullable.MakeGenericType (module, new[] { types.Float }); + var nullable_double = nullable.MakeGenericType (module, new[] { types.Double }); + var nullable_decimal = nullable.MakeGenericType (module, new[] { types.Decimal }); + + return new[] { + new PredefinedOperator (nullable_int, Operator.NullableMask | Operator.ArithmeticMask | Operator.BitwiseMask | Operator.ShiftMask), + new PredefinedOperator (nullable_uint, Operator.NullableMask | Operator.ArithmeticMask | Operator.BitwiseMask), + new PredefinedOperator (nullable_long, Operator.NullableMask | Operator.ArithmeticMask | Operator.BitwiseMask), + new PredefinedOperator (nullable_ulong, Operator.NullableMask | Operator.ArithmeticMask | Operator.BitwiseMask), + new PredefinedOperator (nullable_float, Operator.NullableMask | Operator.ArithmeticMask), + new PredefinedOperator (nullable_double, Operator.NullableMask | Operator.ArithmeticMask), + new PredefinedOperator (nullable_decimal, Operator.NullableMask | Operator.ArithmeticMask), + + new PredefinedOperator (nullable_int, Operator.NullableMask | Operator.ComparisonMask, bool_type), + new PredefinedOperator (nullable_uint, Operator.NullableMask | Operator.ComparisonMask, bool_type), + new PredefinedOperator (nullable_long, Operator.NullableMask | Operator.ComparisonMask, bool_type), + new PredefinedOperator (nullable_ulong, Operator.NullableMask | Operator.ComparisonMask, bool_type), + new PredefinedOperator (nullable_float, Operator.NullableMask | Operator.ComparisonMask, bool_type), + new PredefinedOperator (nullable_double, Operator.NullableMask | Operator.ComparisonMask, bool_type), + new PredefinedOperator (nullable_decimal, Operator.NullableMask | Operator.ComparisonMask, bool_type), + + new PredefinedOperator (nullable_bool, Operator.NullableMask | Operator.BitwiseMask, nullable_bool), + + new PredefinedOperator (nullable_uint, nullable_int, Operator.NullableMask | Operator.ShiftMask), + new PredefinedOperator (nullable_long, nullable_int, Operator.NullableMask | Operator.ShiftMask), + new PredefinedOperator (nullable_ulong, nullable_int, Operator.NullableMask | Operator.ShiftMask), + + string_operators [0], + string_operators [1] + }; + } + + public static PredefinedOperator[] CreateEqualityOperatorsTable (BuiltinTypes types) + { + TypeSpec bool_type = types.Bool; + + return new[] { + new PredefinedEqualityOperator (types.String, bool_type), + new PredefinedEqualityOperator (types.Delegate, bool_type), + new PredefinedOperator (bool_type, Operator.EqualityMask, bool_type), + new PredefinedOperator (types.Int, Operator.EqualityMask, bool_type), + new PredefinedOperator (types.UInt, Operator.EqualityMask, bool_type), + new PredefinedOperator (types.Long, Operator.EqualityMask, bool_type), + new PredefinedOperator (types.ULong, Operator.EqualityMask, bool_type), + new PredefinedOperator (types.Float, Operator.EqualityMask, bool_type), + new PredefinedOperator (types.Double, Operator.EqualityMask, bool_type), + new PredefinedOperator (types.Decimal, Operator.EqualityMask, bool_type), + }; + } + + public static PredefinedOperator[] CreateEqualityLiftedOperatorsTable (ModuleContainer module) + { + var nullable = module.PredefinedTypes.Nullable.TypeSpec; + + if (nullable == null) + return new PredefinedOperator [0]; + + var types = module.Compiler.BuiltinTypes; + var bool_type = types.Bool; + var nullable_bool = nullable.MakeGenericType (module, new [] { bool_type }); + var nullable_int = nullable.MakeGenericType (module, new[] { types.Int }); + var nullable_uint = nullable.MakeGenericType (module, new[] { types.UInt }); + var nullable_long = nullable.MakeGenericType (module, new[] { types.Long }); + var nullable_ulong = nullable.MakeGenericType (module, new[] { types.ULong }); + var nullable_float = nullable.MakeGenericType (module, new[] { types.Float }); + var nullable_double = nullable.MakeGenericType (module, new[] { types.Double }); + var nullable_decimal = nullable.MakeGenericType (module, new[] { types.Decimal }); + + return new [] { + new PredefinedOperator (nullable_bool, Operator.NullableMask | Operator.EqualityMask, bool_type), + new PredefinedOperator (nullable_int, Operator.NullableMask | Operator.EqualityMask, bool_type), + new PredefinedOperator (nullable_uint, Operator.NullableMask | Operator.EqualityMask, bool_type), + new PredefinedOperator (nullable_long, Operator.NullableMask | Operator.EqualityMask, bool_type), + new PredefinedOperator (nullable_ulong, Operator.NullableMask | Operator.EqualityMask, bool_type), + new PredefinedOperator (nullable_float, Operator.NullableMask | Operator.EqualityMask, bool_type), + new PredefinedOperator (nullable_double, Operator.NullableMask | Operator.EqualityMask, bool_type), + new PredefinedOperator (nullable_decimal, Operator.NullableMask | Operator.EqualityMask, bool_type) + }; + } + + // + // 7.2.6.2 Binary numeric promotions + // + bool DoBinaryOperatorPromotion (ResolveContext rc) + { + TypeSpec ltype = left.Type; + if (ltype.IsNullableType) { + ltype = Nullable.NullableInfo.GetUnderlyingType (ltype); + } + + // + // This is numeric promotion code only + // + if (ltype.BuiltinType == BuiltinTypeSpec.Type.Bool) + return true; + + TypeSpec rtype = right.Type; + if (rtype.IsNullableType) { + rtype = Nullable.NullableInfo.GetUnderlyingType (rtype); + } + + var lb = ltype.BuiltinType; + var rb = rtype.BuiltinType; + TypeSpec type; + Expression expr; + + if (lb == BuiltinTypeSpec.Type.Decimal || rb == BuiltinTypeSpec.Type.Decimal) { + type = rc.BuiltinTypes.Decimal; + } else if (lb == BuiltinTypeSpec.Type.Double || rb == BuiltinTypeSpec.Type.Double) { + type = rc.BuiltinTypes.Double; + } else if (lb == BuiltinTypeSpec.Type.Float || rb == BuiltinTypeSpec.Type.Float) { + type = rc.BuiltinTypes.Float; + } else if (lb == BuiltinTypeSpec.Type.ULong || rb == BuiltinTypeSpec.Type.ULong) { + type = rc.BuiltinTypes.ULong; + + if (IsSignedType (lb)) { + expr = ConvertSignedConstant (left, type); + if (expr == null) + return false; + left = expr; + } else if (IsSignedType (rb)) { + expr = ConvertSignedConstant (right, type); + if (expr == null) + return false; + right = expr; + } + + } else if (lb == BuiltinTypeSpec.Type.Long || rb == BuiltinTypeSpec.Type.Long) { + type = rc.BuiltinTypes.Long; + } else if (lb == BuiltinTypeSpec.Type.UInt || rb == BuiltinTypeSpec.Type.UInt) { + type = rc.BuiltinTypes.UInt; + + if (IsSignedType (lb)) { + expr = ConvertSignedConstant (left, type); + if (expr == null) + type = rc.BuiltinTypes.Long; + } else if (IsSignedType (rb)) { + expr = ConvertSignedConstant (right, type); + if (expr == null) + type = rc.BuiltinTypes.Long; + } + } else { + type = rc.BuiltinTypes.Int; + } + + if (ltype != type) { + expr = PromoteExpression (rc, left, type); + if (expr == null) + return false; + + left = expr; + } + + if (rtype != type) { + expr = PromoteExpression (rc, right, type); + if (expr == null) + return false; + + right = expr; + } + + return true; + } + + static bool IsSignedType (BuiltinTypeSpec.Type type) + { + switch (type) { + case BuiltinTypeSpec.Type.Int: + case BuiltinTypeSpec.Type.Short: + case BuiltinTypeSpec.Type.SByte: + case BuiltinTypeSpec.Type.Long: + return true; + default: + return false; + } + } + + static Expression ConvertSignedConstant (Expression expr, TypeSpec type) + { + var c = expr as Constant; + if (c == null) + return null; + + return c.ConvertImplicitly (type); + } + + static Expression PromoteExpression (ResolveContext rc, Expression expr, TypeSpec type) + { + if (expr.Type.IsNullableType) { + return Convert.ImplicitConversionStandard (rc, expr, + rc.Module.PredefinedTypes.Nullable.TypeSpec.MakeGenericType (rc, new[] { type }), expr.Location); + } + + var c = expr as Constant; + if (c != null) + return c.ConvertImplicitly (type); + + return Convert.ImplicitNumericConversion (expr, type); + } + + protected override Expression DoResolve (ResolveContext ec) + { + if (left == null) + return null; + + if ((oper == Operator.Subtraction) && (left is ParenthesizedExpression)) { + left = ((ParenthesizedExpression) left).Expr; + left = left.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.Type); + if (left == null) + return null; + + if (left.eclass == ExprClass.Type) { + ec.Report.Error (75, loc, "To cast a negative value, you must enclose the value in parentheses"); + return null; + } + } else + left = left.Resolve (ec); + + if (left == null) + return null; + + right = right.Resolve (ec); + if (right == null) + return null; + + Constant lc = left as Constant; + Constant rc = right as Constant; + + // The conversion rules are ignored in enum context but why + if (!ec.HasSet (ResolveContext.Options.EnumScope) && lc != null && rc != null && (left.Type.IsEnum || right.Type.IsEnum)) { + lc = EnumLiftUp (ec, lc, rc); + if (lc != null) + rc = EnumLiftUp (ec, rc, lc); + } + + if (rc != null && lc != null) { + int prev_e = ec.Report.Errors; + Expression e = ConstantFold.BinaryFold (ec, oper, lc, rc, loc); + if (e != null || ec.Report.Errors != prev_e) + return e; + } + + // Comparison warnings + if ((oper & Operator.ComparisonMask) != 0) { + if (left.Equals (right)) { + ec.Report.Warning (1718, 3, loc, "A comparison made to same variable. Did you mean to compare something else?"); + } + CheckOutOfRangeComparison (ec, lc, right.Type); + CheckOutOfRangeComparison (ec, rc, left.Type); + } + + if (left.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic || right.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) + return DoResolveDynamic (ec); + + return DoResolveCore (ec, left, right); + } + + Expression DoResolveDynamic (ResolveContext rc) + { + var lt = left.Type; + var rt = right.Type; + if (lt.Kind == MemberKind.Void || lt == InternalType.MethodGroup || lt == InternalType.AnonymousMethod || + rt.Kind == MemberKind.Void || rt == InternalType.MethodGroup || rt == InternalType.AnonymousMethod) { + Error_OperatorCannotBeApplied (rc, left, right); + return null; + } + + Arguments args; + + // + // Special handling for logical boolean operators which require rhs not to be + // evaluated based on lhs value + // + if ((oper & Operator.LogicalMask) != 0) { + Expression cond_left, cond_right, expr; + + args = new Arguments (2); + + if (lt.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + LocalVariable temp = LocalVariable.CreateCompilerGenerated (lt, rc.CurrentBlock, loc); + + var cond_args = new Arguments (1); + cond_args.Add (new Argument (new SimpleAssign (temp.CreateReferenceExpression (rc, loc), left).Resolve (rc))); + + // + // dynamic && bool => IsFalse (temp = left) ? temp : temp && right; + // dynamic || bool => IsTrue (temp = left) ? temp : temp || right; + // + left = temp.CreateReferenceExpression (rc, loc); + if (oper == Operator.LogicalAnd) { + expr = DynamicUnaryConversion.CreateIsFalse (rc, cond_args, loc); + cond_left = left; + } else { + expr = DynamicUnaryConversion.CreateIsTrue (rc, cond_args, loc); + cond_left = left; + } + + args.Add (new Argument (left)); + args.Add (new Argument (right)); + cond_right = new DynamicExpressionStatement (this, args, loc); + } else { + LocalVariable temp = LocalVariable.CreateCompilerGenerated (rc.BuiltinTypes.Bool, rc.CurrentBlock, loc); + + if (!Convert.ImplicitConversionExists (rc, left, temp.Type) && (oper == Operator.LogicalAnd ? GetOperatorFalse (rc, left, loc) : GetOperatorTrue (rc, left, loc)) == null) { + rc.Report.Error (7083, left.Location, + "Expression must be implicitly convertible to Boolean or its type `{0}' must define operator `{1}'", + lt.GetSignatureForError (), oper == Operator.LogicalAnd ? "false" : "true"); + return null; + } + + args.Add (new Argument (temp.CreateReferenceExpression (rc, loc).Resolve (rc))); + args.Add (new Argument (right)); + right = new DynamicExpressionStatement (this, args, loc); + + // + // bool && dynamic => (temp = left) ? temp && right : temp; + // bool || dynamic => (temp = left) ? temp : temp || right; + // + if (oper == Operator.LogicalAnd) { + cond_left = right; + cond_right = temp.CreateReferenceExpression (rc, loc); + } else { + cond_left = temp.CreateReferenceExpression (rc, loc); + cond_right = right; + } + + expr = new BooleanExpression (new SimpleAssign (temp.CreateReferenceExpression (rc, loc), left)); + } + + return new Conditional (expr, cond_left, cond_right, loc).Resolve (rc); + } + + args = new Arguments (2); + args.Add (new Argument (left)); + args.Add (new Argument (right)); + return new DynamicExpressionStatement (this, args, loc).Resolve (rc); + } + + Expression DoResolveCore (ResolveContext ec, Expression left_orig, Expression right_orig) + { + Expression expr = ResolveOperator (ec); + if (expr == null) + Error_OperatorCannotBeApplied (ec, left_orig, right_orig); + + if (left == null || right == null) + throw new InternalErrorException ("Invalid conversion"); + + if (oper == Operator.BitwiseOr) + CheckBitwiseOrOnSignExtended (ec); + + return expr; + } + + public override SLE.Expression MakeExpression (BuilderContext ctx) + { + return MakeExpression (ctx, left, right); + } + + public SLE.Expression MakeExpression (BuilderContext ctx, Expression left, Expression right) + { + var le = left.MakeExpression (ctx); + var re = right.MakeExpression (ctx); + bool is_checked = ctx.HasSet (BuilderContext.Options.CheckedScope); + + switch (oper) { + case Operator.Addition: + return is_checked ? SLE.Expression.AddChecked (le, re) : SLE.Expression.Add (le, re); + case Operator.BitwiseAnd: + return SLE.Expression.And (le, re); + case Operator.BitwiseOr: + return SLE.Expression.Or (le, re); + case Operator.Division: + return SLE.Expression.Divide (le, re); + case Operator.Equality: + return SLE.Expression.Equal (le, re); + case Operator.ExclusiveOr: + return SLE.Expression.ExclusiveOr (le, re); + case Operator.GreaterThan: + return SLE.Expression.GreaterThan (le, re); + case Operator.GreaterThanOrEqual: + return SLE.Expression.GreaterThanOrEqual (le, re); + case Operator.Inequality: + return SLE.Expression.NotEqual (le, re); + case Operator.LeftShift: + return SLE.Expression.LeftShift (le, re); + case Operator.LessThan: + return SLE.Expression.LessThan (le, re); + case Operator.LessThanOrEqual: + return SLE.Expression.LessThanOrEqual (le, re); + case Operator.LogicalAnd: + return SLE.Expression.AndAlso (le, re); + case Operator.LogicalOr: + return SLE.Expression.OrElse (le, re); + case Operator.Modulus: + return SLE.Expression.Modulo (le, re); + case Operator.Multiply: + return is_checked ? SLE.Expression.MultiplyChecked (le, re) : SLE.Expression.Multiply (le, re); + case Operator.RightShift: + return SLE.Expression.RightShift (le, re); + case Operator.Subtraction: + return is_checked ? SLE.Expression.SubtractChecked (le, re) : SLE.Expression.Subtract (le, re); + default: + throw new NotImplementedException (oper.ToString ()); + } + } + + // + // D operator + (D x, D y) + // D operator - (D x, D y) + // + Expression ResolveOperatorDelegate (ResolveContext ec, TypeSpec l, TypeSpec r) + { + if (l != r && !TypeSpecComparer.Variant.IsEqual (r, l)) { + Expression tmp; + if (right.eclass == ExprClass.MethodGroup || r == InternalType.AnonymousMethod || r == InternalType.NullLiteral) { + tmp = Convert.ImplicitConversionRequired (ec, right, l, loc); + if (tmp == null) + return null; + right = tmp; + r = right.Type; + } else if (left.eclass == ExprClass.MethodGroup || (l == InternalType.AnonymousMethod || l == InternalType.NullLiteral)) { + tmp = Convert.ImplicitConversionRequired (ec, left, r, loc); + if (tmp == null) + return null; + left = tmp; + l = left.Type; + } else { + return null; + } + } + + MethodSpec method = null; + Arguments args = new Arguments (2); + args.Add (new Argument (left)); + args.Add (new Argument (right)); + + if (oper == Operator.Addition) { + method = ec.Module.PredefinedMembers.DelegateCombine.Resolve (loc); + } else if (oper == Operator.Subtraction) { + method = ec.Module.PredefinedMembers.DelegateRemove.Resolve (loc); + } + + if (method == null) + return new EmptyExpression (ec.BuiltinTypes.Decimal); + + Expression expr = new UserOperatorCall (method, args, CreateExpressionTree, loc); + return new ClassCast (expr, l); + } + + // + // Resolves enumeration operators where only single predefined overload exists, handles lifted versions too + // + Expression ResolveSingleEnumOperators (ResolveContext rc, bool lenum, bool renum, TypeSpec ltype, TypeSpec rtype) + { + // + // bool operator == (E x, E y); + // bool operator != (E x, E y); + // bool operator < (E x, E y); + // bool operator > (E x, E y); + // bool operator <= (E x, E y); + // bool operator >= (E x, E y); + // + // E operator & (E x, E y); + // E operator | (E x, E y); + // E operator ^ (E x, E y); + // + Expression expr; + if ((oper & Operator.ComparisonMask) != 0) { + type = rc.BuiltinTypes.Bool; + } else { + if (lenum) + type = ltype; + else if (renum) + type = rtype; + else if (ltype.IsNullableType && Nullable.NullableInfo.GetUnderlyingType (ltype).IsEnum) + type = ltype; + else + type = rtype; + } + + if (ltype == rtype) { + if (lenum || renum) + return this; + + var lifted = new Nullable.LiftedBinaryOperator (this); + lifted.Left = left; + lifted.Right = right; + return lifted.Resolve (rc); + } + + if (renum && !ltype.IsNullableType) { + expr = Convert.ImplicitConversion (rc, left, rtype, loc); + if (expr != null) { + left = expr; + return this; + } + } else if (lenum && !rtype.IsNullableType) { + expr = Convert.ImplicitConversion (rc, right, ltype, loc); + if (expr != null) { + right = expr; + return this; + } + } + + // + // Now try lifted version of predefined operator + // + var nullable_type = rc.Module.PredefinedTypes.Nullable.TypeSpec; + if (nullable_type != null) { + if (renum && !ltype.IsNullableType) { + var lifted_type = nullable_type.MakeGenericType (rc.Module, new[] { rtype }); + + expr = Convert.ImplicitConversion (rc, left, lifted_type, loc); + if (expr != null) { + left = expr; + right = Convert.ImplicitConversion (rc, right, lifted_type, loc); + } + + if ((oper & Operator.BitwiseMask) != 0) + type = lifted_type; + + if (left.IsNull) { + if ((oper & Operator.BitwiseMask) != 0) + return Nullable.LiftedNull.CreateFromExpression (rc, this); + + return CreateLiftedValueTypeResult (rc, rtype); + } + + if (expr != null) { + var lifted = new Nullable.LiftedBinaryOperator (this); + lifted.Left = expr; + lifted.Right = right; + return lifted.Resolve (rc); + } + } else if (lenum && !rtype.IsNullableType) { + var lifted_type = nullable_type.MakeGenericType (rc.Module, new[] { ltype }); + + expr = Convert.ImplicitConversion (rc, right, lifted_type, loc); + if (expr != null) { + right = expr; + left = Convert.ImplicitConversion (rc, left, lifted_type, loc); + } + + if ((oper & Operator.BitwiseMask) != 0) + type = lifted_type; + + if (right.IsNull) { + if ((oper & Operator.BitwiseMask) != 0) + return Nullable.LiftedNull.CreateFromExpression (rc, this); + + return CreateLiftedValueTypeResult (rc, ltype); + } + + if (expr != null) { + var lifted = new Nullable.LiftedBinaryOperator (this); + lifted.Left = left; + lifted.Right = expr; + return lifted.Resolve (rc); + } + } else if (rtype.IsNullableType && Nullable.NullableInfo.GetUnderlyingType (rtype).IsEnum) { + Nullable.Unwrap unwrap = null; + if (left.IsNull || right.IsNull) { + if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion)) + left = Convert.ImplicitConversion (rc, left, rtype, left.Location); + + if ((oper & Operator.RelationalMask) != 0) + return CreateLiftedValueTypeResult (rc, rtype); + + if ((oper & Operator.BitwiseMask) != 0) + return Nullable.LiftedNull.CreateFromExpression (rc, this); + + if (right.IsNull) + return CreateLiftedValueTypeResult (rc, left.Type); + + // Equality operators are valid between E? and null + expr = left; + unwrap = new Nullable.Unwrap (right); + } else { + expr = Convert.ImplicitConversion (rc, left, Nullable.NullableInfo.GetUnderlyingType (rtype), loc); + if (expr == null) + return null; + } + + if (expr != null) { + var lifted = new Nullable.LiftedBinaryOperator (this); + lifted.Left = expr; + lifted.Right = right; + lifted.UnwrapRight = unwrap; + return lifted.Resolve (rc); + } + } else if (ltype.IsNullableType && Nullable.NullableInfo.GetUnderlyingType (ltype).IsEnum) { + Nullable.Unwrap unwrap = null; + if (right.IsNull || left.IsNull) { + if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion)) + right = Convert.ImplicitConversion (rc, right, ltype, right.Location); + + if ((oper & Operator.RelationalMask) != 0) + return CreateLiftedValueTypeResult (rc, ltype); + + if ((oper & Operator.BitwiseMask) != 0) + return Nullable.LiftedNull.CreateFromExpression (rc, this); + + if (left.IsNull) + return CreateLiftedValueTypeResult (rc, right.Type); + + // Equality operators are valid between E? and null + expr = right; + unwrap = new Nullable.Unwrap (left); + } else { + expr = Convert.ImplicitConversion (rc, right, Nullable.NullableInfo.GetUnderlyingType (ltype), loc); + if (expr == null) + return null; + } + + if (expr != null) { + var lifted = new Nullable.LiftedBinaryOperator (this); + lifted.Left = left; + lifted.UnwrapLeft = unwrap; + lifted.Right = expr; + return lifted.Resolve (rc); + } + } + } + + return null; + } + + static Expression ConvertEnumOperandToUnderlyingType (ResolveContext rc, Expression expr, bool liftType) + { + TypeSpec underlying_type; + if (expr.Type.IsNullableType) { + var nt = Nullable.NullableInfo.GetUnderlyingType (expr.Type); + if (nt.IsEnum) + underlying_type = EnumSpec.GetUnderlyingType (nt); + else + underlying_type = nt; + } else if (expr.Type.IsEnum) { + underlying_type = EnumSpec.GetUnderlyingType (expr.Type); + } else { + underlying_type = expr.Type; + } + + switch (underlying_type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + case BuiltinTypeSpec.Type.Byte: + case BuiltinTypeSpec.Type.Short: + case BuiltinTypeSpec.Type.UShort: + underlying_type = rc.BuiltinTypes.Int; + break; + } + + if (expr.Type.IsNullableType || liftType) + underlying_type = rc.Module.PredefinedTypes.Nullable.TypeSpec.MakeGenericType (rc.Module, new[] { underlying_type }); + + if (expr.Type == underlying_type) + return expr; + + return EmptyCast.Create (expr, underlying_type); + } + + Expression ResolveEnumOperators (ResolveContext rc, bool lenum, bool renum, TypeSpec ltype, TypeSpec rtype) + { + // + // U operator - (E e, E f) + // E operator - (E e, U x) // Internal decomposition operator + // E operator - (U x, E e) // Internal decomposition operator + // + // E operator + (E e, U x) + // E operator + (U x, E e) + // + + TypeSpec enum_type; + + if (lenum) + enum_type = ltype; + else if (renum) + enum_type = rtype; + else if (ltype.IsNullableType && Nullable.NullableInfo.GetUnderlyingType (ltype).IsEnum) + enum_type = ltype; + else + enum_type = rtype; + + Expression expr; + if (!enum_type.IsNullableType) { + expr = ResolveOperatorPredefined (rc, rc.Module.GetPredefinedEnumAritmeticOperators (enum_type, false), false); + if (expr != null) { + if (oper == Operator.Subtraction) + expr = ConvertEnumSubtractionResult (rc, expr); + else + expr = ConvertEnumAdditionalResult (expr, enum_type); + + enum_conversion = GetEnumResultCast (expr.Type); + + return expr; + } + + enum_type = rc.Module.PredefinedTypes.Nullable.TypeSpec.MakeGenericType (rc.Module, new[] { enum_type }); + } + + expr = ResolveOperatorPredefined (rc, rc.Module.GetPredefinedEnumAritmeticOperators (enum_type, true), false); + if (expr != null) { + if (oper == Operator.Subtraction) + expr = ConvertEnumSubtractionResult (rc, expr); + else + expr = ConvertEnumAdditionalResult (expr, enum_type); + + enum_conversion = GetEnumResultCast (expr.Type); + } + + return expr; + } + + static Expression ConvertEnumAdditionalResult (Expression expr, TypeSpec enumType) + { + return EmptyCast.Create (expr, enumType); + } + + Expression ConvertEnumSubtractionResult (ResolveContext rc, Expression expr) + { + // + // Enumeration subtraction has different result type based on + // best overload + // + TypeSpec result_type; + if (left.Type == right.Type) { + var c = right as EnumConstant; + if (c != null && c.IsZeroInteger && !right.Type.IsEnum) { + // + // LAMESPEC: This is quite unexpected for expression E - 0 the return type is + // E which is not what expressions E - 1 or 0 - E return + // + result_type = left.Type; + } else { + result_type = left.Type.IsNullableType ? + Nullable.NullableInfo.GetEnumUnderlyingType (rc.Module, left.Type) : + EnumSpec.GetUnderlyingType (left.Type); + } + } else { + if (IsEnumOrNullableEnum (left.Type)) { + result_type = left.Type; + } else { + result_type = right.Type; + } + + if (expr is Nullable.LiftedBinaryOperator && !result_type.IsNullableType) + result_type = rc.Module.PredefinedTypes.Nullable.TypeSpec.MakeGenericType (rc.Module, new[] { result_type }); + } + + return EmptyCast.Create (expr, result_type); + } + + public static ConvCast.Mode GetEnumResultCast (TypeSpec type) + { + if (type.IsNullableType) + type = Nullable.NullableInfo.GetUnderlyingType (type); + + if (type.IsEnum) + type = EnumSpec.GetUnderlyingType (type); + + switch (type.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + return ConvCast.Mode.I4_I1; + case BuiltinTypeSpec.Type.Byte: + return ConvCast.Mode.I4_U1; + case BuiltinTypeSpec.Type.Short: + return ConvCast.Mode.I4_I2; + case BuiltinTypeSpec.Type.UShort: + return ConvCast.Mode.I4_U2; + } + + return 0; + } + + // + // Equality operators rules + // + Expression ResolveEquality (ResolveContext ec, TypeSpec l, TypeSpec r, bool primitives_only) + { + Expression result; + type = ec.BuiltinTypes.Bool; + bool no_arg_conv = false; + + if (!primitives_only) { + + // + // a, Both operands are reference-type values or the value null + // b, One operand is a value of type T where T is a type-parameter and + // the other operand is the value null. Furthermore T does not have the + // value type constraint + // + // LAMESPEC: Very confusing details in the specification, basically any + // reference like type-parameter is allowed + // + var tparam_l = l as TypeParameterSpec; + var tparam_r = r as TypeParameterSpec; + if (tparam_l != null) { + if (right is NullLiteral) { + if (tparam_l.GetEffectiveBase ().BuiltinType == BuiltinTypeSpec.Type.ValueType) + return null; + + left = new BoxedCast (left, ec.BuiltinTypes.Object); + return this; + } + + if (!tparam_l.IsReferenceType) + return null; + + l = tparam_l.GetEffectiveBase (); + left = new BoxedCast (left, l); + } else if (left is NullLiteral && tparam_r == null) { + if (TypeSpec.IsReferenceType (r)) + return this; + + if (r.Kind == MemberKind.InternalCompilerType) + return null; + } + + if (tparam_r != null) { + if (left is NullLiteral) { + if (tparam_r.GetEffectiveBase ().BuiltinType == BuiltinTypeSpec.Type.ValueType) + return null; + + right = new BoxedCast (right, ec.BuiltinTypes.Object); + return this; + } + + if (!tparam_r.IsReferenceType) + return null; + + r = tparam_r.GetEffectiveBase (); + right = new BoxedCast (right, r); + } else if (right is NullLiteral) { + if (TypeSpec.IsReferenceType (l)) + return this; + + if (l.Kind == MemberKind.InternalCompilerType) + return null; + } + + // + // LAMESPEC: method groups can be compared when they convert to other side delegate + // + if (l.IsDelegate) { + if (right.eclass == ExprClass.MethodGroup) { + result = Convert.ImplicitConversion (ec, right, l, loc); + if (result == null) + return null; + + right = result; + r = l; + } else if (r.IsDelegate && l != r) { + return null; + } + } else if (left.eclass == ExprClass.MethodGroup && r.IsDelegate) { + result = Convert.ImplicitConversionRequired (ec, left, r, loc); + if (result == null) + return null; + + left = result; + l = r; + } else { + no_arg_conv = l == r && !l.IsStruct; + } + } + + // + // bool operator != (string a, string b) + // bool operator == (string a, string b) + // + // bool operator != (Delegate a, Delegate b) + // bool operator == (Delegate a, Delegate b) + // + // bool operator != (bool a, bool b) + // bool operator == (bool a, bool b) + // + // LAMESPEC: Reference equality comparison can apply to value/reference types when + // they implement an implicit conversion to any of types above. This does + // not apply when both operands are of same reference type + // + if (r.BuiltinType != BuiltinTypeSpec.Type.Object && l.BuiltinType != BuiltinTypeSpec.Type.Object) { + result = ResolveOperatorPredefined (ec, ec.BuiltinTypes.OperatorsBinaryEquality, no_arg_conv); + if (result != null) + return result; + + // + // Now try lifted version of predefined operators + // + if (no_arg_conv && !l.IsNullableType) { + // + // Optimizes cases which won't match + // + } else { + result = ResolveOperatorPredefined (ec, ec.Module.OperatorsBinaryEqualityLifted, no_arg_conv); + if (result != null) + return result; + } + + // + // The == and != operators permit one operand to be a value of a nullable + // type and the other to be the null literal, even if no predefined or user-defined + // operator (in unlifted or lifted form) exists for the operation. + // + if ((l.IsNullableType && right.IsNull) || (r.IsNullableType && left.IsNull)) { + var lifted = new Nullable.LiftedBinaryOperator (this); + lifted.Left = left; + lifted.Right = right; + return lifted.Resolve (ec); + } + } + + // + // bool operator != (object a, object b) + // bool operator == (object a, object b) + // + // An explicit reference conversion exists from the + // type of either operand to the type of the other operand. + // + + // Optimize common path + if (l == r) { + return l.Kind == MemberKind.InternalCompilerType || l.Kind == MemberKind.Struct ? null : this; + } + + if (!Convert.ExplicitReferenceConversionExists (l, r) && + !Convert.ExplicitReferenceConversionExists (r, l)) + return null; + + // Reject allowed explicit conversions like int->object + if (!TypeSpec.IsReferenceType (l) || !TypeSpec.IsReferenceType (r)) + return null; + + if (l.BuiltinType == BuiltinTypeSpec.Type.String || l.BuiltinType == BuiltinTypeSpec.Type.Delegate || l.IsDelegate || MemberCache.GetUserOperator (l, CSharp.Operator.OpType.Equality, false) != null) + ec.Report.Warning (253, 2, loc, + "Possible unintended reference comparison. Consider casting the right side expression to type `{0}' to get value comparison", + l.GetSignatureForError ()); + + if (r.BuiltinType == BuiltinTypeSpec.Type.String || r.BuiltinType == BuiltinTypeSpec.Type.Delegate || r.IsDelegate || MemberCache.GetUserOperator (r, CSharp.Operator.OpType.Equality, false) != null) + ec.Report.Warning (252, 2, loc, + "Possible unintended reference comparison. Consider casting the left side expression to type `{0}' to get value comparison", + r.GetSignatureForError ()); + + return this; + } + + + Expression ResolveOperatorPointer (ResolveContext ec, TypeSpec l, TypeSpec r) + { + // + // bool operator == (void* x, void* y); + // bool operator != (void* x, void* y); + // bool operator < (void* x, void* y); + // bool operator > (void* x, void* y); + // bool operator <= (void* x, void* y); + // bool operator >= (void* x, void* y); + // + if ((oper & Operator.ComparisonMask) != 0) { + Expression temp; + if (!l.IsPointer) { + temp = Convert.ImplicitConversion (ec, left, r, left.Location); + if (temp == null) + return null; + left = temp; + } + + if (!r.IsPointer) { + temp = Convert.ImplicitConversion (ec, right, l, right.Location); + if (temp == null) + return null; + right = temp; + } + + type = ec.BuiltinTypes.Bool; + return this; + } + + return ResolveOperatorPredefined (ec, ec.BuiltinTypes.OperatorsBinaryUnsafe, false); + } + + // + // Build-in operators method overloading + // + Expression ResolveOperatorPredefined (ResolveContext ec, PredefinedOperator [] operators, bool primitives_only) + { + PredefinedOperator best_operator = null; + TypeSpec l = left.Type; + TypeSpec r = right.Type; + Operator oper_mask = oper & ~Operator.ValuesOnlyMask; + + foreach (PredefinedOperator po in operators) { + if ((po.OperatorsMask & oper_mask) == 0) + continue; + + if (primitives_only) { + if (!po.IsPrimitiveApplicable (l, r)) + continue; + } else { + if (!po.IsApplicable (ec, left, right)) + continue; + } + + if (best_operator == null) { + best_operator = po; + if (primitives_only) + break; + + continue; + } + + best_operator = po.ResolveBetterOperator (ec, best_operator); + + if (best_operator == null) { + ec.Report.Error (34, loc, "Operator `{0}' is ambiguous on operands of type `{1}' and `{2}'", + OperName (oper), l.GetSignatureForError (), r.GetSignatureForError ()); + + best_operator = po; + break; + } + } + + if (best_operator == null) + return null; + + return best_operator.ConvertResult (ec, this); + } + + // + // Optimize & constant expressions with 0 value + // + Expression OptimizeAndOperation (Expression expr) + { + Constant rc = right as Constant; + Constant lc = left as Constant; + if ((lc != null && lc.IsDefaultValue) || (rc != null && rc.IsDefaultValue)) { + // + // The result is a constant with side-effect + // + Constant side_effect = rc == null ? + new SideEffectConstant (lc, right, loc) : + new SideEffectConstant (rc, left, loc); + + return ReducedExpression.Create (side_effect, expr); + } + + return expr; + } + + // + // Value types can be compared with the null literal because of the lifting + // language rules. However the result is always true or false. + // + public Expression CreateLiftedValueTypeResult (ResolveContext rc, TypeSpec valueType) + { + if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion)) { + type = rc.BuiltinTypes.Bool; + return this; + } + + // FIXME: Handle side effect constants + Constant c = new BoolConstant (rc.BuiltinTypes, Oper == Operator.Inequality, loc); + + if ((Oper & Operator.EqualityMask) != 0) { + rc.Report.Warning (472, 2, loc, "The result of comparing value type `{0}' with null is always `{1}'", + valueType.GetSignatureForError (), c.GetValueAsLiteral ()); + } else { + rc.Report.Warning (464, 2, loc, "The result of comparing type `{0}' with null is always `{1}'", + valueType.GetSignatureForError (), c.GetValueAsLiteral ()); + } + + return c; + } + + // + // Performs user-operator overloading + // + Expression ResolveUserOperator (ResolveContext rc, Expression left, Expression right) + { + Expression oper_expr; + + var op = ConvertBinaryToUserOperator (oper); + var l = left.Type; + if (l.IsNullableType) + l = Nullable.NullableInfo.GetUnderlyingType (l); + var r = right.Type; + if (r.IsNullableType) + r = Nullable.NullableInfo.GetUnderlyingType (r); + + IList left_operators = MemberCache.GetUserOperator (l, op, false); + IList right_operators = null; + + if (l != r) { + right_operators = MemberCache.GetUserOperator (r, op, false); + if (right_operators == null && left_operators == null) + return null; + } else if (left_operators == null) { + return null; + } + + Arguments args = new Arguments (2); + Argument larg = new Argument (left); + args.Add (larg); + Argument rarg = new Argument (right); + args.Add (rarg); + + // + // User-defined operator implementations always take precedence + // over predefined operator implementations + // + if (left_operators != null && right_operators != null) { + left_operators = CombineUserOperators (left_operators, right_operators); + } else if (right_operators != null) { + left_operators = right_operators; + } + + const OverloadResolver.Restrictions restr = OverloadResolver.Restrictions.ProbingOnly | + OverloadResolver.Restrictions.NoBaseMembers | OverloadResolver.Restrictions.BaseMembersIncluded; + + var res = new OverloadResolver (left_operators, restr, loc); + + var oper_method = res.ResolveOperator (rc, ref args); + if (oper_method == null) { + // + // Logical && and || cannot be lifted + // + if ((oper & Operator.LogicalMask) != 0) + return null; + + // + // Apply lifted user operators only for liftable types. Implicit conversion + // to nullable types is not allowed + // + if (!IsLiftedOperatorApplicable ()) + return null; + + // TODO: Cache the result in module container + var lifted_methods = CreateLiftedOperators (rc, left_operators); + if (lifted_methods == null) + return null; + + res = new OverloadResolver (lifted_methods, restr | OverloadResolver.Restrictions.ProbingOnly, loc); + + oper_method = res.ResolveOperator (rc, ref args); + if (oper_method == null) + return null; + + MethodSpec best_original = null; + foreach (MethodSpec ms in left_operators) { + if (ms.MemberDefinition == oper_method.MemberDefinition) { + best_original = ms; + break; + } + } + + if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion)) { + // + // Expression trees use lifted notation in this case + // + this.left = Convert.ImplicitConversion (rc, left, oper_method.Parameters.Types[0], left.Location); + this.right = Convert.ImplicitConversion (rc, right, oper_method.Parameters.Types[1], left.Location); + } + + var ptypes = best_original.Parameters.Types; + + if (left.IsNull || right.IsNull) { + // + // The lifted operator produces a null value if one or both operands are null + // + if ((oper & (Operator.ArithmeticMask | Operator.ShiftMask | Operator.BitwiseMask)) != 0) { + type = oper_method.ReturnType; + return Nullable.LiftedNull.CreateFromExpression (rc, this); + } + + // + // The lifted operator produces the value false if one or both operands are null for + // relational operators. + // + if ((oper & Operator.RelationalMask) != 0) { + // + // CSC BUG: This should be different warning, csc reports CS0458 with bool? which is wrong + // because return type is actually bool + // + return CreateLiftedValueTypeResult (rc, left.IsNull ? ptypes [1] : ptypes [0]); + } + + if ((oper & Operator.EqualityMask) != 0 && ((left.IsNull && !right.Type.IsNullableType) || !left.Type.IsNullableType)) { + return CreateLiftedValueTypeResult (rc, left.IsNull ? ptypes [1] : ptypes [0]); + } + } + + type = oper_method.ReturnType; + var lifted = new Nullable.LiftedBinaryOperator (this); + lifted.UserOperator = best_original; + + if (left.Type.IsNullableType && !ptypes[0].IsNullableType) { + lifted.UnwrapLeft = new Nullable.Unwrap (left); + } + + if (right.Type.IsNullableType && !ptypes[1].IsNullableType) { + lifted.UnwrapRight = new Nullable.Unwrap (right); + } + + lifted.Left = Convert.ImplicitConversion (rc, lifted.UnwrapLeft ?? left, ptypes[0], left.Location); + lifted.Right = Convert.ImplicitConversion (rc, lifted.UnwrapRight ?? right, ptypes[1], right.Location); + + return lifted.Resolve (rc); + } + + if ((oper & Operator.LogicalMask) != 0) { + // TODO: CreateExpressionTree is allocated every time + oper_expr = new ConditionalLogicalOperator (oper_method, args, CreateExpressionTree, + oper == Operator.LogicalAnd, loc).Resolve (rc); + } else { + oper_expr = new UserOperatorCall (oper_method, args, CreateExpressionTree, loc); + } + + this.left = larg.Expr; + this.right = rarg.Expr; + + return oper_expr; + } + + bool IsLiftedOperatorApplicable () + { + if (left.Type.IsNullableType) { + if ((oper & Operator.EqualityMask) != 0) + return !right.IsNull; + + return true; + } + + if (right.Type.IsNullableType) { + if ((oper & Operator.EqualityMask) != 0) + return !left.IsNull; + + return true; + } + + if (TypeSpec.IsValueType (left.Type)) + return right.IsNull; + + if (TypeSpec.IsValueType (right.Type)) + return left.IsNull; + + return false; + } + + List CreateLiftedOperators (ResolveContext rc, IList operators) + { + var nullable_type = rc.Module.PredefinedTypes.Nullable.TypeSpec; + if (nullable_type == null) + return null; + + // + // Lifted operators permit predefined and user-defined operators that operate + // on non-nullable value types to also be used with nullable forms of those types. + // Lifted operators are constructed from predefined and user-defined operators + // that meet certain requirements + // + List lifted = null; + foreach (MethodSpec oper in operators) { + TypeSpec rt; + if ((Oper & Operator.ComparisonMask) != 0) { + // + // Result type must be of type bool for lifted comparison operators + // + rt = oper.ReturnType; + if (rt.BuiltinType != BuiltinTypeSpec.Type.Bool) + continue; + } else { + if (!TypeSpec.IsNonNullableValueType (oper.ReturnType)) + continue; + + rt = null; + } + + var ptypes = oper.Parameters.Types; + if (!TypeSpec.IsNonNullableValueType (ptypes [0]) || !TypeSpec.IsNonNullableValueType (ptypes [1])) + continue; + + // + // LAMESPEC: I am not sure why but for equality operators to be lifted + // both types have to match + // + if ((Oper & Operator.EqualityMask) != 0 && ptypes [0] != ptypes [1]) + continue; + + if (lifted == null) + lifted = new List (); + + // + // The lifted form is constructed by adding a single ? modifier to each operand and + // result type except for comparison operators where return type is bool + // + if (rt == null) + rt = nullable_type.MakeGenericType (rc.Module, new[] { oper.ReturnType }); + + var parameters = ParametersCompiled.CreateFullyResolved ( + nullable_type.MakeGenericType (rc.Module, new [] { ptypes[0] }), + nullable_type.MakeGenericType (rc.Module, new [] { ptypes[1] })); + + var lifted_op = new MethodSpec (oper.Kind, oper.DeclaringType, oper.MemberDefinition, + rt, parameters, oper.Modifiers); + + lifted.Add (lifted_op); + } + + return lifted; + } + + // + // Merge two sets of user operators into one, they are mostly distinguish + // except when they share base type and it contains an operator + // + static IList CombineUserOperators (IList left, IList right) + { + var combined = new List (left.Count + right.Count); + combined.AddRange (left); + foreach (var r in right) { + bool same = false; + foreach (var l in left) { + if (l.DeclaringType == r.DeclaringType) { + same = true; + break; + } + } + + if (!same) + combined.Add (r); + } + + return combined; + } + + void CheckOutOfRangeComparison (ResolveContext ec, Constant c, TypeSpec type) + { + if (c is IntegralConstant || c is CharConstant) { + try { + c.ConvertExplicitly (true, type); + } catch (OverflowException) { + ec.Report.Warning (652, 2, loc, + "A comparison between a constant and a variable is useless. The constant is out of the range of the variable type `{0}'", + type.GetSignatureForError ()); + } + } + } + + /// + /// EmitBranchable is called from Statement.EmitBoolExpression in the + /// context of a conditional bool expression. This function will return + /// false if it is was possible to use EmitBranchable, or true if it was. + /// + /// The expression's code is generated, and we will generate a branch to `target' + /// if the resulting expression value is equal to isTrue + /// + public override void EmitBranchable (EmitContext ec, Label target, bool on_true) + { + if (ec.HasSet (BuilderContext.Options.AsyncBody) && right.ContainsEmitWithAwait ()) { + left = left.EmitToField (ec); + + if ((oper & Operator.LogicalMask) == 0) { + right = right.EmitToField (ec); + } + } + + // + // This is more complicated than it looks, but its just to avoid + // duplicated tests: basically, we allow ==, !=, >, <, >= and <= + // but on top of that we want for == and != to use a special path + // if we are comparing against null + // + if ((oper & Operator.EqualityMask) != 0 && (left is Constant || right is Constant)) { + bool my_on_true = oper == Operator.Inequality ? on_true : !on_true; + + // + // put the constant on the rhs, for simplicity + // + if (left is Constant) { + Expression swap = right; + right = left; + left = swap; + } + + // + // brtrue/brfalse works with native int only + // + if (((Constant) right).IsZeroInteger && right.Type.BuiltinType != BuiltinTypeSpec.Type.Long && right.Type.BuiltinType != BuiltinTypeSpec.Type.ULong) { + left.EmitBranchable (ec, target, my_on_true); + return; + } + if (right.Type.BuiltinType == BuiltinTypeSpec.Type.Bool) { + // right is a boolean, and it's not 'false' => it is 'true' + left.EmitBranchable (ec, target, !my_on_true); + return; + } + + } else if (oper == Operator.LogicalAnd) { + + if (on_true) { + Label tests_end = ec.DefineLabel (); + + left.EmitBranchable (ec, tests_end, false); + right.EmitBranchable (ec, target, true); + ec.MarkLabel (tests_end); + } else { + // + // This optimizes code like this + // if (true && i > 4) + // + if (!(left is Constant)) + left.EmitBranchable (ec, target, false); + + if (!(right is Constant)) + right.EmitBranchable (ec, target, false); + } + + return; + + } else if (oper == Operator.LogicalOr){ + if (on_true) { + left.EmitBranchable (ec, target, true); + right.EmitBranchable (ec, target, true); + + } else { + Label tests_end = ec.DefineLabel (); + left.EmitBranchable (ec, tests_end, true); + right.EmitBranchable (ec, target, false); + ec.MarkLabel (tests_end); + } + + return; + + } else if ((oper & Operator.ComparisonMask) == 0) { + base.EmitBranchable (ec, target, on_true); + return; + } + + left.Emit (ec); + right.Emit (ec); + + TypeSpec t = left.Type; + bool is_float = IsFloat (t); + bool is_unsigned = is_float || IsUnsigned (t); + + switch (oper){ + case Operator.Equality: + if (on_true) + ec.Emit (OpCodes.Beq, target); + else + ec.Emit (OpCodes.Bne_Un, target); + break; + + case Operator.Inequality: + if (on_true) + ec.Emit (OpCodes.Bne_Un, target); + else + ec.Emit (OpCodes.Beq, target); + break; + + case Operator.LessThan: + if (on_true) + if (is_unsigned && !is_float) + ec.Emit (OpCodes.Blt_Un, target); + else + ec.Emit (OpCodes.Blt, target); + else + if (is_unsigned) + ec.Emit (OpCodes.Bge_Un, target); + else + ec.Emit (OpCodes.Bge, target); + break; + + case Operator.GreaterThan: + if (on_true) + if (is_unsigned && !is_float) + ec.Emit (OpCodes.Bgt_Un, target); + else + ec.Emit (OpCodes.Bgt, target); + else + if (is_unsigned) + ec.Emit (OpCodes.Ble_Un, target); + else + ec.Emit (OpCodes.Ble, target); + break; + + case Operator.LessThanOrEqual: + if (on_true) + if (is_unsigned && !is_float) + ec.Emit (OpCodes.Ble_Un, target); + else + ec.Emit (OpCodes.Ble, target); + else + if (is_unsigned) + ec.Emit (OpCodes.Bgt_Un, target); + else + ec.Emit (OpCodes.Bgt, target); + break; + + + case Operator.GreaterThanOrEqual: + if (on_true) + if (is_unsigned && !is_float) + ec.Emit (OpCodes.Bge_Un, target); + else + ec.Emit (OpCodes.Bge, target); + else + if (is_unsigned) + ec.Emit (OpCodes.Blt_Un, target); + else + ec.Emit (OpCodes.Blt, target); + break; + default: + throw new InternalErrorException (oper.ToString ()); + } + } + + public override void Emit (EmitContext ec) + { + if (ec.HasSet (BuilderContext.Options.AsyncBody) && right.ContainsEmitWithAwait ()) { + left = left.EmitToField (ec); + + if ((oper & Operator.LogicalMask) == 0) { + right = right.EmitToField (ec); + } + } + + // + // Handle short-circuit operators differently + // than the rest + // + if ((oper & Operator.LogicalMask) != 0) { + Label load_result = ec.DefineLabel (); + Label end = ec.DefineLabel (); + + bool is_or = oper == Operator.LogicalOr; + left.EmitBranchable (ec, load_result, is_or); + right.Emit (ec); + ec.Emit (OpCodes.Br_S, end); + + ec.MarkLabel (load_result); + ec.EmitInt (is_or ? 1 : 0); + ec.MarkLabel (end); + return; + } + + // + // Optimize zero-based operations which cannot be optimized at expression level + // + if (oper == Operator.Subtraction) { + var lc = left as IntegralConstant; + if (lc != null && lc.IsDefaultValue) { + right.Emit (ec); + ec.Emit (OpCodes.Neg); + return; + } + } + + EmitOperator (ec, left, right); + } + + public void EmitOperator (EmitContext ec, Expression left, Expression right) + { + left.Emit (ec); + right.Emit (ec); + + EmitOperatorOpcode (ec, oper, left.Type, right); + + // + // Emit result enumerable conversion this way because it's quite complicated get it + // to resolved tree because expression tree cannot see it. + // + if (enum_conversion != 0) + ConvCast.Emit (ec, enum_conversion); + } + + public override void EmitSideEffect (EmitContext ec) + { + if ((oper & Operator.LogicalMask) != 0 || + (ec.HasSet (EmitContext.Options.CheckedScope) && (oper == Operator.Multiply || oper == Operator.Addition || oper == Operator.Subtraction))) { + base.EmitSideEffect (ec); + } else { + left.EmitSideEffect (ec); + right.EmitSideEffect (ec); + } + } + + public override Expression EmitToField (EmitContext ec) + { + if ((oper & Operator.LogicalMask) == 0) { + var await_expr = left as Await; + if (await_expr != null && right.IsSideEffectFree) { + await_expr.Statement.EmitPrologue (ec); + left = await_expr.Statement.GetResultExpression (ec); + return this; + } + + await_expr = right as Await; + if (await_expr != null && left.IsSideEffectFree) { + await_expr.Statement.EmitPrologue (ec); + right = await_expr.Statement.GetResultExpression (ec); + return this; + } + } + + return base.EmitToField (ec); + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + Binary target = (Binary) t; + + target.left = left.Clone (clonectx); + target.right = right.Clone (clonectx); + } + + public Expression CreateCallSiteBinder (ResolveContext ec, Arguments args) + { + Arguments binder_args = new Arguments (4); + + MemberAccess sle = new MemberAccess (new MemberAccess ( + new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Linq", loc), "Expressions", loc); + + CSharpBinderFlags flags = 0; + if (ec.HasSet (ResolveContext.Options.CheckedScope)) + flags = CSharpBinderFlags.CheckedContext; + + if ((oper & Operator.LogicalMask) != 0) + flags |= CSharpBinderFlags.BinaryOperationLogical; + + binder_args.Add (new Argument (new EnumConstant (new IntLiteral (ec.BuiltinTypes, (int) flags, loc), ec.Module.PredefinedTypes.BinderFlags.Resolve ()))); + binder_args.Add (new Argument (new MemberAccess (new MemberAccess (sle, "ExpressionType", loc), GetOperatorExpressionTypeName (), loc))); + binder_args.Add (new Argument (new TypeOf (ec.CurrentType, loc))); + binder_args.Add (new Argument (new ImplicitlyTypedArrayCreation (args.CreateDynamicBinderArguments (ec), loc))); + + return new Invocation (new MemberAccess (new TypeExpression (ec.Module.PredefinedTypes.Binder.TypeSpec, loc), "BinaryOperation", loc), binder_args); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + return CreateExpressionTree (ec, null); + } + + public Expression CreateExpressionTree (ResolveContext ec, Expression method) + { + string method_name; + bool lift_arg = false; + + switch (oper) { + case Operator.Addition: + if (method == null && ec.HasSet (ResolveContext.Options.CheckedScope) && !IsFloat (type)) + method_name = "AddChecked"; + else + method_name = "Add"; + break; + case Operator.BitwiseAnd: + method_name = "And"; + break; + case Operator.BitwiseOr: + method_name = "Or"; + break; + case Operator.Division: + method_name = "Divide"; + break; + case Operator.Equality: + method_name = "Equal"; + lift_arg = true; + break; + case Operator.ExclusiveOr: + method_name = "ExclusiveOr"; + break; + case Operator.GreaterThan: + method_name = "GreaterThan"; + lift_arg = true; + break; + case Operator.GreaterThanOrEqual: + method_name = "GreaterThanOrEqual"; + lift_arg = true; + break; + case Operator.Inequality: + method_name = "NotEqual"; + lift_arg = true; + break; + case Operator.LeftShift: + method_name = "LeftShift"; + break; + case Operator.LessThan: + method_name = "LessThan"; + lift_arg = true; + break; + case Operator.LessThanOrEqual: + method_name = "LessThanOrEqual"; + lift_arg = true; + break; + case Operator.LogicalAnd: + method_name = "AndAlso"; + break; + case Operator.LogicalOr: + method_name = "OrElse"; + break; + case Operator.Modulus: + method_name = "Modulo"; + break; + case Operator.Multiply: + if (method == null && ec.HasSet (ResolveContext.Options.CheckedScope) && !IsFloat (type)) + method_name = "MultiplyChecked"; + else + method_name = "Multiply"; + break; + case Operator.RightShift: + method_name = "RightShift"; + break; + case Operator.Subtraction: + if (method == null && ec.HasSet (ResolveContext.Options.CheckedScope) && !IsFloat (type)) + method_name = "SubtractChecked"; + else + method_name = "Subtract"; + break; + + default: + throw new InternalErrorException ("Unknown expression tree binary operator " + oper); + } + + Arguments args = new Arguments (2); + args.Add (new Argument (left.CreateExpressionTree (ec))); + args.Add (new Argument (right.CreateExpressionTree (ec))); + if (method != null) { + if (lift_arg) + args.Add (new Argument (new BoolLiteral (ec.BuiltinTypes, false, loc))); + + args.Add (new Argument (method)); + } + + return CreateExpressionFactoryCall (ec, method_name, args); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + + } + + // + // Represents the operation a + b [+ c [+ d [+ ...]]], where a is a string + // b, c, d... may be strings or objects. + // + public class StringConcat : Expression + { + Arguments arguments; + + StringConcat (Location loc) + { + this.loc = loc; + arguments = new Arguments (2); + } + + public override bool ContainsEmitWithAwait () + { + return arguments.ContainsEmitWithAwait (); + } + + public static StringConcat Create (ResolveContext rc, Expression left, Expression right, Location loc) + { + if (left.eclass == ExprClass.Unresolved || right.eclass == ExprClass.Unresolved) + throw new ArgumentException (); + + var s = new StringConcat (loc); + s.type = rc.BuiltinTypes.String; + s.eclass = ExprClass.Value; + + s.Append (rc, left); + s.Append (rc, right); + return s; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Argument arg = arguments [0]; + return CreateExpressionAddCall (ec, arg, arg.CreateExpressionTree (ec), 1); + } + + // + // Creates nested calls tree from an array of arguments used for IL emit + // + Expression CreateExpressionAddCall (ResolveContext ec, Argument left, Expression left_etree, int pos) + { + Arguments concat_args = new Arguments (2); + Arguments add_args = new Arguments (3); + + concat_args.Add (left); + add_args.Add (new Argument (left_etree)); + + concat_args.Add (arguments [pos]); + add_args.Add (new Argument (arguments [pos].CreateExpressionTree (ec))); + + var methods = GetConcatMethodCandidates (); + if (methods == null) + return null; + + var res = new OverloadResolver (methods, OverloadResolver.Restrictions.NoBaseMembers, loc); + var method = res.ResolveMember (ec, ref concat_args); + if (method == null) + return null; + + add_args.Add (new Argument (new TypeOfMethod (method, loc))); + + Expression expr = CreateExpressionFactoryCall (ec, "Add", add_args); + if (++pos == arguments.Count) + return expr; + + left = new Argument (new EmptyExpression (method.ReturnType)); + return CreateExpressionAddCall (ec, left, expr, pos); + } + + protected override Expression DoResolve (ResolveContext ec) + { + return this; + } + + void Append (ResolveContext rc, Expression operand) + { + // + // Constant folding + // + StringConstant sc = operand as StringConstant; + if (sc != null) { + if (arguments.Count != 0) { + Argument last_argument = arguments [arguments.Count - 1]; + StringConstant last_expr_constant = last_argument.Expr as StringConstant; + if (last_expr_constant != null) { + last_argument.Expr = new StringConstant (rc.BuiltinTypes, last_expr_constant.Value + sc.Value, sc.Location); + return; + } + } + } else { + // + // Multiple (3+) concatenation are resolved as multiple StringConcat instances + // + StringConcat concat_oper = operand as StringConcat; + if (concat_oper != null) { + arguments.AddRange (concat_oper.arguments); + return; + } + } + + arguments.Add (new Argument (operand)); + } + + IList GetConcatMethodCandidates () + { + return MemberCache.FindMembers (type, "Concat", true); + } + + public override void Emit (EmitContext ec) + { + // Optimize by removing any extra null arguments, they are no-op + for (int i = 0; i < arguments.Count; ++i) { + if (arguments[i].Expr is NullConstant) + arguments.RemoveAt (i--); + } + + var members = GetConcatMethodCandidates (); + var res = new OverloadResolver (members, OverloadResolver.Restrictions.NoBaseMembers, loc); + var method = res.ResolveMember (new ResolveContext (ec.MemberContext), ref arguments); + if (method != null) { + var call = new CallEmitter (); + call.EmitPredefined (ec, method, arguments, false); + } + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + arguments.FlowAnalysis (fc); + } + + public override SLE.Expression MakeExpression (BuilderContext ctx) + { + if (arguments.Count != 2) + throw new NotImplementedException ("arguments.Count != 2"); + + var concat = typeof (string).GetMethod ("Concat", new[] { typeof (object), typeof (object) }); + return SLE.Expression.Add (arguments[0].Expr.MakeExpression (ctx), arguments[1].Expr.MakeExpression (ctx), concat); + } + } + + // + // User-defined conditional logical operator + // + public class ConditionalLogicalOperator : UserOperatorCall + { + readonly bool is_and; + Expression oper_expr; + + public ConditionalLogicalOperator (MethodSpec oper, Arguments arguments, Func expr_tree, bool is_and, Location loc) + : base (oper, arguments, expr_tree, loc) + { + this.is_and = is_and; + eclass = ExprClass.Unresolved; + } + + protected override Expression DoResolve (ResolveContext ec) + { + AParametersCollection pd = oper.Parameters; + if (!TypeSpecComparer.IsEqual (type, pd.Types[0]) || !TypeSpecComparer.IsEqual (type, pd.Types[1])) { + ec.Report.Error (217, loc, + "A user-defined operator `{0}' must have each parameter type and return type of the same type in order to be applicable as a short circuit operator", + oper.GetSignatureForError ()); + return null; + } + + Expression left_dup = new EmptyExpression (type); + Expression op_true = GetOperatorTrue (ec, left_dup, loc); + Expression op_false = GetOperatorFalse (ec, left_dup, loc); + if (op_true == null || op_false == null) { + ec.Report.Error (218, loc, + "The type `{0}' must have operator `true' and operator `false' defined when `{1}' is used as a short circuit operator", + type.GetSignatureForError (), oper.GetSignatureForError ()); + return null; + } + + oper_expr = is_and ? op_false : op_true; + eclass = ExprClass.Value; + return this; + } + + public override void Emit (EmitContext ec) + { + Label end_target = ec.DefineLabel (); + + // + // Emit and duplicate left argument + // + bool right_contains_await = ec.HasSet (BuilderContext.Options.AsyncBody) && arguments[1].Expr.ContainsEmitWithAwait (); + if (right_contains_await) { + arguments[0] = arguments[0].EmitToField (ec, false); + arguments[0].Expr.Emit (ec); + } else { + arguments[0].Expr.Emit (ec); + ec.Emit (OpCodes.Dup); + arguments.RemoveAt (0); + } + + oper_expr.EmitBranchable (ec, end_target, true); + + base.Emit (ec); + + if (right_contains_await) { + // + // Special handling when right expression contains await and left argument + // could not be left on stack before logical branch + // + Label skip_left_load = ec.DefineLabel (); + ec.Emit (OpCodes.Br_S, skip_left_load); + ec.MarkLabel (end_target); + arguments[0].Expr.Emit (ec); + ec.MarkLabel (skip_left_load); + } else { + ec.MarkLabel (end_target); + } + } + } + + public class PointerArithmetic : Expression { + Expression left, right; + readonly Binary.Operator op; + + // + // We assume that `l' is always a pointer + // + public PointerArithmetic (Binary.Operator op, Expression l, Expression r, TypeSpec t, Location loc) + { + type = t; + this.loc = loc; + left = l; + right = r; + this.op = op; + } + + public override bool ContainsEmitWithAwait () + { + throw new NotImplementedException (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Error_PointerInsideExpressionTree (ec); + return null; + } + + protected override Expression DoResolve (ResolveContext ec) + { + eclass = ExprClass.Variable; + + var pc = left.Type as PointerContainer; + if (pc != null && pc.Element.Kind == MemberKind.Void) { + Error_VoidPointerOperation (ec); + return null; + } + + return this; + } + + public override void Emit (EmitContext ec) + { + TypeSpec op_type = left.Type; + + // It must be either array or fixed buffer + TypeSpec element; + if (TypeManager.HasElementType (op_type)) { + element = TypeManager.GetElementType (op_type); + } else { + FieldExpr fe = left as FieldExpr; + if (fe != null) + element = ((FixedFieldSpec) (fe.Spec)).ElementType; + else + element = op_type; + } + + int size = BuiltinTypeSpec.GetSize(element); + TypeSpec rtype = right.Type; + + if ((op & Binary.Operator.SubtractionMask) != 0 && rtype.IsPointer){ + // + // handle (pointer - pointer) + // + left.Emit (ec); + right.Emit (ec); + ec.Emit (OpCodes.Sub); + + if (size != 1){ + if (size == 0) + ec.Emit (OpCodes.Sizeof, element); + else + ec.EmitInt (size); + ec.Emit (OpCodes.Div); + } + ec.Emit (OpCodes.Conv_I8); + } else { + // + // handle + and - on (pointer op int) + // + Constant left_const = left as Constant; + if (left_const != null) { + // + // Optimize ((T*)null) pointer operations + // + if (left_const.IsDefaultValue) { + left = EmptyExpression.Null; + } else { + left_const = null; + } + } + + left.Emit (ec); + + var right_const = right as Constant; + if (right_const != null) { + // + // Optimize 0-based arithmetic + // + if (right_const.IsDefaultValue) + return; + + if (size != 0) + right = new IntConstant (ec.BuiltinTypes, size, right.Location); + else + right = new SizeOf (new TypeExpression (element, right.Location), right.Location); + + // TODO: Should be the checks resolve context sensitive? + ResolveContext rc = new ResolveContext (ec.MemberContext, ResolveContext.Options.UnsafeScope); + right = new Binary (Binary.Operator.Multiply, right, right_const).Resolve (rc); + if (right == null) + return; + } + + right.Emit (ec); + switch (rtype.BuiltinType) { + case BuiltinTypeSpec.Type.SByte: + case BuiltinTypeSpec.Type.Byte: + case BuiltinTypeSpec.Type.Short: + case BuiltinTypeSpec.Type.UShort: + ec.Emit (OpCodes.Conv_I); + break; + case BuiltinTypeSpec.Type.UInt: + ec.Emit (OpCodes.Conv_U); + break; + } + + if (right_const == null && size != 1){ + if (size == 0) + ec.Emit (OpCodes.Sizeof, element); + else + ec.EmitInt (size); + if (rtype.BuiltinType == BuiltinTypeSpec.Type.Long || rtype.BuiltinType == BuiltinTypeSpec.Type.ULong) + ec.Emit (OpCodes.Conv_I8); + + Binary.EmitOperatorOpcode (ec, Binary.Operator.Multiply, rtype, right); + } + + if (left_const == null) { + if (rtype.BuiltinType == BuiltinTypeSpec.Type.Long) + ec.Emit (OpCodes.Conv_I); + else if (rtype.BuiltinType == BuiltinTypeSpec.Type.ULong) + ec.Emit (OpCodes.Conv_U); + + Binary.EmitOperatorOpcode (ec, op, op_type, right); + } + } + } + } + + // + // A boolean-expression is an expression that yields a result + // of type bool + // + public class BooleanExpression : ShimExpression + { + public BooleanExpression (Expression expr) + : base (expr) + { + this.loc = expr.Location; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + // TODO: We should emit IsTrue (v4) instead of direct user operator + // call but that would break csc compatibility + return base.CreateExpressionTree (ec); + } + + protected override Expression DoResolve (ResolveContext ec) + { + // A boolean-expression is required to be of a type + // that can be implicitly converted to bool or of + // a type that implements operator true + + expr = expr.Resolve (ec); + if (expr == null) + return null; + + Assign ass = expr as Assign; + if (ass != null && ass.Source is Constant) { + ec.Report.Warning (665, 3, loc, + "Assignment in conditional expression is always constant. Did you mean to use `==' instead ?"); + } + + if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.Bool) + return expr; + + if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + Arguments args = new Arguments (1); + args.Add (new Argument (expr)); + return DynamicUnaryConversion.CreateIsTrue (ec, args, loc).Resolve (ec); + } + + type = ec.BuiltinTypes.Bool; + Expression converted = Convert.ImplicitConversion (ec, expr, type, loc); + if (converted != null) + return converted; + + // + // If no implicit conversion to bool exists, try using `operator true' + // + converted = GetOperatorTrue (ec, expr, loc); + if (converted == null) { + expr.Error_ValueCannotBeConverted (ec, type, false); + return null; + } + + return converted; + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class BooleanExpressionFalse : Unary + { + public BooleanExpressionFalse (Expression expr) + : base (Operator.LogicalNot, expr, expr.Location) + { + } + + protected override Expression ResolveOperator (ResolveContext ec, Expression expr) + { + return GetOperatorFalse (ec, expr, loc) ?? base.ResolveOperator (ec, expr); + } + } + + /// + /// Implements the ternary conditional operator (?:) + /// + public class Conditional : Expression { + Expression expr, true_expr, false_expr; + + public Conditional (Expression expr, Expression true_expr, Expression false_expr, Location loc) + { + this.expr = expr; + this.true_expr = true_expr; + this.false_expr = false_expr; + this.loc = loc; + } + + #region Properties + + public Expression Expr { + get { + return expr; + } + } + + public Expression TrueExpr { + get { + return true_expr; + } + } + + public Expression FalseExpr { + get { + return false_expr; + } + } + + #endregion + + public override bool ContainsEmitWithAwait () + { + return Expr.ContainsEmitWithAwait () || true_expr.ContainsEmitWithAwait () || false_expr.ContainsEmitWithAwait (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Arguments args = new Arguments (3); + args.Add (new Argument (expr.CreateExpressionTree (ec))); + args.Add (new Argument (true_expr.CreateExpressionTree (ec))); + args.Add (new Argument (false_expr.CreateExpressionTree (ec))); + return CreateExpressionFactoryCall (ec, "Condition", args); + } + + protected override Expression DoResolve (ResolveContext ec) + { + expr = expr.Resolve (ec); + true_expr = true_expr.Resolve (ec); + false_expr = false_expr.Resolve (ec); + + if (true_expr == null || false_expr == null || expr == null) + return null; + + eclass = ExprClass.Value; + TypeSpec true_type = true_expr.Type; + TypeSpec false_type = false_expr.Type; + type = true_type; + + // + // First, if an implicit conversion exists from true_expr + // to false_expr, then the result type is of type false_expr.Type + // + if (!TypeSpecComparer.IsEqual (true_type, false_type)) { + Expression conv = Convert.ImplicitConversion (ec, true_expr, false_type, loc); + if (conv != null && true_type.BuiltinType != BuiltinTypeSpec.Type.Dynamic) { + // + // Check if both can convert implicitly to each other's type + // + type = false_type; + + if (false_type.BuiltinType != BuiltinTypeSpec.Type.Dynamic) { + var conv_false_expr = Convert.ImplicitConversion (ec, false_expr, true_type, loc); + // + // LAMESPEC: There seems to be hardcoded promotition to int type when + // both sides are numeric constants and one side is int constant and + // other side is numeric constant convertible to int. + // + // var res = condition ? (short)1 : 1; + // + // Type of res is int even if according to the spec the conversion is + // ambiguous because 1 literal can be converted to short. + // + if (conv_false_expr != null) { + if (conv_false_expr.Type.BuiltinType == BuiltinTypeSpec.Type.Int && conv is Constant) { + type = true_type; + conv_false_expr = null; + } else if (type.BuiltinType == BuiltinTypeSpec.Type.Int && conv_false_expr is Constant) { + conv_false_expr = null; + } + } + + if (conv_false_expr != null) { + ec.Report.Error (172, true_expr.Location, + "Type of conditional expression cannot be determined as `{0}' and `{1}' convert implicitly to each other", + true_type.GetSignatureForError (), false_type.GetSignatureForError ()); + } + } + + true_expr = conv; + if (true_expr.Type != type) + true_expr = EmptyCast.Create (true_expr, type); + } else if ((conv = Convert.ImplicitConversion (ec, false_expr, true_type, loc)) != null) { + false_expr = conv; + } else { + ec.Report.Error (173, true_expr.Location, + "Type of conditional expression cannot be determined because there is no implicit conversion between `{0}' and `{1}'", + true_type.GetSignatureForError (), false_type.GetSignatureForError ()); + return null; + } + } + + Constant c = expr as Constant; + if (c != null) { + bool is_false = c.IsDefaultValue; + + // + // Don't issue the warning for constant expressions + // + if (!(is_false ? true_expr is Constant : false_expr is Constant)) { + // CSC: Missing warning + Warning_UnreachableExpression (ec, is_false ? true_expr.Location : false_expr.Location); + } + + return ReducedExpression.Create ( + is_false ? false_expr : true_expr, this, + false_expr is Constant && true_expr is Constant).Resolve (ec); + } + + return this; + } + + public override void Emit (EmitContext ec) + { + Label false_target = ec.DefineLabel (); + Label end_target = ec.DefineLabel (); + + expr.EmitBranchable (ec, false_target, false); + true_expr.Emit (ec); + + // + // Verifier doesn't support interface merging. When there are two types on + // the stack without common type hint and the common type is an interface. + // Use temporary local to give verifier hint on what type to unify the stack + // + if (type.IsInterface && true_expr is EmptyCast && false_expr is EmptyCast) { + var temp = ec.GetTemporaryLocal (type); + ec.Emit (OpCodes.Stloc, temp); + ec.Emit (OpCodes.Ldloc, temp); + ec.FreeTemporaryLocal (temp, type); + } + + ec.Emit (OpCodes.Br, end_target); + ec.MarkLabel (false_target); + false_expr.Emit (ec); + ec.MarkLabel (end_target); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + expr.FlowAnalysisConditional (fc); + var expr_true = fc.DefiniteAssignmentOnTrue; + var expr_false = fc.DefiniteAssignmentOnFalse; + + fc.DefiniteAssignment = new DefiniteAssignmentBitSet (expr_true); + true_expr.FlowAnalysis (fc); + var true_fc = fc.DefiniteAssignment; + + fc.DefiniteAssignment = new DefiniteAssignmentBitSet (expr_false); + false_expr.FlowAnalysis (fc); + + fc.DefiniteAssignment &= true_fc; + } + + public override void FlowAnalysisConditional (FlowAnalysisContext fc) + { + expr.FlowAnalysisConditional (fc); + var expr_true = fc.DefiniteAssignmentOnTrue; + var expr_false = fc.DefiniteAssignmentOnFalse; + + fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = fc.DefiniteAssignment = new DefiniteAssignmentBitSet (expr_true); + true_expr.FlowAnalysisConditional (fc); + var true_fc = fc.DefiniteAssignment; + var true_da_true = fc.DefiniteAssignmentOnTrue; + var true_da_false = fc.DefiniteAssignmentOnFalse; + + fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = fc.DefiniteAssignment = new DefiniteAssignmentBitSet (expr_false); + false_expr.FlowAnalysisConditional (fc); + + fc.DefiniteAssignment &= true_fc; + fc.DefiniteAssignmentOnTrue = true_da_true & fc.DefiniteAssignmentOnTrue; + fc.DefiniteAssignmentOnFalse = true_da_false & fc.DefiniteAssignmentOnFalse; + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + Conditional target = (Conditional) t; + + target.expr = expr.Clone (clonectx); + target.true_expr = true_expr.Clone (clonectx); + target.false_expr = false_expr.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public abstract class VariableReference : Expression, IAssignMethod, IMemoryLocation, IVariableReference + { + LocalTemporary temp; + + #region Abstract + public abstract HoistedVariable GetHoistedVariable (AnonymousExpression ae); + public abstract void SetHasAddressTaken (); + + public abstract bool IsLockedByStatement { get; set; } + + public abstract bool IsFixed { get; } + public abstract bool IsRef { get; } + public abstract string Name { get; } + + // + // Variable IL data, it has to be protected to encapsulate hoisted variables + // + protected abstract ILocalVariable Variable { get; } + + // + // Variable flow-analysis data + // + public abstract VariableInfo VariableInfo { get; } + #endregion + + public virtual void AddressOf (EmitContext ec, AddressOp mode) + { + HoistedVariable hv = GetHoistedVariable (ec); + if (hv != null) { + hv.AddressOf (ec, mode); + return; + } + + Variable.EmitAddressOf (ec); + } + + public override bool ContainsEmitWithAwait () + { + return false; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + HoistedVariable hv = GetHoistedVariable (ec); + if (hv != null) + return hv.CreateExpressionTree (); + + Arguments arg = new Arguments (1); + arg.Add (new Argument (this)); + return CreateExpressionFactoryCall (ec, "Constant", arg); + } + + public override Expression DoResolveLValue (ResolveContext rc, Expression right_side) + { + if (IsLockedByStatement) { + rc.Report.Warning (728, 2, loc, + "Possibly incorrect assignment to `{0}' which is the argument to a using or lock statement", + Name); + } + + return this; + } + + public override void Emit (EmitContext ec) + { + Emit (ec, false); + } + + public override void EmitSideEffect (EmitContext ec) + { + // do nothing + } + + // + // This method is used by parameters that are references, that are + // being passed as references: we only want to pass the pointer (that + // is already stored in the parameter, not the address of the pointer, + // and not the value of the variable). + // + public void EmitLoad (EmitContext ec) + { + Variable.Emit (ec); + } + + public void Emit (EmitContext ec, bool leave_copy) + { + HoistedVariable hv = GetHoistedVariable (ec); + if (hv != null) { + hv.Emit (ec, leave_copy); + return; + } + + EmitLoad (ec); + + if (IsRef) { + // + // If we are a reference, we loaded on the stack a pointer + // Now lets load the real value + // + ec.EmitLoadFromPtr (type); + } + + if (leave_copy) { + ec.Emit (OpCodes.Dup); + + if (IsRef) { + temp = new LocalTemporary (Type); + temp.Store (ec); + } + } + } + + public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, + bool prepare_for_load) + { + HoistedVariable hv = GetHoistedVariable (ec); + if (hv != null) { + hv.EmitAssign (ec, source, leave_copy, prepare_for_load); + return; + } + + New n_source = source as New; + if (n_source != null) { + if (!n_source.Emit (ec, this)) { + if (leave_copy) { + EmitLoad (ec); + if (IsRef) + ec.EmitLoadFromPtr (type); + } + return; + } + } else { + if (IsRef) + EmitLoad (ec); + + source.Emit (ec); + } + + if (leave_copy) { + ec.Emit (OpCodes.Dup); + if (IsRef) { + temp = new LocalTemporary (Type); + temp.Store (ec); + } + } + + if (IsRef) + ec.EmitStoreFromPtr (type); + else + Variable.EmitAssign (ec); + + if (temp != null) { + temp.Emit (ec); + temp.Release (ec); + } + } + + public override Expression EmitToField (EmitContext ec) + { + HoistedVariable hv = GetHoistedVariable (ec); + if (hv != null) { + return hv.EmitToField (ec); + } + + return base.EmitToField (ec); + } + + public HoistedVariable GetHoistedVariable (ResolveContext rc) + { + return GetHoistedVariable (rc.CurrentAnonymousMethod); + } + + public HoistedVariable GetHoistedVariable (EmitContext ec) + { + return GetHoistedVariable (ec.CurrentAnonymousMethod); + } + + public override string GetSignatureForError () + { + return Name; + } + + public bool IsHoisted { + get { return GetHoistedVariable ((AnonymousExpression) null) != null; } + } + } + + // + // Resolved reference to a local variable + // + public class LocalVariableReference : VariableReference + { + public LocalVariable local_info; + + public LocalVariableReference (LocalVariable li, Location l) + { + this.local_info = li; + loc = l; + } + + public override VariableInfo VariableInfo { + get { return local_info.VariableInfo; } + } + + public override HoistedVariable GetHoistedVariable (AnonymousExpression ae) + { + return local_info.HoistedVariant; + } + + #region Properties + + // + // A local variable is always fixed + // + public override bool IsFixed { + get { + return true; + } + } + + public override bool IsLockedByStatement { + get { + return local_info.IsLocked; + } + set { + local_info.IsLocked = value; + } + } + + public override bool IsRef { + get { return false; } + } + + public override string Name { + get { return local_info.Name; } + } + + #endregion + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + VariableInfo variable_info = VariableInfo; + if (variable_info == null) + return; + + if (fc.IsDefinitelyAssigned (variable_info)) + return; + + fc.Report.Error (165, loc, "Use of unassigned local variable `{0}'", Name); + variable_info.SetAssigned (fc.DefiniteAssignment, true); + } + + public override void SetHasAddressTaken () + { + local_info.SetHasAddressTaken (); + } + + void DoResolveBase (ResolveContext ec) + { + // + // If we are referencing a variable from the external block + // flag it for capturing + // + if (ec.MustCaptureVariable (local_info)) { + if (local_info.AddressTaken) { + AnonymousMethodExpression.Error_AddressOfCapturedVar (ec, this, loc); + } else if (local_info.IsFixed) { + ec.Report.Error (1764, loc, + "Cannot use fixed local `{0}' inside an anonymous method, lambda expression or query expression", + GetSignatureForError ()); + } + + if (ec.IsVariableCapturingRequired) { + AnonymousMethodStorey storey = local_info.Block.Explicit.CreateAnonymousMethodStorey (ec); + storey.CaptureLocalVariable (ec, local_info); + } + } + + eclass = ExprClass.Variable; + type = local_info.Type; + } + + protected override Expression DoResolve (ResolveContext ec) + { + local_info.SetIsUsed (); + + DoResolveBase (ec); + return this; + } + + public override Expression DoResolveLValue (ResolveContext ec, Expression rhs) + { + // + // Don't be too pedantic when variable is used as out param or for some broken code + // which uses property/indexer access to run some initialization + // + if (rhs == EmptyExpression.OutAccess || rhs.eclass == ExprClass.PropertyAccess || rhs.eclass == ExprClass.IndexerAccess) + local_info.SetIsUsed (); + + if (local_info.IsReadonly && !ec.HasAny (ResolveContext.Options.FieldInitializerScope | ResolveContext.Options.UsingInitializerScope)) { + if (rhs == EmptyExpression.LValueMemberAccess) { + // CS1654 already reported + } else { + int code; + string msg; + if (rhs == EmptyExpression.OutAccess) { + code = 1657; msg = "Cannot pass `{0}' as a ref or out argument because it is a `{1}'"; + } else if (rhs == EmptyExpression.LValueMemberOutAccess) { + code = 1655; msg = "Cannot pass members of `{0}' as ref or out arguments because it is a `{1}'"; + } else if (rhs == EmptyExpression.UnaryAddress) { + code = 459; msg = "Cannot take the address of {1} `{0}'"; + } else { + code = 1656; msg = "Cannot assign to `{0}' because it is a `{1}'"; + } + ec.Report.Error (code, loc, msg, Name, local_info.GetReadOnlyContext ()); + } + } + + if (eclass == ExprClass.Unresolved) + DoResolveBase (ec); + + return base.DoResolveLValue (ec, rhs); + } + + public override int GetHashCode () + { + return local_info.GetHashCode (); + } + + public override bool Equals (object obj) + { + LocalVariableReference lvr = obj as LocalVariableReference; + if (lvr == null) + return false; + + return local_info == lvr.local_info; + } + + protected override ILocalVariable Variable { + get { return local_info; } + } + + public override string ToString () + { + return String.Format ("{0} ({1}:{2})", GetType (), Name, loc); + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + // Nothing + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + /// + /// This represents a reference to a parameter in the intermediate + /// representation. + /// + public class ParameterReference : VariableReference + { + protected ParametersBlock.ParameterInfo pi; + + public ParameterReference (ParametersBlock.ParameterInfo pi, Location loc) + { + this.pi = pi; + this.loc = loc; + } + + #region Properties + + public override bool IsLockedByStatement { + get { + return pi.IsLocked; + } + set { + pi.IsLocked = value; + } + } + + public override bool IsRef { + get { return (pi.Parameter.ModFlags & Parameter.Modifier.RefOutMask) != 0; } + } + + bool HasOutModifier { + get { return (pi.Parameter.ModFlags & Parameter.Modifier.OUT) != 0; } + } + + public override HoistedVariable GetHoistedVariable (AnonymousExpression ae) + { + return pi.Parameter.HoistedVariant; + } + + // + // A ref or out parameter is classified as a moveable variable, even + // if the argument given for the parameter is a fixed variable + // + public override bool IsFixed { + get { return !IsRef; } + } + + public override string Name { + get { return Parameter.Name; } + } + + public Parameter Parameter { + get { return pi.Parameter; } + } + + public override VariableInfo VariableInfo { + get { return pi.VariableInfo; } + } + + protected override ILocalVariable Variable { + get { return Parameter; } + } + + #endregion + + public override void AddressOf (EmitContext ec, AddressOp mode) + { + // + // ParameterReferences might already be a reference + // + if (IsRef) { + EmitLoad (ec); + return; + } + + base.AddressOf (ec, mode); + } + + public override void SetHasAddressTaken () + { + Parameter.HasAddressTaken = true; + } + + bool DoResolveBase (ResolveContext ec) + { + if (eclass != ExprClass.Unresolved) + return true; + + type = pi.ParameterType; + eclass = ExprClass.Variable; + + // + // If we are referencing a parameter from the external block + // flag it for capturing + // + if (ec.MustCaptureVariable (pi)) { + if (Parameter.HasAddressTaken) + AnonymousMethodExpression.Error_AddressOfCapturedVar (ec, this, loc); + + if (IsRef) { + ec.Report.Error (1628, loc, + "Parameter `{0}' cannot be used inside `{1}' when using `ref' or `out' modifier", + Name, ec.CurrentAnonymousMethod.ContainerType); + } + + if (ec.IsVariableCapturingRequired && !pi.Block.ParametersBlock.IsExpressionTree) { + AnonymousMethodStorey storey = pi.Block.Explicit.CreateAnonymousMethodStorey (ec); + storey.CaptureParameter (ec, pi, this); + } + } + + return true; + } + + public override int GetHashCode () + { + return Name.GetHashCode (); + } + + public override bool Equals (object obj) + { + ParameterReference pr = obj as ParameterReference; + if (pr == null) + return false; + + return Name == pr.Name; + } + + protected override void CloneTo (CloneContext clonectx, Expression target) + { + // Nothing to clone + return; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + HoistedVariable hv = GetHoistedVariable (ec); + if (hv != null) + return hv.CreateExpressionTree (); + + return Parameter.ExpressionTreeVariableReference (); + } + + protected override Expression DoResolve (ResolveContext ec) + { + if (!DoResolveBase (ec)) + return null; + + return this; + } + + public override Expression DoResolveLValue (ResolveContext ec, Expression right_side) + { + if (!DoResolveBase (ec)) + return null; + + if (Parameter.HoistedVariant != null) + Parameter.HoistedVariant.IsAssigned = true; + + return base.DoResolveLValue (ec, right_side); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + VariableInfo variable_info = VariableInfo; + if (variable_info == null) + return; + + if (fc.IsDefinitelyAssigned (variable_info)) + return; + + fc.Report.Error (269, loc, "Use of unassigned out parameter `{0}'", Name); + fc.SetVariableAssigned (variable_info); + } + } + + /// + /// Invocation of methods or delegates. + /// + public class Invocation : ExpressionStatement + { + public class Predefined : Invocation + { + public Predefined (MethodGroupExpr expr, Arguments arguments) + : base (expr, arguments) + { + this.mg = expr; + } + + protected override MethodGroupExpr DoResolveOverload (ResolveContext rc) + { + if (!rc.IsObsolete) { + var member = mg.BestCandidate; + ObsoleteAttribute oa = member.GetAttributeObsolete (); + if (oa != null) + AttributeTester.Report_ObsoleteMessage (oa, member.GetSignatureForError (), loc, rc.Report); + } + + return mg; + } + } + + protected Arguments arguments; + protected Expression expr; + protected MethodGroupExpr mg; + bool conditional_access_receiver; + + public Invocation (Expression expr, Arguments arguments) + { + this.expr = expr; + this.arguments = arguments; + if (expr != null) { + loc = expr.Location; + } + } + + #region Properties + public Arguments Arguments { + get { + return arguments; + } + } + + public Expression Exp { + get { + return expr; + } + } + + public MethodGroupExpr MethodGroup { + get { + return mg; + } + } + + public override Location StartLocation { + get { + return expr.StartLocation; + } + } + + #endregion + + public override MethodGroupExpr CanReduceLambda (AnonymousMethodBody body) + { + if (MethodGroup == null) + return null; + + var candidate = MethodGroup.BestCandidate; + if (candidate == null || !(candidate.IsStatic || Exp is This)) + return null; + + var args_count = arguments == null ? 0 : arguments.Count; + if (args_count != body.Parameters.Count) + return null; + + var lambda_parameters = body.Block.Parameters.FixedParameters; + for (int i = 0; i < args_count; ++i) { + var pr = arguments[i].Expr as ParameterReference; + if (pr == null) + return null; + + if (lambda_parameters[i] != pr.Parameter) + return null; + + if ((lambda_parameters[i].ModFlags & Parameter.Modifier.RefOutMask) != (pr.Parameter.ModFlags & Parameter.Modifier.RefOutMask)) + return null; + } + + var emg = MethodGroup as ExtensionMethodGroupExpr; + if (emg != null) { + var mg = MethodGroupExpr.CreatePredefined (candidate, candidate.DeclaringType, MethodGroup.Location); + if (candidate.IsGeneric) { + var targs = new TypeExpression [candidate.Arity]; + for (int i = 0; i < targs.Length; ++i) { + targs[i] = new TypeExpression (candidate.TypeArguments[i], MethodGroup.Location); + } + + mg.SetTypeArguments (null, new TypeArguments (targs)); + } + + return mg; + } + + return MethodGroup; + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + Invocation target = (Invocation) t; + + if (arguments != null) + target.arguments = arguments.Clone (clonectx); + + target.expr = expr.Clone (clonectx); + } + + public override bool ContainsEmitWithAwait () + { + if (arguments != null && arguments.ContainsEmitWithAwait ()) + return true; + + return mg.ContainsEmitWithAwait (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Expression instance = mg.IsInstance ? + mg.InstanceExpression.CreateExpressionTree (ec) : + new NullLiteral (loc); + + var args = Arguments.CreateForExpressionTree (ec, arguments, + instance, + mg.CreateExpressionTree (ec)); + + return CreateExpressionFactoryCall (ec, "Call", args); + } + + protected override Expression DoResolve (ResolveContext rc) + { + if (!rc.HasSet (ResolveContext.Options.ConditionalAccessReceiver)) { + if (expr.HasConditionalAccess ()) { + conditional_access_receiver = true; + using (rc.Set (ResolveContext.Options.ConditionalAccessReceiver)) { + return DoResolveInvocation (rc); + } + } + } + + return DoResolveInvocation (rc); + } + + Expression DoResolveInvocation (ResolveContext ec) + { + Expression member_expr; + var atn = expr as ATypeNameExpression; + if (atn != null) { + member_expr = atn.LookupNameExpression (ec, MemberLookupRestrictions.InvocableOnly | MemberLookupRestrictions.ReadAccess); + if (member_expr != null) { + var name_of = member_expr as NameOf; + if (name_of != null) { + return name_of.ResolveOverload (ec, arguments); + } + + member_expr = member_expr.Resolve (ec); + } + } else { + member_expr = expr.Resolve (ec); + } + + if (member_expr == null) + return null; + + // + // Next, evaluate all the expressions in the argument list + // + bool dynamic_arg = false; + if (arguments != null) + arguments.Resolve (ec, out dynamic_arg); + + TypeSpec expr_type = member_expr.Type; + if (expr_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) + return DoResolveDynamic (ec, member_expr); + + mg = member_expr as MethodGroupExpr; + Expression invoke = null; + + if (mg == null) { + if (expr_type != null && expr_type.IsDelegate) { + invoke = new DelegateInvocation (member_expr, arguments, conditional_access_receiver, loc); + invoke = invoke.Resolve (ec); + if (invoke == null || !dynamic_arg) + return invoke; + } else { + if (member_expr is RuntimeValueExpression) { + ec.Report.Error (Report.RuntimeErrorId, loc, "Cannot invoke a non-delegate type `{0}'", + member_expr.Type.GetSignatureForError ()); + return null; + } + + MemberExpr me = member_expr as MemberExpr; + if (me == null) { + member_expr.Error_UnexpectedKind (ec, ResolveFlags.MethodGroup, loc); + return null; + } + + ec.Report.Error (1955, loc, "The member `{0}' cannot be used as method or delegate", + member_expr.GetSignatureForError ()); + return null; + } + } + + if (invoke == null) { + mg = DoResolveOverload (ec); + if (mg == null) + return null; + } + + if (dynamic_arg) + return DoResolveDynamic (ec, member_expr); + + var method = mg.BestCandidate; + type = mg.BestCandidateReturnType; + if (conditional_access_receiver) + type = LiftMemberType (ec, type); + + if (arguments == null && method.DeclaringType.BuiltinType == BuiltinTypeSpec.Type.Object && method.Name == Destructor.MetadataName) { + if (mg.IsBase) + ec.Report.Error (250, loc, "Do not directly call your base class Finalize method. It is called automatically from your destructor"); + else + ec.Report.Error (245, loc, "Destructors and object.Finalize cannot be called directly. Consider calling IDisposable.Dispose if available"); + return null; + } + + IsSpecialMethodInvocation (ec, method, loc); + + eclass = ExprClass.Value; + return this; + } + + protected virtual Expression DoResolveDynamic (ResolveContext ec, Expression memberExpr) + { + Arguments args; + DynamicMemberBinder dmb = memberExpr as DynamicMemberBinder; + if (dmb != null) { + args = dmb.Arguments; + if (arguments != null) + args.AddRange (arguments); + } else if (mg == null) { + if (arguments == null) + args = new Arguments (1); + else + args = arguments; + + args.Insert (0, new Argument (memberExpr)); + this.expr = null; + } else { + if (mg.IsBase) { + ec.Report.Error (1971, loc, + "The base call to method `{0}' cannot be dynamically dispatched. Consider casting the dynamic arguments or eliminating the base access", + mg.Name); + return null; + } + + if (arguments == null) + args = new Arguments (1); + else + args = arguments; + + MemberAccess ma = expr as MemberAccess; + if (ma != null) { + var inst = mg.InstanceExpression; + var left_type = inst as TypeExpr; + if (left_type != null) { + args.Insert (0, new Argument (new TypeOf (left_type.Type, loc).Resolve (ec), Argument.AType.DynamicTypeName)); + } else if (inst != null) { + // + // Any value type has to be pass as by-ref to get back the same + // instance on which the member was called + // + var mod = inst is IMemoryLocation && TypeSpec.IsValueType (inst.Type) ? + Argument.AType.Ref : Argument.AType.None; + args.Insert (0, new Argument (inst.Resolve (ec), mod)); + } + } else { // is SimpleName + if (ec.IsStatic) { + args.Insert (0, new Argument (new TypeOf (ec.CurrentType, loc).Resolve (ec), Argument.AType.DynamicTypeName)); + } else { + args.Insert (0, new Argument (new This (loc).Resolve (ec))); + } + } + } + + return new DynamicInvocation (expr as ATypeNameExpression, args, loc).Resolve (ec); + } + + protected virtual MethodGroupExpr DoResolveOverload (ResolveContext ec) + { + return mg.OverloadResolve (ec, ref arguments, null, OverloadResolver.Restrictions.None); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + if (mg.IsConditionallyExcluded) + return; + + mg.FlowAnalysis (fc); + + if (arguments != null) + arguments.FlowAnalysis (fc); + + if (conditional_access_receiver) + fc.ConditionalAccessEnd (); + } + + public override string GetSignatureForError () + { + return mg.GetSignatureForError (); + } + + public override bool HasConditionalAccess () + { + return expr.HasConditionalAccess (); + } + + // + // If a member is a method or event, or if it is a constant, field or property of either a delegate type + // or the type dynamic, then the member is invocable + // + public static bool IsMemberInvocable (MemberSpec member) + { + switch (member.Kind) { + case MemberKind.Event: + return true; + case MemberKind.Field: + case MemberKind.Property: + var m = member as IInterfaceMemberSpec; + return m.MemberType.IsDelegate || m.MemberType.BuiltinType == BuiltinTypeSpec.Type.Dynamic; + default: + return false; + } + } + + public static bool IsSpecialMethodInvocation (ResolveContext ec, MethodSpec method, Location loc) + { + if (!method.IsReservedMethod) + return false; + + if (ec.HasSet (ResolveContext.Options.InvokeSpecialName) || ec.CurrentMemberDefinition.IsCompilerGenerated) + return false; + + ec.Report.SymbolRelatedToPreviousError (method); + ec.Report.Error (571, loc, "`{0}': cannot explicitly call operator or accessor", + method.GetSignatureForError ()); + + return true; + } + + public override void Emit (EmitContext ec) + { + if (mg.IsConditionallyExcluded) + return; + + if (conditional_access_receiver) + mg.EmitCall (ec, arguments, type, false); + else + mg.EmitCall (ec, arguments, false); + } + + public override void EmitStatement (EmitContext ec) + { + if (mg.IsConditionallyExcluded) + return; + + if (conditional_access_receiver) + mg.EmitCall (ec, arguments, type, true); + else + mg.EmitCall (ec, arguments, true); + } + + public override SLE.Expression MakeExpression (BuilderContext ctx) + { + return MakeExpression (ctx, mg.InstanceExpression, mg.BestCandidate, arguments); + } + + public static SLE.Expression MakeExpression (BuilderContext ctx, Expression instance, MethodSpec mi, Arguments args) + { +#if STATIC + throw new NotSupportedException (); +#else + var instance_expr = instance == null ? null : instance.MakeExpression (ctx); + return SLE.Expression.Call (instance_expr, (MethodInfo) mi.GetMetaInfo (), Arguments.MakeExpression (args, ctx)); +#endif + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + // + // Implements simple new expression + // + public class New : ExpressionStatement, IMemoryLocation + { + protected Arguments arguments; + + // + // During bootstrap, it contains the RequestedType, + // but if `type' is not null, it *might* contain a NewDelegate + // (because of field multi-initialization) + // + protected Expression RequestedType; + + protected MethodSpec method; + + public New (Expression requested_type, Arguments arguments, Location l) + { + RequestedType = requested_type; + this.arguments = arguments; + loc = l; + } + + #region Properties + public Arguments Arguments { + get { + return arguments; + } + } + + public Expression TypeRequested { + get { + return RequestedType; + } + } + + // + // Returns true for resolved `new S()' + // + public bool IsDefaultStruct { + get { + return arguments == null && type.IsStruct && GetType () == typeof (New); + } + } + + public Expression TypeExpression { + get { + return RequestedType; + } + } + + #endregion + + /// + /// Converts complex core type syntax like 'new int ()' to simple constant + /// + public static Constant Constantify (TypeSpec t, Location loc) + { + switch (t.BuiltinType) { + case BuiltinTypeSpec.Type.Int: + return new IntConstant (t, 0, loc); + case BuiltinTypeSpec.Type.UInt: + return new UIntConstant (t, 0, loc); + case BuiltinTypeSpec.Type.Long: + return new LongConstant (t, 0, loc); + case BuiltinTypeSpec.Type.ULong: + return new ULongConstant (t, 0, loc); + case BuiltinTypeSpec.Type.Float: + return new FloatConstant (t, 0, loc); + case BuiltinTypeSpec.Type.Double: + return new DoubleConstant (t, 0, loc); + case BuiltinTypeSpec.Type.Short: + return new ShortConstant (t, 0, loc); + case BuiltinTypeSpec.Type.UShort: + return new UShortConstant (t, 0, loc); + case BuiltinTypeSpec.Type.SByte: + return new SByteConstant (t, 0, loc); + case BuiltinTypeSpec.Type.Byte: + return new ByteConstant (t, 0, loc); + case BuiltinTypeSpec.Type.Char: + return new CharConstant (t, '\0', loc); + case BuiltinTypeSpec.Type.Bool: + return new BoolConstant (t, false, loc); + case BuiltinTypeSpec.Type.Decimal: + return new DecimalConstant (t, 0, loc); + } + + if (t.IsEnum) + return new EnumConstant (Constantify (EnumSpec.GetUnderlyingType (t), loc), t); + + if (t.IsNullableType) + return Nullable.LiftedNull.Create (t, loc); + + return null; + } + + public override bool ContainsEmitWithAwait () + { + return arguments != null && arguments.ContainsEmitWithAwait (); + } + + // + // Checks whether the type is an interface that has the + // [ComImport, CoClass] attributes and must be treated + // specially + // + public Expression CheckComImport (ResolveContext ec) + { + if (!type.IsInterface) + return null; + + // + // Turn the call into: + // (the-interface-stated) (new class-referenced-in-coclassattribute ()) + // + var real_class = type.MemberDefinition.GetAttributeCoClass (); + if (real_class == null) + return null; + + New proxy = new New (new TypeExpression (real_class, loc), arguments, loc); + Cast cast = new Cast (new TypeExpression (type, loc), proxy, loc); + return cast.Resolve (ec); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Arguments args; + if (method == null) { + args = new Arguments (1); + args.Add (new Argument (new TypeOf (type, loc))); + } else { + args = Arguments.CreateForExpressionTree (ec, + arguments, new TypeOfMethod (method, loc)); + } + + return CreateExpressionFactoryCall (ec, "New", args); + } + + protected override Expression DoResolve (ResolveContext ec) + { + type = RequestedType.ResolveAsType (ec); + if (type == null) + return null; + + eclass = ExprClass.Value; + + if (type.IsPointer) { + ec.Report.Error (1919, loc, "Unsafe type `{0}' cannot be used in an object creation expression", + type.GetSignatureForError ()); + return null; + } + + if (arguments == null) { + Constant c = Constantify (type, RequestedType.Location); + if (c != null) + return ReducedExpression.Create (c, this); + } + + if (type.IsDelegate) { + return (new NewDelegate (type, arguments, loc)).Resolve (ec); + } + + var tparam = type as TypeParameterSpec; + if (tparam != null) { + // + // Check whether the type of type parameter can be constructed. BaseType can be a struct for method overrides + // where type parameter constraint is inflated to struct + // + if ((tparam.SpecialConstraint & (SpecialConstraint.Struct | SpecialConstraint.Constructor)) == 0 && !TypeSpec.IsValueType (tparam)) { + ec.Report.Error (304, loc, + "Cannot create an instance of the variable type `{0}' because it does not have the new() constraint", + type.GetSignatureForError ()); + } + + if ((arguments != null) && (arguments.Count != 0)) { + ec.Report.Error (417, loc, + "`{0}': cannot provide arguments when creating an instance of a variable type", + type.GetSignatureForError ()); + } + + return this; + } + + if (type.IsStatic) { + ec.Report.SymbolRelatedToPreviousError (type); + ec.Report.Error (712, loc, "Cannot create an instance of the static class `{0}'", type.GetSignatureForError ()); + return null; + } + + if (type.IsInterface || type.IsAbstract){ + if (!TypeManager.IsGenericType (type)) { + RequestedType = CheckComImport (ec); + if (RequestedType != null) + return RequestedType; + } + + ec.Report.SymbolRelatedToPreviousError (type); + ec.Report.Error (144, loc, "Cannot create an instance of the abstract class or interface `{0}'", type.GetSignatureForError ()); + return null; + } + + // + // Any struct always defines parameterless constructor + // + if (type.IsStruct && arguments == null) + return this; + + bool dynamic; + if (arguments != null) { + arguments.Resolve (ec, out dynamic); + } else { + dynamic = false; + } + + method = ConstructorLookup (ec, type, ref arguments, loc); + + if (dynamic) { + arguments.Insert (0, new Argument (new TypeOf (type, loc).Resolve (ec), Argument.AType.DynamicTypeName)); + return new DynamicConstructorBinder (type, arguments, loc).Resolve (ec); + } + + return this; + } + + bool DoEmitTypeParameter (EmitContext ec) + { + var m = ec.Module.PredefinedMembers.ActivatorCreateInstance.Resolve (loc); + if (m == null) + return true; + + var ctor_factory = m.MakeGenericMethod (ec.MemberContext, type); + var tparam = (TypeParameterSpec) type; + + if (tparam.IsReferenceType) { + ec.Emit (OpCodes.Call, ctor_factory); + return true; + } + + // Allow DoEmit() to be called multiple times. + // We need to create a new LocalTemporary each time since + // you can't share LocalBuilders among ILGeneators. + LocalTemporary temp = new LocalTemporary (type); + + Label label_activator = ec.DefineLabel (); + Label label_end = ec.DefineLabel (); + + temp.AddressOf (ec, AddressOp.Store); + ec.Emit (OpCodes.Initobj, type); + + temp.Emit (ec); + ec.Emit (OpCodes.Box, type); + ec.Emit (OpCodes.Brfalse, label_activator); + + temp.AddressOf (ec, AddressOp.Store); + ec.Emit (OpCodes.Initobj, type); + temp.Emit (ec); + temp.Release (ec); + ec.Emit (OpCodes.Br_S, label_end); + + ec.MarkLabel (label_activator); + + ec.Emit (OpCodes.Call, ctor_factory); + ec.MarkLabel (label_end); + return true; + } + + // + // This Emit can be invoked in two contexts: + // * As a mechanism that will leave a value on the stack (new object) + // * As one that wont (init struct) + // + // If we are dealing with a ValueType, we have a few + // situations to deal with: + // + // * The target is a ValueType, and we have been provided + // the instance (this is easy, we are being assigned). + // + // * The target of New is being passed as an argument, + // to a boxing operation or a function that takes a + // ValueType. + // + // In this case, we need to create a temporary variable + // that is the argument of New. + // + // Returns whether a value is left on the stack + // + // *** Implementation note *** + // + // To benefit from this optimization, each assignable expression + // has to manually cast to New and call this Emit. + // + // TODO: It's worth to implement it for arrays and fields + // + public virtual bool Emit (EmitContext ec, IMemoryLocation target) + { + bool is_value_type = TypeSpec.IsValueType (type); + VariableReference vr = target as VariableReference; + + if (target != null && is_value_type && (vr != null || method == null)) { + target.AddressOf (ec, AddressOp.Store); + } else if (vr != null && vr.IsRef) { + vr.EmitLoad (ec); + } + + if (arguments != null) { + if (ec.HasSet (BuilderContext.Options.AsyncBody) && (arguments.Count > (this is NewInitialize ? 0 : 1)) && arguments.ContainsEmitWithAwait ()) + arguments = arguments.Emit (ec, false, true); + + arguments.Emit (ec); + } + + if (is_value_type) { + if (method == null) { + ec.Emit (OpCodes.Initobj, type); + return false; + } + + if (vr != null) { + ec.MarkCallEntry (loc); + ec.Emit (OpCodes.Call, method); + return false; + } + } + + if (type is TypeParameterSpec) + return DoEmitTypeParameter (ec); + + ec.MarkCallEntry (loc); + ec.Emit (OpCodes.Newobj, method); + return true; + } + + public override void Emit (EmitContext ec) + { + LocalTemporary v = null; + if (method == null && TypeSpec.IsValueType (type)) { + // TODO: Use temporary variable from pool + v = new LocalTemporary (type); + } + + if (!Emit (ec, v)) + v.Emit (ec); + } + + public override void EmitStatement (EmitContext ec) + { + LocalTemporary v = null; + if (method == null && TypeSpec.IsValueType (type)) { + // TODO: Use temporary variable from pool + v = new LocalTemporary (type); + } + + if (Emit (ec, v)) + ec.Emit (OpCodes.Pop); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + if (arguments != null) + arguments.FlowAnalysis (fc); + } + + public void AddressOf (EmitContext ec, AddressOp mode) + { + EmitAddressOf (ec, mode); + } + + protected virtual IMemoryLocation EmitAddressOf (EmitContext ec, AddressOp mode) + { + LocalTemporary value_target = new LocalTemporary (type); + + if (type is TypeParameterSpec) { + DoEmitTypeParameter (ec); + value_target.Store (ec); + value_target.AddressOf (ec, mode); + return value_target; + } + + value_target.AddressOf (ec, AddressOp.Store); + + if (method == null) { + ec.Emit (OpCodes.Initobj, type); + } else { + if (arguments != null) + arguments.Emit (ec); + + ec.Emit (OpCodes.Call, method); + } + + value_target.AddressOf (ec, mode); + return value_target; + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + New target = (New) t; + + target.RequestedType = RequestedType.Clone (clonectx); + if (arguments != null){ + target.arguments = arguments.Clone (clonectx); + } + } + + public override SLE.Expression MakeExpression (BuilderContext ctx) + { +#if STATIC + return base.MakeExpression (ctx); +#else + return SLE.Expression.New ((ConstructorInfo) method.GetMetaInfo (), Arguments.MakeExpression (arguments, ctx)); +#endif + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + // + // Array initializer expression, the expression is allowed in + // variable or field initialization only which makes it tricky as + // the type has to be infered based on the context either from field + // type or variable type (think of multiple declarators) + // + public class ArrayInitializer : Expression + { + List elements; + BlockVariable variable; + + public ArrayInitializer (List init, Location loc) + { + elements = init; + this.loc = loc; + } + + public ArrayInitializer (int count, Location loc) + : this (new List (count), loc) + { + } + + public ArrayInitializer (Location loc) + : this (4, loc) + { + } + + #region Properties + + public int Count { + get { return elements.Count; } + } + + public List Elements { + get { + return elements; + } + } + + public Expression this [int index] { + get { + return elements [index]; + } + } + + public BlockVariable VariableDeclaration { + get { + return variable; + } + set { + variable = value; + } + } + #endregion + + public void Add (Expression expr) + { + elements.Add (expr); + } + + public override bool ContainsEmitWithAwait () + { + throw new NotSupportedException (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + throw new NotSupportedException ("ET"); + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + var target = (ArrayInitializer) t; + + target.elements = new List (elements.Count); + foreach (var element in elements) + target.elements.Add (element.Clone (clonectx)); + } + + protected override Expression DoResolve (ResolveContext rc) + { + var current_field = rc.CurrentMemberDefinition as FieldBase; + TypeExpression type; + if (current_field != null && rc.CurrentAnonymousMethod == null) { + type = new TypeExpression (current_field.MemberType, current_field.Location); + } else if (variable != null) { + if (variable.TypeExpression is VarExpr) { + rc.Report.Error (820, loc, "An implicitly typed local variable declarator cannot use an array initializer"); + return EmptyExpression.Null; + } + + type = new TypeExpression (variable.Variable.Type, variable.Variable.Location); + } else { + throw new NotImplementedException ("Unexpected array initializer context"); + } + + return new ArrayCreation (type, this).Resolve (rc); + } + + public override void Emit (EmitContext ec) + { + throw new InternalErrorException ("Missing Resolve call"); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + throw new InternalErrorException ("Missing Resolve call"); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + /// + /// 14.5.10.2: Represents an array creation expression. + /// + /// + /// + /// There are two possible scenarios here: one is an array creation + /// expression that specifies the dimensions and optionally the + /// initialization data and the other which does not need dimensions + /// specified but where initialization data is mandatory. + /// + public class ArrayCreation : Expression + { + FullNamedExpression requested_base_type; + ArrayInitializer initializers; + + // + // The list of Argument types. + // This is used to construct the `newarray' or constructor signature + // + protected List arguments; + + protected TypeSpec array_element_type; + int num_arguments; + protected int dimensions; + protected readonly ComposedTypeSpecifier rank; + Expression first_emit; + LocalTemporary first_emit_temp; + + protected List array_data; + + Dictionary bounds; + +#if STATIC + // The number of constants in array initializers + int const_initializers_count; + bool only_constant_initializers; +#endif + public ArrayCreation (FullNamedExpression requested_base_type, List exprs, ComposedTypeSpecifier rank, ArrayInitializer initializers, Location l) + : this (requested_base_type, rank, initializers, l) + { + arguments = exprs; + num_arguments = arguments.Count; + } + + // + // For expressions like int[] foo = new int[] { 1, 2, 3 }; + // + public ArrayCreation (FullNamedExpression requested_base_type, ComposedTypeSpecifier rank, ArrayInitializer initializers, Location loc) + { + this.requested_base_type = requested_base_type; + this.rank = rank; + this.initializers = initializers; + this.loc = loc; + + if (rank != null) + num_arguments = rank.Dimension; + } + + // + // For compiler generated single dimensional arrays only + // + public ArrayCreation (FullNamedExpression requested_base_type, ArrayInitializer initializers, Location loc) + : this (requested_base_type, ComposedTypeSpecifier.SingleDimension, initializers, loc) + { + } + + // + // For expressions like int[] foo = { 1, 2, 3 }; + // + public ArrayCreation (FullNamedExpression requested_base_type, ArrayInitializer initializers) + : this (requested_base_type, null, initializers, initializers.Location) + { + } + + public ComposedTypeSpecifier Rank { + get { + return this.rank; + } + } + + public FullNamedExpression TypeExpression { + get { + return this.requested_base_type; + } + } + + public ArrayInitializer Initializers { + get { + return this.initializers; + } + } + + public List Arguments { + get { return this.arguments; } + } + + bool CheckIndices (ResolveContext ec, ArrayInitializer probe, int idx, bool specified_dims, int child_bounds) + { + if (initializers != null && bounds == null) { + // + // We use this to store all the data values in the order in which we + // will need to store them in the byte blob later + // + array_data = new List (probe.Count); + bounds = new Dictionary (); + } + + if (specified_dims) { + Expression a = arguments [idx]; + a = a.Resolve (ec); + if (a == null) + return false; + + a = ConvertExpressionToArrayIndex (ec, a); + if (a == null) + return false; + + arguments[idx] = a; + + if (initializers != null) { + Constant c = a as Constant; + if (c == null && a is ArrayIndexCast) + c = ((ArrayIndexCast) a).Child as Constant; + + if (c == null) { + ec.Report.Error (150, a.Location, "A constant value is expected"); + return false; + } + + int value; + try { + value = System.Convert.ToInt32 (c.GetValue ()); + } catch { + ec.Report.Error (150, a.Location, "A constant value is expected"); + return false; + } + + // TODO: probe.Count does not fit ulong in + if (value != probe.Count) { + ec.Report.Error (847, loc, "An array initializer of length `{0}' was expected", value.ToString ()); + return false; + } + + bounds[idx] = value; + } + } + + if (initializers == null) + return true; + + for (int i = 0; i < probe.Count; ++i) { + var o = probe [i]; + if (o is ArrayInitializer) { + var sub_probe = o as ArrayInitializer; + if (idx + 1 >= dimensions){ + ec.Report.Error (623, loc, "Array initializers can only be used in a variable or field initializer. Try using a new expression instead"); + return false; + } + + // When we don't have explicitly specified dimensions, record whatever dimension we first encounter at each level + if (!bounds.ContainsKey(idx + 1)) + bounds[idx + 1] = sub_probe.Count; + + if (bounds[idx + 1] != sub_probe.Count) { + ec.Report.Error(847, sub_probe.Location, "An array initializer of length `{0}' was expected", bounds[idx + 1].ToString()); + return false; + } + + bool ret = CheckIndices (ec, sub_probe, idx + 1, specified_dims, child_bounds - 1); + if (!ret) + return false; + } else if (child_bounds > 1) { + ec.Report.Error (846, o.Location, "A nested array initializer was expected"); + } else { + Expression element = ResolveArrayElement (ec, o); + if (element == null) + continue; +#if STATIC + // Initializers with the default values can be ignored + Constant c = element as Constant; + if (c != null) { + if (!c.IsDefaultInitializer (array_element_type)) { + ++const_initializers_count; + } + } else { + only_constant_initializers = false; + } +#endif + array_data.Add (element); + } + } + + return true; + } + + public override bool ContainsEmitWithAwait () + { + foreach (var arg in arguments) { + if (arg.ContainsEmitWithAwait ()) + return true; + } + + return InitializersContainAwait (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Arguments args; + + if (array_data == null) { + args = new Arguments (arguments.Count + 1); + args.Add (new Argument (new TypeOf (array_element_type, loc))); + foreach (Expression a in arguments) + args.Add (new Argument (a.CreateExpressionTree (ec))); + + return CreateExpressionFactoryCall (ec, "NewArrayBounds", args); + } + + if (dimensions > 1) { + ec.Report.Error (838, loc, "An expression tree cannot contain a multidimensional array initializer"); + return null; + } + + args = new Arguments (array_data == null ? 1 : array_data.Count + 1); + args.Add (new Argument (new TypeOf (array_element_type, loc))); + if (array_data != null) { + for (int i = 0; i < array_data.Count; ++i) { + Expression e = array_data [i]; + args.Add (new Argument (e.CreateExpressionTree (ec))); + } + } + + return CreateExpressionFactoryCall (ec, "NewArrayInit", args); + } + + void UpdateIndices (ResolveContext rc) + { + int i = 0; + for (var probe = initializers; probe != null;) { + Expression e = new IntConstant (rc.BuiltinTypes, probe.Count, Location.Null); + arguments.Add (e); + bounds[i++] = probe.Count; + + if (probe.Count > 0 && probe [0] is ArrayInitializer) { + probe = (ArrayInitializer) probe[0]; + } else if (dimensions > i) { + continue; + } else { + return; + } + } + } + + protected override void Error_NegativeArrayIndex (ResolveContext ec, Location loc) + { + ec.Report.Error (248, loc, "Cannot create an array with a negative size"); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + foreach (var arg in arguments) + arg.FlowAnalysis (fc); + + if (array_data != null) { + foreach (var ad in array_data) + ad.FlowAnalysis (fc); + } + } + + bool InitializersContainAwait () + { + if (array_data == null) + return false; + + foreach (var expr in array_data) { + if (expr.ContainsEmitWithAwait ()) + return true; + } + + return false; + } + + protected virtual Expression ResolveArrayElement (ResolveContext ec, Expression element) + { + element = element.Resolve (ec); + if (element == null) + return null; + + if (element is CompoundAssign.TargetExpression) { + if (first_emit != null) + throw new InternalErrorException ("Can only handle one mutator at a time"); + first_emit = element; + element = first_emit_temp = new LocalTemporary (element.Type); + } + + return Convert.ImplicitConversionRequired ( + ec, element, array_element_type, loc); + } + + protected bool ResolveInitializers (ResolveContext ec) + { +#if STATIC + only_constant_initializers = true; +#endif + + if (arguments != null) { + bool res = true; + for (int i = 0; i < arguments.Count; ++i) { + res &= CheckIndices (ec, initializers, i, true, dimensions); + if (initializers != null) + break; + } + + return res; + } + + arguments = new List (); + + if (!CheckIndices (ec, initializers, 0, false, dimensions)) + return false; + + UpdateIndices (ec); + + return true; + } + + // + // Resolved the type of the array + // + bool ResolveArrayType (ResolveContext ec) + { + // + // Lookup the type + // + FullNamedExpression array_type_expr; + if (num_arguments > 0) { + array_type_expr = new ComposedCast (requested_base_type, rank); + } else { + array_type_expr = requested_base_type; + } + + type = array_type_expr.ResolveAsType (ec); + if (array_type_expr == null) + return false; + + var ac = type as ArrayContainer; + if (ac == null) { + ec.Report.Error (622, loc, "Can only use array initializer expressions to assign to array types. Try using a new expression instead"); + return false; + } + + array_element_type = ac.Element; + dimensions = ac.Rank; + + return true; + } + + protected override Expression DoResolve (ResolveContext ec) + { + if (type != null) + return this; + + if (!ResolveArrayType (ec)) + return null; + + // + // validate the initializers and fill in any missing bits + // + if (!ResolveInitializers (ec)) + return null; + + eclass = ExprClass.Value; + return this; + } + + byte [] MakeByteBlob () + { + int factor; + byte [] data; + byte [] element; + int count = array_data.Count; + + TypeSpec element_type = array_element_type; + if (element_type.IsEnum) + element_type = EnumSpec.GetUnderlyingType (element_type); + + factor = BuiltinTypeSpec.GetSize (element_type); + if (factor == 0) + throw new Exception ("unrecognized type in MakeByteBlob: " + element_type); + + data = new byte [(count * factor + 3) & ~3]; + int idx = 0; + + for (int i = 0; i < count; ++i) { + var c = array_data[i] as Constant; + if (c == null) { + idx += factor; + continue; + } + + object v = c.GetValue (); + + switch (element_type.BuiltinType) { + case BuiltinTypeSpec.Type.Long: + long lval = (long) v; + + for (int j = 0; j < factor; ++j) { + data[idx + j] = (byte) (lval & 0xFF); + lval = (lval >> 8); + } + break; + case BuiltinTypeSpec.Type.ULong: + ulong ulval = (ulong) v; + + for (int j = 0; j < factor; ++j) { + data[idx + j] = (byte) (ulval & 0xFF); + ulval = (ulval >> 8); + } + break; + case BuiltinTypeSpec.Type.Float: + var fval = SingleConverter.SingleToInt32Bits((float) v); + + data[idx] = (byte) (fval & 0xff); + data[idx + 1] = (byte) ((fval >> 8) & 0xff); + data[idx + 2] = (byte) ((fval >> 16) & 0xff); + data[idx + 3] = (byte) (fval >> 24); + break; + case BuiltinTypeSpec.Type.Double: + element = BitConverter.GetBytes ((double) v); + + for (int j = 0; j < factor; ++j) + data[idx + j] = element[j]; + + // FIXME: Handle the ARM float format. + if (!BitConverter.IsLittleEndian) + System.Array.Reverse (data, idx, 8); + break; + case BuiltinTypeSpec.Type.Char: + int chval = (int) ((char) v); + + data[idx] = (byte) (chval & 0xff); + data[idx + 1] = (byte) (chval >> 8); + break; + case BuiltinTypeSpec.Type.Short: + int sval = (int) ((short) v); + + data[idx] = (byte) (sval & 0xff); + data[idx + 1] = (byte) (sval >> 8); + break; + case BuiltinTypeSpec.Type.UShort: + int usval = (int) ((ushort) v); + + data[idx] = (byte) (usval & 0xff); + data[idx + 1] = (byte) (usval >> 8); + break; + case BuiltinTypeSpec.Type.Int: + int val = (int) v; + + data[idx] = (byte) (val & 0xff); + data[idx + 1] = (byte) ((val >> 8) & 0xff); + data[idx + 2] = (byte) ((val >> 16) & 0xff); + data[idx + 3] = (byte) (val >> 24); + break; + case BuiltinTypeSpec.Type.UInt: + uint uval = (uint) v; + + data[idx] = (byte) (uval & 0xff); + data[idx + 1] = (byte) ((uval >> 8) & 0xff); + data[idx + 2] = (byte) ((uval >> 16) & 0xff); + data[idx + 3] = (byte) (uval >> 24); + break; + case BuiltinTypeSpec.Type.SByte: + data[idx] = (byte) (sbyte) v; + break; + case BuiltinTypeSpec.Type.Byte: + data[idx] = (byte) v; + break; + case BuiltinTypeSpec.Type.Bool: + data[idx] = (byte) ((bool) v ? 1 : 0); + break; + case BuiltinTypeSpec.Type.Decimal: + int[] bits = Decimal.GetBits ((decimal) v); + int p = idx; + + // FIXME: For some reason, this doesn't work on the MS runtime. + int[] nbits = new int[4]; + nbits[0] = bits[3]; + nbits[1] = bits[2]; + nbits[2] = bits[0]; + nbits[3] = bits[1]; + + for (int j = 0; j < 4; j++) { + data[p++] = (byte) (nbits[j] & 0xff); + data[p++] = (byte) ((nbits[j] >> 8) & 0xff); + data[p++] = (byte) ((nbits[j] >> 16) & 0xff); + data[p++] = (byte) (nbits[j] >> 24); + } + break; + default: + throw new Exception ("Unrecognized type in MakeByteBlob: " + element_type); + } + + idx += factor; + } + + return data; + } + +#if NET_4_0 || MOBILE_DYNAMIC + public override SLE.Expression MakeExpression (BuilderContext ctx) + { +#if STATIC + return base.MakeExpression (ctx); +#else + var initializers = new SLE.Expression [array_data.Count]; + for (var i = 0; i < initializers.Length; i++) { + if (array_data [i] == null) + initializers [i] = SLE.Expression.Default (array_element_type.GetMetaInfo ()); + else + initializers [i] = array_data [i].MakeExpression (ctx); + } + + return SLE.Expression.NewArrayInit (array_element_type.GetMetaInfo (), initializers); +#endif + } +#endif +#if STATIC + // + // Emits the initializers for the array + // + void EmitStaticInitializers (EmitContext ec, FieldExpr stackArray) + { + var m = ec.Module.PredefinedMembers.RuntimeHelpersInitializeArray.Resolve (loc); + if (m == null) + return; + + // + // First, the static data + // + byte [] data = MakeByteBlob (); + var fb = ec.CurrentTypeDefinition.Module.MakeStaticData (data, loc); + + if (stackArray == null) { + ec.Emit (OpCodes.Dup); + } else { + stackArray.Emit (ec); + } + + ec.Emit (OpCodes.Ldtoken, fb); + ec.Emit (OpCodes.Call, m); + } +#endif + + // + // Emits pieces of the array that can not be computed at compile + // time (variables and string locations). + // + // This always expect the top value on the stack to be the array + // + void EmitDynamicInitializers (EmitContext ec, bool emitConstants, StackFieldExpr stackArray) + { + int dims = bounds.Count; + var current_pos = new int [dims]; + + for (int i = 0; i < array_data.Count; i++){ + + Expression e = array_data [i]; + var c = e as Constant; + + // Constant can be initialized via StaticInitializer + if (c == null || (c != null && emitConstants && !c.IsDefaultInitializer (array_element_type))) { + + var etype = e.Type; + + if (stackArray != null) { + if (e.ContainsEmitWithAwait ()) { + e = e.EmitToField (ec); + } + + stackArray.EmitLoad (ec); + } else { + ec.Emit (OpCodes.Dup); + } + + for (int idx = 0; idx < dims; idx++) + ec.EmitInt (current_pos [idx]); + + // + // If we are dealing with a struct, get the + // address of it, so we can store it. + // + if (dims == 1 && etype.IsStruct && !BuiltinTypeSpec.IsPrimitiveType (etype)) + ec.Emit (OpCodes.Ldelema, etype); + + e.Emit (ec); + + ec.EmitArrayStore ((ArrayContainer) type); + } + + // + // Advance counter + // + for (int j = dims - 1; j >= 0; j--){ + current_pos [j]++; + if (current_pos [j] < bounds [j]) + break; + current_pos [j] = 0; + } + } + + if (stackArray != null) + stackArray.PrepareCleanup (ec); + } + + public override void Emit (EmitContext ec) + { + var await_field = EmitToFieldSource (ec); + if (await_field != null) + await_field.Emit (ec); + } + + protected sealed override FieldExpr EmitToFieldSource (EmitContext ec) + { + if (first_emit != null) { + first_emit.Emit (ec); + first_emit_temp.Store (ec); + } + + StackFieldExpr await_stack_field; + if (ec.HasSet (BuilderContext.Options.AsyncBody) && InitializersContainAwait ()) { + await_stack_field = ec.GetTemporaryField (type); + ec.EmitThis (); + } else { + await_stack_field = null; + } + + EmitExpressionsList (ec, arguments); + + ec.EmitArrayNew ((ArrayContainer) type); + + if (initializers == null) + return await_stack_field; + + if (await_stack_field != null) + await_stack_field.EmitAssignFromStack (ec); + +#if STATIC + // + // Emit static initializer for arrays which contain more than 2 items and + // the static initializer will initialize at least 25% of array values or there + // is more than 10 items to be initialized + // + // NOTE: const_initializers_count does not contain default constant values. + // + if (const_initializers_count > 2 && (array_data.Count > 10 || const_initializers_count * 4 > (array_data.Count)) && + (BuiltinTypeSpec.IsPrimitiveType (array_element_type) || array_element_type.IsEnum)) { + EmitStaticInitializers (ec, await_stack_field); + + if (!only_constant_initializers) + EmitDynamicInitializers (ec, false, await_stack_field); + } else +#endif + { + EmitDynamicInitializers (ec, true, await_stack_field); + } + + if (first_emit_temp != null) + first_emit_temp.Release (ec); + + return await_stack_field; + } + + public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) + { + // no multi dimensional or jagged arrays + if (arguments.Count != 1 || array_element_type.IsArray) { + base.EncodeAttributeValue (rc, enc, targetType, parameterType); + return; + } + + // No array covariance, except for array -> object + if (type != targetType) { + if (targetType.BuiltinType != BuiltinTypeSpec.Type.Object) { + base.EncodeAttributeValue (rc, enc, targetType, parameterType); + return; + } + + if (enc.Encode (type) == AttributeEncoder.EncodedTypeProperties.DynamicType) { + Attribute.Error_AttributeArgumentIsDynamic (rc, loc); + return; + } + } + + // Single dimensional array of 0 size + if (array_data == null) { + IntConstant ic = arguments[0] as IntConstant; + if (ic == null || !ic.IsDefaultValue) { + base.EncodeAttributeValue (rc, enc, targetType, parameterType); + } else { + enc.Encode (0); + } + + return; + } + + enc.Encode (array_data.Count); + foreach (var element in array_data) { + element.EncodeAttributeValue (rc, enc, array_element_type, parameterType); + } + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + ArrayCreation target = (ArrayCreation) t; + + if (requested_base_type != null) + target.requested_base_type = (FullNamedExpression)requested_base_type.Clone (clonectx); + + if (arguments != null){ + target.arguments = new List (arguments.Count); + foreach (Expression e in arguments) + target.arguments.Add (e.Clone (clonectx)); + } + + if (initializers != null) + target.initializers = (ArrayInitializer) initializers.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + // + // Represents an implicitly typed array epxression + // + class ImplicitlyTypedArrayCreation : ArrayCreation + { + TypeInferenceContext best_type_inference; + + public ImplicitlyTypedArrayCreation (ComposedTypeSpecifier rank, ArrayInitializer initializers, Location loc) + : base (null, rank, initializers, loc) + { + } + + public ImplicitlyTypedArrayCreation (ArrayInitializer initializers, Location loc) + : base (null, initializers, loc) + { + } + + protected override Expression DoResolve (ResolveContext ec) + { + if (type != null) + return this; + + dimensions = rank.Dimension; + + best_type_inference = new TypeInferenceContext (); + + if (!ResolveInitializers (ec)) + return null; + + best_type_inference.FixAllTypes (ec); + array_element_type = best_type_inference.InferredTypeArguments[0]; + best_type_inference = null; + + if (array_element_type == null || + array_element_type == InternalType.NullLiteral || array_element_type == InternalType.MethodGroup || array_element_type == InternalType.AnonymousMethod || + arguments.Count != rank.Dimension) { + ec.Report.Error (826, loc, + "The type of an implicitly typed array cannot be inferred from the initializer. Try specifying array type explicitly"); + return null; + } + + // + // At this point we found common base type for all initializer elements + // but we have to be sure that all static initializer elements are of + // same type + // + UnifyInitializerElement (ec); + + type = ArrayContainer.MakeType (ec.Module, array_element_type, dimensions); + eclass = ExprClass.Value; + return this; + } + + // + // Converts static initializer only + // + void UnifyInitializerElement (ResolveContext ec) + { + for (int i = 0; i < array_data.Count; ++i) { + Expression e = array_data[i]; + if (e != null) + array_data [i] = Convert.ImplicitConversion (ec, e, array_element_type, Location.Null); + } + } + + protected override Expression ResolveArrayElement (ResolveContext ec, Expression element) + { + element = element.Resolve (ec); + if (element != null) + best_type_inference.AddCommonTypeBound (element.Type); + + return element; + } + } + + sealed class CompilerGeneratedThis : This + { + public CompilerGeneratedThis (TypeSpec type, Location loc) + : base (loc) + { + this.type = type; + } + + protected override Expression DoResolve (ResolveContext rc) + { + eclass = ExprClass.Variable; + + var block = rc.CurrentBlock; + if (block != null) { + var top = block.ParametersBlock.TopBlock; + if (top.ThisVariable != null) + variable_info = top.ThisVariable.VariableInfo; + + } + + return this; + } + + public override Expression DoResolveLValue (ResolveContext rc, Expression right_side) + { + return DoResolve (rc); + } + + public override HoistedVariable GetHoistedVariable (AnonymousExpression ae) + { + return null; + } + } + + /// + /// Represents the `this' construct + /// + + public class This : VariableReference + { + sealed class ThisVariable : ILocalVariable + { + public static readonly ILocalVariable Instance = new ThisVariable (); + + public void Emit (EmitContext ec) + { + ec.EmitThis (); + } + + public void EmitAssign (EmitContext ec) + { + throw new InvalidOperationException (); + } + + public void EmitAddressOf (EmitContext ec) + { + ec.EmitThis (); + } + } + + protected VariableInfo variable_info; + + public This (Location loc) + { + this.loc = loc; + } + + #region Properties + + public override string Name { + get { return "this"; } + } + + public override bool IsLockedByStatement { + get { + return false; + } + set { + } + } + + public override bool IsRef { + get { return type.IsStruct; } + } + + public override bool IsSideEffectFree { + get { + return true; + } + } + + protected override ILocalVariable Variable { + get { return ThisVariable.Instance; } + } + + public override VariableInfo VariableInfo { + get { return variable_info; } + } + + public override bool IsFixed { + get { return false; } + } + + #endregion + + void CheckStructThisDefiniteAssignment (FlowAnalysisContext fc) + { + // + // It's null for all cases when we don't need to check `this' + // definitive assignment + // + if (variable_info == null) + return; + + if (fc.IsDefinitelyAssigned (variable_info)) + return; + + fc.Report.Error (188, loc, "The `this' object cannot be used before all of its fields are assigned to"); + } + + protected virtual void Error_ThisNotAvailable (ResolveContext ec) + { + if (ec.IsStatic && !ec.HasSet (ResolveContext.Options.ConstantScope)) { + ec.Report.Error (26, loc, "Keyword `this' is not valid in a static property, static method, or static field initializer"); + } else if (ec.CurrentAnonymousMethod != null) { + ec.Report.Error (1673, loc, + "Anonymous methods inside structs cannot access instance members of `this'. " + + "Consider copying `this' to a local variable outside the anonymous method and using the local instead"); + } else { + ec.Report.Error (27, loc, "Keyword `this' is not available in the current context"); + } + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + CheckStructThisDefiniteAssignment (fc); + } + + public override HoistedVariable GetHoistedVariable (AnonymousExpression ae) + { + if (ae == null) + return null; + + AnonymousMethodStorey storey = ae.Storey; + return storey != null ? storey.HoistedThis : null; + } + + public static bool IsThisAvailable (ResolveContext ec, bool ignoreAnonymous) + { + if (ec.IsStatic || ec.HasAny (ResolveContext.Options.FieldInitializerScope | ResolveContext.Options.BaseInitializer | ResolveContext.Options.ConstantScope)) + return false; + + if (ignoreAnonymous || ec.CurrentAnonymousMethod == null) + return true; + + if (ec.CurrentType.IsStruct && !(ec.CurrentAnonymousMethod is StateMachineInitializer)) + return false; + + return true; + } + + public virtual void ResolveBase (ResolveContext ec) + { + eclass = ExprClass.Variable; + type = ec.CurrentType; + + if (!IsThisAvailable (ec, false)) { + Error_ThisNotAvailable (ec); + return; + } + + var block = ec.CurrentBlock; + if (block != null) { + var top = block.ParametersBlock.TopBlock; + if (top.ThisVariable != null) + variable_info = top.ThisVariable.VariableInfo; + + AnonymousExpression am = ec.CurrentAnonymousMethod; + if (am != null && ec.IsVariableCapturingRequired && !block.Explicit.HasCapturedThis) { + // + // Hoisted this is almost like hoisted variable but not exactly. When + // there is no variable hoisted we can simply emit an instance method + // without lifting this into a storey. Unfotunatelly this complicates + // things in other cases because we don't know where this will be hoisted + // until top-level block is fully resolved + // + top.AddThisReferenceFromChildrenBlock (block.Explicit); + am.SetHasThisAccess (); + } + } + } + + protected override Expression DoResolve (ResolveContext ec) + { + ResolveBase (ec); + return this; + } + + public override Expression DoResolveLValue (ResolveContext ec, Expression right_side) + { + if (eclass == ExprClass.Unresolved) + ResolveBase (ec); + + if (type.IsClass){ + if (right_side == EmptyExpression.UnaryAddress) + ec.Report.Error (459, loc, "Cannot take the address of `this' because it is read-only"); + else if (right_side == EmptyExpression.OutAccess) + ec.Report.Error (1605, loc, "Cannot pass `this' as a ref or out argument because it is read-only"); + else + ec.Report.Error (1604, loc, "Cannot assign to `this' because it is read-only"); + } + + return this; + } + + public override int GetHashCode() + { + throw new NotImplementedException (); + } + + public override bool Equals (object obj) + { + This t = obj as This; + if (t == null) + return false; + + return true; + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + // Nothing + } + + public override void SetHasAddressTaken () + { + // Nothing + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + /// + /// Represents the `__arglist' construct + /// + public class ArglistAccess : Expression + { + public ArglistAccess (Location loc) + { + this.loc = loc; + } + + protected override void CloneTo (CloneContext clonectx, Expression target) + { + // nothing. + } + + public override bool ContainsEmitWithAwait () + { + return false; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + throw new NotSupportedException ("ET"); + } + + protected override Expression DoResolve (ResolveContext ec) + { + eclass = ExprClass.Variable; + type = ec.Module.PredefinedTypes.RuntimeArgumentHandle.Resolve (); + + if (ec.HasSet (ResolveContext.Options.FieldInitializerScope) || !ec.CurrentBlock.ParametersBlock.Parameters.HasArglist) { + ec.Report.Error (190, loc, + "The __arglist construct is valid only within a variable argument method"); + } + + return this; + } + + public override void Emit (EmitContext ec) + { + ec.Emit (OpCodes.Arglist); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + /// + /// Represents the `__arglist (....)' construct + /// + public class Arglist : Expression + { + Arguments arguments; + + public Arglist (Location loc) + : this (null, loc) + { + } + + public Arglist (Arguments args, Location l) + { + arguments = args; + loc = l; + } + + public Arguments Arguments { + get { + return arguments; + } + } + + public MetaType[] ArgumentTypes { + get { + if (arguments == null) + return MetaType.EmptyTypes; + + var retval = new MetaType[arguments.Count]; + for (int i = 0; i < retval.Length; i++) + retval[i] = arguments[i].Expr.Type.GetMetaInfo (); + + return retval; + } + } + + public override bool ContainsEmitWithAwait () + { + throw new NotImplementedException (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + ec.Report.Error (1952, loc, "An expression tree cannot contain a method with variable arguments"); + return null; + } + + protected override Expression DoResolve (ResolveContext ec) + { + eclass = ExprClass.Variable; + type = InternalType.Arglist; + if (arguments != null) { + bool dynamic; // Can be ignored as there is always only 1 overload + arguments.Resolve (ec, out dynamic); + } + + return this; + } + + public override void Emit (EmitContext ec) + { + if (arguments != null) + arguments.Emit (ec); + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + Arglist target = (Arglist) t; + + if (arguments != null) + target.arguments = arguments.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class RefValueExpr : ShimExpression, IAssignMethod + { + FullNamedExpression texpr; + + public FullNamedExpression FullNamedExpression { + get { return texpr;} + } + + public RefValueExpr (Expression expr, FullNamedExpression texpr, Location loc) + : base (expr) + { + this.texpr = texpr; + this.loc = loc; + } + + public FullNamedExpression TypeExpression { + get { + return texpr; + } + } + + public override bool ContainsEmitWithAwait () + { + return false; + } + + protected override Expression DoResolve (ResolveContext rc) + { + expr = expr.Resolve (rc); + type = texpr.ResolveAsType (rc); + if (expr == null || type == null) + return null; + + expr = Convert.ImplicitConversionRequired (rc, expr, rc.Module.PredefinedTypes.TypedReference.Resolve (), loc); + eclass = ExprClass.Value; + return this; + } + + public override Expression DoResolveLValue (ResolveContext rc, Expression right_side) + { + return DoResolve (rc); + } + + public override void Emit (EmitContext ec) + { + expr.Emit (ec); + ec.Emit (OpCodes.Refanyval, type); + ec.EmitLoadFromPtr (type); + } + + public void Emit (EmitContext ec, bool leave_copy) + { + throw new NotImplementedException (); + } + + public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound) + { + expr.Emit (ec); + ec.Emit (OpCodes.Refanyval, type); + source.Emit (ec); + + LocalTemporary temporary = null; + if (leave_copy) { + ec.Emit (OpCodes.Dup); + temporary = new LocalTemporary (source.Type); + temporary.Store (ec); + } + + ec.EmitStoreFromPtr (type); + + if (temporary != null) { + temporary.Emit (ec); + temporary.Release (ec); + } + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class RefTypeExpr : ShimExpression + { + public RefTypeExpr (Expression expr, Location loc) + : base (expr) + { + this.loc = loc; + } + + protected override Expression DoResolve (ResolveContext rc) + { + expr = expr.Resolve (rc); + if (expr == null) + return null; + + expr = Convert.ImplicitConversionRequired (rc, expr, rc.Module.PredefinedTypes.TypedReference.Resolve (), loc); + if (expr == null) + return null; + + type = rc.BuiltinTypes.Type; + eclass = ExprClass.Value; + return this; + } + + public override void Emit (EmitContext ec) + { + expr.Emit (ec); + ec.Emit (OpCodes.Refanytype); + var m = ec.Module.PredefinedMembers.TypeGetTypeFromHandle.Resolve (loc); + if (m != null) + ec.Emit (OpCodes.Call, m); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class MakeRefExpr : ShimExpression + { + public MakeRefExpr (Expression expr, Location loc) + : base (expr) + { + this.loc = loc; + } + + public override bool ContainsEmitWithAwait () + { + throw new NotImplementedException (); + } + + protected override Expression DoResolve (ResolveContext rc) + { + expr = expr.ResolveLValue (rc, EmptyExpression.LValueMemberAccess); + type = rc.Module.PredefinedTypes.TypedReference.Resolve (); + eclass = ExprClass.Value; + return this; + } + + public override void Emit (EmitContext ec) + { + ((IMemoryLocation) expr).AddressOf (ec, AddressOp.Load); + ec.Emit (OpCodes.Mkrefany, expr.Type); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + /// + /// Implements the typeof operator + /// + public class TypeOf : Expression { + FullNamedExpression QueriedType; + TypeSpec typearg; + + public TypeOf (FullNamedExpression queried_type, Location l) + { + QueriedType = queried_type; + loc = l; + } + + // + // Use this constructor for any compiler generated typeof expression + // + public TypeOf (TypeSpec type, Location loc) + { + this.typearg = type; + this.loc = loc; + } + + #region Properties + + public override bool IsSideEffectFree { + get { + return true; + } + } + + public TypeSpec TypeArgument { + get { + return typearg; + } + } + + public FullNamedExpression TypeExpression { + get { + return QueriedType; + } + } + + #endregion + + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + TypeOf target = (TypeOf) t; + if (QueriedType != null) + target.QueriedType = (FullNamedExpression) QueriedType.Clone (clonectx); + } + + public override bool ContainsEmitWithAwait () + { + return false; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Arguments args = new Arguments (2); + args.Add (new Argument (this)); + args.Add (new Argument (new TypeOf (new TypeExpression (type, loc), loc))); + return CreateExpressionFactoryCall (ec, "Constant", args); + } + + protected override Expression DoResolve (ResolveContext ec) + { + if (eclass != ExprClass.Unresolved) + return this; + + if (typearg == null) { + // + // Pointer types are allowed without explicit unsafe, they are just tokens + // + using (ec.Set (ResolveContext.Options.UnsafeScope)) { + typearg = QueriedType.ResolveAsType (ec, true); + } + + if (typearg == null) + return null; + + if (typearg.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + ec.Report.Error (1962, QueriedType.Location, + "The typeof operator cannot be used on the dynamic type"); + } + } + + type = ec.BuiltinTypes.Type; + + // Even though what is returned is a type object, it's treated as a value by the compiler. + // In particular, 'typeof (Foo).X' is something totally different from 'Foo.X'. + eclass = ExprClass.Value; + return this; + } + + static bool ContainsDynamicType (TypeSpec type) + { + if (type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) + return true; + + var element_container = type as ElementTypeSpec; + if (element_container != null) + return ContainsDynamicType (element_container.Element); + + foreach (var t in type.TypeArguments) { + if (ContainsDynamicType (t)) { + return true; + } + } + + return false; + } + + public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) + { + // Target type is not System.Type therefore must be object + // and we need to use different encoding sequence + if (targetType != type) + enc.Encode (type); + + if (typearg is InflatedTypeSpec) { + var gt = typearg; + do { + if (InflatedTypeSpec.ContainsTypeParameter (gt)) { + rc.Module.Compiler.Report.Error (416, loc, "`{0}': an attribute argument cannot use type parameters", + typearg.GetSignatureForError ()); + return; + } + + gt = gt.DeclaringType; + } while (gt != null); + } + + if (ContainsDynamicType (typearg)) { + Attribute.Error_AttributeArgumentIsDynamic (rc, loc); + return; + } + + enc.EncodeTypeName (typearg); + } + + public override void Emit (EmitContext ec) + { + ec.Emit (OpCodes.Ldtoken, typearg); + var m = ec.Module.PredefinedMembers.TypeGetTypeFromHandle.Resolve (loc); + if (m != null) + ec.Emit (OpCodes.Call, m); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + sealed class TypeOfMethod : TypeOfMember + { + public TypeOfMethod (MethodSpec method, Location loc) + : base (method, loc) + { + } + + protected override Expression DoResolve (ResolveContext ec) + { + if (member.IsConstructor) { + type = ec.Module.PredefinedTypes.ConstructorInfo.Resolve (); + } else { + type = ec.Module.PredefinedTypes.MethodInfo.Resolve (); + } + + if (type == null) + return null; + + return base.DoResolve (ec); + } + + public override void Emit (EmitContext ec) + { + ec.Emit (OpCodes.Ldtoken, member); + + base.Emit (ec); + ec.Emit (OpCodes.Castclass, type); + } + + protected override PredefinedMember GetTypeFromHandle (EmitContext ec) + { + return ec.Module.PredefinedMembers.MethodInfoGetMethodFromHandle; + } + + protected override PredefinedMember GetTypeFromHandleGeneric (EmitContext ec) + { + return ec.Module.PredefinedMembers.MethodInfoGetMethodFromHandle2; + } + } + + abstract class TypeOfMember : Expression where T : MemberSpec + { + protected readonly T member; + + protected TypeOfMember (T member, Location loc) + { + this.member = member; + this.loc = loc; + } + + public override bool IsSideEffectFree { + get { + return true; + } + } + + public override bool ContainsEmitWithAwait () + { + return false; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Arguments args = new Arguments (2); + args.Add (new Argument (this)); + args.Add (new Argument (new TypeOf (type, loc))); + return CreateExpressionFactoryCall (ec, "Constant", args); + } + + protected override Expression DoResolve (ResolveContext ec) + { + eclass = ExprClass.Value; + return this; + } + + public override void Emit (EmitContext ec) + { + bool is_generic = member.DeclaringType.IsGenericOrParentIsGeneric; + PredefinedMember p; + if (is_generic) { + p = GetTypeFromHandleGeneric (ec); + ec.Emit (OpCodes.Ldtoken, member.DeclaringType); + } else { + p = GetTypeFromHandle (ec); + } + + var mi = p.Resolve (loc); + if (mi != null) + ec.Emit (OpCodes.Call, mi); + } + + protected abstract PredefinedMember GetTypeFromHandle (EmitContext ec); + protected abstract PredefinedMember GetTypeFromHandleGeneric (EmitContext ec); + } + + sealed class TypeOfField : TypeOfMember + { + public TypeOfField (FieldSpec field, Location loc) + : base (field, loc) + { + } + + protected override Expression DoResolve (ResolveContext ec) + { + type = ec.Module.PredefinedTypes.FieldInfo.Resolve (); + if (type == null) + return null; + + return base.DoResolve (ec); + } + + public override void Emit (EmitContext ec) + { + ec.Emit (OpCodes.Ldtoken, member); + base.Emit (ec); + } + + protected override PredefinedMember GetTypeFromHandle (EmitContext ec) + { + return ec.Module.PredefinedMembers.FieldInfoGetFieldFromHandle; + } + + protected override PredefinedMember GetTypeFromHandleGeneric (EmitContext ec) + { + return ec.Module.PredefinedMembers.FieldInfoGetFieldFromHandle2; + } + } + + /// + /// Implements the sizeof expression + /// + public class SizeOf : Expression { + readonly Expression texpr; + TypeSpec type_queried; + + public SizeOf (Expression queried_type, Location l) + { + this.texpr = queried_type; + loc = l; + } + + public override bool IsSideEffectFree { + get { + return true; + } + } + + public Expression TypeExpression { + get { + return texpr; + } + } + + public override bool ContainsEmitWithAwait () + { + return false; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Error_PointerInsideExpressionTree (ec); + return null; + } + + protected override Expression DoResolve (ResolveContext ec) + { + type_queried = texpr.ResolveAsType (ec); + if (type_queried == null) + return null; + + if (type_queried.IsEnum) + type_queried = EnumSpec.GetUnderlyingType (type_queried); + + int size_of = BuiltinTypeSpec.GetSize (type_queried); + if (size_of > 0) { + return new IntConstant (ec.BuiltinTypes, size_of, loc); + } + + if (!TypeManager.VerifyUnmanaged (ec.Module, type_queried, loc)){ + return null; + } + + if (!ec.IsUnsafe) { + ec.Report.Error (233, loc, + "`{0}' does not have a predefined size, therefore sizeof can only be used in an unsafe context (consider using System.Runtime.InteropServices.Marshal.SizeOf)", + type_queried.GetSignatureForError ()); + } + + type = ec.BuiltinTypes.Int; + eclass = ExprClass.Value; + return this; + } + + public override void Emit (EmitContext ec) + { + ec.Emit (OpCodes.Sizeof, type_queried); + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + /// + /// Implements the qualified-alias-member (::) expression. + /// + public class QualifiedAliasMember : MemberAccess + { + public readonly string alias; + public static readonly string GlobalAlias = "global"; + + public QualifiedAliasMember (string alias, string identifier, Location l) + : base (null, identifier, l) + { + this.alias = alias; + } + + public QualifiedAliasMember (string alias, string identifier, TypeArguments targs, Location l) + : base (null, identifier, targs, l) + { + this.alias = alias; + } + + public QualifiedAliasMember (string alias, string identifier, int arity, Location l) + : base (null, identifier, arity, l) + { + this.alias = alias; + } + + public string Alias { + get { + return alias; + } + } + + public FullNamedExpression CreateExpressionFromAlias (IMemberContext mc) + { + if (alias == GlobalAlias) + return new NamespaceExpression (mc.Module.GlobalRootNamespace, loc); + + int errors = mc.Module.Compiler.Report.Errors; + var expr = mc.LookupNamespaceAlias (alias); + if (expr == null) { + if (errors == mc.Module.Compiler.Report.Errors) + mc.Module.Compiler.Report.Error (432, loc, "Alias `{0}' not found", alias); + + return null; + } + + return expr; + } + + public override FullNamedExpression ResolveAsTypeOrNamespace (IMemberContext mc, bool allowUnboundTypeArguments) + { + expr = CreateExpressionFromAlias (mc); + if (expr == null) + return null; + + return base.ResolveAsTypeOrNamespace (mc, allowUnboundTypeArguments); + } + + protected override Expression DoResolve (ResolveContext rc) + { + return ResolveAsTypeOrNamespace (rc, false); + } + + public override string GetSignatureForError () + { + string name = Name; + if (targs != null) { + name = Name + "<" + targs.GetSignatureForError () + ">"; + } + + return alias + "::" + name; + } + + public override bool HasConditionalAccess () + { + return false; + } + + public override Expression LookupNameExpression (ResolveContext rc, MemberLookupRestrictions restrictions) + { + if ((restrictions & MemberLookupRestrictions.InvocableOnly) != 0) { + rc.Module.Compiler.Report.Error (687, loc, + "The namespace alias qualifier `::' cannot be used to invoke a method. Consider using `.' instead", + GetSignatureForError ()); + + return null; + } + + return DoResolve (rc); + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + // Nothing + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + /// + /// Implements the member access expression + /// + public class MemberAccess : ATypeNameExpression + { + protected Expression expr; + + public MemberAccess (Expression expr, string id) + : base (id, expr.Location) + { + this.expr = expr; + } + + public MemberAccess (Expression expr, string identifier, Location loc) + : base (identifier, loc) + { + this.expr = expr; + } + + public MemberAccess (Expression expr, string identifier, TypeArguments args, Location loc) + : base (identifier, args, loc) + { + this.expr = expr; + } + + public MemberAccess (Expression expr, string identifier, int arity, Location loc) + : base (identifier, arity, loc) + { + this.expr = expr; + } + + public Expression LeftExpression { + get { + return expr; + } + } + + public override Location StartLocation { + get { + return expr == null ? loc : expr.StartLocation; + } + } + + protected override Expression DoResolve (ResolveContext rc) + { + var e = LookupNameExpression (rc, MemberLookupRestrictions.ReadAccess); + if (e != null) + e = e.Resolve (rc, ResolveFlags.VariableOrValue | ResolveFlags.Type | ResolveFlags.MethodGroup); + + return e; + } + + public override Expression DoResolveLValue (ResolveContext rc, Expression rhs) + { + var e = LookupNameExpression (rc, MemberLookupRestrictions.None); + + if (e is TypeExpr) { + e.Error_UnexpectedKind (rc, ResolveFlags.VariableOrValue, loc); + return null; + } + + if (e != null) + e = e.ResolveLValue (rc, rhs); + + return e; + } + + protected virtual void Error_OperatorCannotBeApplied (ResolveContext rc, TypeSpec type) + { + if (type == InternalType.NullLiteral && rc.IsRuntimeBinder) + rc.Report.Error (Report.RuntimeErrorId, loc, "Cannot perform member binding on `null' value"); + else + expr.Error_OperatorCannotBeApplied (rc, loc, ".", type); + } + + public override bool HasConditionalAccess () + { + return LeftExpression.HasConditionalAccess (); + } + + public static bool IsValidDotExpression (TypeSpec type) + { + const MemberKind dot_kinds = MemberKind.Class | MemberKind.Struct | MemberKind.Delegate | MemberKind.Enum | + MemberKind.Interface | MemberKind.TypeParameter | MemberKind.ArrayType; + + return (type.Kind & dot_kinds) != 0 || type.BuiltinType == BuiltinTypeSpec.Type.Dynamic; + } + + public override Expression LookupNameExpression (ResolveContext rc, MemberLookupRestrictions restrictions) + { + var sn = expr as SimpleName; + const ResolveFlags flags = ResolveFlags.VariableOrValue | ResolveFlags.Type; + + if (sn != null) { + expr = sn.LookupNameExpression (rc, MemberLookupRestrictions.ReadAccess | MemberLookupRestrictions.ExactArity); + + // + // Resolve expression which does have type set as we need expression type + // with disable flow analysis as we don't know whether left side expression + // is used as variable or type + // + if (expr is VariableReference || expr is ConstantExpr || expr is Linq.TransparentMemberAccess || expr is EventExpr) { + expr = expr.Resolve (rc); + } else if (expr is TypeParameterExpr) { + expr.Error_UnexpectedKind (rc, flags, sn.Location); + expr = null; + } + } else { + using (rc.Set (ResolveContext.Options.ConditionalAccessReceiver)) { + expr = expr.Resolve (rc, flags); + } + } + + if (expr == null) + return null; + + var ns = expr as NamespaceExpression; + if (ns != null) { + var retval = ns.LookupTypeOrNamespace (rc, Name, Arity, LookupMode.Normal, loc); + + if (retval == null) { + ns.Error_NamespaceDoesNotExist (rc, Name, Arity); + return null; + } + + if (HasTypeArguments) + return new GenericTypeExpr (retval.Type, targs, loc); + + return retval; + } + + MemberExpr me; + TypeSpec expr_type = expr.Type; + if (expr_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + me = expr as MemberExpr; + if (me != null) + me.ResolveInstanceExpression (rc, null); + + Arguments args = new Arguments (1); + args.Add (new Argument (expr)); + return new DynamicMemberBinder (Name, args, loc); + } + + var cma = this as ConditionalMemberAccess; + if (cma != null) { + if (!IsNullPropagatingValid (expr.Type)) { + expr.Error_OperatorCannotBeApplied (rc, loc, "?", expr.Type); + return null; + } + + if (expr_type.IsNullableType) { + expr = Nullable.Unwrap.Create (expr, true).Resolve (rc); + expr_type = expr.Type; + } + } + + if (!IsValidDotExpression (expr_type)) { + Error_OperatorCannotBeApplied (rc, expr_type); + return null; + } + + var lookup_arity = Arity; + bool errorMode = false; + Expression member_lookup; + while (true) { + member_lookup = MemberLookup (rc, errorMode, expr_type, Name, lookup_arity, restrictions, loc); + if (member_lookup == null) { + // + // Try to look for extension method when member lookup failed + // + if (MethodGroupExpr.IsExtensionMethodArgument (expr)) { + var methods = rc.LookupExtensionMethod (expr_type, Name, lookup_arity); + if (methods != null) { + var emg = new ExtensionMethodGroupExpr (methods, expr, loc); + if (HasTypeArguments) { + if (!targs.Resolve (rc)) + return null; + + emg.SetTypeArguments (rc, targs); + } + + if (cma != null) + emg.ConditionalAccess = true; + + // TODO: it should really skip the checks bellow + return emg.Resolve (rc); + } + } + } + + if (errorMode) { + if (member_lookup == null) { + var dep = expr_type.GetMissingDependencies (); + if (dep != null) { + ImportedTypeDefinition.Error_MissingDependency (rc, dep, loc); + } else if (expr is TypeExpr) { + base.Error_TypeDoesNotContainDefinition (rc, expr_type, Name); + } else { + Error_TypeDoesNotContainDefinition (rc, expr_type, Name); + } + + return null; + } + + if (member_lookup is MethodGroupExpr || member_lookup is PropertyExpr) { + // Leave it to overload resolution to report correct error + } else if (!(member_lookup is TypeExpr)) { + // TODO: rc.SymbolRelatedToPreviousError + ErrorIsInaccesible (rc, member_lookup.GetSignatureForError (), loc); + } + break; + } + + if (member_lookup != null) + break; + + lookup_arity = 0; + restrictions &= ~MemberLookupRestrictions.InvocableOnly; + errorMode = true; + } + + TypeExpr texpr = member_lookup as TypeExpr; + if (texpr != null) { + if (!(expr is TypeExpr) && (sn == null || expr.ProbeIdenticalTypeName (rc, expr, sn) == expr)) { + rc.Report.Error (572, loc, "`{0}': cannot reference a type through an expression. Consider using `{1}' instead", + Name, texpr.GetSignatureForError ()); + } + + if (!texpr.Type.IsAccessible (rc)) { + rc.Report.SymbolRelatedToPreviousError (member_lookup.Type); + ErrorIsInaccesible (rc, member_lookup.Type.GetSignatureForError (), loc); + return null; + } + + if (HasTypeArguments) { + return new GenericTypeExpr (member_lookup.Type, targs, loc); + } + + return member_lookup; + } + + me = member_lookup as MemberExpr; + + if (sn != null && me.IsStatic && (expr = me.ProbeIdenticalTypeName (rc, expr, sn)) != expr) { + sn = null; + } + + if (cma != null) { + me.ConditionalAccess = true; + } + + me = me.ResolveMemberAccess (rc, expr, sn); + + if (Arity > 0) { + if (!targs.Resolve (rc)) + return null; + + me.SetTypeArguments (rc, targs); + } + + return me; + } + + public override FullNamedExpression ResolveAsTypeOrNamespace (IMemberContext rc, bool allowUnboundTypeArguments) + { + FullNamedExpression fexpr = expr as FullNamedExpression; + if (fexpr == null) { + expr.ResolveAsType (rc); + return null; + } + + FullNamedExpression expr_resolved = fexpr.ResolveAsTypeOrNamespace (rc, allowUnboundTypeArguments); + + if (expr_resolved == null) + return null; + + var ns = expr_resolved as NamespaceExpression; + if (ns != null) { + FullNamedExpression retval = ns.LookupTypeOrNamespace (rc, Name, Arity, LookupMode.Normal, loc); + + if (retval == null) { + ns.Error_NamespaceDoesNotExist (rc, Name, Arity); + } else if (Arity > 0) { + if (HasTypeArguments) { + retval = new GenericTypeExpr (retval.Type, targs, loc); + if (retval.ResolveAsType (rc) == null) + return null; + } else { + if (!allowUnboundTypeArguments) + Error_OpenGenericTypeIsNotAllowed (rc); + + retval = new GenericOpenTypeExpr (retval.Type, loc); + } + } + + return retval; + } + + var tnew_expr = expr_resolved.ResolveAsType (rc); + if (tnew_expr == null) + return null; + + TypeSpec expr_type = tnew_expr; + if (TypeManager.IsGenericParameter (expr_type)) { + rc.Module.Compiler.Report.Error (704, loc, "A nested type cannot be specified through a type parameter `{0}'", + tnew_expr.GetSignatureForError ()); + return null; + } + + var qam = this as QualifiedAliasMember; + if (qam != null) { + rc.Module.Compiler.Report.Error (431, loc, + "Alias `{0}' cannot be used with `::' since it denotes a type. Consider replacing `::' with `.'", + qam.Alias); + + } + + TypeSpec nested = null; + while (expr_type != null) { + nested = MemberCache.FindNestedType (expr_type, Name, Arity); + if (nested == null) { + if (expr_type == tnew_expr) { + Error_IdentifierNotFound (rc, expr_type); + return null; + } + + expr_type = tnew_expr; + nested = MemberCache.FindNestedType (expr_type, Name, Arity); + ErrorIsInaccesible (rc, nested.GetSignatureForError (), loc); + break; + } + + if (nested.IsAccessible (rc)) + break; + + // + // Keep looking after inaccessible candidate but only if + // we are not in same context as the definition itself + // + if (expr_type.MemberDefinition == rc.CurrentMemberDefinition) + break; + + expr_type = expr_type.BaseType; + } + + TypeExpr texpr; + if (Arity > 0) { + if (HasTypeArguments) { + texpr = new GenericTypeExpr (nested, targs, loc); + } else { + if (!allowUnboundTypeArguments || expr_resolved is GenericTypeExpr) // && HasTypeArguments + Error_OpenGenericTypeIsNotAllowed (rc); + + texpr = new GenericOpenTypeExpr (nested, loc); + } + } else if (expr_resolved is GenericOpenTypeExpr) { + texpr = new GenericOpenTypeExpr (nested, loc); + } else { + texpr = new TypeExpression (nested, loc); + } + + if (texpr.ResolveAsType (rc) == null) + return null; + + return texpr; + } + + public void Error_IdentifierNotFound (IMemberContext rc, TypeSpec expr_type) + { + var nested = MemberCache.FindNestedType (expr_type, Name, -System.Math.Max (1, Arity)); + + if (nested != null) { + Error_TypeArgumentsCannotBeUsed (rc, nested, expr.Location); + return; + } + + var any_other_member = MemberLookup (rc, false, expr_type, Name, 0, MemberLookupRestrictions.None, loc); + if (any_other_member != null) { + Error_UnexpectedKind (rc, any_other_member, "type", any_other_member.ExprClassName, loc); + return; + } + + rc.Module.Compiler.Report.Error (426, loc, "The nested type `{0}' does not exist in the type `{1}'", + Name, expr_type.GetSignatureForError ()); + } + + protected override void Error_InvalidExpressionStatement (Report report, Location loc) + { + base.Error_InvalidExpressionStatement (report, LeftExpression.Location); + } + + protected override void Error_TypeDoesNotContainDefinition (ResolveContext ec, TypeSpec type, string name) + { + if (ec.Module.Compiler.Settings.Version > LanguageVersion.ISO_2 && !ec.IsRuntimeBinder && MethodGroupExpr.IsExtensionMethodArgument (expr)) { + ec.Report.SymbolRelatedToPreviousError (type); + + var cand = ec.Module.GlobalRootNamespace.FindExtensionMethodNamespaces (ec, name, Arity); + string missing; + // a using directive or an assembly reference + if (cand != null) { + missing = "`" + string.Join ("' or `", cand.ToArray ()) + "' using directive"; + } else { + missing = "an assembly reference"; + } + + ec.Report.Error (1061, loc, + "Type `{0}' does not contain a definition for `{1}' and no extension method `{1}' of type `{0}' could be found. Are you missing {2}?", + type.GetSignatureForError (), name, missing); + return; + } + + base.Error_TypeDoesNotContainDefinition (ec, type, name); + } + + public override string GetSignatureForError () + { + return expr.GetSignatureForError () + "." + base.GetSignatureForError (); + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + MemberAccess target = (MemberAccess) t; + + target.expr = expr.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class ConditionalMemberAccess : MemberAccess + { + public ConditionalMemberAccess (Expression expr, string identifier, TypeArguments args, Location loc) + : base (expr, identifier, args, loc) + { + } + + public override bool HasConditionalAccess () + { + return true; + } + } + + /// + /// Implements checked expressions + /// + public class CheckedExpr : Expression { + + public Expression Expr; + + public CheckedExpr (Expression e, Location l) + { + Expr = e; + loc = l; + } + + public override bool ContainsEmitWithAwait () + { + return Expr.ContainsEmitWithAwait (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + using (ec.With (ResolveContext.Options.AllCheckStateFlags, true)) + return Expr.CreateExpressionTree (ec); + } + + protected override Expression DoResolve (ResolveContext ec) + { + using (ec.With (ResolveContext.Options.AllCheckStateFlags, true)) + Expr = Expr.Resolve (ec); + + if (Expr == null) + return null; + + if (Expr is Constant || Expr is MethodGroupExpr || Expr is AnonymousMethodExpression || Expr is DefaultValueExpression) + return Expr; + + eclass = Expr.eclass; + type = Expr.Type; + return this; + } + + public override void Emit (EmitContext ec) + { + using (ec.With (EmitContext.Options.CheckedScope, true)) + Expr.Emit (ec); + } + + public override void EmitBranchable (EmitContext ec, Label target, bool on_true) + { + using (ec.With (EmitContext.Options.CheckedScope, true)) + Expr.EmitBranchable (ec, target, on_true); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + Expr.FlowAnalysis (fc); + } + + public override SLE.Expression MakeExpression (BuilderContext ctx) + { + using (ctx.With (BuilderContext.Options.CheckedScope, true)) { + return Expr.MakeExpression (ctx); + } + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + CheckedExpr target = (CheckedExpr) t; + + target.Expr = Expr.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + /// + /// Implements the unchecked expression + /// + public class UnCheckedExpr : Expression { + + public Expression Expr; + + public UnCheckedExpr (Expression e, Location l) + { + Expr = e; + loc = l; + } + + public override bool ContainsEmitWithAwait () + { + return Expr.ContainsEmitWithAwait (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + using (ec.With (ResolveContext.Options.AllCheckStateFlags, false)) + return Expr.CreateExpressionTree (ec); + } + + protected override Expression DoResolve (ResolveContext ec) + { + using (ec.With (ResolveContext.Options.AllCheckStateFlags, false)) + Expr = Expr.Resolve (ec); + + if (Expr == null) + return null; + + if (Expr is Constant || Expr is MethodGroupExpr || Expr is AnonymousMethodExpression || Expr is DefaultValueExpression) + return Expr; + + eclass = Expr.eclass; + type = Expr.Type; + return this; + } + + public override void Emit (EmitContext ec) + { + using (ec.With (EmitContext.Options.CheckedScope, false)) + Expr.Emit (ec); + } + + public override void EmitBranchable (EmitContext ec, Label target, bool on_true) + { + using (ec.With (EmitContext.Options.CheckedScope, false)) + Expr.EmitBranchable (ec, target, on_true); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + Expr.FlowAnalysis (fc); + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + UnCheckedExpr target = (UnCheckedExpr) t; + + target.Expr = Expr.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + /// + /// An Element Access expression. + /// + /// During semantic analysis these are transformed into + /// IndexerAccess, ArrayAccess or a PointerArithmetic. + /// + public class ElementAccess : Expression + { + public Arguments Arguments; + public Expression Expr; + + public ElementAccess (Expression e, Arguments args, Location loc) + { + Expr = e; + this.loc = loc; + this.Arguments = args; + } + + public bool ConditionalAccess { get; set; } + + public override Location StartLocation { + get { + return Expr.StartLocation; + } + } + + public override bool ContainsEmitWithAwait () + { + return Expr.ContainsEmitWithAwait () || Arguments.ContainsEmitWithAwait (); + } + + // + // We perform some simple tests, and then to "split" the emit and store + // code we create an instance of a different class, and return that. + // + Expression CreateAccessExpression (ResolveContext ec, bool conditionalAccessReceiver) + { + Expr = Expr.Resolve (ec); + if (Expr == null) + return null; + + type = Expr.Type; + + if (ConditionalAccess && !IsNullPropagatingValid (type)) { + Error_OperatorCannotBeApplied (ec, loc, "?", type); + return null; + } + + if (type.IsArray) + return new ArrayAccess (this, loc) { + ConditionalAccess = ConditionalAccess, + ConditionalAccessReceiver = conditionalAccessReceiver + }; + + if (type.IsPointer) + return MakePointerAccess (ec, type); + + FieldExpr fe = Expr as FieldExpr; + if (fe != null) { + var ff = fe.Spec as FixedFieldSpec; + if (ff != null) { + return MakePointerAccess (ec, ff.ElementType); + } + } + + var indexers = MemberCache.FindMembers (type, MemberCache.IndexerNameAlias, false); + if (indexers != null || type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + var indexer = new IndexerExpr (indexers, type, this) { + ConditionalAccess = ConditionalAccess + }; + + if (conditionalAccessReceiver) + indexer.SetConditionalAccessReceiver (); + + return indexer; + } + + Error_CannotApplyIndexing (ec, type, loc); + + return null; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Arguments args = Arguments.CreateForExpressionTree (ec, Arguments, + Expr.CreateExpressionTree (ec)); + + return CreateExpressionFactoryCall (ec, "ArrayIndex", args); + } + + public static void Error_CannotApplyIndexing (ResolveContext rc, TypeSpec type, Location loc) + { + if (type != InternalType.ErrorType) { + rc.Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `{0}'", + type.GetSignatureForError ()); + } + } + + public override bool HasConditionalAccess () + { + return ConditionalAccess || Expr.HasConditionalAccess (); + } + + Expression MakePointerAccess (ResolveContext rc, TypeSpec type) + { + if (Arguments.Count != 1){ + rc.Report.Error (196, loc, "A pointer must be indexed by only one value"); + return null; + } + + var arg = Arguments[0]; + if (arg is NamedArgument) + Error_NamedArgument ((NamedArgument) arg, rc.Report); + + var index = arg.Expr.Resolve (rc); + if (index == null) + return null; + + index = ConvertExpressionToArrayIndex (rc, index, true); + + Expression p = new PointerArithmetic (Binary.Operator.Addition, Expr, index, type, loc); + return new Indirection (p, loc); + } + + protected override Expression DoResolve (ResolveContext rc) + { + Expression expr; + if (!rc.HasSet (ResolveContext.Options.ConditionalAccessReceiver)) { + if (HasConditionalAccess ()) { + using (rc.Set (ResolveContext.Options.ConditionalAccessReceiver)) { + expr = CreateAccessExpression (rc, true); + if (expr == null) + return null; + + return expr.Resolve (rc); + } + } + } + + expr = CreateAccessExpression (rc, false); + if (expr == null) + return null; + + return expr.Resolve (rc); + } + + public override Expression DoResolveLValue (ResolveContext ec, Expression rhs) + { + var res = CreateAccessExpression (ec, false); + if (res == null) + return null; + + return res.ResolveLValue (ec, rhs); + } + + public override void Emit (EmitContext ec) + { + throw new Exception ("Should never be reached"); + } + + public static void Error_NamedArgument (NamedArgument na, Report Report) + { + Report.Error (1742, na.Location, "An element access expression cannot use named argument"); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + Expr.FlowAnalysis (fc); + + if (ConditionalAccess) + fc.BranchConditionalAccessDefiniteAssignment (); + + Arguments.FlowAnalysis (fc); + } + + public override string GetSignatureForError () + { + return Expr.GetSignatureForError (); + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + ElementAccess target = (ElementAccess) t; + + target.Expr = Expr.Clone (clonectx); + if (Arguments != null) + target.Arguments = Arguments.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + /// + /// Implements array access + /// + public class ArrayAccess : Expression, IDynamicAssign, IMemoryLocation { + // + // Points to our "data" repository + // + ElementAccess ea; + + LocalTemporary temp; + bool prepared; + bool? has_await_args; + + public ArrayAccess (ElementAccess ea_data, Location l) + { + ea = ea_data; + loc = l; + } + + public bool ConditionalAccess { get; set; } + + public bool ConditionalAccessReceiver { get; set; } + + public void AddressOf (EmitContext ec, AddressOp mode) + { + var ac = (ArrayContainer) ea.Expr.Type; + + if (!has_await_args.HasValue && ec.HasSet (BuilderContext.Options.AsyncBody) && ea.Arguments.ContainsEmitWithAwait ()) { + LoadInstanceAndArguments (ec, false, true); + } + + LoadInstanceAndArguments (ec, false, false); + + if (ac.Element.IsGenericParameter && mode == AddressOp.Load) + ec.Emit (OpCodes.Readonly); + + ec.EmitArrayAddress (ac); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + if (ConditionalAccess) + Error_NullShortCircuitInsideExpressionTree (ec); + + return ea.CreateExpressionTree (ec); + } + + public override bool ContainsEmitWithAwait () + { + return ea.ContainsEmitWithAwait (); + } + + public override Expression DoResolveLValue (ResolveContext ec, Expression right_side) + { + if (ConditionalAccess) + throw new NotSupportedException ("null propagating operator assignment"); + + return DoResolve (ec); + } + + protected override Expression DoResolve (ResolveContext ec) + { + // dynamic is used per argument in ConvertExpressionToArrayIndex case + bool dynamic; + ea.Arguments.Resolve (ec, out dynamic); + + var ac = ea.Expr.Type as ArrayContainer; + int rank = ea.Arguments.Count; + if (ac.Rank != rank) { + ec.Report.Error (22, ea.Location, "Wrong number of indexes `{0}' inside [], expected `{1}'", + rank.ToString (), ac.Rank.ToString ()); + return null; + } + + type = ac.Element; + if (type.IsPointer && !ec.IsUnsafe) { + UnsafeError (ec, ea.Location); + } + + if (ConditionalAccessReceiver) + type = LiftMemberType (ec, type); + + foreach (Argument a in ea.Arguments) { + var na = a as NamedArgument; + if (na != null) + ElementAccess.Error_NamedArgument (na, ec.Report); + + a.Expr = ConvertExpressionToArrayIndex (ec, a.Expr); + } + + eclass = ExprClass.Variable; + + return this; + } + + protected override void Error_NegativeArrayIndex (ResolveContext ec, Location loc) + { + ec.Report.Warning (251, 2, loc, "Indexing an array with a negative index (array indices always start at zero)"); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + ea.FlowAnalysis (fc); + } + + // + // Load the array arguments into the stack. + // + void LoadInstanceAndArguments (EmitContext ec, bool duplicateArguments, bool prepareAwait) + { + if (prepareAwait) { + ea.Expr = ea.Expr.EmitToField (ec); + } else { + var ie = new InstanceEmitter (ea.Expr, false); + ie.Emit (ec, ConditionalAccess); + + if (duplicateArguments) { + ec.Emit (OpCodes.Dup); + + var copy = new LocalTemporary (ea.Expr.Type); + copy.Store (ec); + ea.Expr = copy; + } + } + + var dup_args = ea.Arguments.Emit (ec, duplicateArguments, prepareAwait); + if (dup_args != null) + ea.Arguments = dup_args; + } + + public void Emit (EmitContext ec, bool leave_copy) + { + if (prepared) { + ec.EmitLoadFromPtr (type); + } else { + if (!has_await_args.HasValue && ec.HasSet (BuilderContext.Options.AsyncBody) && ea.Arguments.ContainsEmitWithAwait ()) { + LoadInstanceAndArguments (ec, false, true); + } + + if (ConditionalAccessReceiver) + ec.ConditionalAccess = new ConditionalAccessContext (type, ec.DefineLabel ()); + + var ac = (ArrayContainer) ea.Expr.Type; + LoadInstanceAndArguments (ec, false, false); + ec.EmitArrayLoad (ac); + + if (ConditionalAccessReceiver) + ec.CloseConditionalAccess (type.IsNullableType && type != ac.Element ? type : null); + } + + if (leave_copy) { + ec.Emit (OpCodes.Dup); + temp = new LocalTemporary (this.type); + temp.Store (ec); + } + } + + public override void Emit (EmitContext ec) + { + Emit (ec, false); + } + + public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound) + { + var ac = (ArrayContainer) ea.Expr.Type; + TypeSpec t = source.Type; + + has_await_args = ec.HasSet (BuilderContext.Options.AsyncBody) && (ea.Arguments.ContainsEmitWithAwait () || source.ContainsEmitWithAwait ()); + + // + // When we are dealing with a struct, get the address of it to avoid value copy + // Same cannot be done for reference type because array covariance and the + // check in ldelema requires to specify the type of array element stored at the index + // + if (t.IsStruct && ((isCompound && !(source is DynamicExpressionStatement)) || !BuiltinTypeSpec.IsPrimitiveType (t))) { + LoadInstanceAndArguments (ec, false, has_await_args.Value); + + if (has_await_args.Value) { + if (source.ContainsEmitWithAwait ()) { + source = source.EmitToField (ec); + isCompound = false; + prepared = true; + } + + LoadInstanceAndArguments (ec, isCompound, false); + } else { + prepared = true; + } + + ec.EmitArrayAddress (ac); + + if (isCompound) { + ec.Emit (OpCodes.Dup); + prepared = true; + } + } else { + LoadInstanceAndArguments (ec, isCompound, has_await_args.Value); + + if (has_await_args.Value) { + if (source.ContainsEmitWithAwait ()) + source = source.EmitToField (ec); + + LoadInstanceAndArguments (ec, false, false); + } + } + + source.Emit (ec); + + if (isCompound) { + var lt = ea.Expr as LocalTemporary; + if (lt != null) + lt.Release (ec); + } + + if (leave_copy) { + ec.Emit (OpCodes.Dup); + temp = new LocalTemporary (this.type); + temp.Store (ec); + } + + if (prepared) { + ec.EmitStoreFromPtr (t); + } else { + ec.EmitArrayStore (ac); + } + + if (temp != null) { + temp.Emit (ec); + temp.Release (ec); + } + } + + public override Expression EmitToField (EmitContext ec) + { + // + // Have to be specialized for arrays to get access to + // underlying element. Instead of another result copy we + // need direct access to element + // + // Consider: + // + // CallRef (ref a[await Task.Factory.StartNew (() => 1)]); + // + ea.Expr = ea.Expr.EmitToField (ec); + ea.Arguments = ea.Arguments.Emit (ec, false, true); + return this; + } + + public SLE.Expression MakeAssignExpression (BuilderContext ctx, Expression source) + { +#if NET_4_0 || MOBILE_DYNAMIC + return SLE.Expression.ArrayAccess (ea.Expr.MakeExpression (ctx), MakeExpressionArguments (ctx)); +#else + throw new NotImplementedException (); +#endif + } + + public override SLE.Expression MakeExpression (BuilderContext ctx) + { + return SLE.Expression.ArrayIndex (ea.Expr.MakeExpression (ctx), MakeExpressionArguments (ctx)); + } + + SLE.Expression[] MakeExpressionArguments (BuilderContext ctx) + { + using (ctx.With (BuilderContext.Options.CheckedScope, true)) { + return Arguments.MakeExpression (ea.Arguments, ctx); + } + } + } + + // + // Indexer access expression + // + class IndexerExpr : PropertyOrIndexerExpr, OverloadResolver.IBaseMembersProvider + { + IList indexers; + Arguments arguments; + TypeSpec queried_type; + + public IndexerExpr (IList indexers, TypeSpec queriedType, ElementAccess ea) + : this (indexers, queriedType, ea.Expr, ea.Arguments, ea.Location) + { + } + + public IndexerExpr (IList indexers, TypeSpec queriedType, Expression instance, Arguments args, Location loc) + : base (loc) + { + this.indexers = indexers; + this.queried_type = queriedType; + this.InstanceExpression = instance; + this.arguments = args; + } + + #region Properties + + protected override Arguments Arguments { + get { + return arguments; + } + set { + arguments = value; + } + } + + protected override TypeSpec DeclaringType { + get { + return best_candidate.DeclaringType; + } + } + + public override bool IsInstance { + get { + return true; + } + } + + public override bool IsStatic { + get { + return false; + } + } + + public override string KindName { + get { return "indexer"; } + } + + public override string Name { + get { + return "this"; + } + } + + #endregion + + public override bool ContainsEmitWithAwait () + { + return base.ContainsEmitWithAwait () || arguments.ContainsEmitWithAwait (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + if (ConditionalAccess) { + Error_NullShortCircuitInsideExpressionTree (ec); + } + + Arguments args = Arguments.CreateForExpressionTree (ec, arguments, + InstanceExpression.CreateExpressionTree (ec), + new TypeOfMethod (Getter, loc)); + + return CreateExpressionFactoryCall (ec, "Call", args); + } + + public override void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound) + { + LocalTemporary await_source_arg = null; + + if (isCompound) { + emitting_compound_assignment = true; + if (source is DynamicExpressionStatement) { + Emit (ec, false); + } else { + source.Emit (ec); + } + emitting_compound_assignment = false; + + if (has_await_arguments) { + await_source_arg = new LocalTemporary (Type); + await_source_arg.Store (ec); + + arguments.Add (new Argument (await_source_arg)); + + if (leave_copy) { + temp = await_source_arg; + } + + has_await_arguments = false; + } else { + arguments = null; + + if (leave_copy) { + ec.Emit (OpCodes.Dup); + temp = new LocalTemporary (Type); + temp.Store (ec); + } + } + } else { + if (leave_copy) { + if (ec.HasSet (BuilderContext.Options.AsyncBody) && (arguments.ContainsEmitWithAwait () || source.ContainsEmitWithAwait ())) { + source = source.EmitToField (ec); + } else { + temp = new LocalTemporary (Type); + source.Emit (ec); + temp.Store (ec); + source = temp; + } + } + + arguments.Add (new Argument (source)); + } + + var call = new CallEmitter (); + call.InstanceExpression = InstanceExpression; + if (arguments == null) + call.InstanceExpressionOnStack = true; + + call.Emit (ec, Setter, arguments, loc); + + if (temp != null) { + temp.Emit (ec); + temp.Release (ec); + } else if (leave_copy) { + source.Emit (ec); + } + + if (await_source_arg != null) { + await_source_arg.Release (ec); + } + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + base.FlowAnalysis (fc); + arguments.FlowAnalysis (fc); + + if (conditional_access_receiver) + fc.ConditionalAccessEnd (); + } + + public override string GetSignatureForError () + { + return best_candidate.GetSignatureForError (); + } + + public override SLE.Expression MakeAssignExpression (BuilderContext ctx, Expression source) + { +#if STATIC + throw new NotSupportedException (); +#else + var value = new[] { source.MakeExpression (ctx) }; + var args = Arguments.MakeExpression (arguments, ctx).Concat (value); +#if NET_4_0 || MOBILE_DYNAMIC + return SLE.Expression.Block ( + SLE.Expression.Call (InstanceExpression.MakeExpression (ctx), (MethodInfo) Setter.GetMetaInfo (), args), + value [0]); +#else + return args.First (); +#endif +#endif + } + + public override SLE.Expression MakeExpression (BuilderContext ctx) + { +#if STATIC + return base.MakeExpression (ctx); +#else + var args = Arguments.MakeExpression (arguments, ctx); + return SLE.Expression.Call (InstanceExpression.MakeExpression (ctx), (MethodInfo) Getter.GetMetaInfo (), args); +#endif + } + + protected override Expression OverloadResolve (ResolveContext rc, Expression right_side) + { + if (best_candidate != null) + return this; + + eclass = ExprClass.IndexerAccess; + + bool dynamic; + arguments.Resolve (rc, out dynamic); + + if (indexers == null && InstanceExpression.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + dynamic = true; + } else { + var res = new OverloadResolver (indexers, OverloadResolver.Restrictions.None, loc); + res.BaseMembersProvider = this; + res.InstanceQualifier = this; + + // TODO: Do I need 2 argument sets? + best_candidate = res.ResolveMember (rc, ref arguments); + if (best_candidate != null) + type = res.BestCandidateReturnType; + else if (!res.BestCandidateIsDynamic) + return null; + } + + // + // It has dynamic arguments + // + if (dynamic) { + Arguments args = new Arguments (arguments.Count + 1); + if (IsBase) { + rc.Report.Error (1972, loc, + "The indexer base access cannot be dynamically dispatched. Consider casting the dynamic arguments or eliminating the base access"); + } else { + args.Add (new Argument (InstanceExpression)); + } + args.AddRange (arguments); + + best_candidate = null; + return new DynamicIndexBinder (args, loc); + } + + // + // Try to avoid resolving left expression again + // + if (right_side != null) + ResolveInstanceExpression (rc, right_side); + + return this; + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + IndexerExpr target = (IndexerExpr) t; + + if (arguments != null) + target.arguments = arguments.Clone (clonectx); + } + + public void SetConditionalAccessReceiver () + { + conditional_access_receiver = true; + } + + public override void SetTypeArguments (ResolveContext ec, TypeArguments ta) + { + Error_TypeArgumentsCannotBeUsed (ec, "indexer", GetSignatureForError (), loc); + } + + #region IBaseMembersProvider Members + + IList OverloadResolver.IBaseMembersProvider.GetBaseMembers (TypeSpec baseType) + { + return baseType == null ? null : MemberCache.FindMembers (baseType, MemberCache.IndexerNameAlias, false); + } + + IParametersMember OverloadResolver.IBaseMembersProvider.GetOverrideMemberParameters (MemberSpec member) + { + if (queried_type == member.DeclaringType) + return null; + + var filter = new MemberFilter (MemberCache.IndexerNameAlias, 0, MemberKind.Indexer, ((IndexerSpec) member).Parameters, null); + return MemberCache.FindMember (queried_type, filter, BindingRestriction.InstanceOnly | BindingRestriction.OverrideOnly) as IParametersMember; + } + + MethodGroupExpr OverloadResolver.IBaseMembersProvider.LookupExtensionMethod (ResolveContext rc) + { + return null; + } + + #endregion + } + + // + // A base access expression + // + public class BaseThis : This + { + public BaseThis (Location loc) + : base (loc) + { + } + + public BaseThis (TypeSpec type, Location loc) + : base (loc) + { + this.type = type; + eclass = ExprClass.Variable; + } + + #region Properties + + public override string Name { + get { + return "base"; + } + } + + #endregion + + public override Expression CreateExpressionTree (ResolveContext ec) + { + ec.Report.Error (831, loc, "An expression tree may not contain a base access"); + return base.CreateExpressionTree (ec); + } + + public override void Emit (EmitContext ec) + { + base.Emit (ec); + + if (type == ec.Module.Compiler.BuiltinTypes.ValueType) { + var context_type = ec.CurrentType; + ec.Emit (OpCodes.Ldobj, context_type); + ec.Emit (OpCodes.Box, context_type); + } + } + + protected override void Error_ThisNotAvailable (ResolveContext ec) + { + if (ec.IsStatic) { + ec.Report.Error (1511, loc, "Keyword `base' is not available in a static method"); + } else { + ec.Report.Error (1512, loc, "Keyword `base' is not available in the current context"); + } + } + + public override void ResolveBase (ResolveContext ec) + { + base.ResolveBase (ec); + type = ec.CurrentType.BaseType; + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + /// + /// This class exists solely to pass the Type around and to be a dummy + /// that can be passed to the conversion functions (this is used by + /// foreach implementation to typecast the object return value from + /// get_Current into the proper type. All code has been generated and + /// we only care about the side effect conversions to be performed + /// + /// This is also now used as a placeholder where a no-action expression + /// is needed (the `New' class). + /// + public class EmptyExpression : Expression + { + sealed class OutAccessExpression : EmptyExpression + { + public OutAccessExpression (TypeSpec t) + : base (t) + { + } + + public override Expression DoResolveLValue (ResolveContext rc, Expression right_side) + { + rc.Report.Error (206, right_side.Location, + "A property, indexer or dynamic member access may not be passed as `ref' or `out' parameter"); + + return null; + } + } + + public static readonly EmptyExpression LValueMemberAccess = new EmptyExpression (InternalType.FakeInternalType); + public static readonly EmptyExpression LValueMemberOutAccess = new EmptyExpression (InternalType.FakeInternalType); + public static readonly EmptyExpression UnaryAddress = new EmptyExpression (InternalType.FakeInternalType); + public static readonly EmptyExpression EventAddition = new EmptyExpression (InternalType.FakeInternalType); + public static readonly EmptyExpression EventSubtraction = new EmptyExpression (InternalType.FakeInternalType); + public static readonly EmptyExpression MissingValue = new EmptyExpression (InternalType.FakeInternalType); + public static readonly Expression Null = new EmptyExpression (InternalType.FakeInternalType); + public static readonly EmptyExpression OutAccess = new OutAccessExpression (InternalType.FakeInternalType); + + public EmptyExpression (TypeSpec t) + { + type = t; + eclass = ExprClass.Value; + loc = Location.Null; + } + + protected override void CloneTo (CloneContext clonectx, Expression target) + { + } + + public override bool ContainsEmitWithAwait () + { + return false; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + throw new NotSupportedException ("ET"); + } + + protected override Expression DoResolve (ResolveContext ec) + { + return this; + } + + public override void Emit (EmitContext ec) + { + // nothing, as we only exist to not do anything. + } + + public override void EmitSideEffect (EmitContext ec) + { + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + sealed class EmptyAwaitExpression : EmptyExpression + { + public EmptyAwaitExpression (TypeSpec type) + : base (type) + { + } + + public override bool ContainsEmitWithAwait () + { + return true; + } + } + + // + // Empty statement expression + // + public sealed class EmptyExpressionStatement : ExpressionStatement + { + public static readonly EmptyExpressionStatement Instance = new EmptyExpressionStatement (); + + private EmptyExpressionStatement () + { + loc = Location.Null; + } + + public override bool ContainsEmitWithAwait () + { + return false; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + return null; + } + + public override void EmitStatement (EmitContext ec) + { + // Do nothing + } + + protected override Expression DoResolve (ResolveContext ec) + { + eclass = ExprClass.Value; + type = ec.BuiltinTypes.Object; + return this; + } + + public override void Emit (EmitContext ec) + { + // Do nothing + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class ErrorExpression : EmptyExpression + { + public static readonly ErrorExpression Instance = new ErrorExpression (); + public readonly int ErrorCode; + public readonly string Error; + + private ErrorExpression () + : base (InternalType.ErrorType) + { + } + + ErrorExpression (int errorCode, Location location, string error) + : base (InternalType.ErrorType) + { + this.ErrorCode = errorCode; + base.loc = location; + this.Error = error; + } + + public static ErrorExpression Create (int errorCode, Location location, string error) + { + return new ErrorExpression (errorCode, location, error); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + return this; + } + + public override Expression DoResolveLValue (ResolveContext rc, Expression right_side) + { + return this; + } + + public override void Error_ValueAssignment (ResolveContext rc, Expression rhs) + { + } + + public override void Error_UnexpectedKind (ResolveContext ec, ResolveFlags flags, Location loc) + { + } + + public override void Error_ValueCannotBeConverted (ResolveContext ec, TypeSpec target, bool expl) + { + } + + public override void Error_OperatorCannotBeApplied (ResolveContext rc, Location loc, string oper, TypeSpec t) + { + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class UserCast : Expression { + MethodSpec method; + Expression source; + + public UserCast (MethodSpec method, Expression source, Location l) + { + if (source == null) + throw new ArgumentNullException ("source"); + + this.method = method; + this.source = source; + type = method.ReturnType; + loc = l; + } + + public Expression Source { + get { + return source; + } + } + + public override bool ContainsEmitWithAwait () + { + return source.ContainsEmitWithAwait (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Arguments args = new Arguments (3); + args.Add (new Argument (source.CreateExpressionTree (ec))); + args.Add (new Argument (new TypeOf (type, loc))); + args.Add (new Argument (new TypeOfMethod (method, loc))); + return CreateExpressionFactoryCall (ec, "Convert", args); + } + + protected override Expression DoResolve (ResolveContext ec) + { + ObsoleteAttribute oa = method.GetAttributeObsolete (); + if (oa != null) + AttributeTester.Report_ObsoleteMessage (oa, GetSignatureForError (), loc, ec.Report); + + eclass = ExprClass.Value; + return this; + } + + public override void Emit (EmitContext ec) + { + source.Emit (ec); + ec.MarkCallEntry (loc); + ec.Emit (OpCodes.Call, method); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + source.FlowAnalysis (fc); + } + + public override string GetSignatureForError () + { + return TypeManager.CSharpSignature (method); + } + + public override SLE.Expression MakeExpression (BuilderContext ctx) + { +#if STATIC + return base.MakeExpression (ctx); +#else + return SLE.Expression.Convert (source.MakeExpression (ctx), type.GetMetaInfo (), (MethodInfo) method.GetMetaInfo ()); +#endif + } + } + + // + // Holds additional type specifiers like ?, *, [] + // + public class ComposedTypeSpecifier + { + public static readonly ComposedTypeSpecifier SingleDimension = new ComposedTypeSpecifier (1, Location.Null); + + public readonly int Dimension; + public readonly Location Location; + + public ComposedTypeSpecifier (int specifier, Location loc) + { + this.Dimension = specifier; + this.Location = loc; + } + + #region Properties + public bool IsNullable { + get { + return Dimension == -1; + } + } + + public bool IsPointer { + get { + return Dimension == -2; + } + } + + public ComposedTypeSpecifier Next { get; set; } + + #endregion + + public static ComposedTypeSpecifier CreateArrayDimension (int dimension, Location loc) + { + return new ComposedTypeSpecifier (dimension, loc); + } + + public static ComposedTypeSpecifier CreateNullable (Location loc) + { + return new ComposedTypeSpecifier (-1, loc); + } + + public static ComposedTypeSpecifier CreatePointer (Location loc) + { + return new ComposedTypeSpecifier (-2, loc); + } + + public string GetSignatureForError () + { + string s = + IsPointer ? "*" : + IsNullable ? "?" : + ArrayContainer.GetPostfixSignature (Dimension); + + return Next != null ? s + Next.GetSignatureForError () : s; + } + } + + // + // This class is used to "construct" the type during a typecast + // operation. Since the Type.GetType class in .NET can parse + // the type specification, we just use this to construct the type + // one bit at a time. + // + public class ComposedCast : TypeExpr { + FullNamedExpression left; + ComposedTypeSpecifier spec; + + public FullNamedExpression Left { + get { return this.left; } + } + + public ComposedTypeSpecifier Spec { + get { + return this.spec; + } + } + + public ComposedCast (FullNamedExpression left, ComposedTypeSpecifier spec) + { + if (spec == null) + throw new ArgumentNullException ("spec"); + + this.left = left; + this.spec = spec; + this.loc = left.Location; + } + + public override TypeSpec ResolveAsType (IMemberContext ec, bool allowUnboundTypeArguments) + { + type = left.ResolveAsType (ec); + if (type == null) + return null; + + eclass = ExprClass.Type; + + var single_spec = spec; + + if (single_spec.IsNullable) { + type = new Nullable.NullableType (type, loc).ResolveAsType (ec); + if (type == null) + return null; + + single_spec = single_spec.Next; + } else if (single_spec.IsPointer) { + if (!TypeManager.VerifyUnmanaged (ec.Module, type, loc)) + return null; + + if (!ec.IsUnsafe) { + UnsafeError (ec.Module.Compiler.Report, loc); + } + + do { + type = PointerContainer.MakeType (ec.Module, type); + single_spec = single_spec.Next; + } while (single_spec != null && single_spec.IsPointer); + } + + if (single_spec != null && single_spec.Dimension > 0) { + if (type.IsSpecialRuntimeType) { + ec.Module.Compiler.Report.Error (611, loc, "Array elements cannot be of type `{0}'", type.GetSignatureForError ()); + } else if (type.IsStatic) { + ec.Module.Compiler.Report.SymbolRelatedToPreviousError (type); + ec.Module.Compiler.Report.Error (719, loc, "Array elements cannot be of static type `{0}'", + type.GetSignatureForError ()); + } else { + MakeArray (ec.Module, single_spec); + } + } + + return type; + } + + void MakeArray (ModuleContainer module, ComposedTypeSpecifier spec) + { + if (spec.Next != null) + MakeArray (module, spec.Next); + + type = ArrayContainer.MakeType (module, type, spec.Dimension); + } + + public override string GetSignatureForError () + { + return left.GetSignatureForError () + spec.GetSignatureForError (); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + class FixedBufferPtr : Expression + { + readonly Expression array; + + public FixedBufferPtr (Expression array, TypeSpec array_type, Location l) + { + this.type = array_type; + this.array = array; + this.loc = l; + } + + public override bool ContainsEmitWithAwait () + { + throw new NotImplementedException (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Error_PointerInsideExpressionTree (ec); + return null; + } + + public override void Emit(EmitContext ec) + { + array.Emit (ec); + } + + protected override Expression DoResolve (ResolveContext ec) + { + type = PointerContainer.MakeType (ec.Module, type); + eclass = ExprClass.Value; + return this; + } + } + + + // + // This class is used to represent the address of an array, used + // only by the Fixed statement, this generates "&a [0]" construct + // for fixed (char *pa = a) + // + class ArrayPtr : FixedBufferPtr + { + public ArrayPtr (Expression array, TypeSpec array_type, Location l): + base (array, array_type, l) + { + } + + public override void Emit (EmitContext ec) + { + base.Emit (ec); + + ec.EmitInt (0); + ec.Emit (OpCodes.Ldelema, ((PointerContainer) type).Element); + } + } + + // + // Encapsulates a conversion rules required for array indexes + // + public class ArrayIndexCast : TypeCast + { + public ArrayIndexCast (Expression expr, TypeSpec returnType) + : base (expr, returnType) + { + if (expr.Type == returnType) // int -> int + throw new ArgumentException ("unnecessary array index conversion"); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + using (ec.Set (ResolveContext.Options.CheckedScope)) { + return base.CreateExpressionTree (ec); + } + } + + public override void Emit (EmitContext ec) + { + child.Emit (ec); + + switch (child.Type.BuiltinType) { + case BuiltinTypeSpec.Type.UInt: + ec.Emit (OpCodes.Conv_U); + break; + case BuiltinTypeSpec.Type.Long: + ec.Emit (OpCodes.Conv_Ovf_I); + break; + case BuiltinTypeSpec.Type.ULong: + ec.Emit (OpCodes.Conv_Ovf_I_Un); + break; + default: + throw new InternalErrorException ("Cannot emit cast to unknown array element type", type); + } + } + } + + // + // Implements the `stackalloc' keyword + // + public class StackAlloc : Expression { + TypeSpec otype; + Expression t; + Expression count; + + public StackAlloc (Expression type, Expression count, Location l) + { + t = type; + this.count = count; + loc = l; + } + + public Expression TypeExpression { + get { + return this.t; + } + } + + public Expression CountExpression { + get { + return this.count; + } + } + + public override bool ContainsEmitWithAwait () + { + return false; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + throw new NotSupportedException ("ET"); + } + + protected override Expression DoResolve (ResolveContext ec) + { + count = count.Resolve (ec); + if (count == null) + return null; + + if (count.Type.BuiltinType != BuiltinTypeSpec.Type.UInt){ + count = Convert.ImplicitConversionRequired (ec, count, ec.BuiltinTypes.Int, loc); + if (count == null) + return null; + } + + Constant c = count as Constant; + if (c != null && c.IsNegative) { + ec.Report.Error (247, loc, "Cannot use a negative size with stackalloc"); + } + + if (ec.HasAny (ResolveContext.Options.CatchScope | ResolveContext.Options.FinallyScope)) { + ec.Report.Error (255, loc, "Cannot use stackalloc in finally or catch"); + } + + otype = t.ResolveAsType (ec); + if (otype == null) + return null; + + if (!TypeManager.VerifyUnmanaged (ec.Module, otype, loc)) + return null; + + type = PointerContainer.MakeType (ec.Module, otype); + eclass = ExprClass.Value; + + return this; + } + + public override void Emit (EmitContext ec) + { + int size = BuiltinTypeSpec.GetSize (otype); + + count.Emit (ec); + + if (size == 0) + ec.Emit (OpCodes.Sizeof, otype); + else + ec.EmitInt (size); + + ec.Emit (OpCodes.Mul_Ovf_Un); + ec.Emit (OpCodes.Localloc); + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + StackAlloc target = (StackAlloc) t; + target.count = count.Clone (clonectx); + target.t = t.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + // + // An object initializer expression + // + public class ElementInitializer : Assign + { + public readonly string Name; + + public ElementInitializer (string name, Expression initializer, Location loc) + : base (null, initializer, loc) + { + this.Name = name; + } + + public bool IsDictionaryInitializer { + get { + return Name == null; + } + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + ElementInitializer target = (ElementInitializer) t; + target.source = source.Clone (clonectx); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Arguments args = new Arguments (2); + FieldExpr fe = target as FieldExpr; + if (fe != null) + args.Add (new Argument (fe.CreateTypeOfExpression ())); + else + args.Add (new Argument (((PropertyExpr) target).CreateSetterTypeOfExpression (ec))); + + string mname; + Expression arg_expr; + var cinit = source as CollectionOrObjectInitializers; + if (cinit == null) { + mname = "Bind"; + arg_expr = source.CreateExpressionTree (ec); + } else { + mname = cinit.IsEmpty || cinit.Initializers[0] is ElementInitializer ? "MemberBind" : "ListBind"; + arg_expr = cinit.CreateExpressionTree (ec, !cinit.IsEmpty); + } + + args.Add (new Argument (arg_expr)); + return CreateExpressionFactoryCall (ec, mname, args); + } + + protected override Expression DoResolve (ResolveContext ec) + { + if (source == null) + return EmptyExpressionStatement.Instance; + + if (!ResolveElement (ec)) + return null; + + if (source is CollectionOrObjectInitializers) { + Expression previous = ec.CurrentInitializerVariable; + ec.CurrentInitializerVariable = target; + source = source.Resolve (ec); + ec.CurrentInitializerVariable = previous; + if (source == null) + return null; + + eclass = source.eclass; + type = source.Type; + return this; + } + + return base.DoResolve (ec); + } + + public override void EmitStatement (EmitContext ec) + { + if (source is CollectionOrObjectInitializers) + source.Emit (ec); + else + base.EmitStatement (ec); + } + + protected virtual bool ResolveElement (ResolveContext rc) + { + var t = rc.CurrentInitializerVariable.Type; + if (t.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + Arguments args = new Arguments (1); + args.Add (new Argument (rc.CurrentInitializerVariable)); + target = new DynamicMemberBinder (Name, args, loc); + } else { + + var member = MemberLookup (rc, false, t, Name, 0, MemberLookupRestrictions.ExactArity, loc); + if (member == null) { + member = Expression.MemberLookup (rc, true, t, Name, 0, MemberLookupRestrictions.ExactArity, loc); + + if (member != null) { + // TODO: ec.Report.SymbolRelatedToPreviousError (member); + ErrorIsInaccesible (rc, member.GetSignatureForError (), loc); + return false; + } + } + + if (member == null) { + Error_TypeDoesNotContainDefinition (rc, loc, t, Name); + return false; + } + + var me = member as MemberExpr; + if (me is EventExpr) { + me = me.ResolveMemberAccess (rc, null, null); + } else if (!(member is PropertyExpr || member is FieldExpr)) { + rc.Report.Error (1913, loc, + "Member `{0}' cannot be initialized. An object initializer may only be used for fields, or properties", + member.GetSignatureForError ()); + + return false; + } + + if (me.IsStatic) { + rc.Report.Error (1914, loc, + "Static field or property `{0}' cannot be assigned in an object initializer", + me.GetSignatureForError ()); + } + + target = me; + me.InstanceExpression = rc.CurrentInitializerVariable; + } + + return true; + } + } + + // + // A collection initializer expression + // + class CollectionElementInitializer : Invocation + { + public readonly bool IsSingle; + + + public class ElementInitializerArgument : Argument + { + public ElementInitializerArgument (Expression e) + : base (e) + { + } + } + + sealed class AddMemberAccess : MemberAccess + { + public AddMemberAccess (Expression expr, Location loc) + : base (expr, "Add", loc) + { + } + + protected override void Error_TypeDoesNotContainDefinition (ResolveContext ec, TypeSpec type, string name) + { + if (TypeManager.HasElementType (type)) + return; + + base.Error_TypeDoesNotContainDefinition (ec, type, name); + } + } + + public CollectionElementInitializer (Expression argument) + : base (null, new Arguments (1)) + { + IsSingle = true; + base.arguments.Add (new ElementInitializerArgument (argument)); + this.loc = argument.Location; + } + + public CollectionElementInitializer (List arguments, Location loc) + : base (null, new Arguments (arguments.Count)) + { + IsSingle = false; + foreach (Expression e in arguments) + base.arguments.Add (new ElementInitializerArgument (e)); + + this.loc = loc; + } + + public CollectionElementInitializer (Location loc) + : base (null, null) + { + this.loc = loc; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Arguments args = new Arguments (2); + args.Add (new Argument (mg.CreateExpressionTree (ec))); + + var expr_initializers = new ArrayInitializer (arguments.Count, loc); + foreach (Argument a in arguments) + expr_initializers.Add (a.CreateExpressionTree (ec)); + + args.Add (new Argument (new ArrayCreation ( + CreateExpressionTypeExpression (ec, loc), expr_initializers, loc))); + return CreateExpressionFactoryCall (ec, "ElementInit", args); + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + CollectionElementInitializer target = (CollectionElementInitializer) t; + if (arguments != null) + target.arguments = arguments.Clone (clonectx); + } + + protected override Expression DoResolve (ResolveContext ec) + { + base.expr = new AddMemberAccess (ec.CurrentInitializerVariable, loc); + + return base.DoResolve (ec); + } + } + + class DictionaryElementInitializer : ElementInitializer + { + readonly Arguments args; + + public DictionaryElementInitializer (List arguments, Expression initializer, Location loc) + : base (null, initializer, loc) + { + this.args = new Arguments (arguments.Count); + foreach (var arg in arguments) + this.args.Add (new Argument (arg)); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + ec.Report.Error (8074, loc, "Expression tree cannot contain a dictionary initializer"); + return null; + } + + protected override bool ResolveElement (ResolveContext rc) + { + var init = rc.CurrentInitializerVariable; + var type = init.Type; + + var indexers = MemberCache.FindMembers (type, MemberCache.IndexerNameAlias, false); + if (indexers == null && type.BuiltinType != BuiltinTypeSpec.Type.Dynamic) { + ElementAccess.Error_CannotApplyIndexing (rc, type, loc); + return false; + } + + target = new IndexerExpr (indexers, type, init, args, loc).Resolve (rc); + return true; + } + } + + // + // A block of object or collection initializers + // + public class CollectionOrObjectInitializers : ExpressionStatement + { + IList initializers; + bool is_collection_initialization; + + public CollectionOrObjectInitializers (Location loc) + : this (new Expression[0], loc) + { + } + + public CollectionOrObjectInitializers (IList initializers, Location loc) + { + this.initializers = initializers; + this.loc = loc; + } + + public bool IsEmpty { + get { + return initializers.Count == 0; + } + } + + public bool IsCollectionInitializer { + get { + return is_collection_initialization; + } + } + + public IList Initializers { + get { + return initializers; + } + } + + protected override void CloneTo (CloneContext clonectx, Expression target) + { + CollectionOrObjectInitializers t = (CollectionOrObjectInitializers) target; + + t.initializers = new List (initializers.Count); + foreach (var e in initializers) + t.initializers.Add (e.Clone (clonectx)); + } + + public override bool ContainsEmitWithAwait () + { + foreach (var e in initializers) { + if (e.ContainsEmitWithAwait ()) + return true; + } + + return false; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + return CreateExpressionTree (ec, false); + } + + public Expression CreateExpressionTree (ResolveContext ec, bool inferType) + { + var expr_initializers = new ArrayInitializer (initializers.Count, loc); + foreach (Expression e in initializers) { + Expression expr = e.CreateExpressionTree (ec); + if (expr != null) + expr_initializers.Add (expr); + } + + if (inferType) + return new ImplicitlyTypedArrayCreation (expr_initializers, loc); + + return new ArrayCreation (new TypeExpression (ec.Module.PredefinedTypes.MemberBinding.Resolve (), loc), expr_initializers, loc); + } + + protected override Expression DoResolve (ResolveContext ec) + { + List element_names = null; + for (int i = 0; i < initializers.Count; ++i) { + Expression initializer = initializers [i]; + ElementInitializer element_initializer = initializer as ElementInitializer; + + if (i == 0) { + if (element_initializer != null) { + element_names = new List (initializers.Count); + if (!element_initializer.IsDictionaryInitializer) + element_names.Add (element_initializer.Name); + } else if (initializer is CompletingExpression) { + initializer.Resolve (ec); + throw new InternalErrorException ("This line should never be reached"); + } else { + var t = ec.CurrentInitializerVariable.Type; + // LAMESPEC: The collection must implement IEnumerable only, no dynamic support + if (!t.ImplementsInterface (ec.BuiltinTypes.IEnumerable, false) && t.BuiltinType != BuiltinTypeSpec.Type.Dynamic) { + ec.Report.Error (1922, loc, "A field or property `{0}' cannot be initialized with a collection " + + "object initializer because type `{1}' does not implement `{2}' interface", + ec.CurrentInitializerVariable.GetSignatureForError (), + ec.CurrentInitializerVariable.Type.GetSignatureForError (), + ec.BuiltinTypes.IEnumerable.GetSignatureForError ()); + return null; + } + is_collection_initialization = true; + } + } else { + if (is_collection_initialization != (element_initializer == null)) { + ec.Report.Error (747, initializer.Location, "Inconsistent `{0}' member declaration", + is_collection_initialization ? "collection initializer" : "object initializer"); + continue; + } + + if (!is_collection_initialization && !element_initializer.IsDictionaryInitializer) { + if (element_names.Contains (element_initializer.Name)) { + ec.Report.Error (1912, element_initializer.Location, + "An object initializer includes more than one member `{0}' initialization", + element_initializer.Name); + } else { + element_names.Add (element_initializer.Name); + } + } + } + + Expression e = initializer.Resolve (ec); + if (e == EmptyExpressionStatement.Instance) + initializers.RemoveAt (i--); + else + initializers [i] = e; + } + + type = ec.CurrentInitializerVariable.Type; + if (is_collection_initialization) { + if (TypeManager.HasElementType (type)) { + ec.Report.Error (1925, loc, "Cannot initialize object of type `{0}' with a collection initializer", + type.GetSignatureForError ()); + } + } + + eclass = ExprClass.Variable; + return this; + } + + public override void Emit (EmitContext ec) + { + EmitStatement (ec); + } + + public override void EmitStatement (EmitContext ec) + { + foreach (ExpressionStatement e in initializers) { + // TODO: need location region + ec.Mark (e.Location); + e.EmitStatement (ec); + } + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + foreach (var initializer in initializers) { + if (initializer != null) + initializer.FlowAnalysis (fc); + } + } + } + + // + // New expression with element/object initializers + // + public class NewInitialize : New + { + // + // This class serves as a proxy for variable initializer target instances. + // A real variable is assigned later when we resolve left side of an + // assignment + // + sealed class InitializerTargetExpression : Expression, IMemoryLocation + { + NewInitialize new_instance; + + public InitializerTargetExpression (NewInitialize newInstance) + { + this.type = newInstance.type; + this.loc = newInstance.loc; + this.eclass = newInstance.eclass; + this.new_instance = newInstance; + } + + public override bool ContainsEmitWithAwait () + { + return false; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + // Should not be reached + throw new NotSupportedException ("ET"); + } + + protected override Expression DoResolve (ResolveContext ec) + { + return this; + } + + public override Expression DoResolveLValue (ResolveContext ec, Expression right_side) + { + return this; + } + + public override void Emit (EmitContext ec) + { + Expression e = (Expression) new_instance.instance; + e.Emit (ec); + } + + public override Expression EmitToField (EmitContext ec) + { + return (Expression) new_instance.instance; + } + + #region IMemoryLocation Members + + public void AddressOf (EmitContext ec, AddressOp mode) + { + new_instance.instance.AddressOf (ec, mode); + } + + #endregion + } + + CollectionOrObjectInitializers initializers; + IMemoryLocation instance; + DynamicExpressionStatement dynamic; + + public NewInitialize (FullNamedExpression requested_type, Arguments arguments, CollectionOrObjectInitializers initializers, Location l) + : base (requested_type, arguments, l) + { + this.initializers = initializers; + } + + public CollectionOrObjectInitializers Initializers { + get { + return initializers; + } + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + base.CloneTo (clonectx, t); + + NewInitialize target = (NewInitialize) t; + target.initializers = (CollectionOrObjectInitializers) initializers.Clone (clonectx); + } + + public override bool ContainsEmitWithAwait () + { + return base.ContainsEmitWithAwait () || initializers.ContainsEmitWithAwait (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + Arguments args = new Arguments (2); + args.Add (new Argument (base.CreateExpressionTree (ec))); + if (!initializers.IsEmpty) + args.Add (new Argument (initializers.CreateExpressionTree (ec, initializers.IsCollectionInitializer))); + + return CreateExpressionFactoryCall (ec, + initializers.IsCollectionInitializer ? "ListInit" : "MemberInit", + args); + } + + protected override Expression DoResolve (ResolveContext ec) + { + Expression e = base.DoResolve (ec); + if (type == null) + return null; + + if (type.IsDelegate) { + ec.Report.Error (1958, Initializers.Location, + "Object and collection initializers cannot be used to instantiate a delegate"); + } + + Expression previous = ec.CurrentInitializerVariable; + ec.CurrentInitializerVariable = new InitializerTargetExpression (this); + initializers.Resolve (ec); + ec.CurrentInitializerVariable = previous; + + dynamic = e as DynamicExpressionStatement; + if (dynamic != null) + return this; + + return e; + } + + public override void Emit (EmitContext ec) + { + if (method == null && TypeSpec.IsValueType (type) && initializers.Initializers.Count > 1 && ec.HasSet (BuilderContext.Options.AsyncBody) && initializers.ContainsEmitWithAwait ()) { + var fe = ec.GetTemporaryField (type); + + if (!Emit (ec, fe)) + fe.Emit (ec); + + return; + } + + base.Emit (ec); + } + + public override bool Emit (EmitContext ec, IMemoryLocation target) + { + bool left_on_stack; + if (dynamic != null) { + dynamic.Emit (ec); + left_on_stack = true; + } else { + left_on_stack = base.Emit (ec, target); + } + + if (initializers.IsEmpty) + return left_on_stack; + + LocalTemporary temp = null; + + instance = target as LocalTemporary; + if (instance == null) + instance = target as StackFieldExpr; + + if (instance == null) { + if (!left_on_stack) { + VariableReference vr = target as VariableReference; + + // FIXME: This still does not work correctly for pre-set variables + if (vr != null && vr.IsRef) + target.AddressOf (ec, AddressOp.Load); + + ((Expression) target).Emit (ec); + left_on_stack = true; + } + + if (ec.HasSet (BuilderContext.Options.AsyncBody) && initializers.ContainsEmitWithAwait ()) { + instance = new EmptyAwaitExpression (Type).EmitToField (ec) as IMemoryLocation; + } else { + temp = new LocalTemporary (type); + instance = temp; + } + } + + if (left_on_stack && temp != null) + temp.Store (ec); + + initializers.Emit (ec); + + if (left_on_stack) { + if (temp != null) { + temp.Emit (ec); + temp.Release (ec); + } else { + ((Expression) instance).Emit (ec); + } + } + + return left_on_stack; + } + + protected override IMemoryLocation EmitAddressOf (EmitContext ec, AddressOp Mode) + { + instance = base.EmitAddressOf (ec, Mode); + + if (!initializers.IsEmpty) + initializers.Emit (ec); + + return instance; + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + base.FlowAnalysis (fc); + initializers.FlowAnalysis (fc); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class NewAnonymousType : New + { + static readonly AnonymousTypeParameter[] EmptyParameters = new AnonymousTypeParameter[0]; + + List parameters; + readonly TypeContainer parent; + AnonymousTypeClass anonymous_type; + + public NewAnonymousType (List parameters, TypeContainer parent, Location loc) + : base (null, null, loc) + { + this.parameters = parameters; + this.parent = parent; + } + + public List Parameters { + get { + return this.parameters; + } + } + + protected override void CloneTo (CloneContext clonectx, Expression target) + { + if (parameters == null) + return; + + NewAnonymousType t = (NewAnonymousType) target; + t.parameters = new List (parameters.Count); + foreach (AnonymousTypeParameter atp in parameters) + t.parameters.Add ((AnonymousTypeParameter) atp.Clone (clonectx)); + } + + AnonymousTypeClass CreateAnonymousType (ResolveContext ec, IList parameters) + { + AnonymousTypeClass type = parent.Module.GetAnonymousType (parameters); + if (type != null) + return type; + + type = AnonymousTypeClass.Create (parent, parameters, loc); + if (type == null) + return null; + + int errors = ec.Report.Errors; + type.CreateContainer (); + type.DefineContainer (); + type.Define (); + if ((ec.Report.Errors - errors) == 0) { + parent.Module.AddAnonymousType (type); + type.PrepareEmit (); + } + + return type; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + if (parameters == null) + return base.CreateExpressionTree (ec); + + var init = new ArrayInitializer (parameters.Count, loc); + foreach (var m in anonymous_type.Members) { + var p = m as Property; + if (p != null) + init.Add (new TypeOfMethod (MemberCache.GetMember (type, p.Get.Spec), loc)); + } + + var ctor_args = new ArrayInitializer (arguments.Count, loc); + foreach (Argument a in arguments) + ctor_args.Add (a.CreateExpressionTree (ec)); + + Arguments args = new Arguments (3); + args.Add (new Argument (new TypeOfMethod (method, loc))); + args.Add (new Argument (new ArrayCreation (CreateExpressionTypeExpression (ec, loc), ctor_args, loc))); + args.Add (new Argument (new ImplicitlyTypedArrayCreation (init, loc))); + + return CreateExpressionFactoryCall (ec, "New", args); + } + + protected override Expression DoResolve (ResolveContext ec) + { + if (ec.HasSet (ResolveContext.Options.ConstantScope)) { + ec.Report.Error (836, loc, "Anonymous types cannot be used in this expression"); + return null; + } + + if (parameters == null) { + anonymous_type = CreateAnonymousType (ec, EmptyParameters); + RequestedType = new TypeExpression (anonymous_type.Definition, loc); + return base.DoResolve (ec); + } + + bool error = false; + arguments = new Arguments (parameters.Count); + var t_args = new TypeSpec [parameters.Count]; + for (int i = 0; i < parameters.Count; ++i) { + Expression e = parameters [i].Resolve (ec); + if (e == null) { + error = true; + continue; + } + + arguments.Add (new Argument (e)); + t_args [i] = e.Type; + } + + if (error) + return null; + + anonymous_type = CreateAnonymousType (ec, parameters); + if (anonymous_type == null) + return null; + + type = anonymous_type.Definition.MakeGenericType (ec.Module, t_args); + method = (MethodSpec) MemberCache.FindMember (type, MemberFilter.Constructor (null), BindingRestriction.DeclaredOnly); + eclass = ExprClass.Value; + return this; + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class AnonymousTypeParameter : ShimExpression + { + public readonly string Name; + + public AnonymousTypeParameter (Expression initializer, string name, Location loc) + : base (initializer) + { + this.Name = name; + this.loc = loc; + } + + public AnonymousTypeParameter (Parameter parameter) + : base (new SimpleName (parameter.Name, parameter.Location)) + { + this.Name = parameter.Name; + this.loc = parameter.Location; + } + + public override bool Equals (object o) + { + AnonymousTypeParameter other = o as AnonymousTypeParameter; + return other != null && Name == other.Name; + } + + public override int GetHashCode () + { + return Name.GetHashCode (); + } + + protected override Expression DoResolve (ResolveContext ec) + { + Expression e = expr.Resolve (ec); + if (e == null) + return null; + + if (e.eclass == ExprClass.MethodGroup) { + Error_InvalidInitializer (ec, e.ExprClassName); + return null; + } + + type = e.Type; + if (type.Kind == MemberKind.Void || type == InternalType.NullLiteral || type == InternalType.AnonymousMethod || type.IsPointer) { + Error_InvalidInitializer (ec, type.GetSignatureForError ()); + return null; + } + + return e; + } + + protected virtual void Error_InvalidInitializer (ResolveContext ec, string initializer) + { + ec.Report.Error (828, loc, "An anonymous type property `{0}' cannot be initialized with `{1}'", + Name, initializer); + } + } + + public class CatchFilterExpression : BooleanExpression + { + public CatchFilterExpression (Expression expr, Location loc) + : base (expr) + { + this.loc = loc; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/field.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/field.cs new file mode 100644 index 000000000..265e43620 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/field.cs @@ -0,0 +1,733 @@ +// +// field.cs: All field handlers +// +// Authors: Miguel de Icaza (miguel@gnu.org) +// Martin Baulig (martin@ximian.com) +// Marek Safar (marek.safar@seznam.cz) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com) +// Copyright 2004-2008 Novell, Inc +// Copyright 2011 Xamarin Inc +// + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +#if STATIC +using MetaType = IKVM.Reflection.Type; +using IKVM.Reflection; +using IKVM.Reflection.Emit; +#else +using MetaType = System.Type; +using System.Reflection; +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp +{ + public class FieldDeclarator + { + public FieldDeclarator (SimpleMemberName name, Expression initializer) + { + this.Name = name; + this.Initializer = initializer; + } + + #region Properties + + public SimpleMemberName Name { get; private set; } + public Expression Initializer { get; private set; } + + #endregion + + public virtual FullNamedExpression GetFieldTypeExpression (FieldBase field) + { + return new TypeExpression (field.MemberType, Name.Location); + } + } + + // + // Abstract class for all fields + // + abstract public class FieldBase : MemberBase + { + protected FieldBuilder FieldBuilder; + protected FieldSpec spec; + public Status status; + protected Expression initializer; + protected List declarators; + + [Flags] + public enum Status : byte { + HAS_OFFSET = 4 // Used by FieldMember. + } + + static readonly string[] attribute_targets = new string [] { "field" }; + + protected FieldBase (TypeDefinition parent, FullNamedExpression type, Modifiers mod, Modifiers allowed_mod, MemberName name, Attributes attrs) + : base (parent, type, mod, allowed_mod | Modifiers.ABSTRACT, Modifiers.PRIVATE, name, attrs) + { + if ((mod & Modifiers.ABSTRACT) != 0) + Report.Error (681, Location, "The modifier 'abstract' is not valid on fields. Try using a property instead"); + } + + #region Properties + + public override AttributeTargets AttributeTargets { + get { + return AttributeTargets.Field; + } + } + + public Expression Initializer { + get { + return initializer; + } + set { + this.initializer = value; + } + } + + public string Name { + get { + return MemberName.Name; + } + } + + public FieldSpec Spec { + get { + return spec; + } + } + + public override string[] ValidAttributeTargets { + get { + return attribute_targets; + } + } + + public List Declarators { + get { + return this.declarators; + } + } + #endregion + + public void AddDeclarator (FieldDeclarator declarator) + { + if (declarators == null) + declarators = new List (2); + + declarators.Add (declarator); + + Parent.AddNameToContainer (this, declarator.Name.Value); + } + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.Type == pa.FieldOffset) { + status |= Status.HAS_OFFSET; + + if (!Parent.PartialContainer.HasExplicitLayout) { + Report.Error (636, Location, "The FieldOffset attribute can only be placed on members of types marked with the StructLayout(LayoutKind.Explicit)"); + return; + } + + if ((ModFlags & Modifiers.STATIC) != 0 || this is Const) { + Report.Error (637, Location, "The FieldOffset attribute is not allowed on static or const fields"); + return; + } + } + + if (a.Type == pa.FixedBuffer) { + Report.Error (1716, Location, "Do not use 'System.Runtime.CompilerServices.FixedBuffer' attribute. Use the 'fixed' field modifier instead"); + return; + } + +#if false + if (a.Type == pa.MarshalAs) { + UnmanagedMarshal marshal = a.GetMarshal (this); + if (marshal != null) { + FieldBuilder.SetMarshal (marshal); + } + return; + } +#endif + if ((a.HasSecurityAttribute)) { + a.Error_InvalidSecurityParent (); + return; + } + + if (a.Type == pa.Dynamic) { + a.Error_MisusedDynamicAttribute (); + return; + } + + FieldBuilder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), cdata); + } + + public void SetCustomAttribute (MethodSpec ctor, byte[] data) + { + FieldBuilder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), data); + } + + protected override bool CheckBase () + { + if (!base.CheckBase ()) + return false; + + MemberSpec candidate; + bool overrides = false; + var conflict_symbol = MemberCache.FindBaseMember (this, out candidate, ref overrides); + if (conflict_symbol == null) + conflict_symbol = candidate; + + if (conflict_symbol == null) { + if ((ModFlags & Modifiers.NEW) != 0) { + Report.Warning (109, 4, Location, "The member `{0}' does not hide an inherited member. The new keyword is not required", + GetSignatureForError ()); + } + } else { + if ((ModFlags & (Modifiers.NEW | Modifiers.OVERRIDE | Modifiers.BACKING_FIELD)) == 0) { + Report.SymbolRelatedToPreviousError (conflict_symbol); + Report.Warning (108, 2, Location, "`{0}' hides inherited member `{1}'. Use the new keyword if hiding was intended", + GetSignatureForError (), conflict_symbol.GetSignatureForError ()); + } + + if (conflict_symbol.IsAbstract) { + Report.SymbolRelatedToPreviousError (conflict_symbol); + Report.Error (533, Location, "`{0}' hides inherited abstract member `{1}'", + GetSignatureForError (), conflict_symbol.GetSignatureForError ()); + } + } + + return true; + } + + public virtual Constant ConvertInitializer (ResolveContext rc, Constant expr) + { + return expr.ConvertImplicitly (MemberType); + } + + protected override void DoMemberTypeDependentChecks () + { + base.DoMemberTypeDependentChecks (); + + if (MemberType.IsGenericParameter) + return; + + if (MemberType.IsStatic) + Error_VariableOfStaticClass (Location, GetSignatureForError (), MemberType, Report); + + if (!IsCompilerGenerated) + CheckBase (); + + IsTypePermitted (); + } + + // + // Represents header string for documentation comment. + // + public override string DocCommentHeader { + get { return "F:"; } + } + + public override void Emit () + { + if (member_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + Module.PredefinedAttributes.Dynamic.EmitAttribute (FieldBuilder); + } else if (!Parent.IsCompilerGenerated && member_type.HasDynamicElement) { + Module.PredefinedAttributes.Dynamic.EmitAttribute (FieldBuilder, member_type, Location); + } + + if ((ModFlags & Modifiers.COMPILER_GENERATED) != 0 && !Parent.IsCompilerGenerated) + Module.PredefinedAttributes.CompilerGenerated.EmitAttribute (FieldBuilder); + if ((ModFlags & Modifiers.DEBUGGER_HIDDEN) != 0) + Module.PredefinedAttributes.DebuggerBrowsable.EmitAttribute (FieldBuilder, System.Diagnostics.DebuggerBrowsableState.Never); + + if (OptAttributes != null) { + OptAttributes.Emit (); + } + + if (((status & Status.HAS_OFFSET) == 0) && (ModFlags & (Modifiers.STATIC | Modifiers.BACKING_FIELD)) == 0 && Parent.PartialContainer.HasExplicitLayout) { + Report.Error (625, Location, "`{0}': Instance field types marked with StructLayout(LayoutKind.Explicit) must have a FieldOffset attribute", GetSignatureForError ()); + } + + ConstraintChecker.Check (this, member_type, type_expr.Location); + + base.Emit (); + } + + public static void Error_VariableOfStaticClass (Location loc, string variable_name, TypeSpec static_class, Report Report) + { + Report.SymbolRelatedToPreviousError (static_class); + Report.Error (723, loc, "`{0}': cannot declare variables of static types", + variable_name); + } + + protected override bool VerifyClsCompliance () + { + if (!base.VerifyClsCompliance ()) + return false; + + if (!MemberType.IsCLSCompliant () || this is FixedField) { + Report.Warning (3003, 1, Location, "Type of `{0}' is not CLS-compliant", + GetSignatureForError ()); + } + return true; + } + } + + // + // Field specification + // + public class FieldSpec : MemberSpec, IInterfaceMemberSpec + { + FieldInfo metaInfo; + TypeSpec memberType; + + public FieldSpec (TypeSpec declaringType, IMemberDefinition definition, TypeSpec memberType, FieldInfo info, Modifiers modifiers) + : base (MemberKind.Field, declaringType, definition, modifiers) + { + this.metaInfo = info; + this.memberType = memberType; + } + + #region Properties + + public bool IsReadOnly { + get { + return (Modifiers & Modifiers.READONLY) != 0; + } + } + + public TypeSpec MemberType { + get { + return memberType; + } + } + +#endregion + + public FieldInfo GetMetaInfo () + { + if ((state & StateFlags.PendingMetaInflate) != 0) { + var decl_meta = DeclaringType.GetMetaInfo (); + if (DeclaringType.IsTypeBuilder) { + metaInfo = TypeBuilder.GetField (decl_meta, metaInfo); + } else { + var orig_token = metaInfo.MetadataToken; + metaInfo = decl_meta.GetField (Name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); + if (metaInfo.MetadataToken != orig_token) + throw new NotImplementedException ("Resolved to wrong meta token"); + + // What a stupid API, does not work because field handle is imported + // metaInfo = FieldInfo.GetFieldFromHandle (metaInfo.FieldHandle, DeclaringType.MetaInfo.TypeHandle); + } + + state &= ~StateFlags.PendingMetaInflate; + } + + return metaInfo; + } + + public override MemberSpec InflateMember (TypeParameterInflator inflator) + { + var fs = (FieldSpec) base.InflateMember (inflator); + fs.memberType = inflator.Inflate (memberType); + return fs; + } + + public FieldSpec Mutate (TypeParameterMutator mutator) + { + var decl = DeclaringType; + if (DeclaringType.IsGenericOrParentIsGeneric) + decl = mutator.Mutate (decl); + + if (decl == DeclaringType) + return this; + + var fs = (FieldSpec) MemberwiseClone (); + fs.declaringType = decl; + fs.state |= StateFlags.PendingMetaInflate; + + // Gets back FieldInfo in case of metaInfo was inflated + fs.metaInfo = MemberCache.GetMember (TypeParameterMutator.GetMemberDeclaringType (DeclaringType), this).metaInfo; + return fs; + } + + public override List ResolveMissingDependencies (MemberSpec caller) + { + return memberType.ResolveMissingDependencies (this); + } + } + + /// + /// Fixed buffer implementation + /// + public class FixedField : FieldBase + { + public const string FixedElementName = "FixedElementField"; + static int GlobalCounter; + + TypeBuilder fixed_buffer_type; + + const Modifiers AllowedModifiers = + Modifiers.NEW | + Modifiers.PUBLIC | + Modifiers.PROTECTED | + Modifiers.INTERNAL | + Modifiers.PRIVATE | + Modifiers.UNSAFE; + + public FixedField (TypeDefinition parent, FullNamedExpression type, Modifiers mod, MemberName name, Attributes attrs) + : base (parent, type, mod, AllowedModifiers, name, attrs) + { + } + + #region Properties + + // + // Explicit struct layout set by parent + // + public CharSet? CharSet { + get; set; + } + + #endregion + + public override Constant ConvertInitializer (ResolveContext rc, Constant expr) + { + return expr.ImplicitConversionRequired (rc, rc.BuiltinTypes.Int); + } + + public override bool Define () + { + if (!base.Define ()) + return false; + + if (!BuiltinTypeSpec.IsPrimitiveType (MemberType)) { + Report.Error (1663, Location, + "`{0}': Fixed size buffers type must be one of the following: bool, byte, short, int, long, char, sbyte, ushort, uint, ulong, float or double", + GetSignatureForError ()); + } else if (declarators != null) { + foreach (var d in declarators) { + var f = new FixedField (Parent, d.GetFieldTypeExpression (this), ModFlags, new MemberName (d.Name.Value, d.Name.Location), OptAttributes); + f.initializer = d.Initializer; + ((ConstInitializer) f.initializer).Name = d.Name.Value; + f.Define (); + Parent.PartialContainer.Members.Add (f); + } + } + + // Create nested fixed buffer container + string name = String.Format ("<{0}>__FixedBuffer{1}", Name, GlobalCounter++); + fixed_buffer_type = Parent.TypeBuilder.DefineNestedType (name, + TypeAttributes.NestedPublic | TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit, + Compiler.BuiltinTypes.ValueType.GetMetaInfo ()); + + var ffield = fixed_buffer_type.DefineField (FixedElementName, MemberType.GetMetaInfo (), FieldAttributes.Public); + + FieldBuilder = Parent.TypeBuilder.DefineField (Name, fixed_buffer_type, ModifiersExtensions.FieldAttr (ModFlags)); + + var element_spec = new FieldSpec (null, this, MemberType, ffield, ModFlags); + spec = new FixedFieldSpec (Module, Parent.Definition, this, FieldBuilder, element_spec, ModFlags); + + Parent.MemberCache.AddMember (spec); + return true; + } + + protected override void DoMemberTypeIndependentChecks () + { + base.DoMemberTypeIndependentChecks (); + + if (!IsUnsafe) + Expression.UnsafeError (Report, Location); + + if (Parent.PartialContainer.Kind != MemberKind.Struct) { + Report.Error (1642, Location, "`{0}': Fixed size buffer fields may only be members of structs", + GetSignatureForError ()); + } + } + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + + public override void Emit() + { + ResolveContext rc = new ResolveContext (this); + IntConstant buffer_size_const = initializer.Resolve (rc) as IntConstant; + if (buffer_size_const == null) + return; + + int buffer_size = buffer_size_const.Value; + + if (buffer_size <= 0) { + Report.Error (1665, Location, "`{0}': Fixed size buffers must have a length greater than zero", GetSignatureForError ()); + return; + } + + EmitFieldSize (buffer_size); + +#if STATIC + if (Module.HasDefaultCharSet) + fixed_buffer_type.__SetAttributes (fixed_buffer_type.Attributes | Module.DefaultCharSetType); +#endif + + Module.PredefinedAttributes.UnsafeValueType.EmitAttribute (fixed_buffer_type); + Module.PredefinedAttributes.CompilerGenerated.EmitAttribute (fixed_buffer_type); + fixed_buffer_type.CreateType (); + + base.Emit (); + } + + void EmitFieldSize (int buffer_size) + { + int type_size = BuiltinTypeSpec.GetSize (MemberType); + + if (buffer_size > int.MaxValue / type_size) { + Report.Error (1664, Location, "Fixed size buffer `{0}' of length `{1}' and type `{2}' exceeded 2^31 limit", + GetSignatureForError (), buffer_size.ToString (), MemberType.GetSignatureForError ()); + return; + } + + AttributeEncoder encoder; + + var ctor = Module.PredefinedMembers.StructLayoutAttributeCtor.Resolve (Location); + if (ctor == null) + return; + + var field_size = Module.PredefinedMembers.StructLayoutSize.Resolve (Location); + var field_charset = Module.PredefinedMembers.StructLayoutCharSet.Resolve (Location); + if (field_size == null || field_charset == null) + return; + + var char_set = CharSet ?? Module.DefaultCharSet ?? 0; + + encoder = new AttributeEncoder (); + encoder.Encode ((short)LayoutKind.Sequential); + encoder.EncodeNamedArguments ( + new [] { field_size, field_charset }, + new Constant [] { + new IntConstant (Compiler.BuiltinTypes, buffer_size * type_size, Location), + new IntConstant (Compiler.BuiltinTypes, (int) char_set, Location) + } + ); + + fixed_buffer_type.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), encoder.ToArray ()); + + // + // Don't emit FixedBufferAttribute attribute for private types + // + if ((ModFlags & Modifiers.PRIVATE) != 0) + return; + + ctor = Module.PredefinedMembers.FixedBufferAttributeCtor.Resolve (Location); + if (ctor == null) + return; + + encoder = new AttributeEncoder (); + encoder.EncodeTypeName (MemberType); + encoder.Encode (buffer_size); + encoder.EncodeEmptyNamedArguments (); + + FieldBuilder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), encoder.ToArray ()); + } + } + + class FixedFieldSpec : FieldSpec + { + readonly FieldSpec element; + + public FixedFieldSpec (ModuleContainer module, TypeSpec declaringType, IMemberDefinition definition, FieldInfo info, FieldSpec element, Modifiers modifiers) + : base (declaringType, definition, PointerContainer.MakeType (module, element.MemberType), info, modifiers) + { + this.element = element; + + // It's never CLS-Compliant + state &= ~StateFlags.CLSCompliant_Undetected; + } + + public FieldSpec Element { + get { + return element; + } + } + + public TypeSpec ElementType { + get { + return element.MemberType; + } + } + } + + // + // The Field class is used to represents class/struct fields during parsing. + // + public class Field : FieldBase { + // + // Modifiers allowed in a class declaration + // + const Modifiers AllowedModifiers = + Modifiers.NEW | + Modifiers.PUBLIC | + Modifiers.PROTECTED | + Modifiers.INTERNAL | + Modifiers.PRIVATE | + Modifiers.STATIC | + Modifiers.VOLATILE | + Modifiers.UNSAFE | + Modifiers.READONLY; + + public Field (TypeDefinition parent, FullNamedExpression type, Modifiers mod, MemberName name, Attributes attrs) + : base (parent, type, mod, AllowedModifiers, name, attrs) + { + } + + bool CanBeVolatile () + { + switch (MemberType.BuiltinType) { + case BuiltinTypeSpec.Type.Bool: + case BuiltinTypeSpec.Type.Char: + case BuiltinTypeSpec.Type.SByte: + case BuiltinTypeSpec.Type.Byte: + case BuiltinTypeSpec.Type.Short: + case BuiltinTypeSpec.Type.UShort: + case BuiltinTypeSpec.Type.Int: + case BuiltinTypeSpec.Type.UInt: + case BuiltinTypeSpec.Type.Float: + case BuiltinTypeSpec.Type.UIntPtr: + case BuiltinTypeSpec.Type.IntPtr: + return true; + } + + if (TypeSpec.IsReferenceType (MemberType)) + return true; + + if (MemberType.IsEnum) + return true; + + return false; + } + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + + public override bool Define () + { + if (!base.Define ()) + return false; + + MetaType[] required_modifier = null; + if ((ModFlags & Modifiers.VOLATILE) != 0) { + var mod = Module.PredefinedTypes.IsVolatile.Resolve (); + if (mod != null) + required_modifier = new MetaType[] { mod.GetMetaInfo () }; + } + + FieldBuilder = Parent.TypeBuilder.DefineField ( + Name, member_type.GetMetaInfo (), required_modifier, null, ModifiersExtensions.FieldAttr (ModFlags)); + + spec = new FieldSpec (Parent.Definition, this, MemberType, FieldBuilder, ModFlags); + + // + // Don't cache inaccessible fields except for struct where we + // need them for definitive assignment checks + // + if ((ModFlags & Modifiers.BACKING_FIELD) == 0 || Parent.Kind == MemberKind.Struct) { + Parent.MemberCache.AddMember (spec); + } + + if (initializer != null) { + Parent.RegisterFieldForInitialization (this, new FieldInitializer (this, initializer, TypeExpression.Location)); + } + + if (declarators != null) { + foreach (var d in declarators) { + var f = new Field (Parent, d.GetFieldTypeExpression (this), ModFlags, new MemberName (d.Name.Value, d.Name.Location), OptAttributes); + if (d.Initializer != null) + f.initializer = d.Initializer; + + f.Define (); + Parent.PartialContainer.Members.Add (f); + } + } + + return true; + } + + protected override void DoMemberTypeDependentChecks () + { + if ((ModFlags & Modifiers.BACKING_FIELD) != 0) + return; + + base.DoMemberTypeDependentChecks (); + + if ((ModFlags & Modifiers.VOLATILE) != 0) { + if (!CanBeVolatile ()) { + Report.Error (677, Location, "`{0}': A volatile field cannot be of the type `{1}'", + GetSignatureForError (), MemberType.GetSignatureForError ()); + } + + if ((ModFlags & Modifiers.READONLY) != 0) { + Report.Error (678, Location, "`{0}': A field cannot be both volatile and readonly", + GetSignatureForError ()); + } + } + } + + protected override bool VerifyClsCompliance () + { + if (!base.VerifyClsCompliance ()) + return false; + + if ((ModFlags & Modifiers.VOLATILE) != 0) { + Report.Warning (3026, 1, Location, "CLS-compliant field `{0}' cannot be volatile", GetSignatureForError ()); + } + + return true; + } + } + + class PrimaryConstructorField : Field + { + // + // Proxy resolved parameter type expression to avoid type double resolve + // and problems with correct resolve context on partial classes + // + sealed class TypeExpressionFromParameter : TypeExpr + { + Parameter parameter; + + public TypeExpressionFromParameter (Parameter parameter) + { + this.parameter = parameter; + eclass = ExprClass.Type; + loc = parameter.Location; + } + + public override TypeSpec ResolveAsType (IMemberContext mc, bool allowUnboundTypeArguments) + { + return parameter.Type; + } + } + + public PrimaryConstructorField (TypeDefinition parent, Parameter parameter) + : base (parent, new TypeExpressionFromParameter (parameter), Modifiers.PRIVATE, new MemberName (parameter.Name, parameter.Location), null) + { + caching_flags |= Flags.IsUsed | Flags.IsAssigned; + } + + public override string GetSignatureForError () + { + return MemberName.Name; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/flowanalysis.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/flowanalysis.cs new file mode 100644 index 000000000..dbb74a751 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/flowanalysis.cs @@ -0,0 +1,685 @@ +// +// flowanalyis.cs: The control flow analysis code +// +// Authors: +// Martin Baulig (martin@ximian.com) +// Raja R Harinath (rharinath@novell.com) +// Marek Safar (marek.safar@gmail.com) +// +// Copyright 2001, 2002, 2003 Ximian, Inc. +// Copyright 2003-2008 Novell, Inc. +// Copyright 2011 Xamarin, Inc. +// + +using System; +using System.Text; +using System.Collections.Generic; + +namespace Mono.CSharp +{ + // + // This is used by the flow analysis code to keep track of the type of local variables. + // + // The flow code uses a BitVector to keep track of whether a variable has been assigned + // or not. This is easy for fundamental types (int, char etc.) or reference types since + // you can only assign the whole variable as such. + // + // For structs, we also need to keep track of all its fields. To do this, we allocate one + // bit for the struct itself (it's used if you assign/access the whole struct) followed by + // one bit for each of its fields. + // + // This class computes this `layout' for each type. + // + public class TypeInfo + { + // + // Total number of bits a variable of this type consumes in the flow vector. + // + public readonly int TotalLength; + + // + // Number of bits the simple fields of a variable of this type consume + // in the flow vector. + // + public readonly int Length; + + // + // This is only used by sub-structs. + // + public readonly int Offset; + + // + // If this is a struct. + // + public readonly bool IsStruct; + + // + // If this is a struct, all fields which are structs theirselves. + // + public TypeInfo[] SubStructInfo; + + readonly StructInfo struct_info; + private static Dictionary type_hash; + + static readonly TypeInfo simple_type = new TypeInfo (1); + + static TypeInfo () + { + Reset (); + } + + public static void Reset () + { + type_hash = new Dictionary (); + StructInfo.field_type_hash = new Dictionary (); + } + + TypeInfo (int totalLength) + { + this.TotalLength = totalLength; + } + + TypeInfo (StructInfo struct_info, int offset) + { + this.struct_info = struct_info; + this.Offset = offset; + this.Length = struct_info.Length; + this.TotalLength = struct_info.TotalLength; + this.SubStructInfo = struct_info.StructFields; + this.IsStruct = true; + } + + public int GetFieldIndex (string name) + { + if (struct_info == null) + return 0; + + return struct_info [name]; + } + + public TypeInfo GetStructField (string name) + { + if (struct_info == null) + return null; + + return struct_info.GetStructField (name); + } + + public static TypeInfo GetTypeInfo (TypeSpec type) + { + if (!type.IsStruct) + return simple_type; + + TypeInfo info; + if (type_hash.TryGetValue (type, out info)) + return info; + + var struct_info = StructInfo.GetStructInfo (type); + if (struct_info != null) { + info = new TypeInfo (struct_info, 0); + } else { + info = simple_type; + } + + type_hash.Add (type, info); + return info; + } + + // + // A struct's constructor must always assign all fields. + // This method checks whether it actually does so. + // + public bool IsFullyInitialized (FlowAnalysisContext fc, VariableInfo vi, Location loc) + { + if (struct_info == null) + return true; + + bool ok = true; + for (int i = 0; i < struct_info.Count; i++) { + var field = struct_info.Fields[i]; + + if (!fc.IsStructFieldDefinitelyAssigned (vi, field.Name)) { + var bf = field.MemberDefinition as Property.BackingField; + if (bf != null) { + if (bf.Initializer != null) + continue; + + fc.Report.Error (843, loc, + "An automatically implemented property `{0}' must be fully assigned before control leaves the constructor. Consider calling the default struct contructor from a constructor initializer", + field.GetSignatureForError ()); + + ok = false; + continue; + } + + fc.Report.Error (171, loc, + "Field `{0}' must be fully assigned before control leaves the constructor", + field.GetSignatureForError ()); + ok = false; + } + } + + return ok; + } + + public override string ToString () + { + return String.Format ("TypeInfo ({0}:{1}:{2})", + Offset, Length, TotalLength); + } + + class StructInfo + { + readonly List fields; + public readonly TypeInfo[] StructFields; + public readonly int Length; + public readonly int TotalLength; + + public static Dictionary field_type_hash; + private Dictionary struct_field_hash; + private Dictionary field_hash; + + bool InTransit; + + // + // We only need one instance per type + // + StructInfo (TypeSpec type) + { + field_type_hash.Add (type, this); + + fields = MemberCache.GetAllFieldsForDefiniteAssignment (type); + + struct_field_hash = new Dictionary (); + field_hash = new Dictionary (fields.Count); + + StructFields = new TypeInfo[fields.Count]; + StructInfo[] sinfo = new StructInfo[fields.Count]; + + InTransit = true; + + for (int i = 0; i < fields.Count; i++) { + var field = fields [i]; + + if (field.MemberType.IsStruct) + sinfo [i] = GetStructInfo (field.MemberType); + + if (sinfo [i] == null) + field_hash.Add (field.Name, ++Length); + else if (sinfo [i].InTransit) { + sinfo [i] = null; + return; + } + } + + InTransit = false; + + TotalLength = Length + 1; + for (int i = 0; i < fields.Count; i++) { + var field = fields [i]; + + if (sinfo [i] == null) + continue; + + field_hash.Add (field.Name, TotalLength); + + StructFields [i] = new TypeInfo (sinfo [i], TotalLength); + struct_field_hash.Add (field.Name, StructFields [i]); + TotalLength += sinfo [i].TotalLength; + } + } + + public int Count { + get { + return fields.Count; + } + } + + public List Fields { + get { + return fields; + } + } + + public int this [string name] { + get { + int val; + if (!field_hash.TryGetValue (name, out val)) + return 0; + + return val; + } + } + + public TypeInfo GetStructField (string name) + { + TypeInfo ti; + if (struct_field_hash.TryGetValue (name, out ti)) + return ti; + + return null; + } + + public static StructInfo GetStructInfo (TypeSpec type) + { + if (type.BuiltinType > 0) + return null; + + StructInfo info; + if (field_type_hash.TryGetValue (type, out info)) + return info; + + return new StructInfo (type); + } + } + } + + // + // This is used by definite assignment analysis code to store information about a local variable + // or parameter. Depending on the variable's type, we need to allocate one or more elements + // in the BitVector - if it's a fundamental or reference type, we just need to know whether + // it has been assigned or not, but for structs, we need this information for each of its fields. + // + public class VariableInfo + { + readonly string Name; + + readonly TypeInfo TypeInfo; + + // + // The bit offset of this variable in the flow vector. + // + readonly int Offset; + + // + // The number of bits this variable needs in the flow vector. + // The first bit always specifies whether the variable as such has been assigned while + // the remaining bits contain this information for each of a struct's fields. + // + readonly int Length; + + // + // If this is a parameter of local variable. + // + public bool IsParameter; + + VariableInfo[] sub_info; + + VariableInfo (string name, TypeSpec type, int offset) + { + this.Name = name; + this.Offset = offset; + this.TypeInfo = TypeInfo.GetTypeInfo (type); + + Length = TypeInfo.TotalLength; + + Initialize (); + } + + VariableInfo (VariableInfo parent, TypeInfo type) + { + this.Name = parent.Name; + this.TypeInfo = type; + this.Offset = parent.Offset + type.Offset; + this.Length = type.TotalLength; + + this.IsParameter = parent.IsParameter; + + Initialize (); + } + + void Initialize () + { + TypeInfo[] sub_fields = TypeInfo.SubStructInfo; + if (sub_fields != null) { + sub_info = new VariableInfo [sub_fields.Length]; + for (int i = 0; i < sub_fields.Length; i++) { + if (sub_fields [i] != null) + sub_info [i] = new VariableInfo (this, sub_fields [i]); + } + } else + sub_info = new VariableInfo [0]; + } + + public static VariableInfo Create (BlockContext bc, LocalVariable variable) + { + var info = new VariableInfo (variable.Name, variable.Type, bc.AssignmentInfoOffset); + bc.AssignmentInfoOffset += info.Length; + return info; + } + + public static VariableInfo Create (BlockContext bc, Parameter parameter) + { + var info = new VariableInfo (parameter.Name, parameter.Type, bc.AssignmentInfoOffset) { + IsParameter = true + }; + + bc.AssignmentInfoOffset += info.Length; + return info; + } + + public bool IsAssigned (DefiniteAssignmentBitSet vector) + { + if (vector == null) + return true; + + if (vector [Offset]) + return true; + + // Unless this is a struct + if (!TypeInfo.IsStruct) + return false; + + // + // Following case cannot be handled fully by SetStructFieldAssigned + // because we may encounter following case + // + // struct A { B b } + // struct B { int value; } + // + // setting a.b.value is propagated only to B's vector and not upwards to possible parents + // + // + // Each field must be assigned + // + for (int i = Offset + 1; i <= TypeInfo.Length + Offset; i++) { + if (!vector[i]) + return false; + } + + // Ok, now check all fields which are structs. + for (int i = 0; i < sub_info.Length; i++) { + VariableInfo sinfo = sub_info[i]; + if (sinfo == null) + continue; + + if (!sinfo.IsAssigned (vector)) + return false; + } + + vector.Set (Offset); + return true; + } + + public bool IsEverAssigned { get; set; } + + public bool IsFullyInitialized (FlowAnalysisContext fc, Location loc) + { + return TypeInfo.IsFullyInitialized (fc, this, loc); + } + + public bool IsStructFieldAssigned (DefiniteAssignmentBitSet vector, string field_name) + { + int field_idx = TypeInfo.GetFieldIndex (field_name); + + if (field_idx == 0) + return true; + + return vector [Offset + field_idx]; + } + + public void SetAssigned (DefiniteAssignmentBitSet vector, bool generatedAssignment) + { + if (Length == 1) + vector.Set (Offset); + else + vector.Set (Offset, Length); + + if (!generatedAssignment) + IsEverAssigned = true; + } + + public void SetStructFieldAssigned (DefiniteAssignmentBitSet vector, string field_name) + { + if (vector [Offset]) + return; + + int field_idx = TypeInfo.GetFieldIndex (field_name); + + if (field_idx == 0) + return; + + var complex_field = TypeInfo.GetStructField (field_name); + if (complex_field != null) { + vector.Set (Offset + complex_field.Offset, complex_field.TotalLength); + } else { + vector.Set (Offset + field_idx); + } + + IsEverAssigned = true; + + // + // Each field must be assigned before setting master bit + // + for (int i = Offset + 1; i < TypeInfo.TotalLength + Offset; i++) { + if (!vector[i]) + return; + } + + // + // Set master struct flag to assigned when all tested struct + // fields have been assigned + // + vector.Set (Offset); + } + + public VariableInfo GetStructFieldInfo (string fieldName) + { + TypeInfo type = TypeInfo.GetStructField (fieldName); + + if (type == null) + return null; + + return new VariableInfo (this, type); + } + + public override string ToString () + { + return String.Format ("Name={0} Offset={1} Length={2} {3})", Name, Offset, Length, TypeInfo); + } + } + + public struct Reachability + { + readonly bool unreachable; + + Reachability (bool unreachable) + { + this.unreachable = unreachable; + } + + public bool IsUnreachable { + get { + return unreachable; + } + } + + public static Reachability CreateUnreachable () + { + return new Reachability (true); + } + + public static Reachability operator & (Reachability a, Reachability b) + { + return new Reachability (a.unreachable && b.unreachable); + } + + public static Reachability operator | (Reachability a, Reachability b) + { + return new Reachability (a.unreachable | b.unreachable); + } + } + + // + // Special version of bit array. Many operations can be simplified because + // we are always dealing with arrays of same sizes + // + public class DefiniteAssignmentBitSet + { + const uint copy_on_write_flag = 1u << 31; + + uint bits; + + // Used when bits overflows + int[] large_bits; + + public static readonly DefiniteAssignmentBitSet Empty = new DefiniteAssignmentBitSet (0); + + public DefiniteAssignmentBitSet (int length) + { + if (length > 31) + large_bits = new int[(length + 31) / 32]; + } + + public DefiniteAssignmentBitSet (DefiniteAssignmentBitSet source) + { + if (source.large_bits != null) { + large_bits = source.large_bits; + bits = source.bits | copy_on_write_flag; + } else { + bits = source.bits & ~copy_on_write_flag; + } + } + + public static DefiniteAssignmentBitSet operator & (DefiniteAssignmentBitSet a, DefiniteAssignmentBitSet b) + { + if (AreEqual (a, b)) + return a; + + DefiniteAssignmentBitSet res; + if (a.large_bits == null) { + res = new DefiniteAssignmentBitSet (a); + res.bits &= (b.bits & ~copy_on_write_flag); + return res; + } + + res = new DefiniteAssignmentBitSet (a); + res.Clone (); + var dest = res.large_bits; + var src = b.large_bits; + for (int i = 0; i < dest.Length; ++i) { + dest[i] &= src[i]; + } + + return res; + } + + public static DefiniteAssignmentBitSet operator | (DefiniteAssignmentBitSet a, DefiniteAssignmentBitSet b) + { + if (AreEqual (a, b)) + return a; + + DefiniteAssignmentBitSet res; + if (a.large_bits == null) { + res = new DefiniteAssignmentBitSet (a); + res.bits |= b.bits; + res.bits &= ~copy_on_write_flag; + return res; + } + + res = new DefiniteAssignmentBitSet (a); + res.Clone (); + var dest = res.large_bits; + var src = b.large_bits; + + for (int i = 0; i < dest.Length; ++i) { + dest[i] |= src[i]; + } + + return res; + } + + public static DefiniteAssignmentBitSet And (List das) + { + if (das.Count == 0) + throw new ArgumentException ("Empty das"); + + DefiniteAssignmentBitSet res = das[0]; + for (int i = 1; i < das.Count; ++i) { + res &= das[i]; + } + + return res; + } + + bool CopyOnWrite { + get { + return (bits & copy_on_write_flag) != 0; + } + } + + int Length { + get { + return large_bits == null ? 31 : large_bits.Length * 32; + } + } + + public void Set (int index) + { + if (CopyOnWrite && !this[index]) + Clone (); + + SetBit (index); + } + + public void Set (int index, int length) + { + for (int i = 0; i < length; ++i) { + if (CopyOnWrite && !this[index + i]) + Clone (); + + SetBit (index + i); + } + } + + public bool this[int index] { + get { + return GetBit (index); + } + } + + public override string ToString () + { + var length = Length; + StringBuilder sb = new StringBuilder (length); + for (int i = 0; i < length; ++i) { + sb.Append (this[i] ? '1' : '0'); + } + + return sb.ToString (); + } + + void Clone () + { + large_bits = (int[]) large_bits.Clone (); + } + + bool GetBit (int index) + { + return large_bits == null ? + (bits & (1 << index)) != 0 : + (large_bits[index >> 5] & (1 << (index & 31))) != 0; + } + + void SetBit (int index) + { + if (large_bits == null) + bits = (uint) ((int) bits | (1 << index)); + else + large_bits[index >> 5] |= (1 << (index & 31)); + } + + static bool AreEqual (DefiniteAssignmentBitSet a, DefiniteAssignmentBitSet b) + { + if (a.large_bits == null) + return (a.bits & ~copy_on_write_flag) == (b.bits & ~copy_on_write_flag); + + for (int i = 0; i < a.large_bits.Length; ++i) { + if (a.large_bits[i] != b.large_bits[i]) + return false; + } + + return true; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/generic.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/generic.cs new file mode 100644 index 000000000..f6d594fd0 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/generic.cs @@ -0,0 +1,3660 @@ +// +// generic.cs: Generics support +// +// Authors: Martin Baulig (martin@ximian.com) +// Miguel de Icaza (miguel@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com) +// Copyright 2004-2008 Novell, Inc +// Copyright 2011 Xamarin, Inc (http://www.xamarin.com) +// + +using System; +using System.Collections.Generic; +using System.Text; +using System.Linq; + +#if STATIC +using MetaType = IKVM.Reflection.Type; +using IKVM.Reflection; +using IKVM.Reflection.Emit; +#else +using MetaType = System.Type; +using System.Reflection; +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp { + public class VarianceDecl + { + public VarianceDecl (Variance variance, Location loc) + { + this.Variance = variance; + this.Location = loc; + } + + public Variance Variance { get; private set; } + public Location Location { get; private set; } + + public static Variance CheckTypeVariance (TypeSpec t, Variance expected, IMemberContext member) + { + var tp = t as TypeParameterSpec; + if (tp != null) { + var v = tp.Variance; + if (expected == Variance.None && v != expected || + expected == Variance.Covariant && v == Variance.Contravariant || + expected == Variance.Contravariant && v == Variance.Covariant) { + ((TypeParameter) tp.MemberDefinition).ErrorInvalidVariance (member, expected); + } + + return expected; + } + + if (t.TypeArguments.Length > 0) { + var targs_definition = t.MemberDefinition.TypeParameters; + TypeSpec[] targs = TypeManager.GetTypeArguments (t); + for (int i = 0; i < targs.Length; ++i) { + var v = targs_definition[i].Variance; + CheckTypeVariance (targs[i], (Variance) ((int) v * (int) expected), member); + } + + return expected; + } + + var ac = t as ArrayContainer; + if (ac != null) + return CheckTypeVariance (ac.Element, expected, member); + + return Variance.None; + } + } + + public enum Variance + { + // + // Don't add or modify internal values, they are used as -/+ calculation signs + // + None = 0, + Covariant = 1, + Contravariant = -1 + } + + [Flags] + public enum SpecialConstraint + { + None = 0, + Constructor = 1 << 2, + Class = 1 << 3, + Struct = 1 << 4 + } + + public class SpecialContraintExpr : FullNamedExpression + { + public SpecialContraintExpr (SpecialConstraint constraint, Location loc) + { + this.loc = loc; + this.Constraint = constraint; + } + + public SpecialConstraint Constraint { get; private set; } + + protected override Expression DoResolve (ResolveContext rc) + { + throw new NotImplementedException (); + } + + public override FullNamedExpression ResolveAsTypeOrNamespace (IMemberContext mc, bool allowUnboundTypeArguments) + { + throw new NotImplementedException (); + } + } + + // + // A set of parsed constraints for a type parameter + // + public class Constraints + { + readonly SimpleMemberName tparam; + readonly List constraints; + readonly Location loc; + bool resolved; + bool resolving; + + public IEnumerable ConstraintExpressions { + get { + return constraints; + } + } + + public Constraints (SimpleMemberName tparam, List constraints, Location loc) + { + this.tparam = tparam; + this.constraints = constraints; + this.loc = loc; + } + + #region Properties + + public List TypeExpressions { + get { + return constraints; + } + } + + public Location Location { + get { + return loc; + } + } + + public SimpleMemberName TypeParameter { + get { + return tparam; + } + } + + #endregion + + public static bool CheckConflictingInheritedConstraint (TypeParameterSpec spec, TypeSpec bb, IMemberContext context, Location loc) + { + if (spec.HasSpecialClass && bb.IsStruct) { + context.Module.Compiler.Report.Error (455, loc, + "Type parameter `{0}' inherits conflicting constraints `{1}' and `{2}'", + spec.Name, "class", bb.GetSignatureForError ()); + + return false; + } + + return CheckConflictingInheritedConstraint (spec, spec.BaseType, bb, context, loc); + } + + static bool CheckConflictingInheritedConstraint (TypeParameterSpec spec, TypeSpec ba, TypeSpec bb, IMemberContext context, Location loc) + { + if (ba == bb) + return true; + + if (TypeSpec.IsBaseClass (ba, bb, false) || TypeSpec.IsBaseClass (bb, ba, false)) + return true; + + Error_ConflictingConstraints (context, spec, ba, bb, loc); + return false; + } + + public static void Error_ConflictingConstraints (IMemberContext context, TypeParameterSpec tp, TypeSpec ba, TypeSpec bb, Location loc) + { + context.Module.Compiler.Report.Error (455, loc, + "Type parameter `{0}' inherits conflicting constraints `{1}' and `{2}'", + tp.Name, ba.GetSignatureForError (), bb.GetSignatureForError ()); + } + + public void CheckGenericConstraints (IMemberContext context, bool obsoleteCheck) + { + foreach (var c in constraints) { + if (c == null) + continue; + + var t = c.Type; + if (t == null) + continue; + + if (obsoleteCheck) { + ObsoleteAttribute obsolete_attr = t.GetAttributeObsolete (); + if (obsolete_attr != null) + AttributeTester.Report_ObsoleteMessage (obsolete_attr, t.GetSignatureForError (), c.Location, context.Module.Compiler.Report); + } + + ConstraintChecker.Check (context, t, c.Location); + } + } + + // + // Resolve the constraints types with only possible early checks, return + // value `false' is reserved for recursive failure + // + public bool Resolve (IMemberContext context, TypeParameter tp) + { + if (resolved) + return true; + + if (resolving) + return false; + + resolving = true; + var spec = tp.Type; + List tparam_types = null; + bool iface_found = false; + + spec.BaseType = context.Module.Compiler.BuiltinTypes.Object; + + for (int i = 0; i < constraints.Count; ++i) { + var constraint = constraints[i]; + + if (constraint is SpecialContraintExpr) { + spec.SpecialConstraint |= ((SpecialContraintExpr) constraint).Constraint; + if (spec.HasSpecialStruct) + spec.BaseType = context.Module.Compiler.BuiltinTypes.ValueType; + + // Set to null as it does not have a type + constraints[i] = null; + continue; + } + + var type = constraint.ResolveAsType (context); + if (type == null) + continue; + + if (type.Arity > 0 && ((InflatedTypeSpec) type).HasDynamicArgument ()) { + context.Module.Compiler.Report.Error (1968, constraint.Location, + "A constraint cannot be the dynamic type `{0}'", type.GetSignatureForError ()); + continue; + } + + if (!context.CurrentMemberDefinition.IsAccessibleAs (type)) { + context.Module.Compiler.Report.SymbolRelatedToPreviousError (type); + context.Module.Compiler.Report.Error (703, loc, + "Inconsistent accessibility: constraint type `{0}' is less accessible than `{1}'", + type.GetSignatureForError (), context.GetSignatureForError ()); + } + + if (type.IsInterface) { + if (!spec.AddInterface (type)) { + context.Module.Compiler.Report.Error (405, constraint.Location, + "Duplicate constraint `{0}' for type parameter `{1}'", type.GetSignatureForError (), tparam.Value); + } + + iface_found = true; + continue; + } + + var constraint_tp = type as TypeParameterSpec; + if (constraint_tp != null) { + if (tparam_types == null) { + tparam_types = new List (2); + } else if (tparam_types.Contains (constraint_tp)) { + context.Module.Compiler.Report.Error (405, constraint.Location, + "Duplicate constraint `{0}' for type parameter `{1}'", type.GetSignatureForError (), tparam.Value); + continue; + } + + // + // Checks whether each generic method parameter constraint type + // is valid with respect to T + // + if (tp.IsMethodTypeParameter) { + VarianceDecl.CheckTypeVariance (type, Variance.Contravariant, context); + } + + var tp_def = constraint_tp.MemberDefinition as TypeParameter; + if (tp_def != null && !tp_def.ResolveConstraints (context)) { + context.Module.Compiler.Report.Error (454, constraint.Location, + "Circular constraint dependency involving `{0}' and `{1}'", + constraint_tp.GetSignatureForError (), tp.GetSignatureForError ()); + continue; + } + + // + // Checks whether there are no conflicts between type parameter constraints + // + // class Foo + // where T : A + // where U : B, T + // + // A and B are not convertible and only 1 class constraint is allowed + // + if (constraint_tp.HasTypeConstraint) { + if (spec.HasTypeConstraint || spec.HasSpecialStruct) { + if (!CheckConflictingInheritedConstraint (spec, constraint_tp.BaseType, context, constraint.Location)) + continue; + } else { + for (int ii = 0; ii < tparam_types.Count; ++ii) { + if (!tparam_types[ii].HasTypeConstraint) + continue; + + if (!CheckConflictingInheritedConstraint (spec, tparam_types[ii].BaseType, constraint_tp.BaseType, context, constraint.Location)) + break; + } + } + } + + if (constraint_tp.TypeArguments != null) { + var eb = constraint_tp.GetEffectiveBase (); + if (eb != null && !CheckConflictingInheritedConstraint (spec, eb, spec.BaseType, context, constraint.Location)) + break; + } + + if (constraint_tp.HasSpecialStruct) { + context.Module.Compiler.Report.Error (456, constraint.Location, + "Type parameter `{0}' has the `struct' constraint, so it cannot be used as a constraint for `{1}'", + constraint_tp.GetSignatureForError (), tp.GetSignatureForError ()); + continue; + } + + tparam_types.Add (constraint_tp); + continue; + } + + if (iface_found || spec.HasTypeConstraint) { + context.Module.Compiler.Report.Error (406, constraint.Location, + "The class type constraint `{0}' must be listed before any other constraints. Consider moving type constraint to the beginning of the constraint list", + type.GetSignatureForError ()); + } + + if (spec.HasSpecialStruct || spec.HasSpecialClass) { + context.Module.Compiler.Report.Error (450, constraint.Location, + "`{0}': cannot specify both a constraint class and the `class' or `struct' constraint", + type.GetSignatureForError ()); + } + + switch (type.BuiltinType) { + case BuiltinTypeSpec.Type.Array: + case BuiltinTypeSpec.Type.Delegate: + case BuiltinTypeSpec.Type.MulticastDelegate: + case BuiltinTypeSpec.Type.Enum: + case BuiltinTypeSpec.Type.ValueType: + case BuiltinTypeSpec.Type.Object: + context.Module.Compiler.Report.Error (702, constraint.Location, + "A constraint cannot be special class `{0}'", type.GetSignatureForError ()); + continue; + case BuiltinTypeSpec.Type.Dynamic: + context.Module.Compiler.Report.Error (1967, constraint.Location, + "A constraint cannot be the dynamic type"); + continue; + } + + if (type.IsSealed || !type.IsClass) { + context.Module.Compiler.Report.Error (701, loc, + "`{0}' is not a valid constraint. A constraint must be an interface, a non-sealed class or a type parameter", + type.GetSignatureForError ()); + continue; + } + + if (type.IsStatic) { + context.Module.Compiler.Report.Error (717, constraint.Location, + "`{0}' is not a valid constraint. Static classes cannot be used as constraints", + type.GetSignatureForError ()); + } + + spec.BaseType = type; + } + + if (tparam_types != null) + spec.TypeArguments = tparam_types.ToArray (); + + resolving = false; + resolved = true; + return true; + } + + public void VerifyClsCompliance (Report report) + { + foreach (var c in constraints) + { + if (c == null) + continue; + + if (!c.Type.IsCLSCompliant ()) { + report.SymbolRelatedToPreviousError (c.Type); + report.Warning (3024, 1, loc, "Constraint type `{0}' is not CLS-compliant", + c.Type.GetSignatureForError ()); + } + } + } + } + + // + // A type parameter for a generic type or generic method definition + // + public class TypeParameter : MemberCore, ITypeDefinition + { + static readonly string[] attribute_target = { "type parameter" }; + + Constraints constraints; + GenericTypeParameterBuilder builder; + readonly TypeParameterSpec spec; + + public TypeParameter (int index, MemberName name, Constraints constraints, Attributes attrs, Variance Variance) + : base (null, name, attrs) + { + this.constraints = constraints; + this.spec = new TypeParameterSpec (null, index, this, SpecialConstraint.None, Variance, null); + } + + // + // Used by parser + // + public TypeParameter (MemberName name, Attributes attrs, VarianceDecl variance) + : base (null, name, attrs) + { + var var = variance == null ? Variance.None : variance.Variance; + this.spec = new TypeParameterSpec (null, -1, this, SpecialConstraint.None, var, null); + this.VarianceDecl = variance; + } + + public TypeParameter (TypeParameterSpec spec, TypeSpec parentSpec, MemberName name, Attributes attrs) + : base (null, name, attrs) + { + this.spec = new TypeParameterSpec (parentSpec, spec.DeclaredPosition, spec.MemberDefinition, spec.SpecialConstraint, spec.Variance, null) { + BaseType = spec.BaseType, + InterfacesDefined = spec.InterfacesDefined, + TypeArguments = spec.TypeArguments + }; + } + + #region Properties + + public override AttributeTargets AttributeTargets { + get { + return AttributeTargets.GenericParameter; + } + } + + public Constraints Constraints { + get { + return constraints; + } + set { + constraints = value; + } + } + + public IAssemblyDefinition DeclaringAssembly { + get { + return Module.DeclaringAssembly; + } + } + + public override string DocCommentHeader { + get { + throw new InvalidOperationException ( + "Unexpected attempt to get doc comment from " + this.GetType ()); + } + } + + bool ITypeDefinition.IsComImport { + get { + return false; + } + } + + bool ITypeDefinition.IsPartial { + get { + return false; + } + } + + public bool IsMethodTypeParameter { + get { + return spec.IsMethodOwned; + } + } + + bool ITypeDefinition.IsTypeForwarder { + get { + return false; + } + } + + bool ITypeDefinition.IsCyclicTypeForwarder { + get { + return false; + } + } + + public string Name { + get { + return MemberName.Name; + } + } + + public string Namespace { + get { + return null; + } + } + + public TypeParameterSpec Type { + get { + return spec; + } + } + + public int TypeParametersCount { + get { + return 0; + } + } + + public TypeParameterSpec[] TypeParameters { + get { + return null; + } + } + + public override string[] ValidAttributeTargets { + get { + return attribute_target; + } + } + + public Variance Variance { + get { + return spec.Variance; + } + } + + public VarianceDecl VarianceDecl { get; private set; } + + #endregion + + // + // This is called for each part of a partial generic type definition. + // + // If partial type parameters constraints are not null and we don't + // already have constraints they become our constraints. If we already + // have constraints, we must check that they're same. + // + public bool AddPartialConstraints (TypeDefinition part, TypeParameter tp) + { + if (builder == null) + throw new InvalidOperationException (); + + var new_constraints = tp.constraints; + if (new_constraints == null) + return true; + + // TODO: could create spec only + //tp.Define (null, -1, part.Definition); + tp.spec.DeclaringType = part.Definition; + if (!tp.ResolveConstraints (part)) + return false; + + if (constraints != null) + return spec.HasSameConstraintsDefinition (tp.Type); + + // Copy constraint from resolved part to partial container + spec.SpecialConstraint = tp.spec.SpecialConstraint; + spec.InterfacesDefined = tp.spec.InterfacesDefined; + spec.TypeArguments = tp.spec.TypeArguments; + spec.BaseType = tp.spec.BaseType; + + return true; + } + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + builder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), cdata); + } + + public void CheckGenericConstraints (bool obsoleteCheck) + { + if (constraints != null) + constraints.CheckGenericConstraints (this, obsoleteCheck); + } + + public TypeParameter CreateHoistedCopy (TypeSpec declaringSpec) + { + return new TypeParameter (spec, declaringSpec, MemberName, null); + } + + public override bool Define () + { + return true; + } + + // + // This is the first method which is called during the resolving + // process; we're called immediately after creating the type parameters + // with SRE (by calling `DefineGenericParameters()' on the TypeBuilder / + // MethodBuilder). + // + public void Create (TypeSpec declaringType, TypeContainer parent) + { + if (builder != null) + throw new InternalErrorException (); + + // Needed to get compiler reference + this.Parent = parent; + spec.DeclaringType = declaringType; + } + + public void Define (GenericTypeParameterBuilder type) + { + this.builder = type; + spec.SetMetaInfo (type); + } + + public void Define (TypeParameter tp) + { + builder = tp.builder; + } + + public void EmitConstraints (GenericTypeParameterBuilder builder) + { + var attr = GenericParameterAttributes.None; + if (spec.Variance == Variance.Contravariant) + attr |= GenericParameterAttributes.Contravariant; + else if (spec.Variance == Variance.Covariant) + attr |= GenericParameterAttributes.Covariant; + + if (spec.HasSpecialClass) + attr |= GenericParameterAttributes.ReferenceTypeConstraint; + else if (spec.HasSpecialStruct) + attr |= GenericParameterAttributes.NotNullableValueTypeConstraint | GenericParameterAttributes.DefaultConstructorConstraint; + + if (spec.HasSpecialConstructor) + attr |= GenericParameterAttributes.DefaultConstructorConstraint; + + if (spec.BaseType.BuiltinType != BuiltinTypeSpec.Type.Object) + builder.SetBaseTypeConstraint (spec.BaseType.GetMetaInfo ()); + + if (spec.InterfacesDefined != null) + builder.SetInterfaceConstraints (spec.InterfacesDefined.Select (l => l.GetMetaInfo ()).ToArray ()); + + if (spec.TypeArguments != null) { + var meta_constraints = new List (spec.TypeArguments.Length); + foreach (var c in spec.TypeArguments) { + // + // Inflated type parameters can collide with special constraint types, don't + // emit any such type parameter. + // + if (c.BuiltinType == BuiltinTypeSpec.Type.Object || c.BuiltinType == BuiltinTypeSpec.Type.ValueType) + continue; + + meta_constraints.Add (c.GetMetaInfo ()); + } + + builder.SetInterfaceConstraints (meta_constraints.ToArray ()); + } + + builder.SetGenericParameterAttributes (attr); + } + + public override void Emit () + { + EmitConstraints (builder); + + if (OptAttributes != null) + OptAttributes.Emit (); + + base.Emit (); + } + + public void ErrorInvalidVariance (IMemberContext mc, Variance expected) + { + Report.SymbolRelatedToPreviousError (mc.CurrentMemberDefinition); + string input_variance = Variance == Variance.Contravariant ? "contravariant" : "covariant"; + string gtype_variance; + switch (expected) { + case Variance.Contravariant: gtype_variance = "contravariantly"; break; + case Variance.Covariant: gtype_variance = "covariantly"; break; + default: gtype_variance = "invariantly"; break; + } + + Delegate d = mc as Delegate; + string parameters = d != null ? d.Parameters.GetSignatureForError () : ""; + + Report.Error (1961, Location, + "The {2} type parameter `{0}' must be {3} valid on `{1}{4}'", + GetSignatureForError (), mc.GetSignatureForError (), input_variance, gtype_variance, parameters); + } + + public TypeSpec GetAttributeCoClass () + { + return null; + } + + public string GetAttributeDefaultMember () + { + throw new NotSupportedException (); + } + + public AttributeUsageAttribute GetAttributeUsage (PredefinedAttribute pa) + { + throw new NotSupportedException (); + } + + public override string GetSignatureForDocumentation () + { + throw new NotImplementedException (); + } + + public override string GetSignatureForError () + { + return MemberName.Name; + } + + bool ITypeDefinition.IsInternalAsPublic (IAssemblyDefinition assembly) + { + return spec.MemberDefinition.DeclaringAssembly == assembly; + } + + public void LoadMembers (TypeSpec declaringType, bool onlyTypes, ref MemberCache cache) + { + throw new NotSupportedException ("Not supported for compiled definition"); + } + + // + // Resolves all type parameter constraints + // + public bool ResolveConstraints (IMemberContext context) + { + if (constraints != null) + return constraints.Resolve (context, this); + + if (spec.BaseType == null) + spec.BaseType = context.Module.Compiler.BuiltinTypes.Object; + + return true; + } + + public override bool IsClsComplianceRequired () + { + return false; + } + + public new void VerifyClsCompliance () + { + if (constraints != null) + constraints.VerifyClsCompliance (Report); + } + + public void WarningParentNameConflict (TypeParameter conflict) + { + conflict.Report.SymbolRelatedToPreviousError (conflict.Location, null); + conflict.Report.Warning (693, 3, Location, + "Type parameter `{0}' has the same name as the type parameter from outer type `{1}'", + GetSignatureForError (), conflict.CurrentType.GetSignatureForError ()); + } + } + + [System.Diagnostics.DebuggerDisplay ("{DisplayDebugInfo()}")] + public class TypeParameterSpec : TypeSpec + { + public static readonly new TypeParameterSpec[] EmptyTypes = new TypeParameterSpec[0]; + + Variance variance; + SpecialConstraint spec; + int tp_pos; + TypeSpec[] targs; + TypeSpec[] ifaces_defined; + TypeSpec effective_base; + + // + // Creates type owned type parameter + // + public TypeParameterSpec (TypeSpec declaringType, int index, ITypeDefinition definition, SpecialConstraint spec, Variance variance, MetaType info) + : base (MemberKind.TypeParameter, declaringType, definition, info, Modifiers.PUBLIC) + { + this.variance = variance; + this.spec = spec; + state &= ~StateFlags.Obsolete_Undetected; + tp_pos = index; + } + + // + // Creates method owned type parameter + // + public TypeParameterSpec (int index, ITypeDefinition definition, SpecialConstraint spec, Variance variance, MetaType info) + : this (null, index, definition, spec, variance, info) + { + } + + #region Properties + + public int DeclaredPosition { + get { + return tp_pos; + } + set { + tp_pos = value; + } + } + + public bool HasSpecialConstructor { + get { + return (spec & SpecialConstraint.Constructor) != 0; + } + } + + public bool HasSpecialClass { + get { + return (spec & SpecialConstraint.Class) != 0; + } + } + + public bool HasSpecialStruct { + get { + return (spec & SpecialConstraint.Struct) != 0; + } + } + + public bool HasAnyTypeConstraint { + get { + return (spec & (SpecialConstraint.Class | SpecialConstraint.Struct)) != 0 || ifaces != null || targs != null || HasTypeConstraint; + } + } + + public bool HasTypeConstraint { + get { + var bt = BaseType.BuiltinType; + return bt != BuiltinTypeSpec.Type.Object && bt != BuiltinTypeSpec.Type.ValueType; + } + } + + public override IList Interfaces { + get { + if ((state & StateFlags.InterfacesExpanded) == 0) { + if (ifaces != null) { + if (ifaces_defined == null) + ifaces_defined = ifaces.ToArray (); + + for (int i = 0; i < ifaces_defined.Length; ++i ) { + var iface_type = ifaces_defined[i]; + var td = iface_type.MemberDefinition as TypeDefinition; + if (td != null) + td.DoExpandBaseInterfaces (); + + if (iface_type.Interfaces != null) { + for (int ii = 0; ii < iface_type.Interfaces.Count; ++ii) { + var ii_iface_type = iface_type.Interfaces [ii]; + AddInterface (ii_iface_type); + } + } + } + } else if (ifaces_defined == null) { + ifaces_defined = ifaces == null ? TypeSpec.EmptyTypes : ifaces.ToArray (); + } + + // + // Include all base type interfaces too, see ImportTypeBase for details + // + if (BaseType != null) { + var td = BaseType.MemberDefinition as TypeDefinition; + if (td != null) + td.DoExpandBaseInterfaces (); + + if (BaseType.Interfaces != null) { + foreach (var iface in BaseType.Interfaces) { + AddInterface (iface); + } + } + } + + state |= StateFlags.InterfacesExpanded; + } + + return ifaces; + } + } + + // + // Unexpanded interfaces list + // + public TypeSpec[] InterfacesDefined { + get { + if (ifaces_defined == null) { + ifaces_defined = ifaces == null ? TypeSpec.EmptyTypes : ifaces.ToArray (); + } + + return ifaces_defined.Length == 0 ? null : ifaces_defined; + } + set { + ifaces_defined = value; + if (value != null && value.Length != 0) + ifaces = new List (value); + } + } + + public bool IsConstrained { + get { + return spec != SpecialConstraint.None || ifaces != null || targs != null || HasTypeConstraint; + } + } + + // + // Returns whether the type parameter is known to be a reference type + // + public new bool IsReferenceType { + get { + if ((spec & (SpecialConstraint.Class | SpecialConstraint.Struct)) != 0) + return (spec & SpecialConstraint.Class) != 0; + + // + // Full check is needed (see IsValueType for details) + // + if (HasTypeConstraint && TypeSpec.IsReferenceType (BaseType)) + return true; + + if (targs != null) { + foreach (var ta in targs) { + // + // Secondary special constraints are ignored (I am not sure why) + // + var tp = ta as TypeParameterSpec; + if (tp != null && (tp.spec & (SpecialConstraint.Class | SpecialConstraint.Struct)) != 0) + continue; + + if (TypeSpec.IsReferenceType (ta)) + return true; + } + } + + return false; + } + } + + // + // Returns whether the type parameter is known to be a value type + // + public new bool IsValueType { + get { + // + // Even if structs/enums cannot be used directly as constraints + // they can apear as constraint type when inheriting base constraint + // which has dependant type parameter constraint which has been + // inflated using value type + // + // class A : B { override void Foo () {} } + // class B { virtual void Foo () where U : T {} } + // + if (HasSpecialStruct) + return true; + + if (targs != null) { + foreach (var ta in targs) { + if (TypeSpec.IsValueType (ta)) + return true; + } + } + + return false; + } + } + + public override string Name { + get { + return definition.Name; + } + } + + public bool IsMethodOwned { + get { + return DeclaringType == null; + } + } + + public SpecialConstraint SpecialConstraint { + get { + return spec; + } + set { + spec = value; + } + } + + // + // Types used to inflate the generic type + // + public new TypeSpec[] TypeArguments { + get { + return targs; + } + set { + targs = value; + } + } + + public Variance Variance { + get { + return variance; + } + } + + #endregion + + public string DisplayDebugInfo () + { + var s = GetSignatureForError (); + return IsMethodOwned ? s + "!!" : s + "!"; + } + + // + // Finds effective base class. The effective base class is always a class-type + // + public TypeSpec GetEffectiveBase () + { + if (HasSpecialStruct) + return BaseType; + + // + // If T has a class-type constraint C but no type-parameter constraints, its effective base class is C + // + if (BaseType != null && targs == null) { + // + // If T has a constraint V that is a value-type, use instead the most specific base type of V that is a class-type. + // + // LAMESPEC: Is System.ValueType always the most specific base type in this case? + // + // Note: This can never happen in an explicitly given constraint, but may occur when the constraints of a generic method + // are implicitly inherited by an overriding method declaration or an explicit implementation of an interface method. + // + return BaseType.IsStruct ? BaseType.BaseType : BaseType; + } + + if (effective_base != null) + return effective_base; + + var types = new TypeSpec [HasTypeConstraint ? targs.Length + 1 : targs.Length]; + + for (int i = 0; i < targs.Length; ++i) { + var t = targs [i]; + + // Same issue as above, inherited constraints can be of struct type + if (t.IsStruct) { + types [i] = t.BaseType; + continue; + } + + var tps = t as TypeParameterSpec; + types [i] = tps != null ? tps.GetEffectiveBase () : t; + } + + if (HasTypeConstraint) + types [types.Length - 1] = BaseType; + + return effective_base = Convert.FindMostEncompassedType (types); + } + + public override string GetSignatureForDocumentation () + { + var prefix = IsMethodOwned ? "``" : "`"; + return prefix + DeclaredPosition; + } + + public override string GetSignatureForError () + { + return Name; + } + + // + // Constraints have to match by definition but not position, used by + // partial classes or methods + // + public bool HasSameConstraintsDefinition (TypeParameterSpec other) + { + if (spec != other.spec) + return false; + + if (BaseType != other.BaseType) + return false; + + if (!TypeSpecComparer.Override.IsSame (InterfacesDefined, other.InterfacesDefined)) + return false; + + if (!TypeSpecComparer.Override.IsSame (targs, other.targs)) + return false; + + return true; + } + + // + // Constraints have to match by using same set of types, used by + // implicit interface implementation + // + public bool HasSameConstraintsImplementation (TypeParameterSpec other) + { + if (spec != other.spec) + return false; + + // + // It can be same base type or inflated type parameter + // + // interface I { void Foo where U : T; } + // class A : I { void Foo where X : int {} } + // + bool found; + if (!TypeSpecComparer.Override.IsEqual (BaseType, other.BaseType)) { + if (other.targs == null) + return false; + + found = false; + foreach (var otarg in other.targs) { + if (TypeSpecComparer.Override.IsEqual (BaseType, otarg)) { + found = true; + break; + } + } + + if (!found) + return false; + } + + // Check interfaces implementation -> definition + if (InterfacesDefined != null) { + // + // Iterate over inflated interfaces + // + foreach (var iface in Interfaces) { + found = false; + if (other.InterfacesDefined != null) { + foreach (var oiface in other.Interfaces) { + if (TypeSpecComparer.Override.IsEqual (iface, oiface)) { + found = true; + break; + } + } + } + + if (found) + continue; + + if (other.targs != null) { + foreach (var otarg in other.targs) { + if (TypeSpecComparer.Override.IsEqual (iface, otarg)) { + found = true; + break; + } + } + } + + if (!found) + return false; + } + } + + // Check interfaces implementation <- definition + if (other.InterfacesDefined != null) { + if (InterfacesDefined == null) + return false; + + // + // Iterate over inflated interfaces + // + foreach (var oiface in other.Interfaces) { + found = false; + foreach (var iface in Interfaces) { + if (TypeSpecComparer.Override.IsEqual (iface, oiface)) { + found = true; + break; + } + } + + if (!found) + return false; + } + } + + // Check type parameters implementation -> definition + if (targs != null) { + if (other.targs == null) + return false; + + foreach (var targ in targs) { + found = false; + foreach (var otarg in other.targs) { + if (TypeSpecComparer.Override.IsEqual (targ, otarg)) { + found = true; + break; + } + } + + if (!found) + return false; + } + } + + // Check type parameters implementation <- definition + if (other.targs != null) { + foreach (var otarg in other.targs) { + // Ignore inflated type arguments, were checked above + if (!otarg.IsGenericParameter) + continue; + + if (targs == null) + return false; + + found = false; + foreach (var targ in targs) { + if (TypeSpecComparer.Override.IsEqual (targ, otarg)) { + found = true; + break; + } + } + + if (!found) + return false; + } + } + + return true; + } + + public static TypeParameterSpec[] InflateConstraints (TypeParameterInflator inflator, TypeParameterSpec[] tparams) + { + return InflateConstraints (tparams, l => l, inflator); + } + + public static TypeParameterSpec[] InflateConstraints (TypeParameterSpec[] tparams, Func inflatorFactory, T arg) + { + TypeParameterSpec[] constraints = null; + TypeParameterInflator? inflator = null; + + for (int i = 0; i < tparams.Length; ++i) { + var tp = tparams[i]; + if (tp.HasTypeConstraint || tp.InterfacesDefined != null || tp.TypeArguments != null) { + if (constraints == null) { + constraints = new TypeParameterSpec[tparams.Length]; + Array.Copy (tparams, constraints, constraints.Length); + } + + // + // Using a factory to avoid possibly expensive inflator build up + // + if (inflator == null) + inflator = inflatorFactory (arg); + + constraints[i] = (TypeParameterSpec) constraints[i].InflateMember (inflator.Value); + } + } + + if (constraints == null) + constraints = tparams; + + return constraints; + } + + public void InflateConstraints (TypeParameterInflator inflator, TypeParameterSpec tps) + { + tps.BaseType = inflator.Inflate (BaseType); + + var defined = InterfacesDefined; + if (defined != null) { + tps.ifaces_defined = new TypeSpec[defined.Length]; + for (int i = 0; i < defined.Length; ++i) + tps.ifaces_defined [i] = inflator.Inflate (defined[i]); + } else if (ifaces_defined == TypeSpec.EmptyTypes) { + tps.ifaces_defined = TypeSpec.EmptyTypes; + } + + var ifaces = Interfaces; + if (ifaces != null) { + tps.ifaces = new List (ifaces.Count); + for (int i = 0; i < ifaces.Count; ++i) + tps.ifaces.Add (inflator.Inflate (ifaces[i])); + tps.state |= StateFlags.InterfacesExpanded; + } + + if (targs != null) { + tps.targs = new TypeSpec[targs.Length]; + for (int i = 0; i < targs.Length; ++i) + tps.targs[i] = inflator.Inflate (targs[i]); + } + } + + public override MemberSpec InflateMember (TypeParameterInflator inflator) + { + var tps = (TypeParameterSpec) MemberwiseClone (); +#if DEBUG + tps.ID += 1000000; +#endif + + InflateConstraints (inflator, tps); + return tps; + } + + // + // Populates type parameter members using type parameter constraints + // The trick here is to be called late enough but not too late to + // populate member cache with all members from other types + // + protected override void InitializeMemberCache (bool onlyTypes) + { + cache = new MemberCache (); + + // + // For a type parameter the membercache is the union of the sets of members of the types + // specified as a primary constraint or secondary constraint + // + if (BaseType.BuiltinType != BuiltinTypeSpec.Type.Object && BaseType.BuiltinType != BuiltinTypeSpec.Type.ValueType) + cache.AddBaseType (BaseType); + + if (InterfacesDefined != null) { + foreach (var iface_type in InterfacesDefined) { + cache.AddInterface (iface_type); + } + } + + if (targs != null) { + foreach (var ta in targs) { + var tps = ta as TypeParameterSpec; + IList ifaces; + if (tps != null) { + var b_type = tps.GetEffectiveBase (); + if (b_type != null && b_type.BuiltinType != BuiltinTypeSpec.Type.Object && b_type.BuiltinType != BuiltinTypeSpec.Type.ValueType) + cache.AddBaseType (b_type); + + ifaces = tps.InterfacesDefined; + } else { + ifaces = ta.Interfaces; + } + + if (ifaces != null) { + foreach (var iface_type in ifaces) { + cache.AddInterface (iface_type); + } + } + } + } + } + + public bool IsConvertibleToInterface (TypeSpec iface) + { + if (Interfaces != null) { + foreach (var t in Interfaces) { + if (t == iface) + return true; + } + } + + if (TypeArguments != null) { + foreach (var t in TypeArguments) { + var tps = t as TypeParameterSpec; + if (tps != null) { + if (tps.IsConvertibleToInterface (iface)) + return true; + + continue; + } + + if (t.ImplementsInterface (iface, false)) + return true; + } + } + + return false; + } + + public static bool HasAnyTypeParameterTypeConstrained (IGenericMethodDefinition md) + { + var tps = md.TypeParameters; + for (int i = 0; i < md.TypeParametersCount; ++i) { + if (tps[i].HasAnyTypeConstraint) { + return true; + } + } + + return false; + } + + public static bool HasAnyTypeParameterConstrained (IGenericMethodDefinition md) + { + var tps = md.TypeParameters; + for (int i = 0; i < md.TypeParametersCount; ++i) { + if (tps[i].IsConstrained) { + return true; + } + } + + return false; + } + + public bool HasDependencyOn (TypeSpec type) + { + if (TypeArguments != null) { + foreach (var targ in TypeArguments) { + if (TypeSpecComparer.Override.IsEqual (targ, type)) + return true; + + var tps = targ as TypeParameterSpec; + if (tps != null && tps.HasDependencyOn (type)) + return true; + } + } + + return false; + } + + public override TypeSpec Mutate (TypeParameterMutator mutator) + { + return mutator.Mutate (this); + } + } + + public struct TypeParameterInflator + { + readonly TypeSpec type; + readonly TypeParameterSpec[] tparams; + readonly TypeSpec[] targs; + readonly IModuleContext context; + + public TypeParameterInflator (TypeParameterInflator nested, TypeSpec type) + : this (nested.context, type, nested.tparams, nested.targs) + { + } + + public TypeParameterInflator (IModuleContext context, TypeSpec type, TypeParameterSpec[] tparams, TypeSpec[] targs) + { + if (tparams.Length != targs.Length) + throw new ArgumentException ("Invalid arguments"); + + this.context = context; + this.tparams = tparams; + this.targs = targs; + this.type = type; + } + + #region Properties + + public IModuleContext Context { + get { + return context; + } + } + + public TypeSpec TypeInstance { + get { + return type; + } + } + + // + // Type parameters to inflate + // + public TypeParameterSpec[] TypeParameters { + get { + return tparams; + } + } + + #endregion + + public TypeSpec Inflate (TypeSpec type) + { + var tp = type as TypeParameterSpec; + if (tp != null) + return Inflate (tp); + + var ec = type as ElementTypeSpec; + if (ec != null) { + var et = Inflate (ec.Element); + if (et != ec.Element) { + var ac = ec as ArrayContainer; + if (ac != null) + return ArrayContainer.MakeType (context.Module, et, ac.Rank); + + if (ec is PointerContainer) + return PointerContainer.MakeType (context.Module, et); + + throw new NotImplementedException (); + } + + return ec; + } + + if (type.Kind == MemberKind.MissingType) + return type; + + // + // When inflating a nested type, inflate its parent first + // in case it's using same type parameters (was inflated within the type) + // + TypeSpec[] targs; + int i = 0; + if (type.IsNested) { + var parent = Inflate (type.DeclaringType); + + // + // Keep the inflated type arguments + // + targs = type.TypeArguments; + + // + // When inflating imported nested type used inside same declaring type, we get TypeSpec + // because the import cache helps us to catch it. However, that means we have to look at + // type definition to get type argument (they are in fact type parameter in this case) + // + if (targs.Length == 0 && type.Arity > 0) + targs = type.MemberDefinition.TypeParameters; + + // + // Parent was inflated, find the same type on inflated type + // to use same cache for nested types on same generic parent + // + type = MemberCache.FindNestedType (parent, type.Name, type.Arity); + + // + // Handle the tricky case where parent shares local type arguments + // which means inflating inflated type + // + // class Test { + // public static Nested Foo () { return null; } + // + // public class Nested {} + // } + // + // return type of Test.Foo() has to be Test.Nested + // + if (targs.Length > 0) { + var inflated_targs = new TypeSpec[targs.Length]; + for (; i < targs.Length; ++i) + inflated_targs[i] = Inflate (targs[i]); + + type = type.MakeGenericType (context, inflated_targs); + } + + return type; + } + + // Nothing to do for non-generic type + if (type.Arity == 0) + return type; + + targs = new TypeSpec[type.Arity]; + + // + // Inflating using outside type arguments, var v = new Foo (), class Foo {} + // + if (type is InflatedTypeSpec) { + for (; i < targs.Length; ++i) + targs[i] = Inflate (type.TypeArguments[i]); + + type = type.GetDefinition (); + } else { + // + // Inflating parent using inside type arguments, class Foo { ITest foo; } + // + var args = type.MemberDefinition.TypeParameters; + foreach (var ds_tp in args) + targs[i++] = Inflate (ds_tp); + } + + return type.MakeGenericType (context, targs); + } + + public TypeSpec Inflate (TypeParameterSpec tp) + { + for (int i = 0; i < tparams.Length; ++i) + if (tparams [i] == tp) + return targs[i]; + + // This can happen when inflating nested types + // without type arguments specified + return tp; + } + } + + // + // Before emitting any code we have to change all MVAR references to VAR + // when the method is of generic type and has hoisted variables + // + public class TypeParameterMutator + { + readonly TypeParameters mvar; + readonly TypeParameters var; + readonly TypeParameterSpec[] src; + Dictionary mutated_typespec; + + public TypeParameterMutator (TypeParameters mvar, TypeParameters var) + { + if (mvar.Count != var.Count) + throw new ArgumentException (); + + this.mvar = mvar; + this.var = var; + } + + public TypeParameterMutator (TypeParameterSpec[] srcVar, TypeParameters destVar) + { + if (srcVar.Length != destVar.Count) + throw new ArgumentException (); + + this.src = srcVar; + this.var = destVar; + } + + #region Properties + + public TypeParameters MethodTypeParameters { + get { + return mvar; + } + } + + #endregion + + public static TypeSpec GetMemberDeclaringType (TypeSpec type) + { + if (type is InflatedTypeSpec) { + if (type.DeclaringType == null) + return type.GetDefinition (); + + var parent = GetMemberDeclaringType (type.DeclaringType); + type = MemberCache.GetMember (parent, type); + } + + return type; + } + + public TypeSpec Mutate (TypeSpec ts) + { + TypeSpec value; + if (mutated_typespec != null && mutated_typespec.TryGetValue (ts, out value)) + return value; + + value = ts.Mutate (this); + if (mutated_typespec == null) + mutated_typespec = new Dictionary (); + + mutated_typespec.Add (ts, value); + return value; + } + + public TypeParameterSpec Mutate (TypeParameterSpec tp) + { + if (mvar != null) { + for (int i = 0; i < mvar.Count; ++i) { + if (mvar[i].Type == tp) + return var[i].Type; + } + } else { + for (int i = 0; i < src.Length; ++i) { + if (src[i] == tp) + return var[i].Type; + } + } + + return tp; + } + + public TypeSpec[] Mutate (TypeSpec[] targs) + { + TypeSpec[] mutated = new TypeSpec[targs.Length]; + bool changed = false; + for (int i = 0; i < targs.Length; ++i) { + mutated[i] = Mutate (targs[i]); + changed |= targs[i] != mutated[i]; + } + + return changed ? mutated : targs; + } + } + + /// + /// A TypeExpr which already resolved to a type parameter. + /// + public class TypeParameterExpr : TypeExpression + { + public TypeParameterExpr (TypeParameter type_parameter, Location loc) + : base (type_parameter.Type, loc) + { + this.eclass = ExprClass.TypeParameter; + } + } + + public class InflatedTypeSpec : TypeSpec + { + TypeSpec[] targs; + TypeParameterSpec[] constraints; + readonly TypeSpec open_type; + readonly IModuleContext context; + + public InflatedTypeSpec (IModuleContext context, TypeSpec openType, TypeSpec declaringType, TypeSpec[] targs) + : base (openType.Kind, declaringType, openType.MemberDefinition, null, openType.Modifiers) + { + if (targs == null) + throw new ArgumentNullException ("targs"); + + this.state &= ~SharedStateFlags; + this.state |= (openType.state & SharedStateFlags); + + this.context = context; + this.open_type = openType; + this.targs = targs; + + foreach (var arg in targs) { + if (arg.HasDynamicElement || arg.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + state |= StateFlags.HasDynamicElement; + break; + } + } + + if (open_type.Kind == MemberKind.MissingType) + MemberCache = MemberCache.Empty; + + if ((open_type.Modifiers & Modifiers.COMPILER_GENERATED) != 0) + state |= StateFlags.ConstraintsChecked; + } + + #region Properties + + public override TypeSpec BaseType { + get { + if (cache == null || (state & StateFlags.PendingBaseTypeInflate) != 0) + InitializeMemberCache (true); + + return base.BaseType; + } + } + + // + // Inflated type parameters with constraints array, mapping with type arguments is based on index + // + public TypeParameterSpec[] Constraints { + get { + if (constraints == null) { + constraints = TypeParameterSpec.InflateConstraints (MemberDefinition.TypeParameters, l => l.CreateLocalInflator (context), this); + } + + return constraints; + } + } + + // + // Used to cache expensive constraints validation on constructed types + // + public bool HasConstraintsChecked { + get { + return (state & StateFlags.ConstraintsChecked) != 0; + } + set { + state = value ? state | StateFlags.ConstraintsChecked : state & ~StateFlags.ConstraintsChecked; + } + } + + public override IList Interfaces { + get { + if (cache == null) + InitializeMemberCache (true); + + return base.Interfaces; + } + } + + public override bool IsExpressionTreeType { + get { + return (open_type.state & StateFlags.InflatedExpressionType) != 0; + } + } + + public override bool IsArrayGenericInterface { + get { + return (open_type.state & StateFlags.GenericIterateInterface) != 0; + } + } + + public override bool IsGenericTask { + get { + return (open_type.state & StateFlags.GenericTask) != 0; + } + } + + public override bool IsNullableType { + get { + return (open_type.state & StateFlags.InflatedNullableType) != 0; + } + } + + // + // Types used to inflate the generic type + // + public override TypeSpec[] TypeArguments { + get { + return targs; + } + } + + #endregion + + public override bool AddInterface (TypeSpec iface) + { + var inflator = CreateLocalInflator (context); + iface = inflator.Inflate (iface); + if (iface == null) + return false; + + return base.AddInterface (iface); + } + + public static bool ContainsTypeParameter (TypeSpec type) + { + if (type.Kind == MemberKind.TypeParameter) + return true; + + var element_container = type as ElementTypeSpec; + if (element_container != null) + return ContainsTypeParameter (element_container.Element); + + foreach (var t in type.TypeArguments) { + if (ContainsTypeParameter (t)) { + return true; + } + } + + return false; + } + + public TypeParameterInflator CreateLocalInflator (IModuleContext context) + { + TypeParameterSpec[] tparams_full; + TypeSpec[] targs_full = targs; + if (IsNested) { + // + // Special case is needed when we are inflating an open type (nested type definition) + // on inflated parent. Consider following case + // + // Foo.Bar => Foo.Bar + // + // Any later inflation of Foo.Bar has to also inflate T if used inside Bar + // + List merged_targs = null; + List merged_tparams = null; + + var type = DeclaringType; + + do { + if (type.TypeArguments.Length > 0) { + if (merged_targs == null) { + merged_targs = new List (); + merged_tparams = new List (); + if (targs.Length > 0) { + merged_targs.AddRange (targs); + merged_tparams.AddRange (open_type.MemberDefinition.TypeParameters); + } + } + merged_tparams.AddRange (type.MemberDefinition.TypeParameters); + merged_targs.AddRange (type.TypeArguments); + } + type = type.DeclaringType; + } while (type != null); + + if (merged_targs != null) { + // Type arguments are not in the right order but it should not matter in this case + targs_full = merged_targs.ToArray (); + tparams_full = merged_tparams.ToArray (); + } else if (targs.Length == 0) { + tparams_full = TypeParameterSpec.EmptyTypes; + } else { + tparams_full = open_type.MemberDefinition.TypeParameters; + } + } else if (targs.Length == 0) { + tparams_full = TypeParameterSpec.EmptyTypes; + } else { + tparams_full = open_type.MemberDefinition.TypeParameters; + } + + return new TypeParameterInflator (context, this, tparams_full, targs_full); + } + + MetaType CreateMetaInfo () + { + // + // Converts nested type arguments into right order + // Foo.Bar => string, bool, int + // + var all = new List (); + TypeSpec type = this; + TypeSpec definition = type; + do { + if (type.GetDefinition().IsGeneric) { + all.InsertRange (0, + type.TypeArguments != TypeSpec.EmptyTypes ? + type.TypeArguments.Select (l => l.GetMetaInfo ()) : + type.MemberDefinition.TypeParameters.Select (l => l.GetMetaInfo ())); + } + + definition = definition.GetDefinition (); + type = type.DeclaringType; + } while (type != null); + + return definition.GetMetaInfo ().MakeGenericType (all.ToArray ()); + } + + public override ObsoleteAttribute GetAttributeObsolete () + { + return open_type.GetAttributeObsolete (); + } + + protected override bool IsNotCLSCompliant (out bool attrValue) + { + if (base.IsNotCLSCompliant (out attrValue)) + return true; + + foreach (var ta in TypeArguments) { + if (ta.MemberDefinition.CLSAttributeValue == false) + return true; + } + + return false; + } + + public override TypeSpec GetDefinition () + { + return open_type; + } + + public override MetaType GetMetaInfo () + { + if (info == null) + info = CreateMetaInfo (); + + return info; + } + + public override string GetSignatureForError () + { + if (IsNullableType) + return targs[0].GetSignatureForError () + "?"; + + return base.GetSignatureForError (); + } + + protected override string GetTypeNameSignature () + { + if (targs.Length == 0 || MemberDefinition is AnonymousTypeClass) + return null; + + return "<" + TypeManager.CSharpName (targs) + ">"; + } + + public bool HasDynamicArgument () + { + for (int i = 0; i < targs.Length; ++i) { + var item = targs[i]; + + if (item.BuiltinType == BuiltinTypeSpec.Type.Dynamic) + return true; + + if (item is InflatedTypeSpec) { + if (((InflatedTypeSpec) item).HasDynamicArgument ()) + return true; + + continue; + } + + if (item.IsArray) { + while (item.IsArray) { + item = ((ArrayContainer) item).Element; + } + + if (item.BuiltinType == BuiltinTypeSpec.Type.Dynamic) + return true; + } + } + + return false; + } + + protected override void InitializeMemberCache (bool onlyTypes) + { + if (cache == null) { + var open_cache = onlyTypes ? open_type.MemberCacheTypes : open_type.MemberCache; + + // Surprisingly, calling MemberCache on open type could meantime create cache on this type + // for imported type parameter constraints referencing nested type of this declaration + if (cache == null) + cache = new MemberCache (open_cache); + } + + var inflator = CreateLocalInflator (context); + + // + // Two stage inflate due to possible nested types recursive + // references + // + // class A { + // B b; + // class B { + // T Value; + // } + // } + // + // When resolving type of `b' members of `B' cannot be + // inflated because are not yet available in membercache + // + if ((state & StateFlags.PendingMemberCacheMembers) == 0) { + open_type.MemberCacheTypes.InflateTypes (cache, inflator); + + // + // Inflate any implemented interfaces + // + if (open_type.Interfaces != null) { + ifaces = new List (open_type.Interfaces.Count); + foreach (var iface in open_type.Interfaces) { + var iface_inflated = inflator.Inflate (iface); + if (iface_inflated == null) + continue; + + base.AddInterface (iface_inflated); + } + } + + // + // Handles the tricky case of recursive nested base generic type + // + // class A : Base.Nested> { + // class Nested {} + // } + // + // When inflating A. base type is not yet known, secondary + // inflation is required (not common case) once base scope + // is known + // + if (open_type.BaseType == null) { + if (IsClass) + state |= StateFlags.PendingBaseTypeInflate; + } else { + BaseType = inflator.Inflate (open_type.BaseType); + } + } else if ((state & StateFlags.PendingBaseTypeInflate) != 0) { + // + // It can happen when resolving base type without being defined + // which is not allowed to happen and will always lead to an error + // + // class B { class N {} } + // class A : A {} + // + if (open_type.BaseType == null) + return; + + BaseType = inflator.Inflate (open_type.BaseType); + state &= ~StateFlags.PendingBaseTypeInflate; + } + + if (onlyTypes) { + state |= StateFlags.PendingMemberCacheMembers; + return; + } + + var tc = open_type.MemberDefinition as TypeDefinition; + if (tc != null && !tc.HasMembersDefined) { + // + // Inflating MemberCache with undefined members + // + return; + } + + if ((state & StateFlags.PendingBaseTypeInflate) != 0) { + BaseType = inflator.Inflate (open_type.BaseType); + state &= ~StateFlags.PendingBaseTypeInflate; + } + + state &= ~StateFlags.PendingMemberCacheMembers; + open_type.MemberCache.InflateMembers (cache, open_type, inflator); + } + + public override TypeSpec Mutate (TypeParameterMutator mutator) + { + var targs = TypeArguments; + if (targs != null) + targs = mutator.Mutate (targs); + + var decl = DeclaringType; + if (IsNested && DeclaringType.IsGenericOrParentIsGeneric) + decl = mutator.Mutate (decl); + + if (targs == TypeArguments && decl == DeclaringType) + return this; + + var mutated = (InflatedTypeSpec) MemberwiseClone (); + if (decl != DeclaringType) { + // Gets back MethodInfo in case of metaInfo was inflated + //mutated.info = MemberCache.GetMember (DeclaringType.GetDefinition (), this).info; + + mutated.declaringType = decl; + mutated.state |= StateFlags.PendingMetaInflate; + } + + if (targs != null) { + mutated.targs = targs; + mutated.info = null; + } + + return mutated; + } + } + + + // + // Tracks the type arguments when instantiating a generic type. It's used + // by both type arguments and type parameters + // + public class TypeArguments + { + List args; + TypeSpec[] atypes; + + public List Args { + get { return this.args; } + } + + public TypeArguments (params FullNamedExpression[] types) + { + this.args = new List (types); + } + + public void Add (FullNamedExpression type) + { + args.Add (type); + } + + /// + /// We may only be used after Resolve() is called and return the fully + /// resolved types. + /// + // TODO: Not needed, just return type from resolve + public TypeSpec[] Arguments { + get { + return atypes; + } + set { + atypes = value; + } + } + + public int Count { + get { + return args.Count; + } + } + + public virtual bool IsEmpty { + get { + return false; + } + } + + public List TypeExpressions { + get { + return this.args; + } + } + + public string GetSignatureForError() + { + StringBuilder sb = new StringBuilder (); + for (int i = 0; i < Count; ++i) { + var expr = args[i]; + if (expr != null) + sb.Append (expr.GetSignatureForError ()); + + if (i + 1 < Count) + sb.Append (','); + } + + return sb.ToString (); + } + + /// + /// Resolve the type arguments. + /// + public virtual bool Resolve (IMemberContext ec) + { + if (atypes != null) + return true; + + int count = args.Count; + bool ok = true; + + atypes = new TypeSpec [count]; + + var errors = ec.Module.Compiler.Report.Errors; + + for (int i = 0; i < count; i++){ + var te = args[i].ResolveAsType (ec); + if (te == null) { + ok = false; + continue; + } + + atypes[i] = te; + + if (te.IsStatic) { + ec.Module.Compiler.Report.Error (718, args[i].Location, "`{0}': static classes cannot be used as generic arguments", + te.GetSignatureForError ()); + ok = false; + } + + if (te.IsPointer || te.IsSpecialRuntimeType) { + ec.Module.Compiler.Report.Error (306, args[i].Location, + "The type `{0}' may not be used as a type argument", + te.GetSignatureForError ()); + ok = false; + } + } + + if (!ok || errors != ec.Module.Compiler.Report.Errors) + atypes = null; + + return ok; + } + + public TypeArguments Clone () + { + TypeArguments copy = new TypeArguments (); + foreach (var ta in args) + copy.args.Add (ta); + + return copy; + } + } + + public class UnboundTypeArguments : TypeArguments + { + public UnboundTypeArguments (int arity) + : base (new FullNamedExpression[arity]) + { + } + + public override bool IsEmpty { + get { + return true; + } + } + + public override bool Resolve (IMemberContext ec) + { + // Nothing to be resolved + return true; + } + } + + public class TypeParameters + { + List names; + TypeParameterSpec[] types; + + public TypeParameters () + { + names = new List (); + } + + public TypeParameters (int count) + { + names = new List (count); + } + + #region Properties + + public int Count { + get { + return names.Count; + } + } + + public TypeParameterSpec[] Types { + get { + return types; + } + } + + #endregion + + public void Add (TypeParameter tparam) + { + names.Add (tparam); + } + + public void Add (TypeParameters tparams) + { + names.AddRange (tparams.names); + } + + public void Create (TypeSpec declaringType, int parentOffset, TypeContainer parent) + { + types = new TypeParameterSpec[Count]; + for (int i = 0; i < types.Length; ++i) { + var tp = names[i]; + + tp.Create (declaringType, parent); + types[i] = tp.Type; + types[i].DeclaredPosition = i + parentOffset; + + if (tp.Variance != Variance.None && !(declaringType != null && (declaringType.Kind == MemberKind.Interface || declaringType.Kind == MemberKind.Delegate))) { + parent.Compiler.Report.Error (1960, tp.Location, "Variant type parameters can only be used with interfaces and delegates"); + } + } + } + + public void Define (GenericTypeParameterBuilder[] builders) + { + for (int i = 0; i < types.Length; ++i) { + var tp = names[i]; + tp.Define (builders [types [i].DeclaredPosition]); + } + } + + public TypeParameter this[int index] { + get { + return names [index]; + } + set { + names[index] = value; + } + } + + public TypeParameter Find (string name) + { + foreach (var tp in names) { + if (tp.Name == name) + return tp; + } + + return null; + } + + public string[] GetAllNames () + { + return names.Select (l => l.Name).ToArray (); + } + + public string GetSignatureForError () + { + StringBuilder sb = new StringBuilder (); + for (int i = 0; i < Count; ++i) { + if (i > 0) + sb.Append (','); + + var name = names[i]; + if (name != null) + sb.Append (name.GetSignatureForError ()); + } + + return sb.ToString (); + } + + + public void CheckPartialConstraints (Method part) + { + var partTypeParameters = part.CurrentTypeParameters; + + for (int i = 0; i < Count; i++) { + var tp_a = names[i]; + var tp_b = partTypeParameters [i]; + if (tp_a.Constraints == null) { + if (tp_b.Constraints == null) + continue; + } else if (tp_b.Constraints != null && tp_a.Type.HasSameConstraintsDefinition (tp_b.Type)) { + continue; + } + + part.Compiler.Report.SymbolRelatedToPreviousError (this[i].CurrentMemberDefinition.Location, ""); + part.Compiler.Report.Error (761, part.Location, + "Partial method declarations of `{0}' have inconsistent constraints for type parameter `{1}'", + part.GetSignatureForError (), partTypeParameters[i].GetSignatureForError ()); + } + } + + public void UpdateConstraints (TypeDefinition part) + { + var partTypeParameters = part.MemberName.TypeParameters; + + for (int i = 0; i < Count; i++) { + var tp = names [i]; + if (tp.AddPartialConstraints (part, partTypeParameters [i])) + continue; + + part.Compiler.Report.SymbolRelatedToPreviousError (this[i].CurrentMemberDefinition); + part.Compiler.Report.Error (265, part.Location, + "Partial declarations of `{0}' have inconsistent constraints for type parameter `{1}'", + part.GetSignatureForError (), tp.GetSignatureForError ()); + } + } + + public void VerifyClsCompliance () + { + foreach (var tp in names) { + tp.VerifyClsCompliance (); + } + } + } + + // + // A type expression of generic type with type arguments + // + class GenericTypeExpr : TypeExpr + { + TypeArguments args; + TypeSpec open_type; + + /// + /// Instantiate the generic type `t' with the type arguments `args'. + /// Use this constructor if you already know the fully resolved + /// generic type. + /// + public GenericTypeExpr (TypeSpec open_type, TypeArguments args, Location l) + { + this.open_type = open_type; + loc = l; + this.args = args; + } + + public override string GetSignatureForError () + { + return type.GetSignatureForError (); + } + + public override TypeSpec ResolveAsType (IMemberContext mc, bool allowUnboundTypeArguments = false) + { + if (eclass != ExprClass.Unresolved) + return type; + + if (!args.Resolve (mc)) + return null; + + TypeSpec[] atypes = args.Arguments; + if (atypes == null) + return null; + + // + // Now bind the parameters + // + var inflated = open_type.MakeGenericType (mc, atypes); + type = inflated; + eclass = ExprClass.Type; + + // + // The constraints can be checked only when full type hierarchy is known + // + if (!inflated.HasConstraintsChecked && mc.Module.HasTypesFullyDefined) { + var constraints = inflated.Constraints; + if (constraints != null) { + var cc = new ConstraintChecker (mc); + if (cc.CheckAll (open_type, atypes, constraints, loc)) { + inflated.HasConstraintsChecked = true; + } + } + } + + return type; + } + + public override bool Equals (object obj) + { + GenericTypeExpr cobj = obj as GenericTypeExpr; + if (cobj == null) + return false; + + if ((type == null) || (cobj.type == null)) + return false; + + return type == cobj.type; + } + + public override int GetHashCode () + { + return base.GetHashCode (); + } + } + + // + // Generic type with unbound type arguments, used for typeof (G<,,>) + // + class GenericOpenTypeExpr : TypeExpression + { + public GenericOpenTypeExpr (TypeSpec type, /*UnboundTypeArguments args,*/ Location loc) + : base (type.GetDefinition (), loc) + { + } + } + + struct ConstraintChecker + { + IMemberContext mc; + bool recursive_checks; + + public ConstraintChecker (IMemberContext ctx) + { + this.mc = ctx; + recursive_checks = false; + } + + // + // Checks the constraints of open generic type against type + // arguments. This version is used for types which could not be + // checked immediatelly during construction because the type + // hierarchy was not yet fully setup (before Emit phase) + // + public static bool Check (IMemberContext mc, TypeSpec type, Location loc) + { + // + // Check declaring type first if there is any + // + if (type.DeclaringType != null && !Check (mc, type.DeclaringType, loc)) + return false; + + while (type is ElementTypeSpec) + type = ((ElementTypeSpec) type).Element; + + if (type.Arity == 0) + return true; + + var gtype = type as InflatedTypeSpec; + if (gtype == null) + return true; + + var constraints = gtype.Constraints; + if (constraints == null) + return true; + + if (gtype.HasConstraintsChecked) + return true; + + var cc = new ConstraintChecker (mc); + cc.recursive_checks = true; + + if (cc.CheckAll (gtype.GetDefinition (), type.TypeArguments, constraints, loc)) { + gtype.HasConstraintsChecked = true; + return true; + } + + return false; + } + + // + // Checks all type arguments againts type parameters constraints + // NOTE: It can run in probing mode when `this.mc' is null + // + public bool CheckAll (MemberSpec context, TypeSpec[] targs, TypeParameterSpec[] tparams, Location loc) + { + for (int i = 0; i < tparams.Length; i++) { + var targ = targs[i]; + if (!CheckConstraint (context, targ, tparams [i], loc)) + return false; + + if (!recursive_checks) + continue; + + if (!Check (mc, targ, loc)) + return false; + } + + return true; + } + + bool CheckConstraint (MemberSpec context, TypeSpec atype, TypeParameterSpec tparam, Location loc) + { + // + // First, check the `class' and `struct' constraints. + // + if (tparam.HasSpecialClass && !TypeSpec.IsReferenceType (atype)) { + if (mc != null) { + mc.Module.Compiler.Report.Error (452, loc, + "The type `{0}' must be a reference type in order to use it as type parameter `{1}' in the generic type or method `{2}'", + atype.GetSignatureForError (), tparam.GetSignatureForError (), context.GetSignatureForError ()); + } + + return false; + } + + if (tparam.HasSpecialStruct && (!TypeSpec.IsValueType (atype) || atype.IsNullableType)) { + if (mc != null) { + mc.Module.Compiler.Report.Error (453, loc, + "The type `{0}' must be a non-nullable value type in order to use it as type parameter `{1}' in the generic type or method `{2}'", + atype.GetSignatureForError (), tparam.GetSignatureForError (), context.GetSignatureForError ()); + } + + return false; + } + + bool ok = true; + + // + // Check the class constraint + // + if (tparam.HasTypeConstraint) { + if (!CheckConversion (mc, context, atype, tparam, tparam.BaseType, loc)) { + if (mc == null) + return false; + + ok = false; + } + } + + // + // Check the interfaces constraints + // + if (tparam.InterfacesDefined != null) { + foreach (TypeSpec iface in tparam.InterfacesDefined) { + if (!CheckConversion (mc, context, atype, tparam, iface, loc)) { + if (mc == null) + return false; + + ok = false; + break; + } + } + } + + // + // Check the type parameter constraint + // + if (tparam.TypeArguments != null) { + foreach (var ta in tparam.TypeArguments) { + if (!CheckConversion (mc, context, atype, tparam, ta, loc)) { + if (mc == null) + return false; + + ok = false; + break; + } + } + } + + // + // Finally, check the constructor constraint. + // + if (!tparam.HasSpecialConstructor) + return ok; + + if (!HasDefaultConstructor (atype)) { + if (mc != null) { + mc.Module.Compiler.Report.SymbolRelatedToPreviousError (atype); + mc.Module.Compiler.Report.Error (310, loc, + "The type `{0}' must have a public parameterless constructor in order to use it as parameter `{1}' in the generic type or method `{2}'", + atype.GetSignatureForError (), tparam.GetSignatureForError (), context.GetSignatureForError ()); + } + return false; + } + + return ok; + } + + static bool HasDynamicTypeArgument (TypeSpec[] targs) + { + for (int i = 0; i < targs.Length; ++i) { + var targ = targs [i]; + if (targ.BuiltinType == BuiltinTypeSpec.Type.Dynamic) + return true; + + if (HasDynamicTypeArgument (targ.TypeArguments)) + return true; + } + + return false; + } + + bool CheckConversion (IMemberContext mc, MemberSpec context, TypeSpec atype, TypeParameterSpec tparam, TypeSpec ttype, Location loc) + { + if (atype == ttype) + return true; + + if (atype.IsGenericParameter) { + var tps = (TypeParameterSpec) atype; + if (tps.HasDependencyOn (ttype)) + return true; + + if (Convert.ImplicitTypeParameterConversion (null, tps, ttype) != null) + return true; + + } else if (TypeSpec.IsValueType (atype)) { + if (atype.IsNullableType) { + // + // LAMESPEC: Only identity or base type ValueType or Object satisfy nullable type + // + if (TypeSpec.IsBaseClass (atype, ttype, false)) + return true; + } else { + if (Convert.ImplicitBoxingConversion (null, atype, ttype) != null) + return true; + } + } else { + if (Convert.ImplicitReferenceConversionExists (atype, ttype) || Convert.ImplicitBoxingConversion (null, atype, ttype) != null) + return true; + } + + if (mc != null) { + mc.Module.Compiler.Report.SymbolRelatedToPreviousError (tparam); + if (atype.IsGenericParameter) { + mc.Module.Compiler.Report.Error (314, loc, + "The type `{0}' cannot be used as type parameter `{1}' in the generic type or method `{2}'. There is no boxing or type parameter conversion from `{0}' to `{3}'", + atype.GetSignatureForError (), tparam.GetSignatureForError (), context.GetSignatureForError (), ttype.GetSignatureForError ()); + } else if (TypeSpec.IsValueType (atype)) { + if (atype.IsNullableType) { + if (ttype.IsInterface) { + mc.Module.Compiler.Report.Error (313, loc, + "The type `{0}' cannot be used as type parameter `{1}' in the generic type or method `{2}'. The nullable type `{0}' never satisfies interface constraint `{3}'", + atype.GetSignatureForError (), tparam.GetSignatureForError (), context.GetSignatureForError (), ttype.GetSignatureForError ()); + } else { + mc.Module.Compiler.Report.Error (312, loc, + "The type `{0}' cannot be used as type parameter `{1}' in the generic type or method `{2}'. The nullable type `{0}' does not satisfy constraint `{3}'", + atype.GetSignatureForError (), tparam.GetSignatureForError (), context.GetSignatureForError (), ttype.GetSignatureForError ()); + } + } else { + mc.Module.Compiler.Report.Error (315, loc, + "The type `{0}' cannot be used as type parameter `{1}' in the generic type or method `{2}'. There is no boxing conversion from `{0}' to `{3}'", + atype.GetSignatureForError (), tparam.GetSignatureForError (), context.GetSignatureForError (), ttype.GetSignatureForError ()); + } + } else { + mc.Module.Compiler.Report.Error (311, loc, + "The type `{0}' cannot be used as type parameter `{1}' in the generic type or method `{2}'. There is no implicit reference conversion from `{0}' to `{3}'", + atype.GetSignatureForError (), tparam.GetSignatureForError (), context.GetSignatureForError (), ttype.GetSignatureForError ()); + } + } + + return false; + } + + static bool HasDefaultConstructor (TypeSpec atype) + { + var tp = atype as TypeParameterSpec; + if (tp != null) { + return tp.HasSpecialConstructor || tp.HasSpecialStruct; + } + + if (atype.IsStruct || atype.IsEnum) + return true; + + if (atype.IsAbstract) + return false; + + var tdef = atype.GetDefinition (); + + var found = MemberCache.FindMember (tdef, + MemberFilter.Constructor (ParametersCompiled.EmptyReadOnlyParameters), + BindingRestriction.DeclaredOnly | BindingRestriction.InstanceOnly); + + return found != null && (found.Modifiers & Modifiers.PUBLIC) != 0; + } + } + + // + // Implements C# type inference + // + class TypeInference + { + // + // Tracks successful rate of type inference + // + int score = int.MaxValue; + readonly Arguments arguments; + readonly int arg_count; + + public TypeInference (Arguments arguments) + { + this.arguments = arguments; + if (arguments != null) + arg_count = arguments.Count; + } + + public int InferenceScore { + get { + return score; + } + } + + public TypeSpec[] InferMethodArguments (ResolveContext ec, MethodSpec method) + { + var method_generic_args = method.GenericDefinition.TypeParameters; + TypeInferenceContext context = new TypeInferenceContext (method_generic_args); + if (!context.UnfixedVariableExists) + return TypeSpec.EmptyTypes; + + AParametersCollection pd = method.Parameters; + if (!InferInPhases (ec, context, pd)) + return null; + + return context.InferredTypeArguments; + } + + // + // Implements method type arguments inference + // + bool InferInPhases (ResolveContext ec, TypeInferenceContext tic, AParametersCollection methodParameters) + { + int params_arguments_start; + if (methodParameters.HasParams) { + params_arguments_start = methodParameters.Count - 1; + } else { + params_arguments_start = arg_count; + } + + TypeSpec [] ptypes = methodParameters.Types; + + // + // The first inference phase + // + TypeSpec method_parameter = null; + for (int i = 0; i < arg_count; i++) { + Argument a = arguments [i]; + if (a == null) + continue; + + if (i < params_arguments_start) { + method_parameter = methodParameters.Types [i]; + } else if (i == params_arguments_start) { + if (arg_count == params_arguments_start + 1 && TypeManager.HasElementType (a.Type)) + method_parameter = methodParameters.Types [params_arguments_start]; + else + method_parameter = TypeManager.GetElementType (methodParameters.Types [params_arguments_start]); + + ptypes = (TypeSpec[]) ptypes.Clone (); + ptypes [i] = method_parameter; + } + + // + // When a lambda expression, an anonymous method + // is used an explicit argument type inference takes a place + // + AnonymousMethodExpression am = a.Expr as AnonymousMethodExpression; + if (am != null) { + if (am.ExplicitTypeInference (tic, method_parameter)) + --score; + continue; + } + + if (a.IsByRef) { + score -= tic.ExactInference (a.Type, method_parameter); + continue; + } + + if (a.Expr.Type == InternalType.NullLiteral) + continue; + + if (TypeSpec.IsValueType (method_parameter)) { + score -= tic.LowerBoundInference (a.Type, method_parameter); + continue; + } + + // + // Otherwise an output type inference is made + // + score -= tic.OutputTypeInference (ec, a.Expr, method_parameter); + } + + // + // Part of the second phase but because it happens only once + // we don't need to call it in cycle + // + bool fixed_any = false; + if (!tic.FixIndependentTypeArguments (ec, ptypes, ref fixed_any)) + return false; + + return DoSecondPhase (ec, tic, ptypes, !fixed_any); + } + + bool DoSecondPhase (ResolveContext ec, TypeInferenceContext tic, TypeSpec[] methodParameters, bool fixDependent) + { + bool fixed_any = false; + if (fixDependent && !tic.FixDependentTypes (ec, ref fixed_any)) + return false; + + // If no further unfixed type variables exist, type inference succeeds + if (!tic.UnfixedVariableExists) + return true; + + if (!fixed_any && fixDependent) + return false; + + // For all arguments where the corresponding argument output types + // contain unfixed type variables but the input types do not, + // an output type inference is made + for (int i = 0; i < arg_count; i++) { + + // Align params arguments + TypeSpec t_i = methodParameters [i >= methodParameters.Length ? methodParameters.Length - 1: i]; + + if (!t_i.IsDelegate) { + if (!t_i.IsExpressionTreeType) + continue; + + t_i = TypeManager.GetTypeArguments (t_i) [0]; + } + + var mi = Delegate.GetInvokeMethod (t_i); + TypeSpec rtype = mi.ReturnType; + + if (tic.IsReturnTypeNonDependent (mi, rtype)) { + // It can be null for default arguments + if (arguments[i] == null) + continue; + + score -= tic.OutputTypeInference (ec, arguments[i].Expr, t_i); + } + } + + + return DoSecondPhase (ec, tic, methodParameters, true); + } + } + + public class TypeInferenceContext + { + protected enum BoundKind + { + Exact = 0, + Lower = 1, + Upper = 2 + } + + struct BoundInfo : IEquatable + { + public readonly TypeSpec Type; + public readonly BoundKind Kind; + + public BoundInfo (TypeSpec type, BoundKind kind) + { + this.Type = type; + this.Kind = kind; + } + + public override int GetHashCode () + { + return Type.GetHashCode (); + } + + public Expression GetTypeExpression () + { + return new TypeExpression (Type, Location.Null); + } + + #region IEquatable Members + + public bool Equals (BoundInfo other) + { + return Type == other.Type && Kind == other.Kind; + } + + #endregion + } + + readonly TypeSpec[] tp_args; + readonly TypeSpec[] fixed_types; + readonly List[] bounds; + + // TODO MemberCache: Could it be TypeParameterSpec[] ?? + public TypeInferenceContext (TypeSpec[] typeArguments) + { + if (typeArguments.Length == 0) + throw new ArgumentException ("Empty generic arguments"); + + fixed_types = new TypeSpec [typeArguments.Length]; + for (int i = 0; i < typeArguments.Length; ++i) { + if (typeArguments [i].IsGenericParameter) { + if (bounds == null) { + bounds = new List [typeArguments.Length]; + tp_args = new TypeSpec [typeArguments.Length]; + } + tp_args [i] = typeArguments [i]; + } else { + fixed_types [i] = typeArguments [i]; + } + } + } + + // + // Used together with AddCommonTypeBound fo implement + // 7.4.2.13 Finding the best common type of a set of expressions + // + public TypeInferenceContext () + { + fixed_types = new TypeSpec [1]; + tp_args = new TypeSpec [1]; + tp_args[0] = InternalType.Arglist; // it can be any internal type + bounds = new List [1]; + } + + public TypeSpec[] InferredTypeArguments { + get { + return fixed_types; + } + } + + public void AddCommonTypeBound (TypeSpec type) + { + AddToBounds (new BoundInfo (type, BoundKind.Lower), 0, false); + } + + public void AddCommonTypeBoundAsync (TypeSpec type) + { + AddToBounds (new BoundInfo (type, BoundKind.Lower), 0, true); + } + + void AddToBounds (BoundInfo bound, int index, bool voidAllowed) + { + // + // Some types cannot be used as type arguments + // + if ((bound.Type.Kind == MemberKind.Void && !voidAllowed) || bound.Type.IsPointer || bound.Type.IsSpecialRuntimeType || + bound.Type == InternalType.MethodGroup || bound.Type == InternalType.AnonymousMethod) + return; + + var a = bounds [index]; + if (a == null) { + a = new List (2); + a.Add (bound); + bounds [index] = a; + return; + } + + if (a.Contains (bound)) + return; + + a.Add (bound); + } + + bool AllTypesAreFixed (TypeSpec[] types) + { + foreach (TypeSpec t in types) { + if (t.IsGenericParameter) { + if (!IsFixed (t)) + return false; + continue; + } + + if (TypeManager.IsGenericType (t)) + return AllTypesAreFixed (TypeManager.GetTypeArguments (t)); + } + + return true; + } + + // + // 26.3.3.8 Exact Inference + // + public int ExactInference (TypeSpec u, TypeSpec v) + { + // If V is an array type + if (v.IsArray) { + if (!u.IsArray) + return 0; + + var ac_u = (ArrayContainer) u; + var ac_v = (ArrayContainer) v; + if (ac_u.Rank != ac_v.Rank) + return 0; + + return ExactInference (ac_u.Element, ac_v.Element); + } + + // If V is constructed type and U is constructed type + if (TypeManager.IsGenericType (v)) { + if (!TypeManager.IsGenericType (u) || v.MemberDefinition != u.MemberDefinition) + return 0; + + TypeSpec [] ga_u = TypeManager.GetTypeArguments (u); + TypeSpec [] ga_v = TypeManager.GetTypeArguments (v); + if (ga_u.Length != ga_v.Length) + return 0; + + int score = 0; + for (int i = 0; i < ga_u.Length; ++i) + score += ExactInference (ga_u [i], ga_v [i]); + + return System.Math.Min (1, score); + } + + // If V is one of the unfixed type arguments + int pos = IsUnfixed (v); + if (pos == -1) + return 0; + + AddToBounds (new BoundInfo (u, BoundKind.Exact), pos, false); + return 1; + } + + public bool FixAllTypes (ResolveContext ec) + { + for (int i = 0; i < tp_args.Length; ++i) { + if (!FixType (ec, i)) + return false; + } + return true; + } + + // + // All unfixed type variables Xi are fixed for which all of the following hold: + // a, There is at least one type variable Xj that depends on Xi + // b, Xi has a non-empty set of bounds + // + public bool FixDependentTypes (ResolveContext ec, ref bool fixed_any) + { + for (int i = 0; i < tp_args.Length; ++i) { + if (fixed_types[i] != null) + continue; + + if (bounds[i] == null) + continue; + + if (!FixType (ec, i)) + return false; + + fixed_any = true; + } + + return true; + } + + // + // All unfixed type variables Xi which depend on no Xj are fixed + // + public bool FixIndependentTypeArguments (ResolveContext ec, TypeSpec[] methodParameters, ref bool fixed_any) + { + var types_to_fix = new List (tp_args); + for (int i = 0; i < methodParameters.Length; ++i) { + TypeSpec t = methodParameters[i]; + + if (!t.IsDelegate) { + if (!t.IsExpressionTreeType) + continue; + + t = TypeManager.GetTypeArguments (t) [0]; + } + + if (t.IsGenericParameter) + continue; + + var invoke = Delegate.GetInvokeMethod (t); + TypeSpec rtype = invoke.ReturnType; + while (rtype.IsArray) + rtype = ((ArrayContainer) rtype).Element; + + if (!rtype.IsGenericParameter && !TypeManager.IsGenericType (rtype)) + continue; + + // Remove dependent types, they cannot be fixed yet + RemoveDependentTypes (types_to_fix, rtype); + } + + foreach (TypeSpec t in types_to_fix) { + if (t == null) + continue; + + int idx = IsUnfixed (t); + if (idx >= 0 && !FixType (ec, idx)) { + return false; + } + } + + fixed_any = types_to_fix.Count > 0; + return true; + } + + // + // 26.3.3.10 Fixing + // + public bool FixType (ResolveContext ec, int i) + { + // It's already fixed + if (fixed_types[i] != null) + throw new InternalErrorException ("Type argument has been already fixed"); + + var candidates = bounds [i]; + if (candidates == null) + return false; + + if (candidates.Count == 1) { + TypeSpec t = candidates[0].Type; + if (t == InternalType.NullLiteral) + return false; + + fixed_types [i] = t; + return true; + } + + // + // The set of candidate types Uj starts out as the set of + // all types in the set of bounds for Xi + // + var applicable = new bool [candidates.Count]; + for (int ci = 0; ci < applicable.Length; ++ci) + applicable [ci] = true; + + for (int ci = 0; ci < applicable.Length; ++ci) { + var bound = candidates [ci]; + int cii = 0; + + switch (bound.Kind) { + case BoundKind.Exact: + for (; cii != applicable.Length; ++cii) { + if (ci == cii) + continue; + + if (!applicable[cii]) + break; + + // + // For each exact bound U of Xi all types Uj which are not identical + // to U are removed from the candidate set + // + if (candidates [cii].Type != bound.Type) + applicable[cii] = false; + } + + break; + case BoundKind.Lower: + for (; cii != applicable.Length; ++cii) { + if (ci == cii) + continue; + + if (!applicable[cii]) + break; + + // + // For each lower bound U of Xi all types Uj to which there is not an implicit conversion + // from U are removed from the candidate set + // + if (!Convert.ImplicitConversionExists (ec, bound.GetTypeExpression (), candidates [cii].Type)) { + applicable[cii] = false; + } + } + + break; + + case BoundKind.Upper: + for (; cii != applicable.Length; ++cii) { + if (ci == cii) + continue; + + if (!applicable[cii]) + break; + + // + // For each upper bound U of Xi all types Uj from which there is not an implicit conversion + // to U are removed from the candidate set + // + if (!Convert.ImplicitConversionExists (ec, candidates[cii].GetTypeExpression (), bound.Type)) + applicable[cii] = false; + } + + break; + } + } + + TypeSpec best_candidate = null; + for (int ci = 0; ci < applicable.Length; ++ci) { + if (!applicable[ci]) + continue; + + var bound = candidates [ci]; + if (bound.Type == best_candidate) + continue; + + int cii = 0; + for (; cii < applicable.Length; ++cii) { + if (ci == cii) + continue; + + if (!applicable[cii]) + continue; + + if (!Convert.ImplicitConversionExists (ec, candidates[cii].GetTypeExpression (), bound.Type)) + break; + } + + if (cii != applicable.Length) + continue; + + // + // We already have the best candidate, break if it's different (non-unique) + // + // Dynamic is never ambiguous as we prefer dynamic over other best candidate types + // + if (best_candidate != null) { + + if (best_candidate.BuiltinType == BuiltinTypeSpec.Type.Dynamic) + continue; + + if (bound.Type.BuiltinType != BuiltinTypeSpec.Type.Dynamic && best_candidate != bound.Type) + return false; + } + + best_candidate = bound.Type; + } + + if (best_candidate == null) + return false; + + fixed_types[i] = best_candidate; + return true; + } + + public bool HasBounds (int pos) + { + return bounds[pos] != null; + } + + // + // Uses inferred or partially infered types to inflate delegate type argument. Returns + // null when type parameter has not been fixed + // + public TypeSpec InflateGenericArgument (IModuleContext context, TypeSpec parameter) + { + var tp = parameter as TypeParameterSpec; + if (tp != null) { + // + // Type inference works on generic arguments (MVAR) only + // + if (!tp.IsMethodOwned) + return parameter; + + // + // Ensure the type parameter belongs to same container + // + if (tp.DeclaredPosition < tp_args.Length && tp_args[tp.DeclaredPosition] == parameter) + return fixed_types[tp.DeclaredPosition] ?? parameter; + + return parameter; + } + + var gt = parameter as InflatedTypeSpec; + if (gt != null) { + var inflated_targs = new TypeSpec [gt.TypeArguments.Length]; + for (int ii = 0; ii < inflated_targs.Length; ++ii) { + var inflated = InflateGenericArgument (context, gt.TypeArguments [ii]); + if (inflated == null) + return null; + + inflated_targs[ii] = inflated; + } + + return gt.GetDefinition ().MakeGenericType (context, inflated_targs); + } + + var ac = parameter as ArrayContainer; + if (ac != null) { + var inflated = InflateGenericArgument (context, ac.Element); + if (inflated != ac.Element) + return ArrayContainer.MakeType (context.Module, inflated); + } + + return parameter; + } + + // + // Tests whether all delegate input arguments are fixed and generic output type + // requires output type inference + // + public bool IsReturnTypeNonDependent (MethodSpec invoke, TypeSpec returnType) + { + AParametersCollection d_parameters = invoke.Parameters; + + if (d_parameters.IsEmpty) + return true; + + while (returnType.IsArray) + returnType = ((ArrayContainer) returnType).Element; + + if (returnType.IsGenericParameter) { + if (IsFixed (returnType)) + return false; + } else if (TypeManager.IsGenericType (returnType)) { + TypeSpec[] g_args = TypeManager.GetTypeArguments (returnType); + + // At least one unfixed return type has to exist + if (AllTypesAreFixed (g_args)) + return false; + } else { + return false; + } + + // All generic input arguments have to be fixed + return AllTypesAreFixed (d_parameters.Types); + } + + bool IsFixed (TypeSpec type) + { + return IsUnfixed (type) == -1; + } + + int IsUnfixed (TypeSpec type) + { + if (!type.IsGenericParameter) + return -1; + + for (int i = 0; i < tp_args.Length; ++i) { + if (tp_args[i] == type) { + if (fixed_types[i] != null) + break; + + return i; + } + } + + return -1; + } + + // + // 26.3.3.9 Lower-bound Inference + // + public int LowerBoundInference (TypeSpec u, TypeSpec v) + { + return LowerBoundInference (u, v, false); + } + + // + // Lower-bound (false) or Upper-bound (true) inference based on inversed argument + // + int LowerBoundInference (TypeSpec u, TypeSpec v, bool inversed) + { + // If V is one of the unfixed type arguments + int pos = IsUnfixed (v); + if (pos != -1) { + AddToBounds (new BoundInfo (u, inversed ? BoundKind.Upper : BoundKind.Lower), pos, false); + return 1; + } + + // If U is an array type + var u_ac = u as ArrayContainer; + if (u_ac != null) { + var v_ac = v as ArrayContainer; + if (v_ac != null) { + if (u_ac.Rank != v_ac.Rank) + return 0; + + if (TypeSpec.IsValueType (u_ac.Element)) + return ExactInference (u_ac.Element, v_ac.Element); + + return LowerBoundInference (u_ac.Element, v_ac.Element, inversed); + } + + if (u_ac.Rank != 1 || !v.IsArrayGenericInterface) + return 0; + + var v_i = TypeManager.GetTypeArguments (v) [0]; + if (TypeSpec.IsValueType (u_ac.Element)) + return ExactInference (u_ac.Element, v_i); + + return LowerBoundInference (u_ac.Element, v_i); + } + + if (v.IsGenericOrParentIsGeneric) { + // + // if V is a constructed type C and there is a unique type C + // such that U is identical to, inherits from (directly or indirectly), + // or implements (directly or indirectly) C + // + var u_candidates = new List (); + var open_v = v.MemberDefinition; + + for (TypeSpec t = u; t != null; t = t.BaseType) { + if (open_v == t.MemberDefinition) + u_candidates.Add (t); + + // + // Using this trick for dynamic type inference, the spec says the type arguments are "unknown" but + // that would complicate the process a lot, instead I treat them as dynamic + // + if (t.BuiltinType == BuiltinTypeSpec.Type.Dynamic) + u_candidates.Add (t); + } + + if (u.Interfaces != null) { + foreach (var iface in u.Interfaces) { + if (open_v == iface.MemberDefinition) + u_candidates.Add (iface); + } + } + + TypeSpec[] unique_candidate_targs = null; + var ga_v = TypeSpec.GetAllTypeArguments (v); + foreach (TypeSpec u_candidate in u_candidates) { + // + // The unique set of types U1..Uk means that if we have an interface I, + // class U : I, I then no type inference is made when inferring + // type I by applying type U because T could be int or long + // + if (unique_candidate_targs != null) { + TypeSpec[] second_unique_candidate_targs = TypeSpec.GetAllTypeArguments (u_candidate); + if (TypeSpecComparer.Equals (unique_candidate_targs, second_unique_candidate_targs)) { + unique_candidate_targs = second_unique_candidate_targs; + continue; + } + + // + // Break when candidate arguments are ambiguous + // + return 0; + } + + // + // A candidate is dynamic type expression, to simplify things use dynamic + // for all type parameter of this type. For methods like this one + // + // void M (IList, IList) + // + // dynamic becomes both T and U when the arguments are of dynamic type + // + if (u_candidate.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + unique_candidate_targs = new TypeSpec[ga_v.Length]; + for (int i = 0; i < unique_candidate_targs.Length; ++i) + unique_candidate_targs[i] = u_candidate; + } else { + unique_candidate_targs = TypeSpec.GetAllTypeArguments (u_candidate); + } + } + + if (unique_candidate_targs != null) { + int score = 0; + int tp_index = -1; + TypeParameterSpec[] tps = null; + + for (int i = 0; i < unique_candidate_targs.Length; ++i) { + if (tp_index < 0) { + while (v.Arity == 0) + v = v.DeclaringType; + + tps = v.MemberDefinition.TypeParameters; + tp_index = tps.Length - 1; + } + + Variance variance = tps [tp_index--].Variance; + + TypeSpec u_i = unique_candidate_targs [i]; + if (variance == Variance.None || TypeSpec.IsValueType (u_i)) { + if (ExactInference (u_i, ga_v [i]) == 0) + ++score; + } else { + bool upper_bound = (variance == Variance.Contravariant && !inversed) || + (variance == Variance.Covariant && inversed); + + if (LowerBoundInference (u_i, ga_v [i], upper_bound) == 0) + ++score; + } + } + + return score; + } + } + + return 0; + } + + // + // 26.3.3.6 Output Type Inference + // + public int OutputTypeInference (ResolveContext ec, Expression e, TypeSpec t) + { + // If e is a lambda or anonymous method with inferred return type + AnonymousMethodExpression ame = e as AnonymousMethodExpression; + if (ame != null) { + TypeSpec rt = ame.InferReturnType (ec, this, t); + var invoke = Delegate.GetInvokeMethod (t); + + if (rt == null) { + AParametersCollection pd = invoke.Parameters; + return ame.Parameters.Count == pd.Count ? 1 : 0; + } + + TypeSpec rtype = invoke.ReturnType; + return LowerBoundInference (rt, rtype) + 1; + } + + // + // if E is a method group and T is a delegate type or expression tree type + // return type Tb with parameter types T1..Tk and return type Tb, and overload + // resolution of E with the types T1..Tk yields a single method with return type U, + // then a lower-bound inference is made from U for Tb. + // + if (e is MethodGroupExpr) { + if (!t.IsDelegate) { + if (!t.IsExpressionTreeType) + return 0; + + t = TypeManager.GetTypeArguments (t)[0]; + } + + var invoke = Delegate.GetInvokeMethod (t); + TypeSpec rtype = invoke.ReturnType; + + if (!IsReturnTypeNonDependent (invoke, rtype)) + return 0; + + // LAMESPEC: Standard does not specify that all methodgroup arguments + // has to be fixed but it does not specify how to do recursive type inference + // either. We choose the simple option and infer return type only + // if all delegate generic arguments are fixed. + TypeSpec[] param_types = new TypeSpec [invoke.Parameters.Count]; + for (int i = 0; i < param_types.Length; ++i) { + var inflated = InflateGenericArgument (ec, invoke.Parameters.Types[i]); + if (inflated == null) + return 0; + + param_types[i] = inflated; + } + + MethodGroupExpr mg = (MethodGroupExpr) e; + Arguments args = DelegateCreation.CreateDelegateMethodArguments (ec, invoke.Parameters, param_types, e.Location); + mg = mg.OverloadResolve (ec, ref args, null, OverloadResolver.Restrictions.CovariantDelegate | OverloadResolver.Restrictions.ProbingOnly); + if (mg == null) + return 0; + + return LowerBoundInference (mg.BestCandidateReturnType, rtype) + 1; + } + + // + // if e is an expression with type U, then + // a lower-bound inference is made from U for T + // + return LowerBoundInference (e.Type, t) * 2; + } + + void RemoveDependentTypes (List types, TypeSpec returnType) + { + int idx = IsUnfixed (returnType); + if (idx >= 0) { + types [idx] = null; + return; + } + + if (TypeManager.IsGenericType (returnType)) { + foreach (TypeSpec t in TypeManager.GetTypeArguments (returnType)) { + RemoveDependentTypes (types, t); + } + } + } + + public bool UnfixedVariableExists { + get { + foreach (TypeSpec ut in fixed_types) { + if (ut == null) + return true; + } + + return false; + } + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/import.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/import.cs new file mode 100644 index 000000000..6dea50a33 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/import.cs @@ -0,0 +1,2352 @@ +// +// import.cs: System.Reflection conversions +// +// Authors: Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2009-2011 Novell, Inc +// Copyright 2011-2012 Xamarin, Inc (http://www.xamarin.com) +// + +using System; +using System.Runtime.CompilerServices; +using System.Linq; +using System.Collections.Generic; + +#if STATIC +using MetaType = IKVM.Reflection.Type; +using IKVM.Reflection; +using IKVM.Reflection.Emit; +#else +using MetaType = System.Type; +using System.Reflection; +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp +{ + public abstract class MetadataImporter + { + // + // Dynamic types reader with additional logic to reconstruct a dynamic + // type using DynamicAttribute values + // + protected struct DynamicTypeReader + { + static readonly bool[] single_attribute = { true }; + + public int Position; + bool[] flags; + + // There is no common type for CustomAttributeData and we cannot + // use ICustomAttributeProvider + object provider; + + // + // A member provider which can be used to get CustomAttributeData + // + public DynamicTypeReader (object provider) + { + Position = 0; + flags = null; + this.provider = provider; + } + + // + // Returns true when object at local position has dynamic attribute flag + // + public bool IsDynamicObject () + { + if (provider != null) + ReadAttribute (); + + return flags != null && Position < flags.Length && flags[Position]; + } + + // + // Returns true when DynamicAttribute exists + // + public bool HasDynamicAttribute () + { + if (provider != null) + ReadAttribute (); + + return flags != null; + } + + IList GetCustomAttributes () + { + var mi = provider as MemberInfo; + if (mi != null) + return CustomAttributeData.GetCustomAttributes (mi); + + var pi = provider as ParameterInfo; + if (pi != null) + return CustomAttributeData.GetCustomAttributes (pi); + + provider = null; + return null; + } + + void ReadAttribute () + { + var cad = GetCustomAttributes (); + if (cad == null) { + return; + } + + if (cad.Count > 0) { + foreach (var ca in cad) { + var dt = ca.Constructor.DeclaringType; + if (dt.Name != "DynamicAttribute" || dt.Namespace != CompilerServicesNamespace) + continue; + + if (ca.ConstructorArguments.Count == 0) { + flags = single_attribute; + break; + } + + var arg_type = ca.ConstructorArguments[0].ArgumentType; + + if (arg_type.IsArray && MetaType.GetTypeCode (arg_type.GetElementType ()) == TypeCode.Boolean) { + var carg = (IList) ca.ConstructorArguments[0].Value; + flags = new bool[carg.Count]; + for (int i = 0; i < flags.Length; ++i) { + if (MetaType.GetTypeCode (carg[i].ArgumentType) == TypeCode.Boolean) + flags[i] = (bool) carg[i].Value; + } + + break; + } + } + } + + provider = null; + } + } + + protected readonly Dictionary import_cache; + protected readonly Dictionary compiled_types; + protected readonly Dictionary assembly_2_definition; + protected readonly ModuleContainer module; + + public static readonly string CompilerServicesNamespace = "System.Runtime.CompilerServices"; + + protected MetadataImporter (ModuleContainer module) + { + this.module = module; + + import_cache = new Dictionary (1024, ReferenceEquality.Default); + compiled_types = new Dictionary (40, ReferenceEquality.Default); + assembly_2_definition = new Dictionary (ReferenceEquality.Default); + IgnorePrivateMembers = true; + } + + #region Properties + + public ICollection Assemblies { + get { + return assembly_2_definition.Values; + } + } + + public bool IgnorePrivateMembers { get; set; } + + #endregion + + public abstract void AddCompiledType (TypeBuilder builder, TypeSpec spec); + protected abstract MemberKind DetermineKindFromBaseType (MetaType baseType); + protected abstract bool HasVolatileModifier (MetaType[] modifiers); + + public FieldSpec CreateField (FieldInfo fi, TypeSpec declaringType) + { + Modifiers mod; + var fa = fi.Attributes; + switch (fa & FieldAttributes.FieldAccessMask) { + case FieldAttributes.Public: + mod = Modifiers.PUBLIC; + break; + case FieldAttributes.Assembly: + mod = Modifiers.INTERNAL; + break; + case FieldAttributes.Family: + mod = Modifiers.PROTECTED; + break; + case FieldAttributes.FamORAssem: + mod = Modifiers.PROTECTED | Modifiers.INTERNAL; + break; + default: + // Ignore private fields (even for error reporting) to not require extra dependencies + if ((IgnorePrivateMembers && !declaringType.IsStruct) || + HasAttribute (CustomAttributeData.GetCustomAttributes (fi), "CompilerGeneratedAttribute", CompilerServicesNamespace)) + return null; + + mod = Modifiers.PRIVATE; + break; + } + + TypeSpec field_type; + + try { + field_type = ImportType (fi.FieldType, new DynamicTypeReader (fi)); + + // + // Private field has private type which is not fixed buffer + // + if (field_type == null) + return null; + } catch (Exception e) { + // TODO: I should construct fake TypeSpec based on TypeRef signature + // but there is no way to do it with System.Reflection + throw new InternalErrorException (e, "Cannot import field `{0}.{1}' referenced in assembly `{2}'", + declaringType.GetSignatureForError (), fi.Name, declaringType.MemberDefinition.DeclaringAssembly); + } + + var definition = new ImportedMemberDefinition (fi, field_type, this); + + if ((fa & FieldAttributes.Literal) != 0) { + Constant c = field_type.Kind == MemberKind.MissingType ? + new NullConstant (InternalType.ErrorType, Location.Null) : + Constant.CreateConstantFromValue (field_type, fi.GetRawConstantValue (), Location.Null); + return new ConstSpec (declaringType, definition, field_type, fi, mod, c); + } + + if ((fa & FieldAttributes.InitOnly) != 0) { + if (field_type.BuiltinType == BuiltinTypeSpec.Type.Decimal) { + var dc = ReadDecimalConstant (CustomAttributeData.GetCustomAttributes (fi)); + if (dc != null) + return new ConstSpec (declaringType, definition, field_type, fi, mod, dc); + } + + mod |= Modifiers.READONLY; + } else { + var req_mod = fi.GetRequiredCustomModifiers (); + if (req_mod.Length > 0 && HasVolatileModifier (req_mod)) + mod |= Modifiers.VOLATILE; + } + + if ((fa & FieldAttributes.Static) != 0) { + mod |= Modifiers.STATIC; + } else { + // Fixed buffers cannot be static + if (declaringType.IsStruct && field_type.IsStruct && field_type.IsNested && + HasAttribute (CustomAttributeData.GetCustomAttributes (fi), "FixedBufferAttribute", CompilerServicesNamespace)) { + + // TODO: Sanity check on field_type (only few types are allowed) + var element_field = CreateField (fi.FieldType.GetField (FixedField.FixedElementName), declaringType); + return new FixedFieldSpec (module, declaringType, definition, fi, element_field, mod); + } + } + + return new FieldSpec (declaringType, definition, field_type, fi, mod); + } + + public EventSpec CreateEvent (EventInfo ei, TypeSpec declaringType, MethodSpec add, MethodSpec remove) + { + add.IsAccessor = true; + remove.IsAccessor = true; + + if (add.Modifiers != remove.Modifiers) + throw new NotImplementedException ("Different accessor modifiers " + ei.Name); + + var event_type = ImportType (ei.EventHandlerType, new DynamicTypeReader (ei)); + var definition = new ImportedMemberDefinition (ei, event_type, this); + return new EventSpec (declaringType, definition, event_type, add.Modifiers, add, remove); + } + + TypeParameterSpec[] CreateGenericParameters (MetaType type, TypeSpec declaringType) + { + var tparams = type.GetGenericArguments (); + + int parent_owned_count; + if (type.IsNested) { + parent_owned_count = type.DeclaringType.GetGenericArguments ().Length; + + // + // System.Reflection duplicates parent type parameters for each + // nested type with slightly modified properties (eg. different owner) + // This just makes things more complicated (think of cloned constraints) + // therefore we remap any nested type owned by parent using `type_cache' + // to the single TypeParameterSpec + // + if (declaringType != null && parent_owned_count > 0) { + int read_count = 0; + while (read_count != parent_owned_count) { + var tparams_count = declaringType.Arity; + if (tparams_count != 0) { + var parent_tp = declaringType.MemberDefinition.TypeParameters; + read_count += tparams_count; + for (int i = 0; i < tparams_count; i++) { + import_cache.Add (tparams[parent_owned_count - read_count + i], parent_tp[i]); + } + } + + declaringType = declaringType.DeclaringType; + } + } + } else { + parent_owned_count = 0; + } + + if (tparams.Length - parent_owned_count == 0) + return null; + + return CreateGenericParameters (parent_owned_count, tparams); + } + + TypeParameterSpec[] CreateGenericParameters (int first, MetaType[] tparams) + { + var tspec = new TypeParameterSpec[tparams.Length - first]; + for (int pos = first; pos < tparams.Length; ++pos) { + var type = tparams[pos]; + int index = pos - first; + + tspec[index] = (TypeParameterSpec) CreateType (type, new DynamicTypeReader (), false); + } + + return tspec; + } + + TypeSpec[] CreateGenericArguments (int first, MetaType[] tparams, DynamicTypeReader dtype) + { + ++dtype.Position; + + var tspec = new TypeSpec [tparams.Length - first]; + for (int pos = first; pos < tparams.Length; ++pos) { + var type = tparams[pos]; + int index = pos - first; + + TypeSpec spec; + if (type.HasElementType) { + var element = type.GetElementType (); + ++dtype.Position; + spec = ImportType (element, dtype); + + if (!type.IsArray) { + throw new NotImplementedException ("Unknown element type " + type.ToString ()); + } + + spec = ArrayContainer.MakeType (module, spec, type.GetArrayRank ()); + } else { + spec = CreateType (type, dtype, true); + + // + // We treat nested generic types as inflated internally where + // reflection uses type definition + // + // class A { + // IFoo> foo; // A is definition in this case + // } + // + if (!IsMissingType (type) && type.IsGenericTypeDefinition) { + var start_pos = spec.DeclaringType == null ? 0 : spec.DeclaringType.MemberDefinition.TypeParametersCount; + var targs = CreateGenericArguments (start_pos, type.GetGenericArguments (), dtype); + spec = spec.MakeGenericType (module, targs); + } + } + + if (spec == null) + return null; + + ++dtype.Position; + tspec[index] = spec; + } + + return tspec; + } + + public MethodSpec CreateMethod (MethodBase mb, TypeSpec declaringType) + { + Modifiers mod = ReadMethodModifiers (mb, declaringType); + TypeParameterSpec[] tparams; + + var parameters = CreateParameters (declaringType, mb.GetParameters (), mb); + + if (mb.IsGenericMethod) { + if (!mb.IsGenericMethodDefinition) + throw new NotSupportedException ("assert"); + + tparams = CreateGenericParameters (0, mb.GetGenericArguments ()); + } else { + tparams = null; + } + + MemberKind kind; + TypeSpec returnType; + if (mb.MemberType == MemberTypes.Constructor) { + kind = MemberKind.Constructor; + returnType = module.Compiler.BuiltinTypes.Void; + } else { + // + // Detect operators and destructors + // + string name = mb.Name; + kind = MemberKind.Method; + if (tparams == null && !mb.DeclaringType.IsInterface && name.Length > 6) { + if ((mod & (Modifiers.STATIC | Modifiers.PUBLIC)) == (Modifiers.STATIC | Modifiers.PUBLIC)) { + if (name[2] == '_' && name[1] == 'p' && name[0] == 'o' && (mb.Attributes & MethodAttributes.SpecialName) != 0) { + var op_type = Operator.GetType (name); + if (op_type.HasValue && parameters.Count > 0 && parameters.Count < 3) { + kind = MemberKind.Operator; + } + } + } else if (parameters.IsEmpty && name == Destructor.MetadataName) { + kind = MemberKind.Destructor; + if (declaringType.BuiltinType == BuiltinTypeSpec.Type.Object) { + mod &= ~Modifiers.OVERRIDE; + mod |= Modifiers.VIRTUAL; + } + } + } + + var mi = (MethodInfo) mb; + returnType = ImportType (mi.ReturnType, new DynamicTypeReader (mi.ReturnParameter)); + + // Cannot set to OVERRIDE without full hierarchy checks + // this flag indicates that the method could be override + // but further validation is needed + if ((mod & Modifiers.OVERRIDE) != 0) { + bool is_real_override = false; + if (kind == MemberKind.Method && declaringType.BaseType != null) { + var btype = declaringType.BaseType; + if (IsOverrideMethodBaseTypeAccessible (btype)) { + var filter = MemberFilter.Method (name, tparams != null ? tparams.Length : 0, parameters, null); + var candidate = MemberCache.FindMember (btype, filter, BindingRestriction.None); + + // + // For imported class method do additional validation to be sure that metadata + // override flag was correct + // + // Difference between protected internal and protected is ok + // + const Modifiers conflict_mask = Modifiers.AccessibilityMask & ~Modifiers.INTERNAL; + if (candidate != null && (candidate.Modifiers & conflict_mask) == (mod & conflict_mask) && !candidate.IsStatic) { + is_real_override = true; + } + } + } + + if (!is_real_override) { + mod &= ~Modifiers.OVERRIDE; + if ((mod & Modifiers.SEALED) != 0) + mod &= ~Modifiers.SEALED; + else + mod |= Modifiers.VIRTUAL; + } + } else if (parameters.HasExtensionMethodType) { + mod |= Modifiers.METHOD_EXTENSION; + } + } + + IMethodDefinition definition; + if (tparams != null) { + var gmd = new ImportedGenericMethodDefinition ((MethodInfo) mb, returnType, parameters, tparams, this); + foreach (var tp in gmd.TypeParameters) { + ImportTypeParameterTypeConstraints (tp, tp.GetMetaInfo ()); + } + + definition = gmd; + } else { + definition = new ImportedMethodDefinition (mb, returnType, parameters, this); + } + + MethodSpec ms = new MethodSpec (kind, declaringType, definition, returnType, parameters, mod); + if (tparams != null) + ms.IsGeneric = true; + + return ms; + } + + bool IsOverrideMethodBaseTypeAccessible (TypeSpec baseType) + { + switch (baseType.Modifiers & Modifiers.AccessibilityMask) { + case Modifiers.PUBLIC: + return true; + case Modifiers.INTERNAL: + // + // Check whether imported method in base type is accessible from compiled + // context + // + return baseType.MemberDefinition.IsInternalAsPublic (module.DeclaringAssembly); + case Modifiers.PRIVATE: + return false; + default: + // protected + // protected internal + // + // Method accessibility checks will be done later based on context + // where the method is called (CS0122 error will be reported for inaccessible) + // + return true; + } + } + + // + // Imports System.Reflection parameters + // + AParametersCollection CreateParameters (TypeSpec parent, ParameterInfo[] pi, MethodBase method) + { + int varargs = method != null && (method.CallingConvention & CallingConventions.VarArgs) != 0 ? 1 : 0; + + if (pi.Length == 0 && varargs == 0) + return ParametersCompiled.EmptyReadOnlyParameters; + + TypeSpec[] types = new TypeSpec[pi.Length + varargs]; + IParameterData[] par = new IParameterData[pi.Length + varargs]; + bool is_params = false; + for (int i = 0; i < pi.Length; i++) { + ParameterInfo p = pi[i]; + Parameter.Modifier mod = 0; + Expression default_value = null; + if (p.ParameterType.IsByRef) { + if ((p.Attributes & (ParameterAttributes.Out | ParameterAttributes.In)) == ParameterAttributes.Out) + mod = Parameter.Modifier.OUT; + else + mod = Parameter.Modifier.REF; + + // + // Strip reference wrapping + // + var el = p.ParameterType.GetElementType (); + types[i] = ImportType (el, new DynamicTypeReader (p)); // TODO: 1-based positio to be csc compatible + } else if (i == 0 && method.IsStatic && (parent.Modifiers & Modifiers.METHOD_EXTENSION) != 0 && + HasAttribute (CustomAttributeData.GetCustomAttributes (method), "ExtensionAttribute", CompilerServicesNamespace)) { + mod = Parameter.Modifier.This; + types[i] = ImportType (p.ParameterType, new DynamicTypeReader (p)); + } else { + types[i] = ImportType (p.ParameterType, new DynamicTypeReader (p)); + + if (i >= pi.Length - 2 && types[i] is ArrayContainer) { + if (HasAttribute (CustomAttributeData.GetCustomAttributes (p), "ParamArrayAttribute", "System")) { + mod = Parameter.Modifier.PARAMS; + is_params = true; + } + } + + if (!is_params && p.IsOptional) { + object value = p.RawDefaultValue; + var ptype = types[i]; + if ((p.Attributes & ParameterAttributes.HasDefault) != 0 && ptype.Kind != MemberKind.TypeParameter && (value != null || TypeSpec.IsReferenceType (ptype))) { + if (value == null) { + default_value = Constant.CreateConstantFromValue (ptype, null, Location.Null); + } else { + default_value = ImportParameterConstant (value); + + if (ptype.IsEnum) { + default_value = new EnumConstant ((Constant) default_value, ptype); + } + } + + var attrs = CustomAttributeData.GetCustomAttributes (p); + for (int ii = 0; ii < attrs.Count; ++ii) { + var attr = attrs[ii]; + var dt = attr.Constructor.DeclaringType; + if (dt.Namespace != CompilerServicesNamespace) + continue; + + if (dt.Name == "CallerLineNumberAttribute" && (ptype.BuiltinType == BuiltinTypeSpec.Type.Int || Convert.ImplicitNumericConversionExists (module.Compiler.BuiltinTypes.Int, ptype))) + mod |= Parameter.Modifier.CallerLineNumber; + else if (dt.Name == "CallerFilePathAttribute" && Convert.ImplicitReferenceConversionExists (module.Compiler.BuiltinTypes.String, ptype)) + mod |= Parameter.Modifier.CallerFilePath; + else if (dt.Name == "CallerMemberNameAttribute" && Convert.ImplicitReferenceConversionExists (module.Compiler.BuiltinTypes.String, ptype)) + mod |= Parameter.Modifier.CallerMemberName; + } + } else if (value == Missing.Value) { + default_value = EmptyExpression.MissingValue; + } else if (value == null) { + default_value = new DefaultValueExpression (new TypeExpression (ptype, Location.Null), Location.Null); + } else if (ptype.BuiltinType == BuiltinTypeSpec.Type.Decimal) { + default_value = ImportParameterConstant (value); + } + } + } + + par[i] = new ParameterData (p.Name, mod, default_value); + } + + if (varargs != 0) { + par[par.Length - 1] = new ArglistParameter (Location.Null); + types[types.Length - 1] = InternalType.Arglist; + } + + return method != null ? + new ParametersImported (par, types, varargs != 0, is_params) : + new ParametersImported (par, types, is_params); + } + + // + // Returns null when the property is not valid C# property + // + public PropertySpec CreateProperty (PropertyInfo pi, TypeSpec declaringType, MethodSpec get, MethodSpec set) + { + Modifiers mod = 0; + AParametersCollection param = null; + TypeSpec type = null; + if (get != null) { + mod = get.Modifiers; + param = get.Parameters; + type = get.ReturnType; + } + + bool is_valid_property = true; + if (set != null) { + if (set.ReturnType.Kind != MemberKind.Void) + is_valid_property = false; + + var set_param_count = set.Parameters.Count - 1; + + if (set_param_count < 0) { + set_param_count = 0; + is_valid_property = false; + } + + var set_type = set.Parameters.Types[set_param_count]; + + if (mod == 0) { + AParametersCollection set_based_param; + + if (set_param_count == 0) { + set_based_param = ParametersCompiled.EmptyReadOnlyParameters; + } else { + // + // Create indexer parameters based on setter method parameters (the last parameter has to be removed) + // + var data = new IParameterData[set_param_count]; + var types = new TypeSpec[set_param_count]; + Array.Copy (set.Parameters.FixedParameters, data, set_param_count); + Array.Copy (set.Parameters.Types, types, set_param_count); + set_based_param = new ParametersImported (data, types, set.Parameters.HasParams); + } + + mod = set.Modifiers; + param = set_based_param; + type = set_type; + } else { + if (set_param_count != get.Parameters.Count) + is_valid_property = false; + + if (get.ReturnType != set_type) + is_valid_property = false; + + // Possible custom accessor modifiers + if ((mod & Modifiers.AccessibilityMask) != (set.Modifiers & Modifiers.AccessibilityMask)) { + var get_acc = mod & Modifiers.AccessibilityMask; + if (get_acc != Modifiers.PUBLIC) { + var set_acc = set.Modifiers & Modifiers.AccessibilityMask; + // If the accessor modifiers are not same, do extra restriction checks + if (get_acc != set_acc) { + var get_restr = ModifiersExtensions.IsRestrictedModifier (get_acc, set_acc); + var set_restr = ModifiersExtensions.IsRestrictedModifier (set_acc, get_acc); + if (get_restr && set_restr) { + is_valid_property = false; // Neither is more restrictive + } + + if (get_restr) { + mod &= ~Modifiers.AccessibilityMask; + mod |= set_acc; + } + } + } + } + } + } + + PropertySpec spec = null; + if (!param.IsEmpty) { + if (is_valid_property) { + var index_name = declaringType.MemberDefinition.GetAttributeDefaultMember (); + if (index_name == null) { + is_valid_property = false; + } else { + if (get != null) { + if (get.IsStatic) + is_valid_property = false; + if (get.Name.IndexOf (index_name, StringComparison.Ordinal) != 4) + is_valid_property = false; + } + if (set != null) { + if (set.IsStatic) + is_valid_property = false; + if (set.Name.IndexOf (index_name, StringComparison.Ordinal) != 4) + is_valid_property = false; + } + } + + if (is_valid_property) { + spec = new IndexerSpec (declaringType, new ImportedParameterMemberDefinition (pi, type, param, this), type, param, pi, mod); + } else if (declaringType.MemberDefinition.IsComImport && param.FixedParameters[0].HasDefaultValue) { + // + // Enables support for properties with parameters (must have default value) of COM-imported types + // + is_valid_property = true; + + for (int i = 0; i < param.FixedParameters.Length; ++i) { + if (!param.FixedParameters[i].HasDefaultValue) { + is_valid_property = false; + break; + } + } + } + } + } + + if (spec == null) + spec = new PropertySpec (MemberKind.Property, declaringType, new ImportedMemberDefinition (pi, type, this), type, pi, mod); + + if (!is_valid_property) { + spec.IsNotCSharpCompatible = true; + return spec; + } + + if (set != null) + spec.Set = set; + if (get != null) + spec.Get = get; + + return spec; + } + + public TypeSpec CreateType (MetaType type) + { + return CreateType (type, new DynamicTypeReader (), true); + } + + public TypeSpec CreateNestedType (MetaType type, TypeSpec declaringType) + { + return CreateType (type, declaringType, new DynamicTypeReader (type), false); + } + + TypeSpec CreateType (MetaType type, DynamicTypeReader dtype, bool canImportBaseType) + { + TypeSpec declaring_type; + if (type.IsNested && !type.IsGenericParameter) + declaring_type = CreateType (type.DeclaringType, new DynamicTypeReader (type.DeclaringType), true); + else + declaring_type = null; + + return CreateType (type, declaring_type, dtype, canImportBaseType); + } + + protected TypeSpec CreateType (MetaType type, TypeSpec declaringType, DynamicTypeReader dtype, bool canImportBaseType) + { + TypeSpec spec; + if (import_cache.TryGetValue (type, out spec)) { + if (spec.BuiltinType == BuiltinTypeSpec.Type.Object) { + if (dtype.IsDynamicObject ()) + return module.Compiler.BuiltinTypes.Dynamic; + + return spec; + } + + if (!spec.IsGeneric || type.IsGenericTypeDefinition) + return spec; + + if (!dtype.HasDynamicAttribute ()) + return spec; + + // We've found same object in the cache but this one has a dynamic custom attribute + // and it's most likely dynamic version of same type IFoo agains IFoo + // Do type resolve process again in that case + + // TODO: Handle cases where they still unify + } + + if (IsMissingType (type)) { + spec = new TypeSpec (MemberKind.MissingType, declaringType, new ImportedTypeDefinition (type, this), type, Modifiers.PUBLIC); + spec.MemberCache = MemberCache.Empty; + import_cache.Add (type, spec); + return spec; + } + + if (type.IsGenericType && !type.IsGenericTypeDefinition) { + var type_def = type.GetGenericTypeDefinition (); + + // Generic type definition can also be forwarded + if (compiled_types.TryGetValue (type_def, out spec)) + return spec; + + var targs = CreateGenericArguments (0, type.GetGenericArguments (), dtype); + if (targs == null) + return null; + if (declaringType == null) { + // Simple case, no nesting + spec = CreateType (type_def, null, new DynamicTypeReader (), canImportBaseType); + spec = spec.MakeGenericType (module, targs); + } else { + // + // Nested type case, converting .NET types like + // A`1.B`1.C`1 to typespec like + // A.B.C + // + var nested_hierarchy = new List (); + while (declaringType.IsNested) { + nested_hierarchy.Add (declaringType); + declaringType = declaringType.DeclaringType; + } + + int targs_pos = 0; + if (declaringType.Arity > 0) { + spec = declaringType.MakeGenericType (module, targs.Skip (targs_pos).Take (declaringType.Arity).ToArray ()); + targs_pos = spec.Arity; + } else { + spec = declaringType; + } + + for (int i = nested_hierarchy.Count; i != 0; --i) { + var t = nested_hierarchy [i - 1]; + if (t.Kind == MemberKind.MissingType) + spec = t; + else + spec = MemberCache.FindNestedType (spec, t.Name, t.Arity); + + if (t.Arity > 0) { + spec = spec.MakeGenericType (module, targs.Skip (targs_pos).Take (spec.Arity).ToArray ()); + targs_pos += t.Arity; + } + } + + if (spec.Kind == MemberKind.MissingType) { + spec = new TypeSpec (MemberKind.MissingType, spec, new ImportedTypeDefinition (type_def, this), type_def, Modifiers.PUBLIC); + spec.MemberCache = MemberCache.Empty; + } else { + if ((type_def.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedPrivate && IgnorePrivateMembers) + return null; + + string name = type.Name; + int index = name.IndexOf ('`'); + if (index > 0) + name = name.Substring (0, index); + + spec = MemberCache.FindNestedType (spec, name, targs.Length - targs_pos); + + if (spec.Arity > 0) { + spec = spec.MakeGenericType (module, targs.Skip (targs_pos).ToArray ()); + } + } + } + + // Don't add generic type with dynamic arguments, they can interfere with same type + // using object type arguments + if (!spec.HasDynamicElement) { + + // Add to reading cache to speed up reading + if (!import_cache.ContainsKey (type)) + import_cache.Add (type, spec); + } + + return spec; + } + + Modifiers mod; + MemberKind kind; + + var ma = type.Attributes; + switch (ma & TypeAttributes.VisibilityMask) { + case TypeAttributes.Public: + case TypeAttributes.NestedPublic: + mod = Modifiers.PUBLIC; + break; + case TypeAttributes.NestedPrivate: + mod = Modifiers.PRIVATE; + break; + case TypeAttributes.NestedFamily: + mod = Modifiers.PROTECTED; + break; + case TypeAttributes.NestedFamORAssem: + mod = Modifiers.PROTECTED | Modifiers.INTERNAL; + break; + default: + mod = Modifiers.INTERNAL; + break; + } + + if ((ma & TypeAttributes.Interface) != 0) { + kind = MemberKind.Interface; + } else if (type.IsGenericParameter) { + kind = MemberKind.TypeParameter; + } else { + var base_type = type.BaseType; + if (base_type == null || (ma & TypeAttributes.Abstract) != 0) { + kind = MemberKind.Class; + } else { + kind = DetermineKindFromBaseType (base_type); + if (kind == MemberKind.Struct || kind == MemberKind.Delegate) { + mod |= Modifiers.SEALED; + } + } + + if (kind == MemberKind.Class) { + if ((ma & TypeAttributes.Sealed) != 0) { + if ((ma & TypeAttributes.Abstract) != 0) + mod |= Modifiers.STATIC; + else + mod |= Modifiers.SEALED; + } else if ((ma & TypeAttributes.Abstract) != 0) { + mod |= Modifiers.ABSTRACT; + } + } + } + + var definition = new ImportedTypeDefinition (type, this); + TypeSpec pt; + + if (kind == MemberKind.Enum) { + const BindingFlags underlying_member = BindingFlags.DeclaredOnly | + BindingFlags.Instance | + BindingFlags.Public | BindingFlags.NonPublic; + + var type_members = type.GetFields (underlying_member); + foreach (var type_member in type_members) { + spec = new EnumSpec (declaringType, definition, CreateType (type_member.FieldType), type, mod); + break; + } + + if (spec == null) + kind = MemberKind.Class; + + } else if (kind == MemberKind.TypeParameter) { + spec = CreateTypeParameter (type, declaringType); + } else if (type.IsGenericTypeDefinition) { + definition.TypeParameters = CreateGenericParameters (type, declaringType); + } else if (compiled_types.TryGetValue (type, out pt)) { + // + // Same type was found in inside compiled types. It's + // either build-in type or forward referenced typed + // which point into just compiled assembly. + // + spec = pt; + BuiltinTypeSpec bts = pt as BuiltinTypeSpec; + if (bts != null) + bts.SetDefinition (definition, type, mod); + } + + if (spec == null) + spec = new TypeSpec (kind, declaringType, definition, type, mod); + + import_cache.Add (type, spec); + + if (kind == MemberKind.TypeParameter) { + if (canImportBaseType) + ImportTypeParameterTypeConstraints ((TypeParameterSpec) spec, type); + + return spec; + } + + // + // Two stage setup as the base type can be inflated declaring type or + // another nested type inside same declaring type which has not been + // loaded, therefore we can import a base type of nested types once + // the types have been imported + // + if (canImportBaseType) + ImportTypeBase (spec, type); + + return spec; + } + + public IAssemblyDefinition GetAssemblyDefinition (Assembly assembly) + { + IAssemblyDefinition found; + if (!assembly_2_definition.TryGetValue (assembly, out found)) { + + // This can happen in dynamic context only + var def = new ImportedAssemblyDefinition (assembly); + assembly_2_definition.Add (assembly, def); + def.ReadAttributes (); + found = def; + } + + return found; + } + + public void ImportTypeBase (MetaType type) + { + TypeSpec spec = import_cache[type]; + if (spec != null) + ImportTypeBase (spec, type); + } + + TypeParameterSpec CreateTypeParameter (MetaType type, TypeSpec declaringType) + { + Variance variance; + switch (type.GenericParameterAttributes & GenericParameterAttributes.VarianceMask) { + case GenericParameterAttributes.Covariant: + variance = Variance.Covariant; + break; + case GenericParameterAttributes.Contravariant: + variance = Variance.Contravariant; + break; + default: + variance = Variance.None; + break; + } + + SpecialConstraint special = SpecialConstraint.None; + var import_special = type.GenericParameterAttributes & GenericParameterAttributes.SpecialConstraintMask; + + if ((import_special & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0) { + special |= SpecialConstraint.Struct; + } else if ((import_special & GenericParameterAttributes.DefaultConstructorConstraint) != 0) { + special = SpecialConstraint.Constructor; + } + + if ((import_special & GenericParameterAttributes.ReferenceTypeConstraint) != 0) { + special |= SpecialConstraint.Class; + } + + TypeParameterSpec spec; + var def = new ImportedTypeParameterDefinition (type, this); + if (type.DeclaringMethod != null) { + spec = new TypeParameterSpec (type.GenericParameterPosition, def, special, variance, type); + } else { + spec = new TypeParameterSpec (declaringType, type.GenericParameterPosition, def, special, variance, type); + } + + return spec; + } + + // + // Test for a custom attribute type match. Custom attributes are not really predefined globaly + // they can be assembly specific therefore we do check based on names only + // + public static bool HasAttribute (IList attributesData, string attrName, string attrNamespace) + { + if (attributesData.Count == 0) + return false; + + foreach (var attr in attributesData) { + var dt = attr.Constructor.DeclaringType; + if (dt.Name == attrName && dt.Namespace == attrNamespace) + return true; + } + + return false; + } + + void ImportTypeBase (TypeSpec spec, MetaType type) + { + if (spec.Kind == MemberKind.Interface) + spec.BaseType = module.Compiler.BuiltinTypes.Object; + else if (type.BaseType != null) { + TypeSpec base_type; + if (!IsMissingType (type.BaseType) && type.BaseType.IsGenericType) + base_type = CreateType (type.BaseType, new DynamicTypeReader (type), true); + else + base_type = CreateType (type.BaseType); + + spec.BaseType = base_type; + } + + if (spec.MemberDefinition.TypeParametersCount > 0) { + foreach (var tp in spec.MemberDefinition.TypeParameters) { + ImportTypeParameterTypeConstraints (tp, tp.GetMetaInfo ()); + } + } + } + + protected void ImportTypes (MetaType[] types, Namespace targetNamespace, bool importExtensionTypes) + { + Namespace ns = targetNamespace; + string prev_namespace = null; + foreach (var t in types) { + if (t == null) + continue; + + // Be careful not to trigger full parent type loading + if (t.MemberType == MemberTypes.NestedType) + continue; + + if (t.Name[0] == '<') + continue; + + var it = CreateType (t, null, new DynamicTypeReader (t), true); + if (it == null) + continue; + + if (prev_namespace != t.Namespace) { + ns = t.Namespace == null ? targetNamespace : targetNamespace.GetNamespace (t.Namespace, true); + prev_namespace = t.Namespace; + } + + // Cannot rely on assembly level Extension attribute or static modifier because they + // are not followed by other compilers (e.g. F#). + if (it.IsClass && it.Arity == 0 && importExtensionTypes && + HasAttribute (CustomAttributeData.GetCustomAttributes (t), "ExtensionAttribute", CompilerServicesNamespace)) { + it.SetExtensionMethodContainer (); + } + + ns.AddType (module, it); + } + } + + void ImportTypeParameterTypeConstraints (TypeParameterSpec spec, MetaType type) + { + var constraints = type.GetGenericParameterConstraints (); + List tparams = null; + foreach (var ct in constraints) { + if (ct.IsGenericParameter) { + if (tparams == null) + tparams = new List (); + + tparams.Add (CreateType (ct)); + continue; + } + + var constraint_type = CreateType (ct); + if (constraint_type.IsClass) { + spec.BaseType = constraint_type; + continue; + } + + spec.AddInterface (constraint_type); + } + + if (spec.BaseType == null) + spec.BaseType = module.Compiler.BuiltinTypes.Object; + + if (tparams != null) + spec.TypeArguments = tparams.ToArray (); + } + + Constant ImportParameterConstant (object value) + { + // + // Get type of underlying value as int constant can be used for object + // parameter type. This is not allowed in C# but other languages can do that + // + var types = module.Compiler.BuiltinTypes; + switch (System.Type.GetTypeCode (value.GetType ())) { + case TypeCode.Boolean: + return new BoolConstant (types, (bool) value, Location.Null); + case TypeCode.Byte: + return new ByteConstant (types, (byte) value, Location.Null); + case TypeCode.Char: + return new CharConstant (types, (char) value, Location.Null); + case TypeCode.Decimal: + return new DecimalConstant (types, (decimal) value, Location.Null); + case TypeCode.Double: + return new DoubleConstant (types, (double) value, Location.Null); + case TypeCode.Int16: + return new ShortConstant (types, (short) value, Location.Null); + case TypeCode.Int32: + return new IntConstant (types, (int) value, Location.Null); + case TypeCode.Int64: + return new LongConstant (types, (long) value, Location.Null); + case TypeCode.SByte: + return new SByteConstant (types, (sbyte) value, Location.Null); + case TypeCode.Single: + return new FloatConstant (types, (float) value, Location.Null); + case TypeCode.String: + return new StringConstant (types, (string) value, Location.Null); + case TypeCode.UInt16: + return new UShortConstant (types, (ushort) value, Location.Null); + case TypeCode.UInt32: + return new UIntConstant (types, (uint) value, Location.Null); + case TypeCode.UInt64: + return new ULongConstant (types, (ulong) value, Location.Null); + } + + throw new NotImplementedException (value.GetType ().ToString ()); + } + + public TypeSpec ImportType (MetaType type) + { + return ImportType (type, new DynamicTypeReader (type)); + } + + TypeSpec ImportType (MetaType type, DynamicTypeReader dtype) + { + if (type.HasElementType) { + var element = type.GetElementType (); + ++dtype.Position; + var spec = ImportType (element, dtype); + + if (type.IsArray) + return ArrayContainer.MakeType (module, spec, type.GetArrayRank ()); + if (type.IsByRef) + return ReferenceContainer.MakeType (module, spec); + if (type.IsPointer) + return PointerContainer.MakeType (module, spec); + + throw new NotImplementedException ("Unknown element type " + type.ToString ()); + } + + TypeSpec compiled_type; + if (compiled_types.TryGetValue (type, out compiled_type)) { + if (compiled_type.BuiltinType == BuiltinTypeSpec.Type.Object && dtype.IsDynamicObject ()) + return module.Compiler.BuiltinTypes.Dynamic; + + return compiled_type; + } + + return CreateType (type, dtype, true); + } + + static bool IsMissingType (MetaType type) + { +#if STATIC + return type.__IsMissing; +#else + return false; +#endif + } + + // + // Decimal constants cannot be encoded in the constant blob, and thus are marked + // as IsInitOnly ('readonly' in C# parlance). We get its value from the + // DecimalConstantAttribute metadata. + // + Constant ReadDecimalConstant (IList attrs) + { + if (attrs.Count == 0) + return null; + + foreach (var ca in attrs) { + var dt = ca.Constructor.DeclaringType; + if (dt.Name != "DecimalConstantAttribute" || dt.Namespace != CompilerServicesNamespace) + continue; + + var value = new decimal ( + (int) (uint) ca.ConstructorArguments[4].Value, + (int) (uint) ca.ConstructorArguments[3].Value, + (int) (uint) ca.ConstructorArguments[2].Value, + (byte) ca.ConstructorArguments[1].Value != 0, + (byte) ca.ConstructorArguments[0].Value); + + return new DecimalConstant (module.Compiler.BuiltinTypes, value, Location.Null); + } + + return null; + } + + static Modifiers ReadMethodModifiers (MethodBase mb, TypeSpec declaringType) + { + Modifiers mod; + var ma = mb.Attributes; + switch (ma & MethodAttributes.MemberAccessMask) { + case MethodAttributes.Public: + mod = Modifiers.PUBLIC; + break; + case MethodAttributes.Assembly: + mod = Modifiers.INTERNAL; + break; + case MethodAttributes.Family: + mod = Modifiers.PROTECTED; + break; + case MethodAttributes.FamORAssem: + mod = Modifiers.PROTECTED | Modifiers.INTERNAL; + break; + default: + mod = Modifiers.PRIVATE; + break; + } + + if ((ma & MethodAttributes.Static) != 0) { + mod |= Modifiers.STATIC; + return mod; + } + if ((ma & MethodAttributes.Abstract) != 0 && declaringType.IsClass) { + mod |= Modifiers.ABSTRACT; + return mod; + } + + // It can be sealed and override + if ((ma & MethodAttributes.Final) != 0) + mod |= Modifiers.SEALED; + + if ((ma & MethodAttributes.Virtual) != 0) { + // Not every member can be detected based on MethodAttribute, we + // set virtual or non-virtual only when we are certain. Further checks + // to really find out what `virtual' means for this member are done + // later + if ((ma & MethodAttributes.NewSlot) != 0) { + if ((mod & Modifiers.SEALED) != 0) { + mod &= ~Modifiers.SEALED; + } else { + mod |= Modifiers.VIRTUAL; + } + } else { + mod |= Modifiers.OVERRIDE; + } + } + + return mod; + } + } + + abstract class ImportedDefinition : IMemberDefinition + { + protected class AttributesBag + { + public static readonly AttributesBag Default = new AttributesBag (); + + public AttributeUsageAttribute AttributeUsage; + public ObsoleteAttribute Obsolete; + public string[] Conditionals; + public string DefaultIndexerName; + public bool? CLSAttributeValue; + public TypeSpec CoClass; + + static bool HasMissingType (ConstructorInfo ctor) + { +#if STATIC + // + // Mimic odd csc behaviour where missing type on predefined + // attributes means the attribute is silently ignored. This can + // happen with PCL facades + // + foreach (var p in ctor.GetParameters ()) { + if (p.ParameterType.__ContainsMissingType) + return true; + } +#endif + + return false; + } + + public static AttributesBag Read (MemberInfo mi, MetadataImporter importer) + { + AttributesBag bag = null; + List conditionals = null; + + // It should not throw any loading exception + IList attrs = CustomAttributeData.GetCustomAttributes (mi); + + foreach (var a in attrs) { + var dt = a.Constructor.DeclaringType; + string name = dt.Name; + if (name == "ObsoleteAttribute") { + if (dt.Namespace != "System") + continue; + + if (bag == null) + bag = new AttributesBag (); + + var args = a.ConstructorArguments; + + if (args.Count == 1) { + bag.Obsolete = new ObsoleteAttribute ((string) args[0].Value); + } else if (args.Count == 2) { + bag.Obsolete = new ObsoleteAttribute ((string) args[0].Value, (bool) args[1].Value); + } else { + bag.Obsolete = new ObsoleteAttribute (); + } + + continue; + } + + if (name == "ConditionalAttribute") { + if (dt.Namespace != "System.Diagnostics") + continue; + + if (bag == null) + bag = new AttributesBag (); + + if (conditionals == null) + conditionals = new List (2); + + conditionals.Add ((string) a.ConstructorArguments[0].Value); + continue; + } + + if (name == "CLSCompliantAttribute") { + if (dt.Namespace != "System") + continue; + + if (bag == null) + bag = new AttributesBag (); + + bag.CLSAttributeValue = (bool) a.ConstructorArguments[0].Value; + continue; + } + + // Type only attributes + if (mi.MemberType == MemberTypes.TypeInfo || mi.MemberType == MemberTypes.NestedType) { + if (name == "DefaultMemberAttribute") { + if (dt.Namespace != "System.Reflection") + continue; + + if (bag == null) + bag = new AttributesBag (); + + bag.DefaultIndexerName = (string) a.ConstructorArguments[0].Value; + continue; + } + + if (name == "AttributeUsageAttribute") { + if (dt.Namespace != "System") + continue; + + if (HasMissingType (a.Constructor)) + continue; + + if (bag == null) + bag = new AttributesBag (); + + bag.AttributeUsage = new AttributeUsageAttribute ((AttributeTargets) a.ConstructorArguments[0].Value); + foreach (var named in a.NamedArguments) { + if (named.MemberInfo.Name == "AllowMultiple") + bag.AttributeUsage.AllowMultiple = (bool) named.TypedValue.Value; + else if (named.MemberInfo.Name == "Inherited") + bag.AttributeUsage.Inherited = (bool) named.TypedValue.Value; + } + continue; + } + + // Interface only attribute + if (name == "CoClassAttribute") { + if (dt.Namespace != "System.Runtime.InteropServices") + continue; + + if (HasMissingType (a.Constructor)) + continue; + + if (bag == null) + bag = new AttributesBag (); + + bag.CoClass = importer.ImportType ((MetaType) a.ConstructorArguments[0].Value); + continue; + } + } + } + + if (bag == null) + return Default; + + if (conditionals != null) + bag.Conditionals = conditionals.ToArray (); + + return bag; + } + } + + protected readonly MemberInfo provider; + protected AttributesBag cattrs; + protected readonly MetadataImporter importer; + + protected ImportedDefinition (MemberInfo provider, MetadataImporter importer) + { + this.provider = provider; + this.importer = importer; + } + + #region Properties + + public bool IsImported { + get { + return true; + } + } + + public virtual string Name { + get { + return provider.Name; + } + } + + #endregion + + public string[] ConditionalConditions () + { + if (cattrs == null) + ReadAttributes (); + + return cattrs.Conditionals; + } + + public ObsoleteAttribute GetAttributeObsolete () + { + if (cattrs == null) + ReadAttributes (); + + return cattrs.Obsolete; + } + + public bool? CLSAttributeValue { + get { + if (cattrs == null) + ReadAttributes (); + + return cattrs.CLSAttributeValue; + } + } + + protected void ReadAttributes () + { + cattrs = AttributesBag.Read (provider, importer); + } + + public void SetIsAssigned () + { + // Unused for imported members + } + + public void SetIsUsed () + { + // Unused for imported members + } + } + + public class ImportedModuleDefinition + { + readonly Module module; + bool cls_compliant; + + public ImportedModuleDefinition (Module module) + { + this.module = module; + } + + #region Properties + + public bool IsCLSCompliant { + get { + return cls_compliant; + } + } + + public string Name { + get { + return module.Name; + } + } + + #endregion + + public void ReadAttributes () + { + IList attrs = CustomAttributeData.GetCustomAttributes (module); + + foreach (var a in attrs) { + var dt = a.Constructor.DeclaringType; + if (dt.Name == "CLSCompliantAttribute") { + if (dt.Namespace != "System") + continue; + + cls_compliant = (bool) a.ConstructorArguments[0].Value; + continue; + } + } + } + + // + // Reads assembly attributes which where attached to a special type because + // module does have assembly manifest + // + public List ReadAssemblyAttributes () + { + var t = module.GetType (AssemblyAttributesPlaceholder.GetGeneratedName (Name)); + if (t == null) + return null; + + var field = t.GetField (AssemblyAttributesPlaceholder.AssemblyFieldName, BindingFlags.NonPublic | BindingFlags.Static); + if (field == null) + return null; + + // TODO: implement, the idea is to fabricate specil Attribute class and + // add it to OptAttributes before resolving the source code attributes + // Need to build module location as well for correct error reporting + + //var assembly_attributes = CustomAttributeData.GetCustomAttributes (field); + //var attrs = new List (assembly_attributes.Count); + //foreach (var a in assembly_attributes) + //{ + // var type = metaImporter.ImportType (a.Constructor.DeclaringType); + // var ctor = metaImporter.CreateMethod (a.Constructor, type); + + // foreach (var carg in a.ConstructorArguments) { + // carg.Value + // } + + // attrs.Add (new Attribute ("assembly", ctor, null, Location.Null, true)); + //} + + return null; + } + } + + public class ImportedAssemblyDefinition : IAssemblyDefinition + { + readonly Assembly assembly; + readonly AssemblyName aname; + bool cls_compliant; + + List internals_visible_to; + Dictionary internals_visible_to_cache; + + public ImportedAssemblyDefinition (Assembly assembly) + { + this.assembly = assembly; + this.aname = assembly.GetName (); + } + + #region Properties + + public Assembly Assembly { + get { + return assembly; + } + } + + public string FullName { + get { + return aname.FullName; + } + } + + public bool HasStrongName { + get { + return aname.GetPublicKey ().Length != 0; + } + } + + public bool IsMissing { + get { +#if STATIC + return assembly.__IsMissing; +#else + return false; +#endif + } + } + + public bool IsCLSCompliant { + get { + return cls_compliant; + } + } + + public string Location { + get { + return assembly.Location; + } + } + + public string Name { + get { + return aname.Name; + } + } + + #endregion + + public byte[] GetPublicKeyToken () + { + return aname.GetPublicKeyToken (); + } + + public AssemblyName GetAssemblyVisibleToName (IAssemblyDefinition assembly) + { + return internals_visible_to_cache [assembly]; + } + + public bool IsFriendAssemblyTo (IAssemblyDefinition assembly) + { + if (internals_visible_to == null) + return false; + + AssemblyName is_visible = null; + if (internals_visible_to_cache == null) { + internals_visible_to_cache = new Dictionary (); + } else { + if (internals_visible_to_cache.TryGetValue (assembly, out is_visible)) + return is_visible != null; + } + + var token = assembly.GetPublicKeyToken (); + if (token != null && token.Length == 0) + token = null; + + foreach (var internals in internals_visible_to) { + if (internals.Name != assembly.Name) + continue; + + if (token == null && assembly is AssemblyDefinition) { + is_visible = internals; + break; + } + + if (!ArrayComparer.IsEqual (token, internals.GetPublicKeyToken ())) + continue; + + is_visible = internals; + break; + } + + internals_visible_to_cache.Add (assembly, is_visible); + return is_visible != null; + } + + public void ReadAttributes () + { +#if STATIC + if (assembly.__IsMissing) + return; +#endif + + IList attrs = CustomAttributeData.GetCustomAttributes (assembly); + + foreach (var a in attrs) { + var dt = a.Constructor.DeclaringType; + var name = dt.Name; + if (name == "CLSCompliantAttribute") { + if (dt.Namespace == "System") { + cls_compliant = (bool) a.ConstructorArguments[0].Value; + } + continue; + } + + if (name == "InternalsVisibleToAttribute") { + if (dt.Namespace != MetadataImporter.CompilerServicesNamespace) + continue; + + string s = a.ConstructorArguments[0].Value as string; + if (s == null) + continue; + + var an = new AssemblyName (s); + if (internals_visible_to == null) + internals_visible_to = new List (); + + internals_visible_to.Add (an); + continue; + } + } + } + + public override string ToString () + { + return FullName; + } + } + + class ImportedMemberDefinition : ImportedDefinition + { + readonly TypeSpec type; + + public ImportedMemberDefinition (MemberInfo member, TypeSpec type, MetadataImporter importer) + : base (member, importer) + { + this.type = type; + } + + #region Properties + + public TypeSpec MemberType { + get { + return type; + } + } + + #endregion + } + + class ImportedParameterMemberDefinition : ImportedMemberDefinition, IParametersMember + { + readonly AParametersCollection parameters; + + protected ImportedParameterMemberDefinition (MethodBase provider, TypeSpec type, AParametersCollection parameters, MetadataImporter importer) + : base (provider, type, importer) + { + this.parameters = parameters; + } + + public ImportedParameterMemberDefinition (PropertyInfo provider, TypeSpec type, AParametersCollection parameters, MetadataImporter importer) + : base (provider, type, importer) + { + this.parameters = parameters; + } + + #region Properties + + public AParametersCollection Parameters { + get { + return parameters; + } + } + + #endregion + } + + class ImportedMethodDefinition : ImportedParameterMemberDefinition, IMethodDefinition + { + public ImportedMethodDefinition (MethodBase provider, TypeSpec type, AParametersCollection parameters, MetadataImporter importer) + : base (provider, type, parameters, importer) + { + } + + MethodBase IMethodDefinition.Metadata { + get { + return (MethodBase) provider; + } + } + } + + class ImportedGenericMethodDefinition : ImportedMethodDefinition, IGenericMethodDefinition + { + readonly TypeParameterSpec[] tparams; + + public ImportedGenericMethodDefinition (MethodInfo provider, TypeSpec type, AParametersCollection parameters, TypeParameterSpec[] tparams, MetadataImporter importer) + : base (provider, type, parameters, importer) + { + this.tparams = tparams; + } + + #region Properties + + public TypeParameterSpec[] TypeParameters { + get { + return tparams; + } + } + + public int TypeParametersCount { + get { + return tparams.Length; + } + } + + #endregion + } + + class ImportedTypeDefinition : ImportedDefinition, ITypeDefinition + { + TypeParameterSpec[] tparams; + string name; + + public ImportedTypeDefinition (MetaType type, MetadataImporter importer) + : base (type, importer) + { + } + + #region Properties + + public IAssemblyDefinition DeclaringAssembly { + get { + return importer.GetAssemblyDefinition (provider.Module.Assembly); + } + } + + bool ITypeDefinition.IsComImport { + get { + return ((MetaType) provider).IsImport; + } + } + + + bool ITypeDefinition.IsPartial { + get { + return false; + } + } + + bool ITypeDefinition.IsTypeForwarder { + get { +#if STATIC + return ((MetaType) provider).__IsTypeForwarder; +#else + return false; +#endif + } + } + + bool ITypeDefinition.IsCyclicTypeForwarder { + get { +#if STATIC + return ((MetaType) provider).__IsCyclicTypeForwarder; +#else + return false; +#endif + } + } + + public override string Name { + get { + if (name == null) { + name = base.Name; + if (tparams != null) { + int arity_start = name.IndexOf ('`'); + if (arity_start > 0) + name = name.Substring (0, arity_start); + } + } + + return name; + } + } + + public string Namespace { + get { + return ((MetaType) provider).Namespace; + } + } + + public int TypeParametersCount { + get { + return tparams == null ? 0 : tparams.Length; + } + } + + public TypeParameterSpec[] TypeParameters { + get { + return tparams; + } + set { + tparams = value; + } + } + + #endregion + + public void DefineInterfaces (TypeSpec spec) + { + var type = (MetaType) provider; + MetaType[] ifaces; +#if STATIC + ifaces = type.__GetDeclaredInterfaces (); + if (ifaces.Length != 0) { + foreach (var iface in ifaces) { + var it = importer.CreateType (iface); + if (it == null) + continue; + + spec.AddInterfaceDefined (it); + + // Unfortunately not all languages expand inherited interfaces + var bifaces = it.Interfaces; + if (bifaces != null) { + foreach (var biface in bifaces) { + spec.AddInterfaceDefined (biface); + } + } + } + } + + // + // It's impossible to get declared interfaces only using System.Reflection + // hence we need to mimic the behavior with ikvm-reflection too to keep + // our type look-up logic same + // + if (spec.BaseType != null) { + var bifaces = spec.BaseType.Interfaces; + if (bifaces != null) { + // + // Before adding base class interfaces close defined interfaces + // on type parameter + // + var tp = spec as TypeParameterSpec; + if (tp != null && tp.InterfacesDefined == null) { + tp.InterfacesDefined = TypeSpec.EmptyTypes; + } + + foreach (var iface in bifaces) + spec.AddInterfaceDefined (iface); + } + } +#else + ifaces = type.GetInterfaces (); + + if (ifaces.Length > 0) { + foreach (var iface in ifaces) { + spec.AddInterface (importer.CreateType (iface)); + } + } +#endif + + } + + public static void Error_MissingDependency (IMemberContext ctx, List missing, Location loc) + { + // + // Report details about missing type and most likely cause of the problem. + // csc reports 1683, 1684 as warnings but we report them only when used + // or referenced from the user core in which case compilation error has to + // be reported because compiler cannot continue anyway + // + + var report = ctx.Module.Compiler.Report; + + for (int i = 0; i < missing.Count; ++i) { + var t = missing [i].Type; + + // + // Report missing types only once + // + if (report.Printer.MissingTypeReported (t.MemberDefinition)) + continue; + + string name = t.GetSignatureForError (); + + var caller = missing[i].Caller; + if (caller.Kind != MemberKind.MissingType) + report.SymbolRelatedToPreviousError (caller); + + var definition = t.MemberDefinition; + if (definition.DeclaringAssembly == ctx.Module.DeclaringAssembly) { + report.Error (1683, loc, + "Reference to type `{0}' claims it is defined in this assembly, but it is not defined in source or any added modules", + name); + } else if (definition.DeclaringAssembly.IsMissing) { + if (definition.IsTypeForwarder) { + report.Error (1070, loc, + "The type `{0}' has been forwarded to an assembly that is not referenced. Consider adding a reference to assembly `{1}'", + name, definition.DeclaringAssembly.FullName); + } else { + report.Error (12, loc, + "The type `{0}' is defined in an assembly that is not referenced. Consider adding a reference to assembly `{1}'", + name, definition.DeclaringAssembly.FullName); + } + } else if (definition.IsTypeForwarder) { + report.Error (731, loc, "The type forwarder for type `{0}' in assembly `{1}' has circular dependency", + name, definition.DeclaringAssembly.FullName); + } else { + report.Error (1684, loc, + "Reference to type `{0}' claims it is defined assembly `{1}', but it could not be found", + name, t.MemberDefinition.DeclaringAssembly.FullName); + } + } + } + + public TypeSpec GetAttributeCoClass () + { + if (cattrs == null) + ReadAttributes (); + + return cattrs.CoClass; + } + + public string GetAttributeDefaultMember () + { + if (cattrs == null) + ReadAttributes (); + + return cattrs.DefaultIndexerName; + } + + public AttributeUsageAttribute GetAttributeUsage (PredefinedAttribute pa) + { + if (cattrs == null) + ReadAttributes (); + + return cattrs.AttributeUsage; + } + + bool ITypeDefinition.IsInternalAsPublic (IAssemblyDefinition assembly) + { + var a = importer.GetAssemblyDefinition (provider.Module.Assembly); + return a == assembly || a.IsFriendAssemblyTo (assembly); + } + + public void LoadMembers (TypeSpec declaringType, bool onlyTypes, ref MemberCache cache) + { + // + // Not interested in members of nested private types unless the importer needs them + // + if (declaringType.IsPrivate && importer.IgnorePrivateMembers) { + cache = MemberCache.Empty; + return; + } + + var loading_type = (MetaType) provider; + const BindingFlags all_members = BindingFlags.DeclaredOnly | + BindingFlags.Static | BindingFlags.Instance | + BindingFlags.Public | BindingFlags.NonPublic; + + const MethodAttributes explicit_impl = MethodAttributes.NewSlot | + MethodAttributes.Virtual | MethodAttributes.HideBySig | + MethodAttributes.Final; + + Dictionary possible_accessors = null; + List imported_events = null; + EventSpec event_spec; + MemberSpec imported; + MethodInfo m; + MemberInfo[] all; + try { + all = loading_type.GetMembers (all_members); + } catch (Exception e) { + throw new InternalErrorException (e, "Could not import type `{0}' from `{1}'", + declaringType.GetSignatureForError (), declaringType.MemberDefinition.DeclaringAssembly.FullName); + } + + if (cache == null) { + cache = new MemberCache (all.Length); + + // + // Do the types first as they can be referenced by the members before + // they are found or inflated + // + foreach (var member in all) { + if (member.MemberType != MemberTypes.NestedType) + continue; + + var t = (MetaType) member; + + // Ignore compiler generated types, mostly lambda containers + if ((t.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedPrivate && importer.IgnorePrivateMembers) + continue; + + try { + imported = importer.CreateNestedType (t, declaringType); + } catch (Exception e) { + throw new InternalErrorException (e, "Could not import nested type `{0}' from `{1}'", + t.FullName, declaringType.MemberDefinition.DeclaringAssembly.FullName); + } + + cache.AddMemberImported (imported); + } + + foreach (var member in all) { + if (member.MemberType != MemberTypes.NestedType) + continue; + + var t = (MetaType) member; + + if ((t.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedPrivate && importer.IgnorePrivateMembers) + continue; + + importer.ImportTypeBase (t); + } + } + + // + // Load base interfaces first to minic behaviour of compiled members + // + if (declaringType.IsInterface && declaringType.Interfaces != null) { + foreach (var iface in declaringType.Interfaces) { + cache.AddInterface (iface); + } + } + + if (!onlyTypes) { + // + // The logic here requires methods to be returned first which seems to work for both Mono and .NET + // + foreach (var member in all) { + switch (member.MemberType) { + case MemberTypes.Constructor: + if (declaringType.IsInterface) + continue; + + goto case MemberTypes.Method; + case MemberTypes.Method: + MethodBase mb = (MethodBase) member; + var attrs = mb.Attributes; + + if ((attrs & MethodAttributes.MemberAccessMask) == MethodAttributes.Private) { + if (importer.IgnorePrivateMembers) + continue; + + // Ignore explicitly implemented members + if ((attrs & explicit_impl) == explicit_impl) + continue; + + // Ignore compiler generated methods + if (MetadataImporter.HasAttribute (CustomAttributeData.GetCustomAttributes (mb), "CompilerGeneratedAttribute", MetadataImporter.CompilerServicesNamespace)) + continue; + } + + imported = importer.CreateMethod (mb, declaringType); + if (imported.Kind == MemberKind.Method && !imported.IsGeneric) { + if (possible_accessors == null) + possible_accessors = new Dictionary (ReferenceEquality.Default); + + // There are no metadata rules for accessors, we have to consider any method as possible candidate + possible_accessors.Add (mb, (MethodSpec) imported); + } + + break; + case MemberTypes.Property: + if (possible_accessors == null) + continue; + + var p = (PropertyInfo) member; + // + // Links possible accessors with property + // + MethodSpec get, set; + m = p.GetGetMethod (true); + if (m == null || !possible_accessors.TryGetValue (m, out get)) + get = null; + + m = p.GetSetMethod (true); + if (m == null || !possible_accessors.TryGetValue (m, out set)) + set = null; + + // No accessors registered (e.g. explicit implementation) + if (get == null && set == null) + continue; + + try { + imported = importer.CreateProperty (p, declaringType, get, set); + } catch (Exception ex) { + throw new InternalErrorException (ex, "Could not import property `{0}' inside `{1}'", + p.Name, declaringType.GetSignatureForError ()); + } + + if (imported == null) + continue; + + break; + case MemberTypes.Event: + if (possible_accessors == null) + continue; + + var e = (EventInfo) member; + // + // Links accessors with event + // + MethodSpec add, remove; + m = e.GetAddMethod (true); + if (m == null || !possible_accessors.TryGetValue (m, out add)) + add = null; + + m = e.GetRemoveMethod (true); + if (m == null || !possible_accessors.TryGetValue (m, out remove)) + remove = null; + + // Both accessors are required + if (add == null || remove == null) + continue; + + event_spec = importer.CreateEvent (e, declaringType, add, remove); + if (!importer.IgnorePrivateMembers) { + if (imported_events == null) + imported_events = new List (); + + imported_events.Add (event_spec); + } + + imported = event_spec; + break; + case MemberTypes.Field: + var fi = (FieldInfo) member; + + imported = importer.CreateField (fi, declaringType); + if (imported == null) + continue; + + // + // For dynamic binder event has to be fully restored to allow operations + // within the type container to work correctly + // + if (imported_events != null) { + // The backing event field should be private but it may not + int i; + for (i = 0; i < imported_events.Count; ++i) { + var ev = imported_events[i]; + if (ev.Name == fi.Name) { + ev.BackingField = (FieldSpec) imported; + imported_events.RemoveAt (i); + i = -1; + break; + } + } + + if (i < 0) + continue; + } + + break; + case MemberTypes.NestedType: + // Already in the cache from the first pass + continue; + default: + throw new NotImplementedException (member.ToString ()); + } + + if (imported.IsStatic && declaringType.IsInterface) + continue; + + cache.AddMemberImported (imported); + } + } + } + } + + class ImportedTypeParameterDefinition : ImportedDefinition, ITypeDefinition + { + public ImportedTypeParameterDefinition (MetaType type, MetadataImporter importer) + : base (type, importer) + { + } + + #region Properties + + public IAssemblyDefinition DeclaringAssembly { + get { + throw new NotImplementedException (); + } + } + + bool ITypeDefinition.IsComImport { + get { + return false; + } + } + + bool ITypeDefinition.IsPartial { + get { + return false; + } + } + + bool ITypeDefinition.IsTypeForwarder { + get { + return false; + } + } + + bool ITypeDefinition.IsCyclicTypeForwarder { + get { + return false; + } + } + + public string Namespace { + get { + return null; + } + } + + public int TypeParametersCount { + get { + return 0; + } + } + + public TypeParameterSpec[] TypeParameters { + get { + return null; + } + } + + #endregion + + public TypeSpec GetAttributeCoClass () + { + return null; + } + + public string GetAttributeDefaultMember () + { + throw new NotSupportedException (); + } + + public AttributeUsageAttribute GetAttributeUsage (PredefinedAttribute pa) + { + throw new NotSupportedException (); + } + + bool ITypeDefinition.IsInternalAsPublic (IAssemblyDefinition assembly) + { + throw new NotImplementedException (); + } + + public void LoadMembers (TypeSpec declaringType, bool onlyTypes, ref MemberCache cache) + { + throw new NotImplementedException (); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/iterators.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/iterators.cs new file mode 100644 index 000000000..5dce0ed15 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/iterators.cs @@ -0,0 +1,1260 @@ +// +// iterators.cs: Support for implementing iterators +// +// Author: +// Miguel de Icaza (miguel@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// Copyright 2003 Ximian, Inc. +// Copyright 2003-2008 Novell, Inc. +// Copyright 2011 Xamarin Inc. +// + +using System; +using System.Collections.Generic; +using Mono.CompilerServices.SymbolWriter; + +#if STATIC +using IKVM.Reflection.Emit; +#else +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp +{ + public abstract class YieldStatement : ResumableStatement where T : StateMachineInitializer + { + protected Expression expr; + protected bool unwind_protect; + protected T machine_initializer; + int resume_pc; + ExceptionStatement inside_try_block; + + protected YieldStatement (Expression expr, Location l) + { + this.expr = expr; + loc = l; + } + + public Expression Expr { + get { return this.expr; } + } + + protected override void CloneTo (CloneContext clonectx, Statement t) + { + var target = (YieldStatement) t; + target.expr = expr.Clone (clonectx); + } + + protected override void DoEmit (EmitContext ec) + { + machine_initializer.InjectYield (ec, expr, resume_pc, unwind_protect, resume_point); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + expr.FlowAnalysis (fc); + + RegisterResumePoint (); + + return false; + } + + public override bool Resolve (BlockContext bc) + { + expr = expr.Resolve (bc); + if (expr == null) + return false; + + machine_initializer = bc.CurrentAnonymousMethod as T; + inside_try_block = bc.CurrentTryBlock; + return true; + } + + public void RegisterResumePoint () + { + if (resume_pc != 0) + return; + + if (inside_try_block == null) { + resume_pc = machine_initializer.AddResumePoint (this); + } else { + resume_pc = inside_try_block.AddResumePoint (this, resume_pc, machine_initializer); + unwind_protect = true; + inside_try_block = null; + } + } + } + + public class Yield : YieldStatement + { + public Yield (Expression expr, Location loc) + : base (expr, loc) + { + } + + public static bool CheckContext (BlockContext bc, Location loc) + { + if (!bc.CurrentAnonymousMethod.IsIterator) { + bc.Report.Error (1621, loc, + "The yield statement cannot be used inside anonymous method blocks"); + return false; + } + + if (bc.HasSet (ResolveContext.Options.FinallyScope)) { + bc.Report.Error (1625, loc, "Cannot yield in the body of a finally clause"); + return false; + } + + return true; + } + + public override bool Resolve (BlockContext bc) + { + if (!CheckContext (bc, loc)) + return false; + + if (bc.HasAny (ResolveContext.Options.TryWithCatchScope)) { + bc.Report.Error (1626, loc, "Cannot yield a value in the body of a try block with a catch clause"); + } + + if (bc.HasSet (ResolveContext.Options.CatchScope)) { + bc.Report.Error (1631, loc, "Cannot yield a value in the body of a catch clause"); + } + + if (!base.Resolve (bc)) + return false; + + var otype = bc.CurrentIterator.OriginalIteratorType; + if (expr.Type != otype) { + expr = Convert.ImplicitConversionRequired (bc, expr, otype, loc); + if (expr == null) + return false; + } + + return true; + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class YieldBreak : ExitStatement + { + Iterator iterator; + + public YieldBreak (Location l) + { + loc = l; + } + + protected override bool IsLocalExit { + get { + return false; + } + } + + protected override void CloneTo (CloneContext clonectx, Statement target) + { + throw new NotSupportedException (); + } + + protected override bool DoResolve (BlockContext bc) + { + iterator = bc.CurrentIterator; + return Yield.CheckContext (bc, loc); + } + + protected override void DoEmit (EmitContext ec) + { + iterator.EmitYieldBreak (ec, unwind_protect); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + return true; + } + + public override Reachability MarkReachable (Reachability rc) + { + base.MarkReachable (rc); + return Reachability.CreateUnreachable (); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public abstract class StateMachine : AnonymousMethodStorey + { + public enum State + { + Running = -3, // Used only in CurrentPC, never stored into $PC + Uninitialized = -2, + After = -1, + Start = 0 + } + + Field pc_field; + StateMachineMethod method; + int local_name_idx; + + protected StateMachine (ParametersBlock block, TypeDefinition parent, MemberBase host, TypeParameters tparams, string name, MemberKind kind) + : base (block, parent, host, tparams, name, kind) + { + OriginalTypeParameters = tparams; + } + + #region Properties + + public TypeParameters OriginalTypeParameters { get; private set; } + + public StateMachineMethod StateMachineMethod { + get { + return method; + } + } + + public Field PC { + get { + return pc_field; + } + } + + #endregion + + public void AddEntryMethod (StateMachineMethod method) + { + if (this.method != null) + throw new InternalErrorException (); + + this.method = method; + Members.Add (method); + } + + protected override bool DoDefineMembers () + { + pc_field = AddCompilerGeneratedField ("$PC", new TypeExpression (Compiler.BuiltinTypes.Int, Location)); + + return base.DoDefineMembers (); + } + + protected override string GetVariableMangledName (LocalVariable local_info) + { + if (local_info.IsCompilerGenerated) + return base.GetVariableMangledName (local_info); + + return "<" + local_info.Name + ">__" + local_name_idx++.ToString ("X"); + } + } + + class IteratorStorey : StateMachine + { + class GetEnumeratorMethod : StateMachineMethod + { + sealed class GetEnumeratorStatement : Statement + { + readonly IteratorStorey host; + readonly StateMachineMethod host_method; + + Expression new_storey; + + public GetEnumeratorStatement (IteratorStorey host, StateMachineMethod host_method) + { + this.host = host; + this.host_method = host_method; + loc = host_method.Location; + } + + protected override void CloneTo (CloneContext clonectx, Statement target) + { + throw new NotSupportedException (); + } + + public override bool Resolve (BlockContext ec) + { + TypeExpression storey_type_expr = new TypeExpression (host.Definition, loc); + List init = null; + if (host.hoisted_this != null) { + init = new List (host.hoisted_params == null ? 1 : host.HoistedParameters.Count + 1); + HoistedThis ht = host.hoisted_this; + FieldExpr from = new FieldExpr (ht.Field, loc); + from.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc); + init.Add (new ElementInitializer (ht.Field.Name, from, loc)); + } + + if (host.hoisted_params != null) { + if (init == null) + init = new List (host.HoistedParameters.Count); + + for (int i = 0; i < host.hoisted_params.Count; ++i) { + HoistedParameter hp = host.hoisted_params [i]; + HoistedParameter hp_cp = host.hoisted_params_copy [i] ?? hp; + + FieldExpr from = new FieldExpr (hp_cp.Field, loc); + from.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc); + + init.Add (new ElementInitializer (hp.Field.Name, from, loc)); + } + } + + if (init != null) { + new_storey = new NewInitialize (storey_type_expr, null, + new CollectionOrObjectInitializers (init, loc), loc); + } else { + new_storey = new New (storey_type_expr, null, loc); + } + + new_storey = new_storey.Resolve (ec); + if (new_storey != null) + new_storey = Convert.ImplicitConversionRequired (ec, new_storey, host_method.MemberType, loc); + + return true; + } + + protected override void DoEmit (EmitContext ec) + { + Label label_init = ec.DefineLabel (); + + ec.EmitThis (); + ec.Emit (OpCodes.Ldflda, host.PC.Spec); + ec.EmitInt ((int) State.Start); + ec.EmitInt ((int) State.Uninitialized); + + var m = ec.Module.PredefinedMembers.InterlockedCompareExchange.Resolve (loc); + if (m != null) + ec.Emit (OpCodes.Call, m); + + ec.EmitInt ((int) State.Uninitialized); + ec.Emit (OpCodes.Bne_Un_S, label_init); + + ec.EmitThis (); + ec.Emit (OpCodes.Ret); + + ec.MarkLabel (label_init); + + new_storey.Emit (ec); + ec.Emit (OpCodes.Ret); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + throw new NotImplementedException (); + } + + public override Reachability MarkReachable (Reachability rc) + { + base.MarkReachable (rc); + return Reachability.CreateUnreachable (); + } + } + + GetEnumeratorMethod (IteratorStorey host, FullNamedExpression returnType, MemberName name) + : base (host, null, returnType, Modifiers.DEBUGGER_HIDDEN, name, ToplevelBlock.Flags.CompilerGenerated | ToplevelBlock.Flags.NoFlowAnalysis) + { + } + + public static GetEnumeratorMethod Create (IteratorStorey host, FullNamedExpression returnType, MemberName name) + { + return Create (host, returnType, name, null); + } + + public static GetEnumeratorMethod Create (IteratorStorey host, FullNamedExpression returnType, MemberName name, Statement statement) + { + var m = new GetEnumeratorMethod (host, returnType, name); + var stmt = statement ?? new GetEnumeratorStatement (host, m); + m.block.AddStatement (stmt); + return m; + } + } + + class DisposeMethod : StateMachineMethod + { + sealed class DisposeMethodStatement : Statement + { + Iterator iterator; + + public DisposeMethodStatement (Iterator iterator) + { + this.iterator = iterator; + this.loc = iterator.Location; + } + + protected override void CloneTo (CloneContext clonectx, Statement target) + { + throw new NotSupportedException (); + } + + public override bool Resolve (BlockContext ec) + { + return true; + } + + protected override void DoEmit (EmitContext ec) + { + ec.CurrentAnonymousMethod = iterator; + iterator.EmitDispose (ec); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + throw new NotImplementedException (); + } + } + + public DisposeMethod (IteratorStorey host) + : base (host, null, new TypeExpression (host.Compiler.BuiltinTypes.Void, host.Location), Modifiers.PUBLIC | Modifiers.DEBUGGER_HIDDEN, + new MemberName ("Dispose", host.Location), ToplevelBlock.Flags.CompilerGenerated | ToplevelBlock.Flags.NoFlowAnalysis) + { + host.Members.Add (this); + + Block.AddStatement (new DisposeMethodStatement (host.Iterator)); + } + } + + // + // Uses Method as method info + // + class DynamicMethodGroupExpr : MethodGroupExpr + { + readonly Method method; + + public DynamicMethodGroupExpr (Method method, Location loc) + : base ((IList) null, null, loc) + { + this.method = method; + eclass = ExprClass.Unresolved; + } + + protected override Expression DoResolve (ResolveContext ec) + { + Methods = new List (1) { method.Spec }; + type = method.Parent.Definition; + InstanceExpression = new CompilerGeneratedThis (type, Location); + return base.DoResolve (ec); + } + } + + class DynamicFieldExpr : FieldExpr + { + readonly Field field; + + public DynamicFieldExpr (Field field, Location loc) + : base (loc) + { + this.field = field; + } + + protected override Expression DoResolve (ResolveContext ec) + { + spec = field.Spec; + type = spec.MemberType; + InstanceExpression = new CompilerGeneratedThis (type, Location); + return base.DoResolve (ec); + } + } + + public readonly Iterator Iterator; + + List hoisted_params_copy; + + TypeExpr iterator_type_expr; + Field current_field; + Field disposing_field; + + TypeSpec generic_enumerator_type; + TypeSpec generic_enumerable_type; + + public IteratorStorey (Iterator iterator) + : base (iterator.Container.ParametersBlock, iterator.Host, + iterator.OriginalMethod as MemberBase, iterator.OriginalMethod.CurrentTypeParameters, "Iterator", MemberKind.Class) + { + this.Iterator = iterator; + } + + public Field CurrentField { + get { + return current_field; + } + } + + public Field DisposingField { + get { + return disposing_field; + } + } + + public IList HoistedParameters { + get { return hoisted_params; } + } + + protected override Constructor DefineDefaultConstructor (bool is_static) + { + var ctor = base.DefineDefaultConstructor (is_static); + ctor.ModFlags |= Modifiers.DEBUGGER_HIDDEN; + return ctor; + } + + protected override TypeSpec[] ResolveBaseTypes (out FullNamedExpression base_class) + { + var mtype = Iterator.OriginalIteratorType; + if (Mutator != null) + mtype = Mutator.Mutate (mtype); + + iterator_type_expr = new TypeExpression (mtype, Location); + + var ifaces = new List (5); + if (Iterator.IsEnumerable) { + ifaces.Add (Compiler.BuiltinTypes.IEnumerable); + + if (Module.PredefinedTypes.IEnumerableGeneric.Define ()) { + generic_enumerable_type = Module.PredefinedTypes.IEnumerableGeneric.TypeSpec.MakeGenericType (Module, new[] { mtype }); + ifaces.Add (generic_enumerable_type); + } + } + + ifaces.Add (Compiler.BuiltinTypes.IEnumerator); + ifaces.Add (Compiler.BuiltinTypes.IDisposable); + + var ienumerator_generic = Module.PredefinedTypes.IEnumeratorGeneric; + if (ienumerator_generic.Define ()) { + generic_enumerator_type = ienumerator_generic.TypeSpec.MakeGenericType (Module, new [] { mtype }); + ifaces.Add (generic_enumerator_type); + } + + base_class = null; + + base_type = Compiler.BuiltinTypes.Object; + return ifaces.ToArray (); + } + + protected override bool DoDefineMembers () + { + current_field = AddCompilerGeneratedField ("$current", iterator_type_expr); + disposing_field = AddCompilerGeneratedField ("$disposing", new TypeExpression (Compiler.BuiltinTypes.Bool, Location)); + + if (Iterator.IsEnumerable && hoisted_params != null) { + // + // Iterators are independent, each GetEnumerator call has to + // create same enumerator therefore we have to keep original values + // around for re-initialization + // + hoisted_params_copy = new List (hoisted_params.Count); + foreach (HoistedParameter hp in hoisted_params) { + + // + // Don't create field copy for unmodified captured parameters + // + HoistedParameter hp_copy; + if (hp.IsAssigned) { + hp_copy = new HoistedParameter (hp, "<$>" + hp.Field.Name); + } else { + hp_copy = null; + } + + hoisted_params_copy.Add (hp_copy); + } + } + + if (generic_enumerator_type != null) + Define_Current (true); + + Define_Current (false); + new DisposeMethod (this); + Define_Reset (); + + if (Iterator.IsEnumerable) { + FullNamedExpression explicit_iface = new TypeExpression (Compiler.BuiltinTypes.IEnumerable, Location); + var name = new MemberName ("GetEnumerator", null, explicit_iface, Location.Null); + + if (generic_enumerator_type != null) { + explicit_iface = new TypeExpression (generic_enumerable_type, Location); + var gname = new MemberName ("GetEnumerator", null, explicit_iface, Location.Null); + Method gget_enumerator = GetEnumeratorMethod.Create (this, new TypeExpression (generic_enumerator_type, Location), gname); + + // + // Just call generic GetEnumerator implementation + // + var stmt = new Return (new Invocation (new DynamicMethodGroupExpr (gget_enumerator, Location), null), Location); + Method get_enumerator = GetEnumeratorMethod.Create (this, new TypeExpression (Compiler.BuiltinTypes.IEnumerator, Location), name, stmt); + + Members.Add (get_enumerator); + Members.Add (gget_enumerator); + } else { + Members.Add (GetEnumeratorMethod.Create (this, new TypeExpression (Compiler.BuiltinTypes.IEnumerator, Location), name)); + } + } + + return base.DoDefineMembers (); + } + + void Define_Current (bool is_generic) + { + TypeExpr type; + FullNamedExpression explicit_iface; + + if (is_generic) { + explicit_iface = new TypeExpression (generic_enumerator_type, Location); + type = iterator_type_expr; + } else { + explicit_iface = new TypeExpression (Module.Compiler.BuiltinTypes.IEnumerator, Location); + type = new TypeExpression (Compiler.BuiltinTypes.Object, Location); + } + + var name = new MemberName ("Current", null, explicit_iface, Location); + + ToplevelBlock get_block = new ToplevelBlock (Compiler, ParametersCompiled.EmptyReadOnlyParameters, Location, + Block.Flags.CompilerGenerated | Block.Flags.NoFlowAnalysis); + get_block.AddStatement (new Return (new DynamicFieldExpr (CurrentField, Location), Location)); + + Property current = new Property (this, type, Modifiers.DEBUGGER_HIDDEN | Modifiers.COMPILER_GENERATED, name, null); + current.Get = new Property.GetMethod (current, Modifiers.COMPILER_GENERATED, null, Location); + current.Get.Block = get_block; + + Members.Add (current); + } + + void Define_Reset () + { + Method reset = new Method ( + this, new TypeExpression (Compiler.BuiltinTypes.Void, Location), + Modifiers.PUBLIC | Modifiers.DEBUGGER_HIDDEN | Modifiers.COMPILER_GENERATED, + new MemberName ("Reset", Location), + ParametersCompiled.EmptyReadOnlyParameters, null); + Members.Add (reset); + + reset.Block = new ToplevelBlock (Compiler, reset.ParameterInfo, Location, + Block.Flags.CompilerGenerated | Block.Flags.NoFlowAnalysis); + + TypeSpec ex_type = Module.PredefinedTypes.NotSupportedException.Resolve (); + if (ex_type == null) + return; + + reset.Block.AddStatement (new Throw (new New (new TypeExpression (ex_type, Location), null, Location), Location)); + } + + protected override void EmitHoistedParameters (EmitContext ec, List hoisted) + { + base.EmitHoistedParameters (ec, hoisted); + if (hoisted_params_copy != null) + base.EmitHoistedParameters (ec, hoisted_params_copy); + } + } + + public class StateMachineMethod : Method + { + readonly StateMachineInitializer expr; + + public StateMachineMethod (StateMachine host, StateMachineInitializer expr, FullNamedExpression returnType, + Modifiers mod, MemberName name, ToplevelBlock.Flags blockFlags) + : base (host, returnType, mod | Modifiers.COMPILER_GENERATED, + name, ParametersCompiled.EmptyReadOnlyParameters, null) + { + this.expr = expr; + Block = new ToplevelBlock (host.Compiler, ParametersCompiled.EmptyReadOnlyParameters, Location.Null, blockFlags); + } + + public override EmitContext CreateEmitContext (ILGenerator ig, SourceMethodBuilder sourceMethod) + { + EmitContext ec = new EmitContext (this, ig, MemberType, sourceMethod); + ec.CurrentAnonymousMethod = expr; + + if (expr is AsyncInitializer) + ec.With (BuilderContext.Options.AsyncBody, true); + + return ec; + } + } + + public abstract class StateMachineInitializer : AnonymousExpression + { + sealed class MoveNextBodyStatement : Statement + { + readonly StateMachineInitializer state_machine; + + public MoveNextBodyStatement (StateMachineInitializer stateMachine) + { + this.state_machine = stateMachine; + this.loc = stateMachine.Location; + } + + protected override void CloneTo (CloneContext clonectx, Statement target) + { + throw new NotSupportedException (); + } + + public override bool Resolve (BlockContext ec) + { + return true; + } + + protected override void DoEmit (EmitContext ec) + { + state_machine.EmitMoveNext (ec); + } + + public override void Emit (EmitContext ec) + { + // Don't create sequence point + DoEmit (ec); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + return state_machine.ReturnType.Kind != MemberKind.Void; + } + + public override Reachability MarkReachable (Reachability rc) + { + base.MarkReachable (rc); + + if (state_machine.ReturnType.Kind != MemberKind.Void) + rc = Reachability.CreateUnreachable (); + + return rc; + } + } + + public readonly TypeDefinition Host; + protected StateMachine storey; + + // + // The state as we generate the machine + // + protected Label move_next_ok; + protected Label move_next_error; + LocalBuilder skip_finally; + protected LocalBuilder current_pc; + protected List resume_points; + + protected StateMachineInitializer (ParametersBlock block, TypeDefinition host, TypeSpec returnType) + : base (block, returnType, block.StartLocation) + { + this.Host = host; + } + + #region Properties + + public Label BodyEnd { get; set; } + + public LocalBuilder CurrentPC + { + get { + return current_pc; + } + } + + public LocalBuilder SkipFinally { + get { + return skip_finally; + } + } + + public override AnonymousMethodStorey Storey { + get { + return storey; + } + } + + #endregion + + public int AddResumePoint (ResumableStatement stmt) + { + if (resume_points == null) + resume_points = new List (); + + resume_points.Add (stmt); + return resume_points.Count; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + throw new NotSupportedException ("ET"); + } + + protected virtual BlockContext CreateBlockContext (BlockContext bc) + { + var ctx = new BlockContext (bc, block, bc.ReturnType); + ctx.CurrentAnonymousMethod = this; + + ctx.AssignmentInfoOffset = bc.AssignmentInfoOffset; + ctx.EnclosingLoop = bc.EnclosingLoop; + ctx.EnclosingLoopOrSwitch = bc.EnclosingLoopOrSwitch; + ctx.Switch = bc.Switch; + + return ctx; + } + + protected override Expression DoResolve (ResolveContext rc) + { + var bc = (BlockContext) rc; + var ctx = CreateBlockContext (bc); + + Block.Resolve (ctx); + + if (!rc.IsInProbingMode) { + var move_next = new StateMachineMethod (storey, this, new TypeExpression (ReturnType, loc), Modifiers.PUBLIC, new MemberName ("MoveNext", loc), 0); + move_next.Block.AddStatement (new MoveNextBodyStatement (this)); + storey.AddEntryMethod (move_next); + } + + bc.AssignmentInfoOffset = ctx.AssignmentInfoOffset; + eclass = ExprClass.Value; + return this; + } + + public override void Emit (EmitContext ec) + { + // + // Load state machine instance + // + storey.Instance.Emit (ec); + } + + void EmitMoveNext_NoResumePoints (EmitContext ec) + { + ec.EmitThis (); + ec.Emit (OpCodes.Ldfld, storey.PC.Spec); + + ec.EmitThis (); + ec.EmitInt ((int) IteratorStorey.State.After); + ec.Emit (OpCodes.Stfld, storey.PC.Spec); + + // We only care if the PC is zero (start executing) or non-zero (don't do anything) + ec.Emit (OpCodes.Brtrue, move_next_error); + + BodyEnd = ec.DefineLabel (); + + var async_init = this as AsyncInitializer; + if (async_init != null) + ec.BeginExceptionBlock (); + + block.EmitEmbedded (ec); + + if (async_init != null) + async_init.EmitCatchBlock (ec); + + ec.MarkLabel (BodyEnd); + + EmitMoveNextEpilogue (ec); + + ec.MarkLabel (move_next_error); + + if (ReturnType.Kind != MemberKind.Void) { + ec.EmitInt (0); + ec.Emit (OpCodes.Ret); + } + + ec.MarkLabel (move_next_ok); + } + + void EmitMoveNext (EmitContext ec) + { + move_next_ok = ec.DefineLabel (); + move_next_error = ec.DefineLabel (); + + if (resume_points == null) { + EmitMoveNext_NoResumePoints (ec); + return; + } + + current_pc = ec.GetTemporaryLocal (ec.BuiltinTypes.UInt); + ec.EmitThis (); + ec.Emit (OpCodes.Ldfld, storey.PC.Spec); + ec.Emit (OpCodes.Stloc, current_pc); + + // We're actually in state 'running', but this is as good a PC value as any if there's an abnormal exit + ec.EmitThis (); + ec.EmitInt ((int) IteratorStorey.State.After); + ec.Emit (OpCodes.Stfld, storey.PC.Spec); + + Label[] labels = new Label[1 + resume_points.Count]; + labels[0] = ec.DefineLabel (); + + bool need_skip_finally = false; + for (int i = 0; i < resume_points.Count; ++i) { + ResumableStatement s = resume_points[i]; + need_skip_finally |= s is ExceptionStatement; + labels[i + 1] = s.PrepareForEmit (ec); + } + + if (need_skip_finally) { + skip_finally = ec.GetTemporaryLocal (ec.BuiltinTypes.Bool); + ec.EmitInt (0); + ec.Emit (OpCodes.Stloc, skip_finally); + } + + var async_init = this as AsyncInitializer; + if (async_init != null) + ec.BeginExceptionBlock (); + + ec.Emit (OpCodes.Ldloc, current_pc); + ec.Emit (OpCodes.Switch, labels); + + ec.Emit (async_init != null ? OpCodes.Leave : OpCodes.Br, move_next_error); + + ec.MarkLabel (labels[0]); + + BodyEnd = ec.DefineLabel (); + + block.EmitEmbedded (ec); + + ec.MarkLabel (BodyEnd); + + if (async_init != null) { + async_init.EmitCatchBlock (ec); + } + + ec.Mark (Block.Original.EndLocation); + ec.EmitThis (); + ec.EmitInt ((int) IteratorStorey.State.After); + ec.Emit (OpCodes.Stfld, storey.PC.Spec); + + EmitMoveNextEpilogue (ec); + + ec.MarkLabel (move_next_error); + + if (ReturnType.Kind != MemberKind.Void) { + ec.EmitInt (0); + ec.Emit (OpCodes.Ret); + } + + ec.MarkLabel (move_next_ok); + + if (ReturnType.Kind != MemberKind.Void) { + ec.EmitInt (1); + ec.Emit (OpCodes.Ret); + } + } + + protected virtual void EmitMoveNextEpilogue (EmitContext ec) + { + } + + public void EmitLeave (EmitContext ec, bool unwind_protect) + { + // Return ok + ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, move_next_ok); + } + + // + // Called back from YieldStatement + // + public virtual void InjectYield (EmitContext ec, Expression expr, int resume_pc, bool unwind_protect, Label resume_point) + { + // + // Guard against being disposed meantime + // + Label disposed = ec.DefineLabel (); + var iterator = storey as IteratorStorey; + if (iterator != null) { + ec.EmitThis (); + ec.Emit (OpCodes.Ldfld, iterator.DisposingField.Spec); + ec.Emit (OpCodes.Brtrue_S, disposed); + } + + // + // store resume program-counter + // + ec.EmitThis (); + ec.EmitInt (resume_pc); + ec.Emit (OpCodes.Stfld, storey.PC.Spec); + + if (iterator != null) { + ec.MarkLabel (disposed); + } + + // mark finally blocks as disabled + if (unwind_protect && skip_finally != null) { + ec.EmitInt (1); + ec.Emit (OpCodes.Stloc, skip_finally); + } + } + + public void SetStateMachine (StateMachine stateMachine) + { + this.storey = stateMachine; + } + } + + // + // Iterators are implemented as state machine blocks + // + public class Iterator : StateMachineInitializer + { + sealed class TryFinallyBlockProxyStatement : Statement + { + TryFinallyBlock block; + Iterator iterator; + + public TryFinallyBlockProxyStatement (Iterator iterator, TryFinallyBlock block) + { + this.iterator = iterator; + this.block = block; + } + + protected override void CloneTo (CloneContext clonectx, Statement target) + { + throw new NotSupportedException (); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + throw new NotSupportedException (); + } + + protected override void DoEmit (EmitContext ec) + { + // + // Restore redirection for any captured variables + // + ec.CurrentAnonymousMethod = iterator; + + using (ec.With (BuilderContext.Options.OmitDebugInfo, !ec.HasMethodSymbolBuilder)) { + block.EmitFinallyBody (ec); + } + } + } + + public readonly IMethodData OriginalMethod; + public readonly bool IsEnumerable; + public readonly TypeSpec OriginalIteratorType; + int finally_hosts_counter; + + public Iterator (ParametersBlock block, IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable) + : base (block, host, host.Compiler.BuiltinTypes.Bool) + { + this.OriginalMethod = method; + this.OriginalIteratorType = iterator_type; + this.IsEnumerable = is_enumerable; + this.type = method.ReturnType; + } + + #region Properties + + public ToplevelBlock Container { + get { return OriginalMethod.Block; } + } + + public override string ContainerType { + get { return "iterator"; } + } + + public override bool IsIterator { + get { return true; } + } + + #endregion + + public Method CreateFinallyHost (TryFinallyBlock block) + { + var method = new Method (storey, new TypeExpression (storey.Compiler.BuiltinTypes.Void, loc), + Modifiers.COMPILER_GENERATED, new MemberName (CompilerGeneratedContainer.MakeName (null, null, "Finally", finally_hosts_counter++), loc), + ParametersCompiled.EmptyReadOnlyParameters, null); + + method.Block = new ToplevelBlock (method.Compiler, method.ParameterInfo, loc, + ToplevelBlock.Flags.CompilerGenerated | ToplevelBlock.Flags.NoFlowAnalysis); + method.Block.AddStatement (new TryFinallyBlockProxyStatement (this, block)); + + // Cannot it add to storey because it'd be emitted before nested + // anonoymous methods which could capture shared variable + + return method; + } + + public void EmitYieldBreak (EmitContext ec, bool unwind_protect) + { + ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, move_next_error); + } + + public override string GetSignatureForError () + { + return OriginalMethod.GetSignatureForError (); + } + + public override void Emit (EmitContext ec) + { + // + // Load Iterator storey instance + // + storey.Instance.Emit (ec); + + // + // Initialize iterator PC when it's unitialized + // + if (IsEnumerable) { + ec.Emit (OpCodes.Dup); + ec.EmitInt ((int)IteratorStorey.State.Uninitialized); + + var field = storey.PC.Spec; + if (storey.MemberName.IsGeneric) { + field = MemberCache.GetMember (Storey.Instance.Type, field); + } + + ec.Emit (OpCodes.Stfld, field); + } + } + + public void EmitDispose (EmitContext ec) + { + if (resume_points == null) + return; + + Label end = ec.DefineLabel (); + + Label[] labels = null; + for (int i = 0; i < resume_points.Count; ++i) { + ResumableStatement s = resume_points[i]; + Label ret = s.PrepareForDispose (ec, end); + if (ret.Equals (end) && labels == null) + continue; + if (labels == null) { + labels = new Label[resume_points.Count + 1]; + for (int j = 0; j <= i; ++j) + labels[j] = end; + } + + labels[i + 1] = ret; + } + + if (labels != null) { + current_pc = ec.GetTemporaryLocal (ec.BuiltinTypes.UInt); + ec.EmitThis (); + ec.Emit (OpCodes.Ldfld, storey.PC.Spec); + ec.Emit (OpCodes.Stloc, current_pc); + } + + ec.EmitThis (); + ec.EmitInt (1); + ec.Emit (OpCodes.Stfld, ((IteratorStorey) storey).DisposingField.Spec); + + ec.EmitThis (); + ec.EmitInt ((int) IteratorStorey.State.After); + ec.Emit (OpCodes.Stfld, storey.PC.Spec); + + if (labels != null) { + //SymbolWriter.StartIteratorDispatcher (ec.ig); + ec.Emit (OpCodes.Ldloc, current_pc); + ec.Emit (OpCodes.Switch, labels); + //SymbolWriter.EndIteratorDispatcher (ec.ig); + + foreach (ResumableStatement s in resume_points) + s.EmitForDispose (ec, current_pc, end, true); + } + + ec.MarkLabel (end); + } + + public override void EmitStatement (EmitContext ec) + { + throw new NotImplementedException (); + } + + public override void InjectYield (EmitContext ec, Expression expr, int resume_pc, bool unwind_protect, Label resume_point) + { + // Store the new value into current + var fe = new FieldExpr (((IteratorStorey) storey).CurrentField, loc); + fe.InstanceExpression = new CompilerGeneratedThis (storey.CurrentType, loc); + fe.EmitAssign (ec, expr, false, false); + + base.InjectYield (ec, expr, resume_pc, unwind_protect, resume_point); + + EmitLeave (ec, unwind_protect); + + ec.MarkLabel (resume_point); + } + + public static void CreateIterator (IMethodData method, TypeDefinition parent, Modifiers modifiers) + { + bool is_enumerable; + TypeSpec iterator_type; + + TypeSpec ret = method.ReturnType; + if (ret == null) + return; + + if (!CheckType (ret, parent, out iterator_type, out is_enumerable)) { + parent.Compiler.Report.Error (1624, method.Location, + "The body of `{0}' cannot be an iterator block " + + "because `{1}' is not an iterator interface type", + method.GetSignatureForError (), + ret.GetSignatureForError ()); + return; + } + + ParametersCompiled parameters = method.ParameterInfo; + for (int i = 0; i < parameters.Count; i++) { + Parameter p = parameters [i]; + Parameter.Modifier mod = p.ModFlags; + if ((mod & Parameter.Modifier.RefOutMask) != 0) { + parent.Compiler.Report.Error (1623, p.Location, + "Iterators cannot have ref or out parameters"); + return; + } + + if (p is ArglistParameter) { + parent.Compiler.Report.Error (1636, method.Location, + "__arglist is not allowed in parameter list of iterators"); + return; + } + + if (parameters.Types [i].IsPointer) { + parent.Compiler.Report.Error (1637, p.Location, + "Iterators cannot have unsafe parameters or yield types"); + return; + } + } + + if ((modifiers & Modifiers.UNSAFE) != 0) { + parent.Compiler.Report.Error (1629, method.Location, "Unsafe code may not appear in iterators"); + } + + method.Block = method.Block.ConvertToIterator (method, parent, iterator_type, is_enumerable); + } + + static bool CheckType (TypeSpec ret, TypeContainer parent, out TypeSpec original_iterator_type, out bool is_enumerable) + { + original_iterator_type = null; + is_enumerable = false; + + if (ret.BuiltinType == BuiltinTypeSpec.Type.IEnumerable) { + original_iterator_type = parent.Compiler.BuiltinTypes.Object; + is_enumerable = true; + return true; + } + if (ret.BuiltinType == BuiltinTypeSpec.Type.IEnumerator) { + original_iterator_type = parent.Compiler.BuiltinTypes.Object; + is_enumerable = false; + return true; + } + + InflatedTypeSpec inflated = ret as InflatedTypeSpec; + if (inflated == null) + return false; + + var member_definition = inflated.MemberDefinition; + PredefinedType ptype = parent.Module.PredefinedTypes.IEnumerableGeneric; + + if (ptype.Define () && ptype.TypeSpec.MemberDefinition == member_definition) { + original_iterator_type = inflated.TypeArguments[0]; + is_enumerable = true; + return true; + } + + ptype = parent.Module.PredefinedTypes.IEnumeratorGeneric; + if (ptype.Define () && ptype.TypeSpec.MemberDefinition == member_definition) { + original_iterator_type = inflated.TypeArguments[0]; + is_enumerable = false; + return true; + } + + return false; + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/lambda.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/lambda.cs new file mode 100644 index 000000000..7868c6a2c --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/lambda.cs @@ -0,0 +1,229 @@ +// +// lambda.cs: support for lambda expressions +// +// Authors: Miguel de Icaza (miguel@gnu.org) +// Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2007-2008 Novell, Inc +// Copyright 2011 Xamarin Inc +// + +#if STATIC +using IKVM.Reflection.Emit; +#else +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp { + public class LambdaExpression : AnonymousMethodExpression + { + // + // The parameters can either be: + // A list of Parameters (explicitly typed parameters) + // An ImplicitLambdaParameter + // + public LambdaExpression (Location loc) + : base (loc) + { + } + + protected override Expression CreateExpressionTree (ResolveContext ec, TypeSpec delegate_type) + { + if (ec.IsInProbingMode) + return this; + + BlockContext bc = new BlockContext (ec.MemberContext, ec.ConstructorBlock, ec.BuiltinTypes.Void) { + CurrentAnonymousMethod = ec.CurrentAnonymousMethod + }; + + Expression args = Parameters.CreateExpressionTree (bc, loc); + Expression expr = Block.CreateExpressionTree (ec); + if (expr == null) + return null; + + Arguments arguments = new Arguments (2); + arguments.Add (new Argument (expr)); + arguments.Add (new Argument (args)); + return CreateExpressionFactoryCall (ec, "Lambda", + new TypeArguments (new TypeExpression (delegate_type, loc)), + arguments); + } + + public override bool HasExplicitParameters { + get { + return Parameters.Count > 0 && !(Parameters.FixedParameters [0] is ImplicitLambdaParameter); + } + } + + protected override ParametersCompiled ResolveParameters (ResolveContext ec, TypeInferenceContext tic, TypeSpec delegateType) + { + if (!delegateType.IsDelegate) + return null; + + AParametersCollection d_params = Delegate.GetParameters (delegateType); + + if (HasExplicitParameters) { + if (!VerifyExplicitParameters (ec, tic, delegateType, d_params)) + return null; + + return Parameters; + } + + // + // If L has an implicitly typed parameter list we make implicit parameters explicit + // Set each parameter of L is given the type of the corresponding parameter in D + // + if (!VerifyParameterCompatibility (ec, tic, delegateType, d_params, ec.IsInProbingMode)) + return null; + + TypeSpec [] ptypes = new TypeSpec [Parameters.Count]; + for (int i = 0; i < d_params.Count; i++) { + // D has no ref or out parameters + if ((d_params.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask) != 0) + return null; + + TypeSpec d_param = d_params.Types [i]; + + // + // When type inference context exists try to apply inferred type arguments + // + if (tic != null) { + d_param = tic.InflateGenericArgument (ec, d_param); + } + + ptypes [i] = d_param; + ImplicitLambdaParameter ilp = (ImplicitLambdaParameter) Parameters.FixedParameters [i]; + ilp.SetParameterType (d_param); + ilp.Resolve (null, i); + } + + Parameters.Types = ptypes; + return Parameters; + } + + protected override AnonymousMethodBody CompatibleMethodFactory (TypeSpec returnType, TypeSpec delegateType, ParametersCompiled p, ParametersBlock b) + { + return new LambdaMethod (p, b, returnType, delegateType, loc); + } + + protected override bool DoResolveParameters (ResolveContext rc) + { + // + // Only explicit parameters can be resolved at this point + // + if (HasExplicitParameters) { + return Parameters.Resolve (rc); + } + + return true; + } + + public override string GetSignatureForError () + { + return "lambda expression"; + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + class LambdaMethod : AnonymousMethodBody + { + public LambdaMethod (ParametersCompiled parameters, + ParametersBlock block, TypeSpec return_type, TypeSpec delegate_type, + Location loc) + : base (parameters, block, return_type, delegate_type, loc) + { + } + + #region Properties + + public override string ContainerType { + get { + return "lambda expression"; + } + } + + #endregion + + protected override void CloneTo (CloneContext clonectx, Expression target) + { + // TODO: nothing ?? + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + BlockContext bc = new BlockContext (ec.MemberContext, Block, ReturnType); + Expression args = parameters.CreateExpressionTree (bc, loc); + Expression expr = Block.CreateExpressionTree (ec); + if (expr == null) + return null; + + Arguments arguments = new Arguments (2); + arguments.Add (new Argument (expr)); + arguments.Add (new Argument (args)); + return CreateExpressionFactoryCall (ec, "Lambda", + new TypeArguments (new TypeExpression (type, loc)), + arguments); + } + } + + // + // This is a return statement that is prepended lambda expression bodies that happen + // to be expressions. Depending on the return type of the delegate this will behave + // as either { expr (); return (); } or { return expr (); } + // + public class ContextualReturn : Return + { + ExpressionStatement statement; + + public ContextualReturn (Expression expr) + : base (expr, expr.StartLocation) + { + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + return Expr.CreateExpressionTree (ec); + } + + protected override void DoEmit (EmitContext ec) + { + if (statement != null) { + statement.EmitStatement (ec); + if (unwind_protect) + ec.Emit (OpCodes.Leave, ec.CreateReturnLabel ()); + else { + ec.Emit (OpCodes.Ret); + } + return; + } + + base.DoEmit (ec); + } + + protected override bool DoResolve (BlockContext ec) + { + // + // When delegate returns void, only expression statements can be used + // + if (ec.ReturnType.Kind == MemberKind.Void) { + Expr = Expr.Resolve (ec); + if (Expr == null) + return false; + + statement = Expr as ExpressionStatement; + if (statement == null) + Expr.Error_InvalidExpressionStatement (ec); + + return true; + } + + return base.DoResolve (ec); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/linq.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/linq.cs new file mode 100644 index 000000000..11f01009d --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/linq.cs @@ -0,0 +1,904 @@ +// +// linq.cs: support for query expressions +// +// Authors: Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2007-2008 Novell, Inc +// Copyright 2011 Xamarin Inc +// + +using System; +using System.Collections.Generic; + +namespace Mono.CSharp.Linq +{ + public class QueryExpression : AQueryClause + { + public QueryExpression (AQueryClause start) + : base (null, null, start.Location) + { + this.next = start; + } + + public override Expression BuildQueryClause (ResolveContext ec, Expression lSide, Parameter parentParameter) + { + return next.BuildQueryClause (ec, lSide, parentParameter); + } + + protected override Expression DoResolve (ResolveContext ec) + { + int counter = QueryBlock.TransparentParameter.Counter; + + Expression e = BuildQueryClause (ec, null, null); + if (e != null) + e = e.Resolve (ec); + + // + // Reset counter in probing mode to ensure that all transparent + // identifier anonymous types are created only once + // + if (ec.IsInProbingMode) + QueryBlock.TransparentParameter.Counter = counter; + + return e; + } + + protected override string MethodName { + get { throw new NotSupportedException (); } + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public abstract class AQueryClause : ShimExpression + { + protected class QueryExpressionAccess : MemberAccess + { + public QueryExpressionAccess (Expression expr, string methodName, Location loc) + : base (expr, methodName, loc) + { + } + + public QueryExpressionAccess (Expression expr, string methodName, TypeArguments typeArguments, Location loc) + : base (expr, methodName, typeArguments, loc) + { + } + + protected override void Error_TypeDoesNotContainDefinition (ResolveContext ec, TypeSpec type, string name) + { + ec.Report.Error (1935, loc, "An implementation of `{0}' query expression pattern could not be found. " + + "Are you missing `System.Linq' using directive or `System.Core.dll' assembly reference?", + name); + } + } + + protected class QueryExpressionInvocation : Invocation, OverloadResolver.IErrorHandler + { + public QueryExpressionInvocation (QueryExpressionAccess expr, Arguments arguments) + : base (expr, arguments) + { + } + + protected override MethodGroupExpr DoResolveOverload (ResolveContext ec) + { + MethodGroupExpr rmg = mg.OverloadResolve (ec, ref arguments, this, OverloadResolver.Restrictions.None); + return rmg; + } + + protected override Expression DoResolveDynamic (ResolveContext ec, Expression memberExpr) + { + ec.Report.Error (1979, loc, + "Query expressions with a source or join sequence of type `dynamic' are not allowed"); + return null; + } + + #region IErrorHandler Members + + bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous) + { + ec.Report.SymbolRelatedToPreviousError (best); + ec.Report.SymbolRelatedToPreviousError (ambiguous); + ec.Report.Error (1940, loc, "Ambiguous implementation of the query pattern `{0}' for source type `{1}'", + best.Name, mg.InstanceExpression.GetSignatureForError ()); + return true; + } + + bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index) + { + return false; + } + + bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best) + { + return false; + } + + bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best) + { + var ms = (MethodSpec) best; + TypeSpec source_type = ms.Parameters.ExtensionMethodType; + if (source_type != null) { + Argument a = arguments[0]; + + if (TypeManager.IsGenericType (source_type) && InflatedTypeSpec.ContainsTypeParameter (source_type)) { + TypeInferenceContext tic = new TypeInferenceContext (source_type.TypeArguments); + tic.OutputTypeInference (rc, a.Expr, source_type); + if (tic.FixAllTypes (rc)) { + source_type = source_type.GetDefinition ().MakeGenericType (rc, tic.InferredTypeArguments); + } + } + + if (!Convert.ImplicitConversionExists (rc, a.Expr, source_type)) { + rc.Report.Error (1936, loc, "An implementation of `{0}' query expression pattern for source type `{1}' could not be found", + best.Name, a.Type.GetSignatureForError ()); + return true; + } + } + + if (best.Name == "SelectMany") { + rc.Report.Error (1943, loc, + "An expression type is incorrect in a subsequent `from' clause in a query expression with source type `{0}'", + arguments[0].GetSignatureForError ()); + } else { + rc.Report.Error (1942, loc, + "An expression type in `{0}' clause is incorrect. Type inference failed in the call to `{1}'", + best.Name.ToLowerInvariant (), best.Name); + } + + return true; + } + + #endregion + } + + public AQueryClause next; + public QueryBlock block; + + protected AQueryClause (QueryBlock block, Expression expr, Location loc) + : base (expr) + { + this.block = block; + this.loc = loc; + } + + protected override void CloneTo (CloneContext clonectx, Expression target) + { + base.CloneTo (clonectx, target); + + AQueryClause t = (AQueryClause) target; + + if (block != null) + t.block = (QueryBlock) clonectx.LookupBlock (block); + + if (next != null) + t.next = (AQueryClause) next.Clone (clonectx); + } + + protected override Expression DoResolve (ResolveContext ec) + { + return expr.Resolve (ec); + } + + public virtual Expression BuildQueryClause (ResolveContext ec, Expression lSide, Parameter parameter) + { + Arguments args = null; + CreateArguments (ec, parameter, ref args); + lSide = CreateQueryExpression (lSide, args); + if (next != null) { + parameter = CreateChildrenParameters (parameter); + + Select s = next as Select; + if (s == null || s.IsRequired (parameter)) + return next.BuildQueryClause (ec, lSide, parameter); + + // Skip transparent select clause if any clause follows + if (next.next != null) + return next.next.BuildQueryClause (ec, lSide, parameter); + } + + return lSide; + } + + protected virtual Parameter CreateChildrenParameters (Parameter parameter) + { + // Have to clone the parameter for any children use, it carries block sensitive data + return parameter.Clone (); + } + + protected virtual void CreateArguments (ResolveContext ec, Parameter parameter, ref Arguments args) + { + args = new Arguments (2); + + LambdaExpression selector = new LambdaExpression (loc); + + block.SetParameter (parameter); + selector.Block = block; + selector.Block.AddStatement (new ContextualReturn (expr)); + + args.Add (new Argument (selector)); + } + + protected Invocation CreateQueryExpression (Expression lSide, Arguments arguments) + { + return new QueryExpressionInvocation ( + new QueryExpressionAccess (lSide, MethodName, loc), arguments); + } + + protected abstract string MethodName { get; } + + public AQueryClause Next { + set { + next = value; + } + } + + public AQueryClause Tail { + get { + return next == null ? this : next.Tail; + } + } + } + + // + // A query clause with an identifier (range variable) + // + public abstract class ARangeVariableQueryClause : AQueryClause + { + sealed class RangeAnonymousTypeParameter : AnonymousTypeParameter + { + public RangeAnonymousTypeParameter (Expression initializer, RangeVariable parameter) + : base (initializer, parameter.Name, parameter.Location) + { + } + + protected override void Error_InvalidInitializer (ResolveContext ec, string initializer) + { + ec.Report.Error (1932, loc, "A range variable `{0}' cannot be initialized with `{1}'", + Name, initializer); + } + } + + class RangeParameterReference : ParameterReference + { + Parameter parameter; + + public RangeParameterReference (Parameter p) + : base (null, p.Location) + { + this.parameter = p; + } + + protected override Expression DoResolve (ResolveContext ec) + { + pi = ec.CurrentBlock.ParametersBlock.GetParameterInfo (parameter); + return base.DoResolve (ec); + } + } + + protected RangeVariable identifier; + + public RangeVariable IntoVariable { + get { + return identifier; + } + } + + protected ARangeVariableQueryClause (QueryBlock block, RangeVariable identifier, Expression expr, Location loc) + : base (block, expr, loc) + { + this.identifier = identifier; + } + + public RangeVariable Identifier { + get { + return identifier; + } + } + + public FullNamedExpression IdentifierType { get; set; } + + protected Invocation CreateCastExpression (Expression lSide) + { + return new QueryExpressionInvocation ( + new QueryExpressionAccess (lSide, "Cast", new TypeArguments (IdentifierType), loc), null); + } + + protected override Parameter CreateChildrenParameters (Parameter parameter) + { + return new QueryBlock.TransparentParameter (parameter.Clone (), GetIntoVariable ()); + } + + protected static Expression CreateRangeVariableType (ResolveContext rc, Parameter parameter, RangeVariable name, Expression init) + { + var args = new List (2); + + // + // The first argument is the reference to the parameter + // + args.Add (new AnonymousTypeParameter (new RangeParameterReference (parameter), parameter.Name, parameter.Location)); + + // + // The second argument is the linq expression + // + args.Add (new RangeAnonymousTypeParameter (init, name)); + + // + // Create unique anonymous type + // + return new NewAnonymousType (args, rc.MemberContext.CurrentMemberDefinition.Parent, name.Location); + } + + protected virtual RangeVariable GetIntoVariable () + { + return identifier; + } + } + + public sealed class RangeVariable : INamedBlockVariable + { + Block block; + + public RangeVariable (string name, Location loc) + { + Name = name; + Location = loc; + } + + #region Properties + + public Block Block { + get { + return block; + } + set { + block = value; + } + } + + public bool IsDeclared { + get { + return true; + } + } + + public bool IsParameter { + get { + return false; + } + } + + public Location Location { get; private set; } + + public string Name { get; private set; } + + #endregion + + public Expression CreateReferenceExpression (ResolveContext rc, Location loc) + { + // + // We know the variable name is somewhere in the scope. This generates + // an access expression from current block + // + var pb = rc.CurrentBlock.ParametersBlock; + while (true) { + if (pb is QueryBlock) { + for (int i = pb.Parameters.Count - 1; i >= 0; --i) { + var p = pb.Parameters[i]; + if (p.Name == Name) + return pb.GetParameterReference (i, loc); + + Expression expr = null; + var tp = p as QueryBlock.TransparentParameter; + while (tp != null) { + if (expr == null) + expr = pb.GetParameterReference (i, loc); + else + expr = new TransparentMemberAccess (expr, tp.Name); + + if (tp.Identifier == Name) + return new TransparentMemberAccess (expr, Name); + + if (tp.Parent.Name == Name) + return new TransparentMemberAccess (expr, Name); + + tp = tp.Parent as QueryBlock.TransparentParameter; + } + } + } + + if (pb == block) + return null; + + pb = pb.Parent.ParametersBlock; + } + } + } + + public class QueryStartClause : ARangeVariableQueryClause + { + public QueryStartClause (QueryBlock block, Expression expr, RangeVariable identifier, Location loc) + : base (block, identifier, expr, loc) + { + block.AddRangeVariable (identifier); + } + + public override Expression BuildQueryClause (ResolveContext ec, Expression lSide, Parameter parameter) + { + if (IdentifierType != null) + expr = CreateCastExpression (expr); + + if (parameter == null) + lSide = expr; + + return next.BuildQueryClause (ec, lSide, new ImplicitLambdaParameter (identifier.Name, identifier.Location)); + } + + protected override Expression DoResolve (ResolveContext ec) + { + Expression e = BuildQueryClause (ec, null, null); + return e.Resolve (ec); + } + + protected override string MethodName { + get { throw new NotSupportedException (); } + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + + public class GroupBy : AQueryClause + { + Expression element_selector; + QueryBlock element_block; + + public Expression ElementSelector { + get { return this.element_selector; } + } + + public GroupBy (QueryBlock block, Expression elementSelector, QueryBlock elementBlock, Expression keySelector, Location loc) + : base (block, keySelector, loc) + { + // + // Optimizes clauses like `group A by A' + // + if (!elementSelector.Equals (keySelector)) { + this.element_selector = elementSelector; + this.element_block = elementBlock; + } + } + + public Expression SelectorExpression { + get { + return element_selector; + } + } + + protected override void CreateArguments (ResolveContext ec, Parameter parameter, ref Arguments args) + { + base.CreateArguments (ec, parameter, ref args); + + if (element_selector != null) { + LambdaExpression lambda = new LambdaExpression (element_selector.Location); + + element_block.SetParameter (parameter.Clone ()); + lambda.Block = element_block; + lambda.Block.AddStatement (new ContextualReturn (element_selector)); + args.Add (new Argument (lambda)); + } + } + + protected override void CloneTo (CloneContext clonectx, Expression target) + { + GroupBy t = (GroupBy) target; + if (element_selector != null) { + t.element_selector = element_selector.Clone (clonectx); + t.element_block = (QueryBlock) element_block.Clone (clonectx); + } + + base.CloneTo (clonectx, t); + } + + protected override string MethodName { + get { return "GroupBy"; } + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class Join : SelectMany + { + QueryBlock inner_selector, outer_selector; + + public RangeVariable JoinVariable { + get { return this.GetIntoVariable (); } + } + + public Join (QueryBlock block, RangeVariable lt, Expression inner, QueryBlock outerSelector, QueryBlock innerSelector, Location loc) + : base (block, lt, inner, loc) + { + this.outer_selector = outerSelector; + this.inner_selector = innerSelector; + } + + public QueryBlock InnerSelector { + get { + return inner_selector; + } + } + + public QueryBlock OuterSelector { + get { + return outer_selector; + } + } + + protected override void CreateArguments (ResolveContext ec, Parameter parameter, ref Arguments args) + { + args = new Arguments (4); + + if (IdentifierType != null) + expr = CreateCastExpression (expr); + + args.Add (new Argument (expr)); + + outer_selector.SetParameter (parameter.Clone ()); + var lambda = new LambdaExpression (outer_selector.StartLocation); + lambda.Block = outer_selector; + args.Add (new Argument (lambda)); + + inner_selector.SetParameter (new ImplicitLambdaParameter (identifier.Name, identifier.Location)); + lambda = new LambdaExpression (inner_selector.StartLocation); + lambda.Block = inner_selector; + args.Add (new Argument (lambda)); + + base.CreateArguments (ec, parameter, ref args); + } + + protected override void CloneTo (CloneContext clonectx, Expression target) + { + Join t = (Join) target; + t.inner_selector = (QueryBlock) inner_selector.Clone (clonectx); + t.outer_selector = (QueryBlock) outer_selector.Clone (clonectx); + base.CloneTo (clonectx, t); + } + + protected override string MethodName { + get { return "Join"; } + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class GroupJoin : Join + { + readonly RangeVariable into; + + public GroupJoin (QueryBlock block, RangeVariable lt, Expression inner, + QueryBlock outerSelector, QueryBlock innerSelector, RangeVariable into, Location loc) + : base (block, lt, inner, outerSelector, innerSelector, loc) + { + this.into = into; + } + + protected override RangeVariable GetIntoVariable () + { + return into; + } + + protected override string MethodName { + get { return "GroupJoin"; } + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class Let : ARangeVariableQueryClause + { + public Let (QueryBlock block, RangeVariable identifier, Expression expr, Location loc) + : base (block, identifier, expr, loc) + { + } + + protected override void CreateArguments (ResolveContext ec, Parameter parameter, ref Arguments args) + { + expr = CreateRangeVariableType (ec, parameter, identifier, expr); + base.CreateArguments (ec, parameter, ref args); + } + + protected override string MethodName { + get { return "Select"; } + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class Select : AQueryClause + { + public Select (QueryBlock block, Expression expr, Location loc) + : base (block, expr, loc) + { + } + + // + // For queries like `from a orderby a select a' + // the projection is transparent and select clause can be safely removed + // + public bool IsRequired (Parameter parameter) + { + SimpleName sn = expr as SimpleName; + if (sn == null) + return true; + + return sn.Name != parameter.Name; + } + + protected override string MethodName { + get { return "Select"; } + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + + } + + public class SelectMany : ARangeVariableQueryClause + { + public SelectMany (QueryBlock block, RangeVariable identifier, Expression expr, Location loc) + : base (block, identifier, expr, loc) + { + } + + protected override void CreateArguments (ResolveContext ec, Parameter parameter, ref Arguments args) + { + if (args == null) { + if (IdentifierType != null) + expr = CreateCastExpression (expr); + + base.CreateArguments (ec, parameter.Clone (), ref args); + } + + Expression result_selector_expr; + QueryBlock result_block; + + var target = GetIntoVariable (); + var target_param = new ImplicitLambdaParameter (target.Name, target.Location); + + // + // When select follows use it as a result selector + // + if (next is Select) { + result_selector_expr = next.Expr; + + result_block = next.block; + result_block.SetParameters (parameter, target_param); + + next = next.next; + } else { + result_selector_expr = CreateRangeVariableType (ec, parameter, target, new SimpleName (target.Name, target.Location)); + + result_block = new QueryBlock (block.Parent, block.StartLocation); + result_block.SetParameters (parameter, target_param); + } + + LambdaExpression result_selector = new LambdaExpression (Location); + result_selector.Block = result_block; + result_selector.Block.AddStatement (new ContextualReturn (result_selector_expr)); + + args.Add (new Argument (result_selector)); + } + + protected override string MethodName { + get { return "SelectMany"; } + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class Where : AQueryClause + { + public Where (QueryBlock block, Expression expr, Location loc) + : base (block, expr, loc) + { + } + + protected override string MethodName { + get { return "Where"; } + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class OrderByAscending : AQueryClause + { + public OrderByAscending (QueryBlock block, Expression expr) + : base (block, expr, expr.Location) + { + } + + protected override string MethodName { + get { return "OrderBy"; } + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class OrderByDescending : AQueryClause + { + public OrderByDescending (QueryBlock block, Expression expr) + : base (block, expr, expr.Location) + { + } + + protected override string MethodName { + get { return "OrderByDescending"; } + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class ThenByAscending : OrderByAscending + { + public ThenByAscending (QueryBlock block, Expression expr) + : base (block, expr) + { + } + + protected override string MethodName { + get { return "ThenBy"; } + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class ThenByDescending : OrderByDescending + { + public ThenByDescending (QueryBlock block, Expression expr) + : base (block, expr) + { + } + + protected override string MethodName { + get { return "ThenByDescending"; } + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + // + // Implicit query block + // + public class QueryBlock : ParametersBlock + { + // + // Transparent parameters are used to package up the intermediate results + // and pass them onto next clause + // + public sealed class TransparentParameter : ImplicitLambdaParameter + { + public static int Counter; + const string ParameterNamePrefix = "<>__TranspIdent"; + + public readonly Parameter Parent; + public readonly string Identifier; + + public TransparentParameter (Parameter parent, RangeVariable identifier) + : base (ParameterNamePrefix + Counter++, identifier.Location) + { + Parent = parent; + Identifier = identifier.Name; + } + + public static void Reset () + { + Counter = 0; + } + } + + public QueryBlock (Block parent, Location start) + : base (parent, ParametersCompiled.EmptyReadOnlyParameters, start, Flags.CompilerGenerated) + { + } + + public void AddRangeVariable (RangeVariable variable) + { + variable.Block = this; + TopBlock.AddLocalName (variable.Name, variable, true); + } + + public override void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason) + { + TopBlock.Report.Error (1931, variable.Location, + "A range variable `{0}' conflicts with a previous declaration of `{0}'", + name); + } + + public override void Error_AlreadyDeclared (string name, INamedBlockVariable variable) + { + TopBlock.Report.Error (1930, variable.Location, + "A range variable `{0}' has already been declared in this scope", + name); + } + + public override void Error_AlreadyDeclaredTypeParameter (string name, Location loc) + { + TopBlock.Report.Error (1948, loc, + "A range variable `{0}' conflicts with a method type parameter", + name); + } + + public void SetParameter (Parameter parameter) + { + base.parameters = new ParametersCompiled (parameter); + base.parameter_info = new ParameterInfo[] { + new ParameterInfo (this, 0) + }; + } + + public void SetParameters (Parameter first, Parameter second) + { + base.parameters = new ParametersCompiled (first, second); + base.parameter_info = new ParameterInfo[] { + new ParameterInfo (this, 0), + new ParameterInfo (this, 1) + }; + } + } + + sealed class TransparentMemberAccess : MemberAccess + { + public TransparentMemberAccess (Expression expr, string name) + : base (expr, name) + { + } + + public override Expression DoResolveLValue (ResolveContext rc, Expression right_side) + { + rc.Report.Error (1947, loc, + "A range variable `{0}' cannot be assigned to. Consider using `let' clause to store the value", + Name); + + return null; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/literal.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/literal.cs new file mode 100644 index 000000000..1af2d0c9e --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/literal.cs @@ -0,0 +1,333 @@ +// +// literal.cs: Literal representation for the IL tree. +// +// Author: +// Miguel de Icaza (miguel@ximian.com) +// Marek Safar (marek.safar@seznam.cz) +// +// Copyright 2001 Ximian, Inc. +// Copyright 2011 Xamarin Inc +// +// +// Notice that during parsing we create objects of type Literal, but the +// types are not loaded (thats why the Resolve method has to assign the +// type at that point). +// +// Literals differ from the constants in that we know we encountered them +// as a literal in the source code (and some extra rules apply there) and +// they have to be resolved (since during parsing we have not loaded the +// types yet) while constants are created only after types have been loaded +// and are fully resolved when born. +// + +#if STATIC +using IKVM.Reflection.Emit; +#else +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp +{ + public interface ILiteralConstant + { +#if FULL_AST + char[] ParsedValue { get; set; } +#endif + } + + // + // The null literal + // + // Note: C# specification null-literal is NullLiteral of NullType type + // + public class NullLiteral : NullConstant + { + // + // Default type of null is an object + // + public NullLiteral (Location loc) + : base (InternalType.NullLiteral, loc) + { + } + + public override void Error_ValueCannotBeConverted (ResolveContext ec, TypeSpec t, bool expl) + { + if (t.IsGenericParameter) { + ec.Report.Error(403, loc, + "Cannot convert null to the type parameter `{0}' because it could be a value " + + "type. Consider using `default ({0})' instead", t.Name); + return; + } + + if (TypeSpec.IsValueType (t)) { + ec.Report.Error(37, loc, "Cannot convert null to `{0}' because it is a value type", + t.GetSignatureForError ()); + return; + } + + base.Error_ValueCannotBeConverted (ec, t, expl); + } + + public override string GetValueAsLiteral () + { + return "null"; + } + + public override bool IsLiteral { + get { return true; } + } + + public override System.Linq.Expressions.Expression MakeExpression (BuilderContext ctx) + { + return System.Linq.Expressions.Expression.Constant (null); + } + } + + public class BoolLiteral : BoolConstant, ILiteralConstant + { + public BoolLiteral (BuiltinTypes types, bool val, Location loc) + : base (types, val, loc) + { + } + + public override bool IsLiteral { + get { return true; } + } + +#if FULL_AST + public char[] ParsedValue { get; set; } +#endif + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class CharLiteral : CharConstant, ILiteralConstant + { + public CharLiteral (BuiltinTypes types, char c, Location loc) + : base (types, c, loc) + { + } + + public override bool IsLiteral { + get { return true; } + } + +#if FULL_AST + public char[] ParsedValue { get; set; } +#endif + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class IntLiteral : IntConstant, ILiteralConstant + { + public IntLiteral (BuiltinTypes types, int l, Location loc) + : base (types, l, loc) + { + } + + public override Constant ConvertImplicitly (TypeSpec type) + { + // + // The 0 literal can be converted to an enum value + // + if (Value == 0 && type.IsEnum) { + Constant c = ConvertImplicitly (EnumSpec.GetUnderlyingType (type)); + if (c == null) + return null; + + return new EnumConstant (c, type); + } + + return base.ConvertImplicitly (type); + } + + public override bool IsLiteral { + get { return true; } + } + +#if FULL_AST + public char[] ParsedValue { get; set; } +#endif + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class UIntLiteral : UIntConstant, ILiteralConstant + { + public UIntLiteral (BuiltinTypes types, uint l, Location loc) + : base (types, l, loc) + { + } + + public override bool IsLiteral { + get { return true; } + } + +#if FULL_AST + public char[] ParsedValue { get; set; } +#endif + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class LongLiteral : LongConstant, ILiteralConstant + { + public LongLiteral (BuiltinTypes types, long l, Location loc) + : base (types, l, loc) + { + } + + public override bool IsLiteral { + get { return true; } + } + +#if FULL_AST + public char[] ParsedValue { get; set; } +#endif + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class ULongLiteral : ULongConstant, ILiteralConstant + { + public ULongLiteral (BuiltinTypes types, ulong l, Location loc) + : base (types, l, loc) + { + } + + public override bool IsLiteral { + get { return true; } + } + +#if FULL_AST + public char[] ParsedValue { get; set; } +#endif + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class FloatLiteral : FloatConstant, ILiteralConstant + { + public FloatLiteral (BuiltinTypes types, float f, Location loc) + : base (types, f, loc) + { + } + + public override bool IsLiteral { + get { return true; } + } + +#if FULL_AST + public char[] ParsedValue { get; set; } +#endif + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class DoubleLiteral : DoubleConstant, ILiteralConstant + { + public DoubleLiteral (BuiltinTypes types, double d, Location loc) + : base (types, d, loc) + { + } + + public override void Error_ValueCannotBeConverted (ResolveContext ec, TypeSpec target, bool expl) + { + if (target.BuiltinType == BuiltinTypeSpec.Type.Float) { + Error_664 (ec, loc, "float", "f"); + return; + } + + if (target.BuiltinType == BuiltinTypeSpec.Type.Decimal) { + Error_664 (ec, loc, "decimal", "m"); + return; + } + + base.Error_ValueCannotBeConverted (ec, target, expl); + } + + static void Error_664 (ResolveContext ec, Location loc, string type, string suffix) + { + ec.Report.Error (664, loc, + "Literal of type double cannot be implicitly converted to type `{0}'. Add suffix `{1}' to create a literal of this type", + type, suffix); + } + + public override bool IsLiteral { + get { return true; } + } + +#if FULL_AST + public char[] ParsedValue { get; set; } +#endif + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class DecimalLiteral : DecimalConstant, ILiteralConstant + { + public DecimalLiteral (BuiltinTypes types, decimal d, Location loc) + : base (types, d, loc) + { + } + + public override bool IsLiteral { + get { return true; } + } + +#if FULL_AST + public char[] ParsedValue { get; set; } +#endif + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class StringLiteral : StringConstant, ILiteralConstant + { + public StringLiteral (BuiltinTypes types, string s, Location loc) + : base (types, s, loc) + { + } + + public override bool IsLiteral { + get { return true; } + } + +#if FULL_AST + public char[] ParsedValue { get; set; } +#endif + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/location.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/location.cs new file mode 100644 index 000000000..a27ebf819 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/location.cs @@ -0,0 +1,883 @@ +// +// location.cs: Keeps track of the location of source code entity +// +// Author: +// Miguel de Icaza +// Atsushi Enomoto +// Marek Safar (marek.safar@gmail.com) +// +// Copyright 2001 Ximian, Inc. +// Copyright 2005 Novell, Inc. +// + +using System; +using System.Collections.Generic; +using Mono.CompilerServices.SymbolWriter; +using System.Diagnostics; +using System.Linq; + +namespace Mono.CSharp +{ + // + // This is one single source file. + // + public class SourceFile : IEquatable + { + // + // Used by #line directive to track hidden sequence point + // regions + // + struct LocationRegion : IComparable + { + public readonly Location Start; + public readonly Location End; + + public LocationRegion (Location start, Location end) + { + this.Start = start; + this.End = end; + } + + public int CompareTo (LocationRegion other) + { + if (Start.Row == other.Start.Row) + return Start.Column.CompareTo (other.Start.Column); + + return Start.Row.CompareTo (other.Start.Row); + } + + public override string ToString () + { + return Start.ToString () + " - " + End.ToString (); + } + } + + static readonly byte[] MD5Algorith = { 96, 166, 110, 64, 207, 100, 130, 76, 182, 240, 66, 212, 129, 114, 167, 153 }; + + public readonly string Name; + public readonly string FullPathName; + public readonly int Index; + public bool AutoGenerated; + + SourceFileEntry file; + byte[] algGuid, checksum; + List hidden_lines; + + public SourceFile (string name, string path, int index) + { + this.Index = index; + this.Name = name; + this.FullPathName = path; + } + + public byte[] Checksum { + get { + return checksum; + } + } + + public bool HasChecksum { + get { + return checksum != null; + } + } + + public SourceFileEntry SourceFileEntry { + get { + return file; + } + } + + public void SetChecksum (byte[] checksum) + { + SetChecksum (MD5Algorith, checksum); + } + + public void SetChecksum (byte[] algorithmGuid, byte[] checksum) + { + this.algGuid = algorithmGuid; + this.checksum = checksum; + } + + public SourceFileEntry CreateSymbolInfo (MonoSymbolFile symwriter) + { + if (hidden_lines != null) + hidden_lines.Sort (); + + file = new SourceFileEntry (symwriter, FullPathName, algGuid, checksum); + if (AutoGenerated) + file.SetAutoGenerated (); + + return file; + } + + public bool Equals (SourceFile other) + { + return FullPathName == other.FullPathName; + } + + public bool IsHiddenLocation (Location loc) + { + if (hidden_lines == null) + return false; + + int index = hidden_lines.BinarySearch (new LocationRegion (loc, loc)); + index = ~index; + if (index > 0) { + var found = hidden_lines[index - 1]; + if (loc.Row < found.End.Row) + return true; + } + + return false; + } + + public void RegisterHiddenScope (Location start, Location end) + { + if (hidden_lines == null) + hidden_lines = new List (); + + hidden_lines.Add (new LocationRegion (start, end)); + } + + public override string ToString () + { + return String.Format ("SourceFile ({0}:{1}:{2})", Name, FullPathName, Index); + } + } + + /// + /// Keeps track of the location in the program + /// + /// + /// + /// This uses a compact representation and a couple of auxiliary + /// structures to keep track of tokens to (file,line and column) + /// mappings. The usage of the bits is: + /// + /// - 16 bits for "checkpoint" which is a mixed concept of + /// file and "line segment" + /// - 8 bits for line delta (offset) from the line segment + /// - 8 bits for column number. + /// + /// http://lists.ximian.com/pipermail/mono-devel-list/2004-December/009508.html + /// + public struct Location : IEquatable + { + struct Checkpoint { + public readonly int LineOffset; + public readonly int File; + + public Checkpoint (int file, int line) + { + File = file; + LineOffset = line - (int) (line % (1 << line_delta_bits)); + } + } + +#if FULL_AST + readonly long token; + + const int column_bits = 24; + const int line_delta_bits = 24; +#else + readonly int token; + + const int column_bits = 8; + const int line_delta_bits = 8; +#endif + const int checkpoint_bits = 16; + + const int column_mask = (1 << column_bits) - 1; + const int max_column = column_mask; + + static List source_list; + static Checkpoint [] checkpoints; + static int checkpoint_index; + + public readonly static Location Null = new Location (); + public static bool InEmacs; + + static Location () + { + Reset (); + } + + public static void Reset () + { + source_list = new List (); + checkpoint_index = 0; + } + + public static void AddFile (SourceFile file) + { + source_list.Add (file); + } + + // + // After adding all source files we want to compile with AddFile(), this method + // must be called to `reserve' an appropriate number of bits in the token for the + // source file. We reserve some extra space for files we encounter via #line + // directives while parsing. + // + static public void Initialize (List files) + { +#if NET_4_0 || MOBILE_DYNAMIC + source_list.AddRange (files); +#else + source_list.AddRange (files.ToArray ()); +#endif + + checkpoints = new Checkpoint [System.Math.Max (1, source_list.Count * 2)]; + if (checkpoints.Length > 0) + checkpoints [0] = new Checkpoint (0, 0); + } + + public Location (SourceFile file, int row, int column) + { + if (row <= 0) + token = 0; + else { + if (column > max_column) + column = max_column; + + long target = -1; + long delta = 0; + + // TODO: For eval only, need better handling of empty + int file_index = file == null ? 0 : file.Index; + + // FIXME: This value is certainly wrong but what was the intension + int max = checkpoint_index < 10 ? + checkpoint_index : 10; + for (int i = 0; i < max; i++) { + int offset = checkpoints [checkpoint_index - i].LineOffset; + delta = row - offset; + if (delta >= 0 && + delta < (1 << line_delta_bits) && + checkpoints[checkpoint_index - i].File == file_index) { + target = checkpoint_index - i; + break; + } + } + if (target == -1) { + AddCheckpoint (file_index, row); + target = checkpoint_index; + delta = row % (1 << line_delta_bits); + } + + long l = column + + (delta << column_bits) + + (target << (line_delta_bits + column_bits)); +#if FULL_AST + token = l; +#else + token = l > 0xFFFFFFFF ? 0 : (int) l; +#endif + } + } + + public static Location operator - (Location loc, int columns) + { + return new Location (loc.SourceFile, loc.Row, loc.Column - columns); + } + + static void AddCheckpoint (int file, int row) + { + if (checkpoints.Length == ++checkpoint_index) { + Array.Resize (ref checkpoints, checkpoint_index * 2); + } + checkpoints [checkpoint_index] = new Checkpoint (file, row); + } + + string FormatLocation (string fileName) + { + if (column_bits == 0 || InEmacs) + return fileName + "(" + Row.ToString () + "):"; + + return fileName + "(" + Row.ToString () + "," + Column.ToString () + + (Column == max_column ? "+):" : "):"); + } + + public override string ToString () + { + return FormatLocation (Name); + } + + public string ToStringFullName () + { + return FormatLocation (NameFullPath); + } + + /// + /// Whether the Location is Null + /// + public bool IsNull { + get { return token == 0; } + } + + public string Name { + get { + int index = File; + + if (token == 0 || index <= 0) + return null; + + SourceFile file = source_list [index - 1]; + return file.Name; + } + } + + public string NameFullPath { + get { + int index = File; + if (token == 0 || index <= 0) + return null; + + return source_list[index - 1].FullPathName; + } + } + + int CheckpointIndex { + get { + const int checkpoint_mask = (1 << checkpoint_bits) - 1; + return ((int) (token >> (line_delta_bits + column_bits))) & checkpoint_mask; + } + } + + public int Row { + get { + if (token == 0) + return 1; + + int offset = checkpoints[CheckpointIndex].LineOffset; + + const int line_delta_mask = (1 << column_bits) - 1; + return offset + (((int)(token >> column_bits)) & line_delta_mask); + } + } + + public int Column { + get { + if (token == 0) + return 1; + return (int) (token & column_mask); + } + } + + public int File { + get { + if (token == 0) + return 0; +if (checkpoints.Length <= CheckpointIndex) throw new Exception (String.Format ("Should not happen. Token is {0:X04}, checkpoints are {1}, index is {2}", token, checkpoints.Length, CheckpointIndex)); + return checkpoints [CheckpointIndex].File; + } + } + + // The ISymbolDocumentWriter interface is used by the symbol writer to + // describe a single source file - for each source file there's exactly + // one corresponding ISymbolDocumentWriter instance. + // + // This class has an internal hash table mapping source document names + // to such ISymbolDocumentWriter instances - so there's exactly one + // instance per document. + // + // This property returns the ISymbolDocumentWriter instance which belongs + // to the location's source file. + // + // If we don't have a symbol writer, this property is always null. + public SourceFile SourceFile { + get { + int index = File; + if (index == 0) + return null; + return source_list [index - 1]; + } + } + + #region IEquatable Members + + public bool Equals (Location other) + { + return this.token == other.token; + } + + #endregion + } + + public class SpecialsBag + { + public enum CommentType + { + Single, + Multi, + Documentation, + InactiveCode + } + + public bool Suppress { + get; + set; + } + + public class SpecialVisitor + { + public virtual void Visit (Comment comment) + { + } + public virtual void Visit (NewLineToken newLineToken) + { + } + public virtual void Visit (PreProcessorDirective preProcessorDirective) + { + } + } + public abstract class SpecialBase + { + public abstract void Accept (SpecialVisitor visitor); + } + + public class Comment : SpecialBase + { + public readonly CommentType CommentType; + public readonly bool StartsLine; + public readonly int Line; + public readonly int Col; + public readonly int EndLine; + public readonly int EndCol; + public readonly string Content; + + public Comment (CommentType commentType, bool startsLine, int line, int col, int endLine, int endCol, string content) + { + this.CommentType = commentType; + this.StartsLine = startsLine; + this.Line = line; + this.Col = col; + this.EndLine = endLine; + this.EndCol = endCol; + this.Content = content; + } + + public override string ToString () + { + return string.Format ("[Comment: CommentType={0}, Line={1}, Col={2}, EndLine={3}, EndCol={4}, Content={5}]", CommentType, Line, Col, EndLine, EndCol, Content); + } + + public override void Accept (SpecialVisitor visitor) + { + visitor.Visit (this); + } + } + + public class NewLineToken : SpecialBase + { + public readonly int Line; + public readonly int Col; + public readonly NewLine NewLine; + + public NewLineToken (int line, int col, NewLine newLine) + { + this.Line = line; + this.Col = col; + this.NewLine = newLine; + } + + public override void Accept (SpecialVisitor visitor) + { + visitor.Visit (this); + } + } + + public class PragmaPreProcessorDirective : PreProcessorDirective + { + public bool Disalbe { get; set; } + + public int WarningColumn { + get; + set; + } + + public int DisableRestoreColumn { + get; + set; + } + + public List Codes = new List (); + + public PragmaPreProcessorDirective (int line, int col, int endLine, int endCol, Tokenizer.PreprocessorDirective cmd, string arg) : base (line, col, endLine, endCol, cmd, arg) + { + } + } + + public class LineProcessorDirective : PreProcessorDirective + { + public int LineNumber { get; set; } + public string FileName { get; set; } + + public LineProcessorDirective (int line, int col, int endLine, int endCol, Tokenizer.PreprocessorDirective cmd, string arg) : base (line, col, endLine, endCol, cmd, arg) + { + } + } + + public class PreProcessorDirective : SpecialBase + { + public readonly int Line; + public readonly int Col; + public readonly int EndLine; + public readonly int EndCol; + + public readonly Tokenizer.PreprocessorDirective Cmd; + public readonly string Arg; + + public bool Take = true; + + public PreProcessorDirective (int line, int col, int endLine, int endCol, Tokenizer.PreprocessorDirective cmd, string arg) + { + this.Line = line; + this.Col = col; + this.EndLine = endLine; + this.EndCol = endCol; + this.Cmd = cmd; + this.Arg = arg; + } + + public override void Accept (SpecialVisitor visitor) + { + visitor.Visit (this); + } + + public override string ToString () + { + return string.Format ("[PreProcessorDirective: Line={0}, Col={1}, EndLine={2}, EndCol={3}, Cmd={4}, Arg={5}]", Line, Col, EndLine, EndCol, Cmd, Arg); + } + } + + public readonly List Specials = new List (); + + CommentType curComment; + bool startsLine; + int startLine, startCol; + System.Text.StringBuilder contentBuilder = new System.Text.StringBuilder (); + + [Conditional ("FULL_AST")] + public void StartComment (CommentType type, bool startsLine, int startLine, int startCol) + { + if (Suppress) + return; + inComment = true; + curComment = type; + this.startsLine = startsLine; + this.startLine = startLine; + this.startCol = startCol; + contentBuilder.Length = 0; + } + + [Conditional ("FULL_AST")] + public void PushCommentChar (int ch) + { + if (Suppress) + return; + if (ch < 0) + return; + contentBuilder.Append ((char)ch); + } + [Conditional ("FULL_AST")] + public void PushCommentString (string str) + { + if (Suppress) + return; + contentBuilder.Append (str); + } + + bool inComment; + [Conditional ("FULL_AST")] + public void EndComment (int endLine, int endColumn) + { + if (Suppress) + return; + if (!inComment) + return; + inComment = false; + // Ignore empty comments + if (startLine == endLine && startCol == endColumn) + return; + Specials.Add (new Comment (curComment, startsLine, startLine, startCol, endLine, endColumn, contentBuilder.ToString ())); + } + + [Conditional ("FULL_AST")] + public void AddPreProcessorDirective (int startLine, int startCol, int endLine, int endColumn, Tokenizer.PreprocessorDirective cmd, string arg) + { + if (Suppress) + return; + if (inComment) + EndComment (startLine, startCol); + switch (cmd) { + case Tokenizer.PreprocessorDirective.Pragma: + Specials.Add (new PragmaPreProcessorDirective (startLine, startCol, endLine, endColumn, cmd, arg)); + break; + case Tokenizer.PreprocessorDirective.Line: + Specials.Add (new LineProcessorDirective (startLine, startCol, endLine, endColumn, cmd, arg)); + break; + default: + Specials.Add (new PreProcessorDirective (startLine, startCol, endLine, endColumn, cmd, arg)); + break; + } + } + + #if FULL_AST + public PragmaPreProcessorDirective SetPragmaDisable(bool disable) + { + if (Suppress) + return null; + var pragmaDirective = Specials [Specials.Count - 1] as PragmaPreProcessorDirective; + if (pragmaDirective == null) + return null; + pragmaDirective.Disalbe = disable; + return pragmaDirective; + } + #endif + + public PragmaPreProcessorDirective GetPragmaPreProcessorDirective() + { + if (Suppress) + return null; + return Specials [Specials.Count - 1] as PragmaPreProcessorDirective; + } + + + public LineProcessorDirective GetCurrentLineProcessorDirective() + { + if (Suppress) + return null; + return Specials [Specials.Count - 1] as LineProcessorDirective; + } + + public enum NewLine { Unix, Windows } + + int lastNewLine = -1; + int lastNewCol = -1; + [Conditional ("FULL_AST")] + public void AddNewLine (int line, int col, NewLine newLine) + { + if (Suppress) + return; + if (line == lastNewLine && col == lastNewCol) + return; + lastNewLine = line; + lastNewCol = col; + Specials.Add (new NewLineToken (line, col, newLine)); + } + + public void SkipIf () + { + if (Specials.Count > 0) { + var directive = Specials[Specials.Count - 1] as PreProcessorDirective; + if (directive != null) + directive.Take = false; + } + } + } + + // + // A bag of additional locations to support full ast tree + // + public class LocationsBag + { + public class MemberLocations + { + public IList> Modifiers { get; internal set; } + List locations; + + public MemberLocations (IList> mods, IEnumerable locs) + { + Modifiers = mods; + locations = locs != null ? new List (locs) : null; +/* + public readonly IList> Modifiers; + List locations; + + public MemberLocations (IList> mods) + { + Modifiers = mods; + } + + public MemberLocations (IList> mods, Location loc) + : this (mods) + { + AddLocations (loc); + } + + public MemberLocations (IList> mods, Location[] locs) + : this (mods) + { + AddLocations (locs); + } + + public MemberLocations (IList> mods, List locs) + : this (mods) + { + locations = locs;*/ + } + + #region Properties + + public Location this [int index] { + get { + return locations [index]; + } + } + + public int Count { + get { + return locations != null ? locations.Count : 0; + } + } + + #endregion + + public void AddLocations (Location loc) + { + if (locations == null) { + locations = new List (); + } + + locations.Add (loc); + } + + public void AddLocations (params Location[] additional) + + { + + AddLocations ((IEnumerable)additional); + + } + public void AddLocations (IEnumerable additional) + { + if (additional == null) + return; + if (locations == null) { + locations = new List (additional); + } else { + locations.AddRange (additional); + } + } + } + + public MemberCore LastMember { + get; + private set; + } + + Dictionary> simple_locs = new Dictionary> (ReferenceEquality.Default); + Dictionary member_locs = new Dictionary (ReferenceEquality.Default); + + [Conditional ("FULL_AST")] + public void AddLocation (object element, params Location[] locations) + { + AddLocation (element, (IEnumerable)locations); + } + + [Conditional ("FULL_AST")] + public void AddLocation (object element, IEnumerable locations) + { + if (element == null || locations == null) + return; + List found; + if (!simple_locs.TryGetValue (element, out found)) { + simple_locs.Add (element, new List (locations)); + return; + } + found.AddRange(locations); + } + + [Conditional ("FULL_AST")] + public void InsertLocation (object element, int index, Location location) + { + List found; + if (!simple_locs.TryGetValue (element, out found)) { + found = new List (); + simple_locs.Add (element, found); + } + + found.Insert (index, location); + } + + [Conditional ("FULL_AST")] + public void AddStatement (object element, params Location[] locations) + { + if (element == null) + return; + if (locations.Length == 0) + throw new ArgumentException ("Statement is missing semicolon location"); + simple_locs.Add (element, new List(locations)); + } + + [Conditional ("FULL_AST")] + public void AddMember (MemberCore member, IList> modLocations, params Location[] locations) + { + LastMember = member; + if (member == null) + return; + + MemberLocations existing; + if (member_locs.TryGetValue (member, out existing)) { + existing.Modifiers = modLocations; + existing.AddLocations (locations); + return; + } + member_locs.Add (member, new MemberLocations (modLocations, locations)); + } + + [Conditional ("FULL_AST")] + public void AddMember (MemberCore member, IList> modLocations, IEnumerable locations) + { + LastMember = member; + if (member == null) + return; + + MemberLocations existing; + if (member_locs.TryGetValue (member, out existing)) { + existing.Modifiers = modLocations; + existing.AddLocations (locations); + return; + } + member_locs.Add (member, new MemberLocations (modLocations, locations)); + } + + [Conditional ("FULL_AST")] + public void AppendToMember (MemberCore existing, params Location[] locations) + { + AppendToMember (existing, (IEnumerable)locations); + + } + + [Conditional ("FULL_AST")] + public void AppendToMember (MemberCore existing, IEnumerable locations) + { + if (existing == null) + return; + MemberLocations member; + if (member_locs.TryGetValue (existing, out member)) { + member.AddLocations (locations); + return; + } + member_locs.Add (existing, new MemberLocations (null, locations)); + } + + public List GetLocations (object element) + { + if (element == null) + return null; + List found; + simple_locs.TryGetValue (element, out found); + return found; + } + + public MemberLocations GetMemberLocation (MemberCore element) + { + MemberLocations found; + member_locs.TryGetValue (element, out found); + return found; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/membercache.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/membercache.cs new file mode 100644 index 000000000..3837af2c2 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/membercache.cs @@ -0,0 +1,1501 @@ +// +// membercache.cs: A container for all member lookups +// +// Author: Miguel de Icaza (miguel@gnu.org) +// Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2001 Ximian, Inc (http://www.ximian.com) +// Copyright 2004-2010 Novell, Inc +// Copyright 2011 Xamarin Inc +// +// + +using System; +using System.Collections.Generic; + +namespace Mono.CSharp { + + [Flags] + public enum MemberKind + { + Constructor = 1, + Event = 1 << 1, + Field = 1 << 2, + Method = 1 << 3, + Property = 1 << 4, + Indexer = 1 << 5, + Operator = 1 << 6, + Destructor = 1 << 7, + + Class = 1 << 11, + Struct = 1 << 12, + Delegate = 1 << 13, + Enum = 1 << 14, + Interface = 1 << 15, + TypeParameter = 1 << 16, + + ArrayType = 1 << 19, + PointerType = 1 << 20, + InternalCompilerType = 1 << 21, + MissingType = 1 << 22, + Void = 1 << 23, + Namespace = 1 << 24, + + NestedMask = Class | Struct | Delegate | Enum | Interface, + GenericMask = Method | Class | Struct | Delegate | Interface, + MaskType = Constructor | Event | Field | Method | Property | Indexer | Operator | Destructor | NestedMask + } + + [Flags] + public enum BindingRestriction + { + None = 0, + + // Inspect only queried type members + DeclaredOnly = 1 << 1, + + // Exclude static + InstanceOnly = 1 << 2, + + NoAccessors = 1 << 3, + + // Member has to be override + OverrideOnly = 1 << 4 + } + + public struct MemberFilter : IEquatable + { + public readonly string Name; + public readonly MemberKind Kind; + public readonly AParametersCollection Parameters; + public readonly TypeSpec MemberType; + public readonly int Arity; // -1 to ignore the check + + public MemberFilter (MethodSpec m) + { + Name = m.Name; + Kind = MemberKind.Method; + Parameters = m.Parameters; + MemberType = m.ReturnType; + Arity = m.Arity; + } + + public MemberFilter (string name, int arity, MemberKind kind, AParametersCollection param, TypeSpec type) + { + Name = name; + Kind = kind; + Parameters = param; + MemberType = type; + this.Arity = arity; + } + + public static MemberFilter Constructor (AParametersCollection param) + { + return new MemberFilter (Mono.CSharp.Constructor.ConstructorName, 0, MemberKind.Constructor, param, null); + } + + public static MemberFilter Property (string name, TypeSpec type) + { + return new MemberFilter (name, 0, MemberKind.Property, null, type); + } + + public static MemberFilter Field (string name, TypeSpec type) + { + return new MemberFilter (name, 0, MemberKind.Field, null, type); + } + + public static MemberFilter Method (string name, int arity, AParametersCollection param, TypeSpec type) + { + return new MemberFilter (name, arity, MemberKind.Method, param, type); + } + + #region IEquatable Members + + public bool Equals (MemberSpec other) + { + // Is the member of the correct type ? + // TODO: Isn't this redundant ? + if ((other.Kind & Kind & MemberKind.MaskType) == 0) + return false; + + // Check arity when not disabled + if (Arity >= 0 && Arity != other.Arity) + return false; + + if (Parameters != null) { + if (other is IParametersMember) { + var other_param = ((IParametersMember) other).Parameters; + if (!TypeSpecComparer.Override.IsEqual (Parameters, other_param)) + return false; + } else { + return false; + } + } + + if (MemberType != null) { + if (other is IInterfaceMemberSpec) { + var other_type = ((IInterfaceMemberSpec) other).MemberType; + if (!TypeSpecComparer.Override.IsEqual (other_type, MemberType)) + return false; + } else { + return false; + } + } + + return true; + } + + #endregion + } + + // + // The MemberCache is the main members container used by compiler. It contains + // all members imported or defined during compilation using on demand filling + // process. Inflated containers are also using MemberCache to make inflated + // members look like normal definition. + // + // All of the methods are performance and memory sensitive as the MemberCache + // is the underlying engine of all member based operations. + // + public class MemberCache + { + [Flags] + enum StateFlags + { + HasConversionOperator = 1 << 1, + HasUserOperator = 1 << 2 + } + + readonly Dictionary> member_hash; + Dictionary locase_members; + IList missing_abstract; + StateFlags state; // TODO: Move to TypeSpec or ITypeDefinition + + public static readonly string IndexerNameAlias = ""; + + public static readonly MemberCache Empty = new MemberCache (0); + + public MemberCache () + : this (16) + { + } + + public MemberCache (int capacity) + { + member_hash = new Dictionary> (capacity); + } + + public MemberCache (MemberCache cache) + : this (cache.member_hash.Count) + { + this.state = cache.state; + } + + // + // Creates a new MemberCache for the given `container'. + // + public MemberCache (TypeContainer container) + : this () // TODO: Optimize the size + { + } + + // + // For cases where we need to union cache members + // + public void AddBaseType (TypeSpec baseType) + { + var cache = baseType.MemberCache; + + IList list; + foreach (var entry in cache.member_hash) { + if (!member_hash.TryGetValue (entry.Key, out list)) { + if (entry.Value.Count == 1) { + list = entry.Value; + } else { + list = new List (entry.Value); + } + + member_hash.Add (entry.Key, list); + continue; + } + + foreach (var ce in entry.Value) { + if (list.Contains (ce)) + continue; + + if (list is MemberSpec[]) { + list = new List { list [0] }; + member_hash[entry.Key] = list; + } + + list.Add (ce); + } + } + } + + // + // Member-cache does not contain base members but it does + // contain all base interface members, so the Lookup code + // can use simple inheritance rules. + // + // Does not work recursively because of generic interfaces + // + public void AddInterface (TypeSpec iface) + { + var cache = iface.MemberCache; + + IList list; + foreach (var entry in cache.member_hash) { + if (!member_hash.TryGetValue (entry.Key, out list)) { + if (entry.Value.Count == 1) { + list = entry.Value; + } else { + list = new List (entry.Value); + } + + member_hash.Add (entry.Key, list); + continue; + } + + foreach (var ce in entry.Value) { + // + // When two or more different base interfaces implemenent common + // interface + // + // I : IA, IFoo + // IA : IFoo + // + if (list.Contains (ce)) + continue; + + if (AddInterfaceMember (ce, ref list)) + member_hash[entry.Key] = list; + } + } + } + + public void AddMember (InterfaceMemberBase imb, string exlicitName, MemberSpec ms) + { + // Explicit names cannot be looked-up but can be used for + // collision checking (no name mangling needed) + if (imb.IsExplicitImpl) + AddMember (exlicitName, ms, false); + else + AddMember (ms); + } + + // + // Add non-explicit member to member cache + // + public void AddMember (MemberSpec ms) + { + AddMember (GetLookupName (ms), ms, false); + } + + void AddMember (string name, MemberSpec member, bool removeHiddenMembers) + { + if (member.Kind == MemberKind.Operator) { + var dt = member.DeclaringType; + + + // + // Some core types have user operators but they cannot be used like normal + // user operators as they are predefined and therefore having different + // rules (e.g. binary operators) by not setting the flag we hide them for + // user conversions + // + if (!BuiltinTypeSpec.IsPrimitiveType (dt) || dt.BuiltinType == BuiltinTypeSpec.Type.Char) { + switch (dt.BuiltinType) { + case BuiltinTypeSpec.Type.String: + case BuiltinTypeSpec.Type.Delegate: + case BuiltinTypeSpec.Type.MulticastDelegate: + break; + default: + if (name == Operator.GetMetadataName (Operator.OpType.Implicit) || name == Operator.GetMetadataName (Operator.OpType.Explicit)) { + state |= StateFlags.HasConversionOperator; + } else { + state |= StateFlags.HasUserOperator; + } + + break; + } + } + } + + IList list; + if (!member_hash.TryGetValue (name, out list)) { + member_hash.Add (name, new MemberSpec[] { member }); + return; + } + + if (removeHiddenMembers && member.DeclaringType.IsInterface) { + if (AddInterfaceMember (member, ref list)) + member_hash[name] = list; + } else { + if (list.Count == 1) { + list = new List { list[0] }; + member_hash[name] = list; + } + + list.Add (member); + } + } + + public void AddMemberImported (MemberSpec ms) + { + AddMember (GetLookupName (ms), ms, true); + } + + // + // Ignores any base interface member which can be hidden + // by this interface + // + static bool AddInterfaceMember (MemberSpec member, ref IList existing) + { + var member_param = member is IParametersMember ? ((IParametersMember) member).Parameters : ParametersCompiled.EmptyReadOnlyParameters; + + // + // interface IA : IB { int Prop { set; } } + // interface IB { bool Prop { get; } } + // + // IB.Prop is never accessible from IA interface + // + for (int i = 0; i < existing.Count; ++i) { + var entry = existing[i]; + + if (entry.Arity != member.Arity) + continue; + + if (entry is IParametersMember) { + var entry_param = ((IParametersMember) entry).Parameters; + if (!TypeSpecComparer.Override.IsEqual (entry_param, member_param)) + continue; + } + + if (member.DeclaringType.ImplementsInterface (entry.DeclaringType, false)) { + if (existing.Count == 1) { + existing = new MemberSpec[] { member }; + return true; + } + + existing.RemoveAt (i--); + continue; + } + + if ((entry.DeclaringType == member.DeclaringType && entry.IsAccessor == member.IsAccessor) || + entry.DeclaringType.ImplementsInterface (member.DeclaringType, false)) + return false; + } + + if (existing.Count == 1) { + existing = new List { existing[0], member }; + return true; + } + + existing.Add (member); + return false; + } + + public static MemberSpec FindMember (TypeSpec container, MemberFilter filter, BindingRestriction restrictions) + { + do { + IList applicable; + if (container.MemberCache.member_hash.TryGetValue (filter.Name, out applicable)) { + // Start from the end because interface members are in reverse order + for (int i = applicable.Count - 1; i >= 0; i--) { + var entry = applicable [i]; + + if ((restrictions & BindingRestriction.InstanceOnly) != 0 && entry.IsStatic) + continue; + + if ((restrictions & BindingRestriction.NoAccessors) != 0 && entry.IsAccessor) + continue; + + if ((restrictions & BindingRestriction.OverrideOnly) != 0 && (entry.Modifiers & Modifiers.OVERRIDE) == 0) + continue; + + if (!filter.Equals (entry)) + continue; + + if ((restrictions & BindingRestriction.DeclaredOnly) != 0 && container.IsInterface && entry.DeclaringType != container) + continue; + + return entry; + } + } + + if ((restrictions & BindingRestriction.DeclaredOnly) != 0) + break; + + container = container.BaseType; + } while (container != null); + + return null; + } + + // + // A special method to work with member lookup only. It returns a list of all members named @name + // starting from @container. It's very performance sensitive + // + // declaredOnlyClass cannot be used interfaces. Manual filtering is required because names are + // compacted + // + public static IList FindMembers (TypeSpec container, string name, bool declaredOnlyClass) + { + IList applicable; + + do { + if (container.MemberCache.member_hash.TryGetValue (name, out applicable) || declaredOnlyClass) + return applicable; + + container = container.BaseType; + } while (container != null); + + return null; + } + + // + // Finds the nested type in container + // + public static TypeSpec FindNestedType (TypeSpec container, string name, int arity) + { + IList applicable; + TypeSpec best_match = null; + do { +#if !FULL_AOT_RUNTIME + // TODO: Don't know how to handle this yet + // When resolving base type of nested type, parent type must have + // base type resolved to scan full hierarchy correctly + // Similarly MemberCacheTypes will inflate BaseType and Interfaces + // based on type definition + var tc = container.MemberDefinition as TypeContainer; + if (tc != null) + tc.DefineContainer (); +#endif + + if (container.MemberCacheTypes.member_hash.TryGetValue (name, out applicable)) { + for (int i = applicable.Count - 1; i >= 0; i--) { + var entry = applicable[i]; + if ((entry.Kind & MemberKind.NestedMask) == 0) + continue; + + var ts = (TypeSpec) entry; + if (arity == ts.Arity) + return ts; + + if (arity < 0) { + if (best_match == null) { + best_match = ts; + } else if (System.Math.Abs (ts.Arity + arity) < System.Math.Abs (ts.Arity + arity)) { + best_match = ts; + } + } + } + } + + container = container.BaseType; + } while (container != null); + + return best_match; + } + + // + // Looks for extension methods with defined name and extension type + // + public List FindExtensionMethods (IMemberContext invocationContext, string name, int arity) + { + IList entries; + if (!member_hash.TryGetValue (name, out entries)) + return null; + + List candidates = null; + foreach (var entry in entries) { + if (entry.Kind != MemberKind.Method || (arity > 0 && entry.Arity != arity)) + continue; + + var ms = (MethodSpec) entry; + if (!ms.IsExtensionMethod) + continue; + + if (!ms.IsAccessible (invocationContext)) + continue; + + // + // Extension methods cannot be nested hence checking parent is enough + // + if ((ms.DeclaringType.Modifiers & Modifiers.INTERNAL) != 0 && !ms.DeclaringType.MemberDefinition.IsInternalAsPublic (invocationContext.Module.DeclaringAssembly)) + continue; + + if (candidates == null) + candidates = new List (); + candidates.Add (ms); + } + + return candidates; + } + + // + // Returns base members of @member member if no exact match is found @bestCandidate returns + // the best match + // + public static MemberSpec FindBaseMember (MemberCore member, out MemberSpec bestCandidate, ref bool overrides) + { + bestCandidate = null; + var container = member.Parent.PartialContainer.Definition; + if (!container.IsInterface) { + container = container.BaseType; + + // It can happen for a user definition of System.Object + if (container == null) + return null; + } + + string name = GetLookupName (member); + var member_param = member is IParametersMember ? ((IParametersMember) member).Parameters : null; + + var mkind = GetMemberCoreKind (member); + bool member_with_accessors = mkind == MemberKind.Indexer || mkind == MemberKind.Property; + + IList applicable; + MemberSpec ambig_candidate = null; + + do { + if (container.MemberCache.member_hash.TryGetValue (name, out applicable)) { + for (int i = 0; i < applicable.Count; ++i) { + var entry = applicable [i]; + + if ((entry.Modifiers & Modifiers.PUBLIC) == 0 && !entry.IsAccessible (member)) + continue; + + // + // Isn't the member of same kind ? + // + if ((entry.Kind & ~MemberKind.Destructor & mkind & MemberKind.MaskType) == 0) { + // Destructors are ignored as they cannot be overridden by user + if ((entry.Kind & MemberKind.Destructor) != 0) + continue; + + // A method with different arity does not hide base member + if (mkind != MemberKind.Method && member.MemberName.Arity != entry.Arity) + continue; + + bestCandidate = entry; + return null; + } + + // + // Same kind of different arity is valid + // + if (member.MemberName.Arity != entry.Arity) { + continue; + } + + if ((entry.Kind & mkind & (MemberKind.Method | MemberKind.Indexer)) != 0) { + if (entry.IsAccessor != member is AbstractPropertyEventMethod) + continue; + + var pm = entry as IParametersMember; + if (!TypeSpecComparer.Override.IsEqual (pm.Parameters, member_param)) + continue; + } + + // + // Skip override for member with accessors. It may not fully implement the base member + // but keep flag we found an implementation in case the base member is abstract + // + if (member_with_accessors && ((entry.Modifiers & (Modifiers.OVERRIDE | Modifiers.SEALED)) == Modifiers.OVERRIDE)) { + // + // Set candidate to override implementation to flag we found an implementation + // + overrides = true; + continue; + } + + // + // For members with parameters we can encounter an ambiguous candidates (they match exactly) + // because generic type parameters could be inflated into same types + // + if (ambig_candidate == null && (entry.Kind & mkind & (MemberKind.Method | MemberKind.Indexer)) != 0) { + bestCandidate = null; + ambig_candidate = entry; + continue; + } + + bestCandidate = ambig_candidate; + return entry; + } + } + + if (container.IsInterface || ambig_candidate != null) + break; + + container = container.BaseType; + } while (container != null); + + return ambig_candidate; + } + + // + // Returns inflated version of MemberSpec, it works similarly to + // SRE TypeBuilder.GetMethod + // + public static T GetMember (TypeSpec container, T spec) where T : MemberSpec + { + IList applicable; + if (container.MemberCache.member_hash.TryGetValue (GetLookupName (spec), out applicable)) { + for (int i = applicable.Count - 1; i >= 0; i--) { + var entry = applicable[i]; + if (entry.MemberDefinition == spec.MemberDefinition) + return (T) entry; + } + } + + throw new InternalErrorException ("Missing member `{0}' on inflated type `{1}'", + spec.GetSignatureForError (), container.GetSignatureForError ()); + } + + static MemberKind GetMemberCoreKind (MemberCore member) + { + if (member is FieldBase) + return MemberKind.Field; + if (member is Indexer) + return MemberKind.Indexer; + if (member is Class) + return MemberKind.Class; + if (member is Struct) + return MemberKind.Struct; + if (member is Destructor) + return MemberKind.Destructor; + if (member is Method) + return MemberKind.Method; + if (member is Property) + return MemberKind.Property; + if (member is EventField) + return MemberKind.Event; + if (member is Interface) + return MemberKind.Interface; + if (member is EventProperty) + return MemberKind.Event; + if (member is Delegate) + return MemberKind.Delegate; + if (member is Enum) + return MemberKind.Enum; + + throw new NotImplementedException (member.GetType ().ToString ()); + } + + public static List GetAllFieldsForDefiniteAssignment (TypeSpec container) + { + List fields = null; + foreach (var entry in container.MemberCache.member_hash) { + foreach (var name_entry in entry.Value) { + if (name_entry.Kind != MemberKind.Field) + continue; + + if ((name_entry.Modifiers & Modifiers.STATIC) != 0) + continue; + + // + // Fixed size buffers are not subject to definite assignment checking + // + if (name_entry is FixedFieldSpec || name_entry is ConstSpec) + continue; + + var fs = (FieldSpec) name_entry; + + // + // LAMESPEC: Very bizzare hack, definitive assignment is not done + // for imported non-public reference fields except array. No idea what the + // actual csc rule is + // + if (!fs.IsPublic && container.MemberDefinition.IsImported && (!fs.MemberType.IsArray && TypeSpec.IsReferenceType (fs.MemberType))) + continue; + + //if ((fs.Modifiers & (Modifiers.BACKING_FIELD) != 0) + // continue; + + if (fields == null) + fields = new List (); + + fields.Add (fs); + break; + } + } + + return fields ?? new List (0); + } + + public static IList GetCompletitionMembers (IMemberContext ctx, TypeSpec container, string name) + { + var matches = new List (); + foreach (var entry in container.MemberCache.member_hash) { + foreach (var name_entry in entry.Value) { + if (name_entry.IsAccessor) + continue; + + if ((name_entry.Kind & (MemberKind.Constructor | MemberKind.Destructor | MemberKind.Operator)) != 0) + continue; + + if (!name_entry.IsAccessible (ctx)) + continue; + + if (name == null || name_entry.Name.StartsWith (name)) { + matches.Add (name_entry); + } + } + } + + return matches; + } + + // + // Returns members of @iface only, base members are ignored + // + public static List GetInterfaceMethods (TypeSpec iface) + { + // + // MemberCache flatten interfaces, therefore in cases like this one + // + // interface IA : IB {} + // interface IB { void Foo () } + // + // we would return Foo inside IA which is not expected in this case + // + var methods = new List (); + foreach (var entry in iface.MemberCache.member_hash.Values) { + foreach (var name_entry in entry) { + if (iface == name_entry.DeclaringType) { + if (name_entry.Kind == MemberKind.Method) { + methods.Add ((MethodSpec) name_entry); + } + } + } + } + + return methods; + } + + // + // Returns all not implememted abstract members inside abstract type + // NOTE: Returned list is shared and must not be modified + // + public static IList GetNotImplementedAbstractMethods (TypeSpec type) + { + if (type.MemberCache.missing_abstract != null) + return type.MemberCache.missing_abstract; + + var abstract_methods = new List (); + List hierarchy = null; + + // + // Stage 1: top-to-bottom scan for abstract members + // + var abstract_type = type; + while (true) { + foreach (var entry in abstract_type.MemberCache.member_hash) { + foreach (var name_entry in entry.Value) { + if ((name_entry.Modifiers & Modifiers.ABSTRACT) == 0) + continue; + + var ms = name_entry as MethodSpec; + if (ms == null) + continue; + + abstract_methods.Add (ms); + } + } + + var base_type = abstract_type.BaseType; + if (!base_type.IsAbstract) + break; + + if (hierarchy == null) + hierarchy = new List (); + + hierarchy.Add (abstract_type); + abstract_type = base_type; + } + + int not_implemented_count = abstract_methods.Count; + if (not_implemented_count == 0 || hierarchy == null) { + type.MemberCache.missing_abstract = abstract_methods; + return type.MemberCache.missing_abstract; + } + + // + // Stage 2: Remove already implemented methods + // + foreach (var type_up in hierarchy) { + var members = type_up.MemberCache.member_hash; + if (members.Count == 0) + continue; + + for (int i = 0; i < abstract_methods.Count; ++i) { + var candidate = abstract_methods [i]; + if (candidate == null) + continue; + + IList applicable; + if (!members.TryGetValue (candidate.Name, out applicable)) + continue; + + var filter = new MemberFilter (candidate); + foreach (var item in applicable) { + if ((item.Modifiers & (Modifiers.OVERRIDE | Modifiers.VIRTUAL)) == 0) + continue; + + // + // Abstract override does not override anything + // + if ((item.Modifiers & Modifiers.ABSTRACT) != 0) + continue; + + if (filter.Equals (item)) { + --not_implemented_count; + abstract_methods [i] = null; + break; + } + } + } + } + + if (not_implemented_count == abstract_methods.Count) { + type.MemberCache.missing_abstract = abstract_methods; + return type.MemberCache.missing_abstract; + } + + var not_implemented = new MethodSpec[not_implemented_count]; + int counter = 0; + foreach (var m in abstract_methods) { + if (m == null) + continue; + + not_implemented[counter++] = m; + } + + type.MemberCache.missing_abstract = not_implemented; + return type.MemberCache.missing_abstract; + } + + static string GetLookupName (MemberSpec ms) + { + if (ms.Kind == MemberKind.Indexer) + return IndexerNameAlias; + + if (ms.Kind == MemberKind.Constructor) { + if (ms.IsStatic) + return Constructor.TypeConstructorName; + + return Constructor.ConstructorName; + } + + return ms.Name; + } + + static string GetLookupName (MemberCore mc) + { + if (mc is Indexer) + return IndexerNameAlias; + + if (mc is Constructor) + return mc.IsStatic ? Constructor.TypeConstructorName : Constructor.ConstructorName; + + return mc.MemberName.Name; + } + + // + // Returns all operators declared on container and its base types (until declaredOnly is used) + // + public static IList GetUserOperator (TypeSpec container, Operator.OpType op, bool declaredOnly) + { + IList found = null; + bool shared_list = true; + IList applicable; + do { + var mc = container.MemberCache; + + if (((op == Operator.OpType.Implicit || op == Operator.OpType.Explicit) && (mc.state & StateFlags.HasConversionOperator) != 0) || + (mc.state & StateFlags.HasUserOperator) != 0) { + + if (mc.member_hash.TryGetValue (Operator.GetMetadataName (op), out applicable)) { + int i; + for (i = 0; i < applicable.Count; ++i) { + if (applicable[i].Kind != MemberKind.Operator) { + break; + } + } + + // + // Handles very rare case where a method with same name as operator (op_xxxx) exists + // and we have to resize the applicable list + // + if (i != applicable.Count) { + for (i = 0; i < applicable.Count; ++i) { + if (applicable[i].Kind != MemberKind.Operator) { + continue; + } + + if (found == null) { + found = new List (); + found.Add (applicable[i]); + } else { + List prev; + if (shared_list) { + shared_list = false; + prev = new List (found.Count + 1); + prev.AddRange (found); + } else { + prev = (List) found; + } + + prev.Add (applicable[i]); + } + } + } else { + if (found == null) { + found = applicable; + shared_list = true; + } else { + List merged; + if (shared_list) { + shared_list = false; + merged = new List (found.Count + applicable.Count); + merged.AddRange (found); + found = merged; + } else { + merged = (List) found; + } + + merged.AddRange (applicable); + } + } + } + } + + // BaseType call can be expensive + if (declaredOnly) + break; + + container = container.BaseType; + } while (container != null); + + return found; + } + + // + // Inflates all member cache nested types + // + public void InflateTypes (MemberCache inflated_cache, TypeParameterInflator inflator) + { + foreach (var item in member_hash) { + IList inflated_members = null; + for (int i = 0; i < item.Value.Count; ++i ) { + var member = item.Value[i]; + + // FIXME: When inflating members refering nested types before they are inflated + if (member == null) + continue; + + if ((member.Kind & MemberKind.NestedMask) != 0 && + (member.Modifiers & Modifiers.COMPILER_GENERATED) == 0) { + if (inflated_members == null) { + inflated_members = new MemberSpec[item.Value.Count]; + inflated_cache.member_hash.Add (item.Key, inflated_members); + } + + inflated_members [i] = member.InflateMember (inflator); + } + } + } + } + + // + // Inflates all open type members, requires InflateTypes to be called before + // + public void InflateMembers (MemberCache cacheToInflate, TypeSpec inflatedType, TypeParameterInflator inflator) + { + var inflated_member_hash = cacheToInflate.member_hash; + Dictionary accessor_relation = null; + List accessor_members = null; + + // Copy member specific flags when all members were added + cacheToInflate.state = state; + + foreach (var item in member_hash) { + var members = item.Value; + IList inflated_members = null; + for (int i = 0; i < members.Count; ++i ) { + var member = members[i]; + + // + // All nested types have been inflated earlier except for + // compiler types which are created later and could miss InflateTypes + // + if ((member.Kind & MemberKind.NestedMask) != 0 && + (member.Modifiers & Modifiers.COMPILER_GENERATED) == 0) { + if (inflated_members == null) + inflated_members = inflated_member_hash[item.Key]; + + continue; + } + + // + // Clone the container first + // + if (inflated_members == null) { + inflated_members = new MemberSpec [item.Value.Count]; + inflated_member_hash.Add (item.Key, inflated_members); + } + + var local_inflator = inflator; + + if (member.DeclaringType != inflatedType) { + // + // Don't inflate top-level non-generic interface members + // merged into generic interface + // + if (!member.DeclaringType.IsGeneric && !member.DeclaringType.IsNested) { + inflated_members [i] = member; + continue; + } + + // + // Needed when inflating flatten interfaces. It inflates + // container type only, type parameters are already done + // + // Handles cases like: + // + // interface I {} + // interface I : I {} + // + // class C: I {} + // + var inflated_parent = inflator.Inflate (member.DeclaringType); + if (inflated_parent != inflator.TypeInstance) + local_inflator = new TypeParameterInflator (inflator, inflated_parent); + } + + // + // Inflate every member, its parent is now different + // + var inflated = member.InflateMember (local_inflator); + inflated_members [i] = inflated; + + if (member is PropertySpec || member is EventSpec) { + if (accessor_members == null) + accessor_members = new List (); + + accessor_members.Add (inflated); + continue; + } + + if (member.IsAccessor) { + if (accessor_relation == null) + accessor_relation = new Dictionary (); + accessor_relation.Add (member, (MethodSpec) inflated); + } + } + } + + if (accessor_members != null) { + foreach (var member in accessor_members) { + var prop = member as PropertySpec; + if (prop != null) { + if (prop.Get != null) + prop.Get = accessor_relation[prop.Get]; + if (prop.Set != null) + prop.Set = accessor_relation[prop.Set]; + + continue; + } + + var ev = (EventSpec) member; + ev.AccessorAdd = accessor_relation[ev.AccessorAdd]; + ev.AccessorRemove = accessor_relation[ev.AccessorRemove]; + } + } + } + + // + // Removes hidden base members of an interface. For compiled interfaces we cannot + // do name filtering during Add (as we do for import) because we need all base + // names to be valid during type definition. + // Add replaces hidden base member with current one which means any name collision + // (CS0108) of non-first name would be unnoticed because the name was replaced + // with the one from compiled type + // + public void RemoveHiddenMembers (TypeSpec container) + { + foreach (var entry in member_hash) { + var values = entry.Value; + + int container_members_start_at = 0; + while (values[container_members_start_at].DeclaringType != container && ++container_members_start_at < entry.Value.Count); + + if (container_members_start_at == 0 || container_members_start_at == values.Count) + continue; + + for (int i = 0; i < container_members_start_at; ++i) { + var member = values[i]; + + if (!container.ImplementsInterface (member.DeclaringType, false)) + continue; + + var member_param = member is IParametersMember ? ((IParametersMember) member).Parameters : ParametersCompiled.EmptyReadOnlyParameters; + + for (int ii = container_members_start_at; ii < values.Count; ++ii) { + var container_entry = values[ii]; + + if (container_entry.Arity != member.Arity) + continue; + + if (container_entry is IParametersMember) { + if (!TypeSpecComparer.Override.IsEqual (((IParametersMember) container_entry).Parameters, member_param)) + continue; + } + + values.RemoveAt (i); + --container_members_start_at; + --ii; + --i; + } + } + } + } + + // + // Checks all appropriate container members for CLS compliance + // + public void VerifyClsCompliance (TypeSpec container, Report report) + { + if (locase_members != null) + return; + + if (container.BaseType == null) { + locase_members = new Dictionary (member_hash.Count); // StringComparer.OrdinalIgnoreCase); + } else { + var btype = container.BaseType.GetDefinition (); + btype.MemberCache.VerifyClsCompliance (btype, report); + locase_members = new Dictionary (btype.MemberCache.locase_members); //, StringComparer.OrdinalIgnoreCase); + } + + var is_imported_type = container.MemberDefinition.IsImported; + foreach (var entry in container.MemberCache.member_hash) { + for (int i = 0; i < entry.Value.Count; ++i ) { + var name_entry = entry.Value[i]; + if ((name_entry.Modifiers & (Modifiers.PUBLIC | Modifiers.PROTECTED)) == 0) + continue; + + if ((name_entry.Modifiers & (Modifiers.OVERRIDE | Modifiers.COMPILER_GENERATED)) != 0) + continue; + + if ((name_entry.Kind & MemberKind.MaskType) == 0) + continue; + + if (name_entry.MemberDefinition.CLSAttributeValue == false) + continue; + + IParametersMember p_a = null; + if (!is_imported_type) { + p_a = name_entry as IParametersMember; + if (p_a != null && !name_entry.IsAccessor) { + var p_a_pd = p_a.Parameters; + // + // Check differing overloads in @container + // + for (int ii = i + 1; ii < entry.Value.Count; ++ii) { + var checked_entry = entry.Value[ii]; + IParametersMember p_b = checked_entry as IParametersMember; + if (p_b == null) + continue; + + if (p_a_pd.Count != p_b.Parameters.Count) + continue; + + if (checked_entry.IsAccessor) + continue; + + var res = ParametersCompiled.IsSameClsSignature (p_a.Parameters, p_b.Parameters); + if (res != 0) { + ReportOverloadedMethodClsDifference (name_entry, checked_entry, res, report); + } + } + } + } + + if (i > 0 || name_entry.Kind == MemberKind.Constructor || name_entry.Kind == MemberKind.Indexer) + continue; + + var name_entry_locase = name_entry.Name.ToLowerInvariant (); + + MemberSpec[] found; + if (!locase_members.TryGetValue (name_entry_locase, out found)) { + found = new MemberSpec[] { name_entry }; + locase_members.Add (name_entry_locase, found); + } else { + bool same_names_only = true; + foreach (var f in found) { + if (f.Name == name_entry.Name) { + if (p_a != null) { + IParametersMember p_b = f as IParametersMember; + if (p_b == null) + continue; + + if (p_a.Parameters.Count != p_b.Parameters.Count) + continue; + + if (f.IsAccessor) + continue; + + var res = ParametersCompiled.IsSameClsSignature (p_a.Parameters, p_b.Parameters); + if (res != 0) { + ReportOverloadedMethodClsDifference (f, name_entry, res, report); + } + } + + continue; + } + + same_names_only = false; + if (!is_imported_type) { + var last = GetLaterDefinedMember (f, name_entry); + if (last == f.MemberDefinition) { + report.SymbolRelatedToPreviousError (name_entry); + } else { + report.SymbolRelatedToPreviousError (f); + } + + report.Warning (3005, 1, last.Location, + "Identifier `{0}' differing only in case is not CLS-compliant", last.GetSignatureForError ()); + } + } + + if (!same_names_only) { + Array.Resize (ref found, found.Length + 1); + found[found.Length - 1] = name_entry; + locase_members[name_entry_locase] = found; + } + } + } + } + } + + // + // Local report helper to issue correctly ordered members stored in hashtable + // + static MemberCore GetLaterDefinedMember (MemberSpec a, MemberSpec b) + { + var mc_a = a.MemberDefinition as MemberCore; + var mc_b = b.MemberDefinition as MemberCore; + if (mc_a == null) + return mc_b; + + if (mc_b == null) + return mc_a; + + if (a.DeclaringType.MemberDefinition != b.DeclaringType.MemberDefinition) + return mc_b; + + if (mc_a.Location.File != mc_a.Location.File) + return mc_b; + + return mc_b.Location.Row > mc_a.Location.Row ? mc_b : mc_a; + } + + static void ReportOverloadedMethodClsDifference (MemberSpec a, MemberSpec b, int res, Report report) + { + var last = GetLaterDefinedMember (a, b); + if (last == a.MemberDefinition) { + report.SymbolRelatedToPreviousError (b); + } else { + report.SymbolRelatedToPreviousError (a); + } + + if ((res & 1) != 0) { + report.Warning (3006, 1, last.Location, + "Overloaded method `{0}' differing only in ref or out, or in array rank, is not CLS-compliant", + last.GetSignatureForError ()); + } + + if ((res & 2) != 0) { + report.Warning (3007, 1, last.Location, + "Overloaded method `{0}' differing only by unnamed array types is not CLS-compliant", + last.GetSignatureForError ()); + } + } + + public bool CheckExistingMembersOverloads (MemberCore member, AParametersCollection parameters) + { + var name = GetLookupName (member); + var imb = member as InterfaceMemberBase; + if (imb != null && imb.IsExplicitImpl) { + name = imb.GetFullName (name); + } + + return CheckExistingMembersOverloads (member, name, parameters); + } + + public bool CheckExistingMembersOverloads (MemberCore member, string name, AParametersCollection parameters) + { + IList entries; + if (!member_hash.TryGetValue (name, out entries)) + return false; + + var Report = member.Compiler.Report; + + int method_param_count = parameters.Count; + for (int i = entries.Count - 1; i >= 0; --i) { + var ce = entries[i]; + var pm = ce as IParametersMember; + var pd = pm == null ? ParametersCompiled.EmptyReadOnlyParameters : pm.Parameters; + if (pd.Count != method_param_count) + continue; + + if (ce.Arity != member.MemberName.Arity) + continue; + + // Ignore merged interface members + if (member.Parent.PartialContainer != ce.DeclaringType.MemberDefinition) + continue; + + var p_types = pd.Types; + if (method_param_count > 0) { + int ii = method_param_count - 1; + TypeSpec type_a, type_b; + do { + type_a = parameters.Types [ii]; + type_b = p_types [ii]; + + var a_byref = (pd.FixedParameters[ii].ModFlags & Parameter.Modifier.RefOutMask) != 0; + var b_byref = (parameters.FixedParameters[ii].ModFlags & Parameter.Modifier.RefOutMask) != 0; + + if (a_byref != b_byref) + break; + + } while (TypeSpecComparer.Override.IsEqual (type_a, type_b) && ii-- != 0); + + if (ii >= 0) + continue; + + // + // Operators can differ in return type only + // + if (member is Operator && ce.Kind == MemberKind.Operator && ((MethodSpec) ce).ReturnType != ((Operator) member).ReturnType) + continue; + + // + // Report difference in parameter modifiers only + // + if (pd != null && member is MethodCore) { + ii = method_param_count; + while (ii-- != 0 && + (parameters.FixedParameters[ii].ModFlags & Parameter.Modifier.ModifierMask) == + (pd.FixedParameters[ii].ModFlags & Parameter.Modifier.ModifierMask) && + parameters.ExtensionMethodType == pd.ExtensionMethodType) ; + + if (ii >= 0) { + var mc = ce as MethodSpec; + member.Compiler.Report.SymbolRelatedToPreviousError (ce); + if ((member.ModFlags & Modifiers.PARTIAL) != 0 && (mc.Modifiers & Modifiers.PARTIAL) != 0) { + if (parameters.HasParams || pd.HasParams) { + Report.Error (758, member.Location, + "A partial method declaration and partial method implementation cannot differ on use of `params' modifier"); + } else { + Report.Error (755, member.Location, + "A partial method declaration and partial method implementation must be both an extension method or neither"); + } + } else if (member is Constructor) { + Report.Error (851, member.Location, + "Overloaded contructor `{0}' cannot differ on use of parameter modifiers only", + member.GetSignatureForError ()); + } else { + Report.Error (663, member.Location, + "Overloaded method `{0}' cannot differ on use of parameter modifiers only", + member.GetSignatureForError ()); + } + return false; + } + } + } + + if ((ce.Kind & MemberKind.Method) != 0) { + Method method_a = member as Method; + Method method_b = ce.MemberDefinition as Method; + if (method_a != null && method_b != null && (method_a.ModFlags & method_b.ModFlags & Modifiers.PARTIAL) != 0) { + const Modifiers partial_modifiers = Modifiers.STATIC | Modifiers.UNSAFE; + if (method_a.IsPartialDefinition == method_b.IsPartialImplementation) { + if ((method_a.ModFlags & partial_modifiers) == (method_b.ModFlags & partial_modifiers) || + method_a.Parent.IsUnsafe && method_b.Parent.IsUnsafe) { + if (method_a.IsPartialImplementation) { + method_a.SetPartialDefinition (method_b); + if (entries.Count == 1) + member_hash.Remove (name); + else + entries.RemoveAt (i); + } else { + method_b.SetPartialDefinition (method_a); + method_a.caching_flags |= MemberCore.Flags.PartialDefinitionExists; + } + continue; + } + + if (method_a.IsStatic != method_b.IsStatic) { + Report.SymbolRelatedToPreviousError (ce); + Report.Error (763, member.Location, + "A partial method declaration and partial method implementation must be both `static' or neither"); + } + + if ((method_a.ModFlags & Modifiers.UNSAFE) != (method_b.ModFlags & Modifiers.UNSAFE)) { + Report.SymbolRelatedToPreviousError (ce); + Report.Error (764, member.Location, + "A partial method declaration and partial method implementation must be both `unsafe' or neither"); + } + + return false; + } + + Report.SymbolRelatedToPreviousError (ce); + if (method_a.IsPartialDefinition) { + Report.Error (756, member.Location, "A partial method `{0}' declaration is already defined", + member.GetSignatureForError ()); + } + + Report.Error (757, member.Location, "A partial method `{0}' implementation is already defined", + member.GetSignatureForError ()); + return false; + } + + Report.SymbolRelatedToPreviousError (ce); + + bool is_reserved_a = member is AbstractPropertyEventMethod || member is Operator; + bool is_reserved_b = ((MethodSpec) ce).IsReservedMethod; + + if (is_reserved_a || is_reserved_b) { + Report.Error (82, member.Location, "A member `{0}' is already reserved", + is_reserved_a ? + ce.GetSignatureForError () : + member.GetSignatureForError ()); + return false; + } + } else { + Report.SymbolRelatedToPreviousError (ce); + } + + if (member is Operator && ce.Kind == MemberKind.Operator) { + Report.Error (557, member.Location, "Duplicate user-defined conversion in type `{0}'", + member.Parent.GetSignatureForError ()); + return false; + } + + Report.Error (111, member.Location, + "A member `{0}' is already defined. Rename this member or use different parameter types", + member.GetSignatureForError ()); + return false; + } + + return true; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/method.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/method.cs new file mode 100644 index 000000000..7c42dbc81 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/method.cs @@ -0,0 +1,2890 @@ +// +// method.cs: Method based declarations +// +// Authors: Miguel de Icaza (miguel@gnu.org) +// Martin Baulig (martin@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com) +// Copyright 2004-2008 Novell, Inc +// Copyright 2011 Xamarin Inc. +// + +using System; +using System.Collections.Generic; +using System.Security; +using System.Security.Permissions; +using System.Text; +using System.Linq; +using Mono.CompilerServices.SymbolWriter; +using System.Runtime.CompilerServices; + +#if NET_2_1 +using XmlElement = System.Object; +#else +using System.Xml; +#endif + +#if STATIC +using MetaType = IKVM.Reflection.Type; +using SecurityType = System.Collections.Generic.List; +using IKVM.Reflection; +using IKVM.Reflection.Emit; +#else +using MetaType = System.Type; +using SecurityType = System.Collections.Generic.Dictionary; +using System.Reflection; +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp { + + public abstract class MethodCore : InterfaceMemberBase, IParametersMember + { + protected ParametersCompiled parameters; + protected ToplevelBlock block; + protected MethodSpec spec; + + protected MethodCore (TypeDefinition parent, FullNamedExpression type, Modifiers mod, Modifiers allowed_mod, + MemberName name, Attributes attrs, ParametersCompiled parameters) + : base (parent, type, mod, allowed_mod, name, attrs) + { + this.parameters = parameters; + } + + public override Variance ExpectedMemberTypeVariance { + get { + return Variance.Covariant; + } + } + + // + // Returns the System.Type array for the parameters of this method + // + public TypeSpec [] ParameterTypes { + get { + return parameters.Types; + } + } + + public ParametersCompiled ParameterInfo { + get { + return parameters; + } + } + + AParametersCollection IParametersMember.Parameters { + get { return parameters; } + } + + public ToplevelBlock Block { + get { + return block; + } + + set { + block = value; + } + } + + public CallingConventions CallingConventions { + get { + CallingConventions cc = parameters.CallingConvention; + if (!IsInterface) + if ((ModFlags & Modifiers.STATIC) == 0) + cc |= CallingConventions.HasThis; + + // FIXME: How is `ExplicitThis' used in C#? + + return cc; + } + } + + protected override bool CheckOverrideAgainstBase (MemberSpec base_member) + { + bool res = base.CheckOverrideAgainstBase (base_member); + + // + // Check that the permissions are not being changed + // + if (!CheckAccessModifiers (this, base_member)) { + Error_CannotChangeAccessModifiers (this, base_member); + res = false; + } + + return res; + } + + protected override bool CheckBase () + { + // Check whether arguments were correct. + if (!DefineParameters (parameters)) + return false; + + return base.CheckBase (); + } + + // + // Represents header string for documentation comment. + // + public override string DocCommentHeader + { + get { return "M:"; } + } + + public override void Emit () + { + if ((ModFlags & Modifiers.COMPILER_GENERATED) == 0) { + parameters.CheckConstraints (this); + } + + base.Emit (); + } + + public override bool EnableOverloadChecks (MemberCore overload) + { + if (overload is MethodCore) { + caching_flags |= Flags.MethodOverloadsExist; + return true; + } + + if (overload is AbstractPropertyEventMethod) + return true; + + return base.EnableOverloadChecks (overload); + } + + public override string GetSignatureForDocumentation () + { + string s = base.GetSignatureForDocumentation (); + if (MemberName.Arity > 0) + s += "``" + MemberName.Arity.ToString (); + + return s + parameters.GetSignatureForDocumentation (); + } + + public MethodSpec Spec { + get { return spec; } + } + + protected override bool VerifyClsCompliance () + { + if (!base.VerifyClsCompliance ()) + return false; + + if (parameters.HasArglist) { + Report.Warning (3000, 1, Location, "Methods with variable arguments are not CLS-compliant"); + } + + if (member_type != null && !member_type.IsCLSCompliant ()) { + Report.Warning (3002, 1, Location, "Return type of `{0}' is not CLS-compliant", + GetSignatureForError ()); + } + + parameters.VerifyClsCompliance (this); + return true; + } + } + + public interface IGenericMethodDefinition : IMethodDefinition + { + TypeParameterSpec[] TypeParameters { get; } + int TypeParametersCount { get; } + +// MethodInfo MakeGenericMethod (TypeSpec[] targs); + } + + public sealed class MethodSpec : MemberSpec, IParametersMember + { + MethodBase inflatedMetaInfo; + AParametersCollection parameters; + TypeSpec returnType; + + TypeSpec[] targs; + TypeParameterSpec[] constraints; + + public static readonly MethodSpec Excluded = new MethodSpec (MemberKind.Method, InternalType.FakeInternalType, null, null, ParametersCompiled.EmptyReadOnlyParameters, 0); + + public MethodSpec (MemberKind kind, TypeSpec declaringType, IMethodDefinition details, TypeSpec returnType, + AParametersCollection parameters, Modifiers modifiers) + : base (kind, declaringType, details, modifiers) + { + this.parameters = parameters; + this.returnType = returnType; + } + + #region Properties + + public override int Arity { + get { + return IsGeneric ? GenericDefinition.TypeParametersCount : 0; + } + } + + public TypeParameterSpec[] Constraints { + get { + if (constraints == null && IsGeneric) + constraints = GenericDefinition.TypeParameters; + + return constraints; + } + } + + public bool IsConstructor { + get { + return Kind == MemberKind.Constructor; + } + } + + public new IMethodDefinition MemberDefinition { + get { + return (IMethodDefinition) definition; + } + } + + public IGenericMethodDefinition GenericDefinition { + get { + return (IGenericMethodDefinition) definition; + } + } + + public bool IsAsync { + get { + return (Modifiers & Modifiers.ASYNC) != 0; + } + } + + public bool IsExtensionMethod { + get { + return IsStatic && parameters.HasExtensionMethodType; + } + } + + public bool IsSealed { + get { + return (Modifiers & Modifiers.SEALED) != 0; + } + } + + // When is virtual or abstract + public bool IsVirtual { + get { + return (Modifiers & (Modifiers.VIRTUAL | Modifiers.ABSTRACT | Modifiers.OVERRIDE)) != 0; + } + } + + public bool IsReservedMethod { + get { + return Kind == MemberKind.Operator || IsAccessor; + } + } + + TypeSpec IInterfaceMemberSpec.MemberType { + get { + return returnType; + } + } + + public AParametersCollection Parameters { + get { + return parameters; + } + } + + public TypeSpec ReturnType { + get { + return returnType; + } + } + + public TypeSpec[] TypeArguments { + get { + return targs; + } + } + + #endregion + + public MethodSpec GetGenericMethodDefinition () + { + if (!IsGeneric && !DeclaringType.IsGeneric) + return this; + + return MemberCache.GetMember (declaringType, this); + } + + public MethodBase GetMetaInfo () + { + // + // inflatedMetaInfo is extra field needed for cases where we + // inflate method but another nested type can later inflate + // again (the cache would be build with inflated metaInfo) and + // TypeBuilder can work with method definitions only + // + if (inflatedMetaInfo == null) { + if ((state & StateFlags.PendingMetaInflate) != 0) { + var dt_meta = DeclaringType.GetMetaInfo (); + + if (DeclaringType.IsTypeBuilder) { + if (IsConstructor) + inflatedMetaInfo = TypeBuilder.GetConstructor (dt_meta, (ConstructorInfo) MemberDefinition.Metadata); + else + inflatedMetaInfo = TypeBuilder.GetMethod (dt_meta, (MethodInfo) MemberDefinition.Metadata); + } else { +#if STATIC + // it should not be reached + throw new NotImplementedException (); +#else + inflatedMetaInfo = MethodInfo.GetMethodFromHandle (MemberDefinition.Metadata.MethodHandle, dt_meta.TypeHandle); +#endif + } + + state &= ~StateFlags.PendingMetaInflate; + } else { + inflatedMetaInfo = MemberDefinition.Metadata; + } + } + + if ((state & StateFlags.PendingMakeMethod) != 0) { + var sre_targs = new MetaType[targs.Length]; + for (int i = 0; i < sre_targs.Length; ++i) + sre_targs[i] = targs[i].GetMetaInfo (); + + inflatedMetaInfo = ((MethodInfo) inflatedMetaInfo).MakeGenericMethod (sre_targs); + state &= ~StateFlags.PendingMakeMethod; + } + + return inflatedMetaInfo; + } + + public override string GetSignatureForDocumentation () + { + string name; + switch (Kind) { + case MemberKind.Constructor: + name = "#ctor"; + break; + case MemberKind.Method: + if (Arity > 0) + name = Name + "``" + Arity.ToString (); + else + name = Name; + + break; + default: + name = Name; + break; + } + + name = DeclaringType.GetSignatureForDocumentation () + "." + name + parameters.GetSignatureForDocumentation (); + if (Kind == MemberKind.Operator) { + var op = Operator.GetType (Name).Value; + if (op == Operator.OpType.Explicit || op == Operator.OpType.Implicit) { + name += "~" + ReturnType.GetSignatureForDocumentation (); + } + } + + return name; + } + + public override string GetSignatureForError () + { + string name; + if (IsConstructor) { + name = DeclaringType.GetSignatureForError () + "." + DeclaringType.Name; + } else if (Kind == MemberKind.Operator) { + var op = Operator.GetType (Name).Value; + if (op == Operator.OpType.Implicit || op == Operator.OpType.Explicit) { + name = DeclaringType.GetSignatureForError () + "." + Operator.GetName (op) + " operator " + returnType.GetSignatureForError (); + } else { + name = DeclaringType.GetSignatureForError () + ".operator " + Operator.GetName (op); + } + } else if (IsAccessor) { + int split = Name.IndexOf ('_'); + name = Name.Substring (split + 1); + var postfix = Name.Substring (0, split); + if (split == 3) { + var pc = parameters.Count; + if (pc > 0 && postfix == "get") { + name = "this" + parameters.GetSignatureForError ("[", "]", pc); + } else if (pc > 1 && postfix == "set") { + name = "this" + parameters.GetSignatureForError ("[", "]", pc - 1); + } + } + + return DeclaringType.GetSignatureForError () + "." + name + "." + postfix; + } else { + name = base.GetSignatureForError (); + if (targs != null) + name += "<" + TypeManager.CSharpName (targs) + ">"; + else if (IsGeneric) + name += "<" + TypeManager.CSharpName (GenericDefinition.TypeParameters) + ">"; + } + + return name + parameters.GetSignatureForError (); + } + + public override MemberSpec InflateMember (TypeParameterInflator inflator) + { + var ms = (MethodSpec) base.InflateMember (inflator); + ms.inflatedMetaInfo = null; + ms.returnType = inflator.Inflate (returnType); + ms.parameters = parameters.Inflate (inflator); + if (IsGeneric) + ms.constraints = TypeParameterSpec.InflateConstraints (inflator, Constraints); + + return ms; + } + + public MethodSpec MakeGenericMethod (IMemberContext context, params TypeSpec[] targs) + { + if (targs == null) + throw new ArgumentNullException (); +// TODO MemberCache +// if (generic_intances != null && generic_intances.TryGetValue (targs, out ginstance)) +// return ginstance; + + //if (generic_intances == null) + // generic_intances = new Dictionary (TypeSpecArrayComparer.Default); + + var inflator = new TypeParameterInflator (context, DeclaringType, GenericDefinition.TypeParameters, targs); + + var inflated = (MethodSpec) MemberwiseClone (); + inflated.declaringType = inflator.TypeInstance; + inflated.returnType = inflator.Inflate (returnType); + inflated.parameters = parameters.Inflate (inflator); + inflated.targs = targs; + inflated.constraints = TypeParameterSpec.InflateConstraints (inflator, constraints ?? GenericDefinition.TypeParameters); + inflated.state |= StateFlags.PendingMakeMethod; + + // if (inflated.parent == null) + // inflated.parent = parent; + + //generic_intances.Add (targs, inflated); + return inflated; + } + + public MethodSpec Mutate (TypeParameterMutator mutator) + { + var targs = TypeArguments; + if (targs != null) + targs = mutator.Mutate (targs); + + var decl = DeclaringType; + if (DeclaringType.IsGenericOrParentIsGeneric) { + decl = mutator.Mutate (decl); + } + + if (targs == TypeArguments && decl == DeclaringType) + return this; + + var ms = (MethodSpec) MemberwiseClone (); + if (decl != DeclaringType) { + ms.inflatedMetaInfo = null; + ms.declaringType = decl; + ms.state |= StateFlags.PendingMetaInflate; + } + + if (targs != null) { + ms.targs = targs; + ms.state |= StateFlags.PendingMakeMethod; + } + + return ms; + } + + public override List ResolveMissingDependencies (MemberSpec caller) + { + var missing = returnType.ResolveMissingDependencies (this); + foreach (var pt in parameters.Types) { + var m = pt.GetMissingDependencies (this); + if (m == null) + continue; + + if (missing == null) + missing = new List (); + + missing.AddRange (m); + } + + if (Arity > 0) { + foreach (var tp in GenericDefinition.TypeParameters) { + var m = tp.GetMissingDependencies (this); + + if (m == null) + continue; + + if (missing == null) + missing = new List (); + + missing.AddRange (m); + } + } + + return missing; + } + } + + public abstract class MethodOrOperator : MethodCore, IMethodData, IMethodDefinition + { + ReturnParameter return_attributes; + SecurityType declarative_security; + protected MethodData MethodData; + + static readonly string[] attribute_targets = new string [] { "method", "return" }; + + protected MethodOrOperator (TypeDefinition parent, FullNamedExpression type, Modifiers mod, Modifiers allowed_mod, MemberName name, + Attributes attrs, ParametersCompiled parameters) + : base (parent, type, mod, allowed_mod, name, attrs, parameters) + { + } + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.Target == AttributeTargets.ReturnValue) { + if (return_attributes == null) + return_attributes = new ReturnParameter (this, MethodBuilder, Location); + + return_attributes.ApplyAttributeBuilder (a, ctor, cdata, pa); + return; + } + + if (a.Type == pa.MethodImpl) { + if ((ModFlags & Modifiers.ASYNC) != 0 && (a.GetMethodImplOptions () & MethodImplOptions.Synchronized) != 0) { + Report.Error (4015, a.Location, "`{0}': Async methods cannot use `MethodImplOptions.Synchronized'", + GetSignatureForError ()); + } + + is_external_implementation = a.IsInternalCall (); + } else if (a.Type == pa.DllImport) { + const Modifiers extern_static = Modifiers.EXTERN | Modifiers.STATIC; + if ((ModFlags & extern_static) != extern_static) { + Report.Error (601, a.Location, "The DllImport attribute must be specified on a method marked `static' and `extern'"); + } + + if (MemberName.IsGeneric || Parent.IsGenericOrParentIsGeneric) { + Report.Error (7042, a.Location, + "The DllImport attribute cannot be applied to a method that is generic or contained in a generic type"); + } + + is_external_implementation = true; + } + + if (a.IsValidSecurityAttribute ()) { + a.ExtractSecurityPermissionSet (ctor, ref declarative_security); + return; + } + + if (MethodBuilder != null) + MethodBuilder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), cdata); + } + + public override AttributeTargets AttributeTargets { + get { + return AttributeTargets.Method; + } + } + + MethodBase IMethodDefinition.Metadata { + get { + return MethodData.MethodBuilder; + } + } + + // TODO: Remove and use MethodData abstraction + public MethodBuilder MethodBuilder { + get { + return MethodData.MethodBuilder; + } + } + + protected override bool CheckForDuplications () + { + return Parent.MemberCache.CheckExistingMembersOverloads (this, parameters); + } + + public virtual EmitContext CreateEmitContext (ILGenerator ig, SourceMethodBuilder sourceMethod) + { + return new EmitContext (this, ig, MemberType, sourceMethod); + } + + public override bool Define () + { + if (!base.Define ()) + return false; + + if (!CheckBase ()) + return false; + + MemberKind kind; + if (this is Operator) + kind = MemberKind.Operator; + else if (this is Destructor) + kind = MemberKind.Destructor; + else + kind = MemberKind.Method; + + string explicit_name; + + if (IsPartialDefinition) { + caching_flags &= ~Flags.Excluded_Undetected; + caching_flags |= Flags.Excluded; + + // Add to member cache only when a partial method implementation has not been found yet + if ((caching_flags & Flags.PartialDefinitionExists) != 0) + return true; + + if (IsExplicitImpl) + return true; + + explicit_name = null; + } else { + MethodData = new MethodData (this, ModFlags, flags, this, base_method); + + if (!MethodData.Define (Parent.PartialContainer, GetFullName (MemberName))) + return false; + + explicit_name = MethodData.MetadataName; + } + + spec = new MethodSpec (kind, Parent.Definition, this, ReturnType, parameters, ModFlags); + if (MemberName.Arity > 0) + spec.IsGeneric = true; + + Parent.MemberCache.AddMember (this, explicit_name, spec); + + return true; + } + + protected override void DoMemberTypeIndependentChecks () + { + base.DoMemberTypeIndependentChecks (); + + CheckAbstractAndExtern (block != null); + + if ((ModFlags & Modifiers.PARTIAL) != 0) { + for (int i = 0; i < parameters.Count; ++i) { + IParameterData p = parameters.FixedParameters [i]; + if ((p.ModFlags & Parameter.Modifier.OUT) != 0) { + Report.Error (752, Location, "`{0}': A partial method parameters cannot use `out' modifier", + GetSignatureForError ()); + } + + if (p.HasDefaultValue && IsPartialImplementation) + ((Parameter) p).Warning_UselessOptionalParameter (Report); + } + } + } + + protected override void DoMemberTypeDependentChecks () + { + base.DoMemberTypeDependentChecks (); + + if (MemberType.IsStatic) { + Error_StaticReturnType (); + } + } + + public override void Emit () + { + if ((ModFlags & Modifiers.COMPILER_GENERATED) != 0 && !Parent.IsCompilerGenerated) + Module.PredefinedAttributes.CompilerGenerated.EmitAttribute (MethodBuilder); + if ((ModFlags & Modifiers.DEBUGGER_HIDDEN) != 0) + Module.PredefinedAttributes.DebuggerHidden.EmitAttribute (MethodBuilder); + if ((ModFlags & Modifiers.DEBUGGER_STEP_THROUGH) != 0) + Module.PredefinedAttributes.DebuggerStepThrough.EmitAttribute (MethodBuilder); + + if (ReturnType.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + return_attributes = new ReturnParameter (this, MethodBuilder, Location); + Module.PredefinedAttributes.Dynamic.EmitAttribute (return_attributes.Builder); + } else if (ReturnType.HasDynamicElement) { + return_attributes = new ReturnParameter (this, MethodBuilder, Location); + Module.PredefinedAttributes.Dynamic.EmitAttribute (return_attributes.Builder, ReturnType, Location); + } + + if (OptAttributes != null) + OptAttributes.Emit (); + + if (declarative_security != null) { + foreach (var de in declarative_security) { +#if STATIC + MethodBuilder.__AddDeclarativeSecurity (de); +#else + MethodBuilder.AddDeclarativeSecurity (de.Key, de.Value); +#endif + } + } + + if (type_expr != null) + ConstraintChecker.Check (this, member_type, type_expr.Location); + + base.Emit (); + + if (MethodData != null) + MethodData.Emit (Parent); + + if (block != null && block.StateMachine is AsyncTaskStorey) { + var psm = Module.PredefinedAttributes.AsyncStateMachine; + psm.EmitAttribute (MethodBuilder, block.StateMachine); + } + + if ((ModFlags & Modifiers.PARTIAL) == 0) + Block = null; + } + + protected void Error_ConditionalAttributeIsNotValid () + { + Report.Error (577, Location, + "Conditional not valid on `{0}' because it is a constructor, destructor, operator or explicit interface implementation", + GetSignatureForError ()); + } + + public bool IsPartialDefinition { + get { + return (ModFlags & Modifiers.PARTIAL) != 0 && Block == null; + } + } + + public bool IsPartialImplementation { + get { + return (ModFlags & Modifiers.PARTIAL) != 0 && Block != null; + } + } + + public override string[] ValidAttributeTargets { + get { + return attribute_targets; + } + } + + #region IMethodData Members + + bool IMethodData.IsAccessor { + get { + return false; + } + } + + public TypeSpec ReturnType { + get { + return MemberType; + } + } + + public MemberName MethodName { + get { + return MemberName; + } + } + + /// + /// Returns true if method has conditional attribute and the conditions is not defined (method is excluded). + /// + public override string[] ConditionalConditions () + { + if ((caching_flags & (Flags.Excluded_Undetected | Flags.Excluded)) == 0) + return null; + + if ((ModFlags & Modifiers.PARTIAL) != 0 && (caching_flags & Flags.Excluded) != 0) + return new string [0]; + + caching_flags &= ~Flags.Excluded_Undetected; + string[] conditions; + + if (base_method == null) { + if (OptAttributes == null) + return null; + + Attribute[] attrs = OptAttributes.SearchMulti (Module.PredefinedAttributes.Conditional); + if (attrs == null) + return null; + + conditions = new string[attrs.Length]; + for (int i = 0; i < conditions.Length; ++i) + conditions[i] = attrs[i].GetConditionalAttributeValue (); + } else { + conditions = base_method.MemberDefinition.ConditionalConditions(); + } + + if (conditions != null) + caching_flags |= Flags.Excluded; + + return conditions; + } + + #endregion + + public virtual void PrepareEmit () + { + var mb = MethodData.DefineMethodBuilder (Parent); + + if (CurrentTypeParameters != null) { + string[] gnames = new string[CurrentTypeParameters.Count]; + for (int i = 0; i < gnames.Length; ++i) { + gnames[i] = CurrentTypeParameters[i].Name; + } + + var gen_params = MethodBuilder.DefineGenericParameters (gnames); + + for (int i = 0; i < CurrentTypeParameters.Count; ++i) { + var tp = CurrentTypeParameters[i]; + + tp.Define (gen_params[i]); + } + } + + // + // Generic method has been already defined to resolve method parameters + // correctly when they use type parameters + // + mb.SetParameters (parameters.GetMetaInfo ()); + mb.SetReturnType (ReturnType.GetMetaInfo ()); + } + + public override void WriteDebugSymbol (MonoSymbolFile file) + { + if (MethodData != null && !IsPartialDefinition) + MethodData.WriteDebugSymbol (file); + } + } + + public class Method : MethodOrOperator, IGenericMethodDefinition + { + Method partialMethodImplementation; + + public Method (TypeDefinition parent, FullNamedExpression return_type, Modifiers mod, MemberName name, ParametersCompiled parameters, Attributes attrs) + : base (parent, return_type, mod, + parent.PartialContainer.Kind == MemberKind.Interface ? AllowedModifiersInterface : + parent.PartialContainer.Kind == MemberKind.Struct ? AllowedModifiersStruct | Modifiers.ASYNC : + AllowedModifiersClass | Modifiers.ASYNC, + name, attrs, parameters) + { + } + + protected Method (TypeDefinition parent, FullNamedExpression return_type, Modifiers mod, Modifiers amod, + MemberName name, ParametersCompiled parameters, Attributes attrs) + : base (parent, return_type, mod, amod, name, attrs, parameters) + { + } + + #region Properties + + public override TypeParameters CurrentTypeParameters { + get { + return MemberName.TypeParameters; + } + } + + public TypeParameterSpec[] TypeParameters { + get { + return CurrentTypeParameters.Types; + } + } + + public int TypeParametersCount { + get { + return CurrentTypeParameters == null ? 0 : CurrentTypeParameters.Count; + } + } + + #endregion + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + + public static Method Create (TypeDefinition parent, FullNamedExpression returnType, Modifiers mod, + MemberName name, ParametersCompiled parameters, Attributes attrs) + { + var m = new Method (parent, returnType, mod, name, parameters, attrs); + + if ((mod & Modifiers.PARTIAL) != 0) { + const Modifiers invalid_partial_mod = Modifiers.AccessibilityMask | Modifiers.ABSTRACT | Modifiers.EXTERN | + Modifiers.NEW | Modifiers.OVERRIDE | Modifiers.SEALED | Modifiers.VIRTUAL; + + if ((mod & invalid_partial_mod) != 0) { + m.Report.Error (750, m.Location, + "A partial method cannot define access modifier or any of abstract, extern, new, override, sealed, or virtual modifiers"); + mod &= ~invalid_partial_mod; + } + + if ((parent.ModFlags & Modifiers.PARTIAL) == 0) { + m.Report.Error (751, m.Location, + "A partial method must be declared within a partial class or partial struct"); + } + } + + if ((mod & Modifiers.STATIC) == 0 && parameters.HasExtensionMethodType) { + m.Report.Error (1105, m.Location, "`{0}': Extension methods must be declared static", + m.GetSignatureForError ()); + } + + + return m; + } + + public override string GetSignatureForError() + { + return base.GetSignatureForError () + parameters.GetSignatureForError (); + } + + void Error_DuplicateEntryPoint (Method b) + { + Report.Error (17, b.Location, + "Program `{0}' has more than one entry point defined: `{1}'", + b.Module.Builder.ScopeName, b.GetSignatureForError ()); + } + + bool IsEntryPoint () + { + if (ReturnType.Kind != MemberKind.Void && ReturnType.BuiltinType != BuiltinTypeSpec.Type.Int) + return false; + + if (parameters.IsEmpty) + return true; + + if (parameters.Count > 1) + return false; + + var ac = parameters.Types [0] as ArrayContainer; + return ac != null && ac.Rank == 1 && ac.Element.BuiltinType == BuiltinTypeSpec.Type.String && + (parameters[0].ModFlags & Parameter.Modifier.RefOutMask) == 0; + } + + public override FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc) + { + if (arity == 0) { + var tp = CurrentTypeParameters; + if (tp != null) { + TypeParameter t = tp.Find (name); + if (t != null) + return new TypeParameterExpr (t, loc); + } + } + + return base.LookupNamespaceOrType (name, arity, mode, loc); + } + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.Type == pa.Conditional) { + if (IsExplicitImpl) { + Error_ConditionalAttributeIsNotValid (); + return; + } + + if ((ModFlags & Modifiers.OVERRIDE) != 0) { + Report.Error (243, Location, "Conditional not valid on `{0}' because it is an override method", GetSignatureForError ()); + return; + } + + if (ReturnType.Kind != MemberKind.Void) { + Report.Error (578, Location, "Conditional not valid on `{0}' because its return type is not void", GetSignatureForError ()); + return; + } + + if (IsInterface) { + Report.Error (582, Location, "Conditional not valid on interface members"); + return; + } + + if (MethodData.implementing != null) { + Report.SymbolRelatedToPreviousError (MethodData.implementing.DeclaringType); + Report.Error (629, Location, "Conditional member `{0}' cannot implement interface member `{1}'", + GetSignatureForError (), TypeManager.CSharpSignature (MethodData.implementing)); + return; + } + + for (int i = 0; i < parameters.Count; ++i) { + if ((parameters.FixedParameters [i].ModFlags & Parameter.Modifier.OUT) != 0) { + Report.Error (685, Location, "Conditional method `{0}' cannot have an out parameter", GetSignatureForError ()); + return; + } + } + } + + if (a.Type == pa.Extension) { + a.Error_MisusedExtensionAttribute (); + return; + } + + base.ApplyAttributeBuilder (a, ctor, cdata, pa); + } + + void CreateTypeParameters () + { + var tparams = MemberName.TypeParameters; + var parent_tparams = Parent.TypeParametersAll; + + for (int i = 0; i < MemberName.Arity; i++) { + string type_argument_name = tparams[i].MemberName.Name; + + if (block == null) { + int idx = parameters.GetParameterIndexByName (type_argument_name); + if (idx >= 0) { + var b = block; + if (b == null) + b = new ToplevelBlock (Compiler, Location); + + b.Error_AlreadyDeclaredTypeParameter (type_argument_name, parameters[i].Location); + } + } else { + INamedBlockVariable variable = null; + block.GetLocalName (type_argument_name, block, ref variable); + if (variable != null) + variable.Block.Error_AlreadyDeclaredTypeParameter (type_argument_name, variable.Location); + } + + if (parent_tparams != null) { + var tp = parent_tparams.Find (type_argument_name); + if (tp != null) { + tparams[i].WarningParentNameConflict (tp); + } + } + } + + tparams.Create (null, 0, Parent); + } + + protected virtual void DefineTypeParameters () + { + var tparams = CurrentTypeParameters; + + TypeParameterSpec[] base_tparams = null; + TypeParameterSpec[] base_decl_tparams = TypeParameterSpec.EmptyTypes; + TypeSpec[] base_targs = TypeSpec.EmptyTypes; + if (((ModFlags & Modifiers.OVERRIDE) != 0 || IsExplicitImpl)) { + MethodSpec base_override = base_method ?? MethodData.implementing; + + if (base_override != null) { + base_tparams = base_override.GenericDefinition.TypeParameters; + + if (base_override.DeclaringType.IsGeneric) { + base_decl_tparams = base_override.DeclaringType.MemberDefinition.TypeParameters; + + if (base_method != null) { + var base_type_parent = CurrentType; + while (base_type_parent.BaseType != base_override.DeclaringType) { + base_type_parent = base_type_parent.BaseType; + } + + base_targs = base_type_parent.BaseType.TypeArguments; + } else { + foreach (var iface in Parent.CurrentType.Interfaces) { + if (iface == base_override.DeclaringType) { + base_targs = iface.TypeArguments; + break; + } + } + } + } + + if (base_override.IsGeneric) { + ObsoleteAttribute oa; + foreach (var base_tp in base_tparams) { + oa = base_tp.BaseType.GetAttributeObsolete (); + if (oa != null) { + AttributeTester.Report_ObsoleteMessage (oa, base_tp.BaseType.GetSignatureForError (), Location, Report); + } + + if (base_tp.InterfacesDefined != null) { + foreach (var iface in base_tp.InterfacesDefined) { + oa = iface.GetAttributeObsolete (); + if (oa != null) { + AttributeTester.Report_ObsoleteMessage (oa, iface.GetSignatureForError (), Location, Report); + } + } + } + } + + if (base_decl_tparams.Length != 0) { + base_decl_tparams = base_decl_tparams.Concat (base_tparams).ToArray (); + base_targs = base_targs.Concat (tparams.Types).ToArray (); + } else { + base_decl_tparams = base_tparams; + base_targs = tparams.Types; + } + } + } + } + + for (int i = 0; i < tparams.Count; ++i) { + var tp = tparams [i]; + + if (base_tparams == null) { + tp.ResolveConstraints (this); + continue; + } + + // + // Copy base constraints for override/explicit methods + // + var base_tparam = base_tparams [i]; + var local_tparam = tp.Type; + local_tparam.SpecialConstraint = base_tparam.SpecialConstraint; + + var inflator = new TypeParameterInflator (this, CurrentType, base_decl_tparams, base_targs); + base_tparam.InflateConstraints (inflator, local_tparam); + + // + // Check all type argument constraints for possible collision or unification + // introduced by inflating inherited constraints in this context + // + // Conflict example: + // + // class A { virtual void Foo () where U : class, T {} } + // class B : A { override void Foo {} } + // + var local_tparam_targs = local_tparam.TypeArguments; + if (local_tparam_targs != null) { + for (int ii = 0; ii < local_tparam_targs.Length; ++ii) { + var ta = local_tparam_targs [ii]; + if (!ta.IsClass && !ta.IsStruct) + continue; + + TypeSpec[] unique_tparams = null; + for (int iii = ii + 1; iii < local_tparam_targs.Length; ++iii) { + // + // Remove any identical or unified constraint types + // + var tparam_checked = local_tparam_targs [iii]; + if (TypeSpecComparer.IsEqual (ta, tparam_checked) || TypeSpec.IsBaseClass (ta, tparam_checked, false)) { + unique_tparams = new TypeSpec[local_tparam_targs.Length - 1]; + Array.Copy (local_tparam_targs, 0, unique_tparams, 0, iii); + Array.Copy (local_tparam_targs, iii + 1, unique_tparams, iii, local_tparam_targs.Length - iii - 1); + } else if (!TypeSpec.IsBaseClass (tparam_checked, ta, false)) { + Constraints.Error_ConflictingConstraints (this, local_tparam, ta, tparam_checked, Location); + } + } + + if (unique_tparams != null) { + local_tparam_targs = unique_tparams; + local_tparam.TypeArguments = local_tparam_targs; + continue; + } + + Constraints.CheckConflictingInheritedConstraint (local_tparam, ta, this, Location); + } + } + } + + if (base_tparams == null && MethodData != null && MethodData.implementing != null) { + CheckImplementingMethodConstraints (Parent, spec, MethodData.implementing); + } + } + + public static bool CheckImplementingMethodConstraints (TypeContainer container, MethodSpec method, MethodSpec baseMethod) + { + var tparams = method.Constraints; + var base_tparams = baseMethod.Constraints; + for (int i = 0; i < tparams.Length; ++i) { + if (!tparams[i].HasSameConstraintsImplementation (base_tparams[i])) { + container.Compiler.Report.SymbolRelatedToPreviousError (method); + container.Compiler.Report.SymbolRelatedToPreviousError (baseMethod); + + // Using container location because the interface can be implemented + // by base class + var tp = (tparams [i].MemberDefinition as MemberCore) ?? container; + container.Compiler.Report.Error (425, tp.Location, + "The constraints for type parameter `{0}' of method `{1}' must match the constraints for type parameter `{2}' of interface method `{3}'. Consider using an explicit interface implementation instead", + tparams[i].GetSignatureForError (), method.GetSignatureForError (), + base_tparams[i].GetSignatureForError (), baseMethod.GetSignatureForError ()); + + return false; + } + } + + return true; + } + + // + // Creates the type + // + public override bool Define () + { + if (!base.Define ()) + return false; + + if (member_type.Kind == MemberKind.Void && parameters.IsEmpty && MemberName.Arity == 0 && MemberName.Name == Destructor.MetadataName) { + Report.Warning (465, 1, Location, + "Introducing `Finalize' method can interfere with destructor invocation. Did you intend to declare a destructor?"); + } + + if (Compiler.Settings.StdLib && ReturnType.IsSpecialRuntimeType) { + Error1599 (Location, ReturnType, Report); + return false; + } + + if (CurrentTypeParameters == null) { + if (base_method != null && !IsExplicitImpl) { + if (parameters.Count == 1 && ParameterTypes[0].BuiltinType == BuiltinTypeSpec.Type.Object && MemberName.Name == "Equals") + Parent.PartialContainer.Mark_HasEquals (); + else if (parameters.IsEmpty && MemberName.Name == "GetHashCode") + Parent.PartialContainer.Mark_HasGetHashCode (); + } + + } else { + DefineTypeParameters (); + } + + if (block != null) { + if (block.IsIterator) { + // + // Current method is turned into automatically generated + // wrapper which creates an instance of iterator + // + Iterator.CreateIterator (this, Parent.PartialContainer, ModFlags); + ModFlags |= Modifiers.DEBUGGER_HIDDEN; + } + + if ((ModFlags & Modifiers.ASYNC) != 0) { + if (ReturnType.Kind != MemberKind.Void && + ReturnType != Module.PredefinedTypes.Task.TypeSpec && + !ReturnType.IsGenericTask) { + Report.Error (1983, Location, "The return type of an async method must be void, Task, or Task"); + } + + block = (ToplevelBlock) block.ConvertToAsyncTask (this, Parent.PartialContainer, parameters, ReturnType, null, Location); + ModFlags |= Modifiers.DEBUGGER_STEP_THROUGH; + } + + if (Compiler.Settings.WriteMetadataOnly) + block = null; + } + + if ((ModFlags & Modifiers.STATIC) == 0) + return true; + + if (parameters.HasExtensionMethodType) { + if (Parent.PartialContainer.IsStatic && !Parent.IsGenericOrParentIsGeneric) { + if (!Parent.IsTopLevel) + Report.Error (1109, Location, "`{0}': Extension methods cannot be defined in a nested class", + GetSignatureForError ()); + + PredefinedAttribute pa = Module.PredefinedAttributes.Extension; + if (!pa.IsDefined) { + Report.Error (1110, Location, + "`{0}': Extension methods require `System.Runtime.CompilerServices.ExtensionAttribute' type to be available. Are you missing an assembly reference?", + GetSignatureForError ()); + } + + ModFlags |= Modifiers.METHOD_EXTENSION; + Parent.PartialContainer.ModFlags |= Modifiers.METHOD_EXTENSION; + Spec.DeclaringType.SetExtensionMethodContainer (); + Parent.Module.HasExtensionMethod = true; + } else { + Report.Error (1106, Location, "`{0}': Extension methods must be defined in a non-generic static class", + GetSignatureForError ()); + } + } + + // + // This is used to track the Entry Point, + // + var settings = Compiler.Settings; + if (settings.NeedsEntryPoint && MemberName.Name == "Main" && !IsPartialDefinition && (settings.MainClass == null || settings.MainClass == Parent.TypeBuilder.FullName)) { + if (IsEntryPoint ()) { + if (Parent.DeclaringAssembly.EntryPoint == null) { + if (Parent.IsGenericOrParentIsGeneric || MemberName.IsGeneric) { + Report.Warning (402, 4, Location, "`{0}': an entry point cannot be generic or in a generic type", + GetSignatureForError ()); + } else if ((ModFlags & Modifiers.ASYNC) != 0) { + Report.Error (4009, Location, "`{0}': an entry point cannot be async method", + GetSignatureForError ()); + } else { + SetIsUsed (); + Parent.DeclaringAssembly.EntryPoint = this; + } + } else { + Error_DuplicateEntryPoint (Parent.DeclaringAssembly.EntryPoint); + Error_DuplicateEntryPoint (this); + } + } else { + Report.Warning (28, 4, Location, "`{0}' has the wrong signature to be an entry point", + GetSignatureForError ()); + } + } + + return true; + } + + public override void PrepareEmit () + { + if (IsPartialDefinition) { + // + // Use partial method implementation builder for partial method declaration attributes + // + if (partialMethodImplementation != null) { + MethodData = partialMethodImplementation.MethodData; + } + + return; + } + + base.PrepareEmit (); + } + + // + // Emits the code + // + public override void Emit () + { + try { + if (IsPartialDefinition) { + if (partialMethodImplementation != null && CurrentTypeParameters != null) { + CurrentTypeParameters.CheckPartialConstraints (partialMethodImplementation); + + var otp = partialMethodImplementation.CurrentTypeParameters; + for (int i = 0; i < CurrentTypeParameters.Count; ++i) { + var tp = CurrentTypeParameters [i]; + tp.Define (otp[i]); + } + } + + return; + } + + if ((ModFlags & Modifiers.PARTIAL) != 0 && (caching_flags & Flags.PartialDefinitionExists) == 0) { + Report.Error (759, Location, "A partial method `{0}' implementation is missing a partial method declaration", + GetSignatureForError ()); + } + + if (CurrentTypeParameters != null) { + for (int i = 0; i < CurrentTypeParameters.Count; ++i) { + var tp = CurrentTypeParameters [i]; + + tp.CheckGenericConstraints (false); + tp.Emit (); + } + } + + if ((ModFlags & Modifiers.METHOD_EXTENSION) != 0) + Module.PredefinedAttributes.Extension.EmitAttribute (MethodBuilder); + + base.Emit (); + } catch (Exception e) { + throw new InternalErrorException (this, e); + } + } + + public override bool EnableOverloadChecks (MemberCore overload) + { + if (overload is Indexer) + return false; + + return base.EnableOverloadChecks (overload); + } + + public static void Error1599 (Location loc, TypeSpec t, Report Report) + { + Report.Error (1599, loc, "Method or delegate cannot return type `{0}'", t.GetSignatureForError ()); + } + + protected override bool ResolveMemberType () + { + if (CurrentTypeParameters != null) { + CreateTypeParameters (); + } + + return base.ResolveMemberType (); + } + + public void SetPartialDefinition (Method methodDefinition) + { + caching_flags |= Flags.PartialDefinitionExists; + methodDefinition.partialMethodImplementation = this; + + // Ensure we are always using method declaration parameters + for (int i = 0; i < methodDefinition.parameters.Count; ++i ) { + var md_p = methodDefinition.parameters [i]; + var p = parameters [i]; + p.Name = md_p.Name; + p.DefaultValue = md_p.DefaultValue; + if (md_p.OptAttributes != null) { + if (p.OptAttributes == null) { + p.OptAttributes = md_p.OptAttributes; + } else { + p.OptAttributes.Attrs.AddRange (md_p.OptAttributes.Attrs); + } + } + } + + if (methodDefinition.attributes != null) { + if (attributes == null) { + attributes = methodDefinition.attributes; + } else { + attributes.Attrs.AddRange (methodDefinition.attributes.Attrs); + } + } + + if (CurrentTypeParameters != null) { + for (int i = 0; i < CurrentTypeParameters.Count; ++i) { + var tp_other = methodDefinition.CurrentTypeParameters [i]; + if (tp_other.OptAttributes == null) + continue; + + var tp = CurrentTypeParameters [i]; + if (tp.OptAttributes == null) { + tp.OptAttributes = tp_other.OptAttributes; + } else { + tp.OptAttributes.Attrs.AddRange (tp.OptAttributes.Attrs); + } + } + } + } + } + + public abstract class ConstructorInitializer : ExpressionStatement + { + Arguments argument_list; + MethodSpec base_ctor; + + protected ConstructorInitializer (Arguments argument_list, Location loc) + { + this.argument_list = argument_list; + this.loc = loc; + } + + public Arguments Arguments { + get { + return argument_list; + } + } + + public override bool ContainsEmitWithAwait () + { + throw new NotSupportedException (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + throw new NotSupportedException ("ET"); + } + + protected override Expression DoResolve (ResolveContext ec) + { + eclass = ExprClass.Value; + + // FIXME: Hack + var caller_builder = (Constructor) ec.MemberContext; + + // + // Spec mandates that constructor initializer will not have `this' access + // + using (ec.Set (ResolveContext.Options.BaseInitializer)) { + if (argument_list != null) { + bool dynamic; + argument_list.Resolve (ec, out dynamic); + + if (dynamic) { + ec.Report.Error (1975, loc, + "The constructor call cannot be dynamically dispatched within constructor initializer"); + + return null; + } + } + + type = ec.CurrentType; + if (this is ConstructorBaseInitializer) { + if (ec.CurrentType.BaseType == null) + return this; + + type = ec.CurrentType.BaseType; + if (ec.CurrentType.IsStruct) { + ec.Report.Error (522, loc, + "`{0}': Struct constructors cannot call base constructors", caller_builder.GetSignatureForError ()); + return this; + } + } else { + // + // It is legal to have "this" initializers that take no arguments + // in structs + // + // struct D { public D (int a) : this () {} + // + if (ec.CurrentType.IsStruct && argument_list == null) + return this; + } + + base_ctor = ConstructorLookup (ec, type, ref argument_list, loc); + } + + if (base_ctor != null && base_ctor.MemberDefinition == caller_builder.Spec.MemberDefinition) { + ec.Report.Error (516, loc, "Constructor `{0}' cannot call itself", + caller_builder.GetSignatureForError ()); + } + + return this; + } + + public override void Emit (EmitContext ec) + { + // + // It can be null for struct initializers or System.Object + // + if (base_ctor == null) { + if (type == ec.BuiltinTypes.Object) + return; + + ec.Emit (OpCodes.Ldarg_0); + ec.Emit (OpCodes.Initobj, type); + return; + } + + var call = new CallEmitter (); + call.InstanceExpression = new CompilerGeneratedThis (type, loc); + call.EmitPredefined (ec, base_ctor, argument_list, false); + } + + public override void EmitStatement (EmitContext ec) + { + Emit (ec); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + if (argument_list != null) + argument_list.FlowAnalysis (fc); + } + } + + public class ConstructorBaseInitializer : ConstructorInitializer { + public ConstructorBaseInitializer (Arguments argument_list, Location l) : + base (argument_list, l) + { + } + } + + class GeneratedBaseInitializer: ConstructorBaseInitializer { + public GeneratedBaseInitializer (Location loc, Arguments arguments) + : base (arguments, loc) + { + } + } + + public class ConstructorThisInitializer : ConstructorInitializer { + public ConstructorThisInitializer (Arguments argument_list, Location l) : + base (argument_list, l) + { + } + } + + public class Constructor : MethodCore, IMethodData, IMethodDefinition + { + public ConstructorBuilder ConstructorBuilder; + public ConstructorInitializer Initializer; + SecurityType declarative_security; + bool has_compliant_args; + SourceMethodBuilder debug_builder; + + // + // Modifiers allowed for a constructor. + // + public const Modifiers AllowedModifiers = + Modifiers.PUBLIC | + Modifiers.PROTECTED | + Modifiers.INTERNAL | + Modifiers.STATIC | + Modifiers.UNSAFE | + Modifiers.EXTERN | + Modifiers.PRIVATE; + + static readonly string[] attribute_targets = new string [] { "method" }; + + public static readonly string ConstructorName = ".ctor"; + public static readonly string TypeConstructorName = ".cctor"; + + public Constructor (TypeDefinition parent, string name, Modifiers mod, Attributes attrs, ParametersCompiled args, Location loc) + : base (parent, null, mod, AllowedModifiers, new MemberName (name, loc), attrs, args) + { + } + + public bool HasCompliantArgs { + get { + return has_compliant_args; + } + } + + public override AttributeTargets AttributeTargets { + get { + return AttributeTargets.Constructor; + } + } + + bool IMethodData.IsAccessor { + get { + return false; + } + } + + public bool IsPrimaryConstructor { get; set; } + + MethodBase IMethodDefinition.Metadata { + get { + return ConstructorBuilder; + } + } + + // + // Returns true if this is a default constructor + // + public bool IsDefault () + { + if ((ModFlags & Modifiers.STATIC) != 0) + return parameters.IsEmpty; + + return parameters.IsEmpty && + (Initializer is ConstructorBaseInitializer) && + (Initializer.Arguments == null); + } + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.IsValidSecurityAttribute ()) { + a.ExtractSecurityPermissionSet (ctor, ref declarative_security); + return; + } + + if (a.Type == pa.MethodImpl) { + is_external_implementation = a.IsInternalCall (); + } + + ConstructorBuilder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), cdata); + } + + protected override bool CheckBase () + { + if ((ModFlags & Modifiers.STATIC) != 0) { + if (!parameters.IsEmpty) { + Report.Error (132, Location, "`{0}': The static constructor must be parameterless", + GetSignatureForError ()); + return false; + } + + if ((caching_flags & Flags.MethodOverloadsExist) != 0) + Parent.MemberCache.CheckExistingMembersOverloads (this, parameters); + + // the rest can be ignored + return true; + } + + // Check whether arguments were correct. + if (!DefineParameters (parameters)) + return false; + + if ((caching_flags & Flags.MethodOverloadsExist) != 0) + Parent.MemberCache.CheckExistingMembersOverloads (this, parameters); + + if (Parent.PartialContainer.Kind == MemberKind.Struct && parameters.IsEmpty) { + Report.Error (568, Location, + "Structs cannot contain explicit parameterless constructors"); + return false; + } + + CheckProtectedModifier (); + + return true; + } + + // + // Creates the ConstructorBuilder + // + public override bool Define () + { + if (ConstructorBuilder != null) + return true; + + if (!CheckAbstractAndExtern (block != null)) + return false; + + // Check if arguments were correct. + if (!CheckBase ()) + return false; + + if (Parent.PrimaryConstructorParameters != null && !IsPrimaryConstructor && !IsStatic) { + if (Parent.Kind == MemberKind.Struct && Initializer is ConstructorThisInitializer && Initializer.Arguments == null) { + Report.Error (8043, Location, "`{0}': Structs with primary constructor cannot specify default constructor initializer", + GetSignatureForError ()); + } else if (Initializer == null || Initializer is ConstructorBaseInitializer) { + Report.Error (8037, Location, "`{0}': Instance constructor of type with primary constructor must specify `this' constructor initializer", + GetSignatureForError ()); + } + } + + var ca = ModifiersExtensions.MethodAttr (ModFlags) | MethodAttributes.RTSpecialName | MethodAttributes.SpecialName; + + ConstructorBuilder = Parent.TypeBuilder.DefineConstructor ( + ca, CallingConventions, + parameters.GetMetaInfo ()); + + spec = new MethodSpec (MemberKind.Constructor, Parent.Definition, this, Compiler.BuiltinTypes.Void, parameters, ModFlags); + + Parent.MemberCache.AddMember (spec); + + if (block != null) { + // It's here only to report an error + if (block.IsIterator) { + member_type = Compiler.BuiltinTypes.Void; + Iterator.CreateIterator (this, Parent.PartialContainer, ModFlags); + } + + if (Compiler.Settings.WriteMetadataOnly) + block = null; + } + + return true; + } + + // + // Emits the code + // + public override void Emit () + { + if (Parent.PartialContainer.IsComImport) { + if (!IsDefault ()) { + Report.Error (669, Location, "`{0}': A class with the ComImport attribute cannot have a user-defined constructor", + Parent.GetSignatureForError ()); + } + + // Set as internal implementation and reset block data + // to ensure no IL is generated + ConstructorBuilder.SetImplementationFlags (MethodImplAttributes.InternalCall); + block = null; + } + + if ((ModFlags & Modifiers.DEBUGGER_HIDDEN) != 0) + Module.PredefinedAttributes.DebuggerHidden.EmitAttribute (ConstructorBuilder); + + if (OptAttributes != null) + OptAttributes.Emit (); + + base.Emit (); + parameters.ApplyAttributes (this, ConstructorBuilder); + + + BlockContext bc = new BlockContext (this, block, Compiler.BuiltinTypes.Void); + bc.Set (ResolveContext.Options.ConstructorScope); + + if (block != null) { + if (!IsStatic && Initializer == null && Parent.PartialContainer.Kind == MemberKind.Struct) { + // + // If this is a non-static `struct' constructor and doesn't have any + // initializer, it must initialize all of the struct's fields. + // + block.AddThisVariable (bc); + } + + // + // If we use a "this (...)" constructor initializer, then + // do not emit field initializers, they are initialized in the other constructor + // + if (!(Initializer is ConstructorThisInitializer)) + Parent.PartialContainer.ResolveFieldInitializers (bc); + + if (!IsStatic) { + if (Initializer == null && Parent.PartialContainer.Kind == MemberKind.Class) { + Initializer = new GeneratedBaseInitializer (Location, null); + } + + if (Initializer != null) { + // + // mdb format does not support reqions. Try to workaround this by emitting the + // sequence point at initializer. Any breakpoint at constructor header should + // be adjusted to this sequence point as it's the next one which follows. + // + block.AddScopeStatement (new StatementExpression (Initializer)); + } + } + + if (block.Resolve (bc, this)) { + debug_builder = Parent.CreateMethodSymbolEntry (); + EmitContext ec = new EmitContext (this, ConstructorBuilder.GetILGenerator (), bc.ReturnType, debug_builder); + ec.With (EmitContext.Options.ConstructorScope, true); + + block.Emit (ec); + } + } + + if (declarative_security != null) { + foreach (var de in declarative_security) { +#if STATIC + ConstructorBuilder.__AddDeclarativeSecurity (de); +#else + ConstructorBuilder.AddDeclarativeSecurity (de.Key, de.Value); +#endif + } + } + + block = null; + } + + protected override MemberSpec FindBaseMember (out MemberSpec bestCandidate, ref bool overrides) + { + // Is never override + bestCandidate = null; + return null; + } + + public override string GetCallerMemberName () + { + return IsStatic ? TypeConstructorName : ConstructorName; + } + + public override string GetSignatureForDocumentation () + { + return Parent.GetSignatureForDocumentation () + ".#ctor" + parameters.GetSignatureForDocumentation (); + } + + public override string GetSignatureForError() + { + return base.GetSignatureForError () + parameters.GetSignatureForError (); + } + + public override string[] ValidAttributeTargets { + get { + return attribute_targets; + } + } + + protected override bool VerifyClsCompliance () + { + if (!base.VerifyClsCompliance () || !IsExposedFromAssembly ()) { + return false; + } + + if (!parameters.IsEmpty && Parent.Definition.IsAttribute) { + foreach (TypeSpec param in parameters.Types) { + if (param.IsArray) { + return true; + } + } + } + + has_compliant_args = true; + return true; + } + + public override void WriteDebugSymbol (MonoSymbolFile file) + { + if (debug_builder == null) + return; + + var token = ConstructorBuilder.GetToken (); + int t = token.Token; +#if STATIC + if (ModuleBuilder.IsPseudoToken (t)) + t = Module.Builder.ResolvePseudoToken (t); +#endif + + debug_builder.DefineMethod (file, t); + } + + #region IMethodData Members + + public MemberName MethodName { + get { + return MemberName; + } + } + + public TypeSpec ReturnType { + get { + return MemberType; + } + } + + EmitContext IMethodData.CreateEmitContext (ILGenerator ig, SourceMethodBuilder sourceMethod) + { + throw new NotImplementedException (); + } + + #endregion + } + + /// + /// Interface for MethodData class. Holds links to parent members to avoid member duplication. + /// + public interface IMethodData : IMemberContext + { + CallingConventions CallingConventions { get; } + Location Location { get; } + MemberName MethodName { get; } + TypeSpec ReturnType { get; } + ParametersCompiled ParameterInfo { get; } + MethodSpec Spec { get; } + bool IsAccessor { get; } + + Attributes OptAttributes { get; } + ToplevelBlock Block { get; set; } + + EmitContext CreateEmitContext (ILGenerator ig, SourceMethodBuilder sourceMethod); + } + + // + // Encapsulates most of the Method's state + // + public class MethodData + { + public readonly IMethodData method; + + // + // Are we implementing an interface ? + // + public MethodSpec implementing; + + // + // Protected data. + // + protected InterfaceMemberBase member; + protected Modifiers modifiers; + protected MethodAttributes flags; + protected TypeSpec declaring_type; + protected MethodSpec parent_method; + SourceMethodBuilder debug_builder; + string full_name; + + MethodBuilder builder; + public MethodBuilder MethodBuilder { + get { + return builder; + } + } + + public TypeSpec DeclaringType { + get { + return declaring_type; + } + } + + public string MetadataName { + get { + return full_name; + } + } + + public MethodData (InterfaceMemberBase member, + Modifiers modifiers, MethodAttributes flags, IMethodData method) + { + this.member = member; + this.modifiers = modifiers; + this.flags = flags; + + this.method = method; + } + + public MethodData (InterfaceMemberBase member, + Modifiers modifiers, MethodAttributes flags, + IMethodData method, + MethodSpec parent_method) + : this (member, modifiers, flags, method) + { + this.parent_method = parent_method; + } + + public bool Define (TypeDefinition container, string method_full_name) + { + PendingImplementation pending = container.PendingImplementations; + MethodSpec ambig_iface_method; + bool optional = false; + + if (pending != null) { + implementing = pending.IsInterfaceMethod (method.MethodName, member.InterfaceType, this, out ambig_iface_method, ref optional); + + if (member.InterfaceType != null) { + if (implementing == null) { + if (member is PropertyBase) { + container.Compiler.Report.Error (550, method.Location, + "`{0}' is an accessor not found in interface member `{1}{2}'", + method.GetSignatureForError (), member.InterfaceType.GetSignatureForError (), + member.GetSignatureForError ().Substring (member.GetSignatureForError ().LastIndexOf ('.'))); + + } else { + container.Compiler.Report.Error (539, method.Location, + "`{0}.{1}' in explicit interface declaration is not a member of interface", + member.InterfaceType.GetSignatureForError (), member.ShortName); + } + return false; + } + if (implementing.IsAccessor && !method.IsAccessor) { + container.Compiler.Report.SymbolRelatedToPreviousError (implementing); + container.Compiler.Report.Error (683, method.Location, + "`{0}' explicit method implementation cannot implement `{1}' because it is an accessor", + member.GetSignatureForError (), implementing.GetSignatureForError ()); + return false; + } + } else { + if (implementing != null && !optional) { + if (!method.IsAccessor) { + if (implementing.IsAccessor) { + container.Compiler.Report.SymbolRelatedToPreviousError (implementing); + container.Compiler.Report.Error (470, method.Location, + "Method `{0}' cannot implement interface accessor `{1}'", + method.GetSignatureForError (), TypeManager.CSharpSignature (implementing)); + } + } else if (implementing.DeclaringType.IsInterface) { + if (!implementing.IsAccessor) { + container.Compiler.Report.SymbolRelatedToPreviousError (implementing); + container.Compiler.Report.Error (686, method.Location, + "Accessor `{0}' cannot implement interface member `{1}' for type `{2}'. Use an explicit interface implementation", + method.GetSignatureForError (), TypeManager.CSharpSignature (implementing), container.GetSignatureForError ()); + } else { + PropertyBase.PropertyMethod pm = method as PropertyBase.PropertyMethod; + if (pm != null && pm.HasCustomAccessModifier && (pm.ModFlags & Modifiers.PUBLIC) == 0) { + container.Compiler.Report.SymbolRelatedToPreviousError (implementing); + container.Compiler.Report.Error (277, method.Location, + "Accessor `{0}' must be declared public to implement interface member `{1}'", + method.GetSignatureForError (), implementing.GetSignatureForError ()); + } + } + } + } + } + } else { + ambig_iface_method = null; + } + + // + // For implicit implementations, make sure we are public, for + // explicit implementations, make sure we are private. + // + if (implementing != null){ + if (member.IsExplicitImpl) { + if (method.ParameterInfo.HasParams && !implementing.Parameters.HasParams) { + container.Compiler.Report.SymbolRelatedToPreviousError (implementing); + container.Compiler.Report.Error (466, method.Location, + "`{0}': the explicit interface implementation cannot introduce the params modifier", + method.GetSignatureForError ()); + } + + if (ambig_iface_method != null) { + container.Compiler.Report.SymbolRelatedToPreviousError (ambig_iface_method); + container.Compiler.Report.SymbolRelatedToPreviousError (implementing); + container.Compiler.Report.Warning (473, 2, method.Location, + "Explicit interface implementation `{0}' matches more than one interface member. Consider using a non-explicit implementation instead", + method.GetSignatureForError ()); + } + } else { + // + // Setting implementin to null inside this block will trigger a more + // verbose error reporting for missing interface implementations + // + if (implementing.DeclaringType.IsInterface) { + // + // If this is an interface method implementation, + // check for public accessibility + // + if ((flags & MethodAttributes.MemberAccessMask) != MethodAttributes.Public) { + implementing = null; + } else if (optional && (container.Interfaces == null || !container.Definition.Interfaces.Contains (implementing.DeclaringType))) { + // + // We are not implementing interface when base class already implemented it + // + implementing = null; + } + } else if ((flags & MethodAttributes.MemberAccessMask) == MethodAttributes.Private) { + // We may never be private. + implementing = null; + + } else if ((modifiers & Modifiers.OVERRIDE) == 0) { + // + // We may be protected if we're overriding something. + // + implementing = null; + } + } + + // + // Static is not allowed + // + if ((modifiers & Modifiers.STATIC) != 0){ + implementing = null; + } + } + + // + // If implementing is still valid, set flags + // + if (implementing != null){ + // + // When implementing interface methods, set NewSlot + // unless, we are overwriting a method. + // + if ((modifiers & Modifiers.OVERRIDE) == 0 && implementing.DeclaringType.IsInterface) { + flags |= MethodAttributes.NewSlot; + } + + flags |= MethodAttributes.Virtual | MethodAttributes.HideBySig; + + // Set Final unless we're virtual, abstract or already overriding a method. + if ((modifiers & (Modifiers.VIRTUAL | Modifiers.ABSTRACT | Modifiers.OVERRIDE)) == 0) + flags |= MethodAttributes.Final; + + // + // clear the pending implementation flag (requires explicit methods to be defined first) + // + pending.ImplementMethod (method.MethodName, + member.InterfaceType, this, member.IsExplicitImpl, out ambig_iface_method, ref optional); + + // + // Update indexer accessor name to match implementing abstract accessor + // + if (!implementing.DeclaringType.IsInterface && !member.IsExplicitImpl && implementing.IsAccessor) + method_full_name = implementing.MemberDefinition.Name; + } + + full_name = method_full_name; + declaring_type = container.Definition; + + return true; + } + + void DefineOverride (TypeDefinition container) + { + if (implementing == null) + return; + + if (!member.IsExplicitImpl) + return; + + container.TypeBuilder.DefineMethodOverride (builder, (MethodInfo) implementing.GetMetaInfo ()); + } + + // + // Creates partial MethodBuilder for the method when has generic parameters used + // as arguments or return type + // + public MethodBuilder DefineMethodBuilder (TypeDefinition container) + { + if (builder != null) + throw new InternalErrorException (); + + builder = container.TypeBuilder.DefineMethod (full_name, flags, method.CallingConventions); + return builder; + } + + // + // Creates full MethodBuilder for the method + // + public MethodBuilder DefineMethodBuilder (TypeDefinition container, ParametersCompiled param) + { + DefineMethodBuilder (container); + builder.SetReturnType (method.ReturnType.GetMetaInfo ()); + builder.SetParameters (param.GetMetaInfo ()); + return builder; + } + + // + // Emits the code + // + public void Emit (TypeDefinition parent) + { + DefineOverride (parent); + + method.ParameterInfo.ApplyAttributes (method, MethodBuilder); + + ToplevelBlock block = method.Block; + if (block != null) { + BlockContext bc = new BlockContext (method, block, method.ReturnType); + if (block.Resolve (bc, method)) { + debug_builder = member.Parent.CreateMethodSymbolEntry (); + EmitContext ec = method.CreateEmitContext (MethodBuilder.GetILGenerator (), debug_builder); + + block.Emit (ec); + } + } + } + + public void WriteDebugSymbol (MonoSymbolFile file) + { + if (debug_builder == null) + return; + + var token = builder.GetToken (); + int t = token.Token; +#if STATIC + if (ModuleBuilder.IsPseudoToken (t)) + t = member.Module.Builder.ResolvePseudoToken (t); +#endif + + debug_builder.DefineMethod (file, t); + } + } + + public class Destructor : MethodOrOperator + { + const Modifiers AllowedModifiers = + Modifiers.UNSAFE | + Modifiers.EXTERN; + + static readonly string[] attribute_targets = new string [] { "method" }; + + public static readonly string MetadataName = "Finalize"; + + public string Identifier { + get; + set; + } + + public Destructor (TypeDefinition parent, Modifiers mod, ParametersCompiled parameters, Attributes attrs, Location l) + : base (parent, null, mod, AllowedModifiers, new MemberName (MetadataName, l), attrs, parameters) + { + ModFlags &= ~Modifiers.PRIVATE; + ModFlags |= Modifiers.PROTECTED | Modifiers.OVERRIDE; + } + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.Type == pa.Conditional) { + Error_ConditionalAttributeIsNotValid (); + return; + } + + base.ApplyAttributeBuilder (a, ctor, cdata, pa); + } + + protected override bool CheckBase () + { + if ((caching_flags & Flags.MethodOverloadsExist) != 0) + CheckForDuplications (); + + // Don't check base, destructors have special syntax + return true; + } + + public override bool Define () + { + base.Define (); + + if (Compiler.Settings.WriteMetadataOnly) + block = null; + + return true; + } + + public override void Emit() + { + var base_type = Parent.PartialContainer.BaseType; + if (base_type != null && Block != null) { + var base_dtor = MemberCache.FindMember (base_type, + new MemberFilter (MetadataName, 0, MemberKind.Destructor, null, null), BindingRestriction.InstanceOnly) as MethodSpec; + + if (base_dtor == null) + throw new NotImplementedException (); + + MethodGroupExpr method_expr = MethodGroupExpr.CreatePredefined (base_dtor, base_type, Location); + method_expr.InstanceExpression = new BaseThis (base_type, Location); + + var try_block = new ExplicitBlock (block, block.StartLocation, block.EndLocation) { + IsCompilerGenerated = true + }; + var finaly_block = new ExplicitBlock (block, Location, Location) { + IsCompilerGenerated = true + }; + + // + // 0-size arguments to avoid CS0250 error + // TODO: Should use AddScopeStatement or something else which emits correct + // debugger scope + // + finaly_block.AddStatement (new StatementExpression (new Invocation (method_expr, new Arguments (0)), Location.Null)); + + var tf = new TryFinally (try_block, finaly_block, Location); + block.WrapIntoDestructor (tf, try_block); + } + + base.Emit (); + } + + public override string GetSignatureForError () + { + return Parent.GetSignatureForError () + ".~" + Parent.MemberName.Name + "()"; + } + + protected override bool ResolveMemberType () + { + member_type = Compiler.BuiltinTypes.Void; + return true; + } + + public override string[] ValidAttributeTargets { + get { + return attribute_targets; + } + } + } + + // Ooouh Martin, templates are missing here. + // When it will be possible move here a lot of child code and template method type. + public abstract class AbstractPropertyEventMethod : MemberCore, IMethodData, IMethodDefinition { + protected MethodData method_data; + protected ToplevelBlock block; + protected SecurityType declarative_security; + + protected readonly string prefix; + + ReturnParameter return_attributes; + + protected AbstractPropertyEventMethod (InterfaceMemberBase member, string prefix, Attributes attrs, Location loc) + : base (member.Parent, SetupName (prefix, member, loc), attrs) + { + this.prefix = prefix; + } + + static MemberName SetupName (string prefix, InterfaceMemberBase member, Location loc) + { + return new MemberName (member.MemberName.Left, prefix + member.ShortName, member.MemberName.ExplicitInterface, loc); + } + + public void UpdateName (InterfaceMemberBase member) + { + SetMemberName (SetupName (prefix, member, Location)); + } + + #region IMethodData Members + + public ToplevelBlock Block { + get { + return block; + } + + set { + block = value; + } + } + + public CallingConventions CallingConventions { + get { + return CallingConventions.Standard; + } + } + + public EmitContext CreateEmitContext (ILGenerator ig, SourceMethodBuilder sourceMethod) + { + return new EmitContext (this, ig, ReturnType, sourceMethod); + } + + public bool IsAccessor { + get { + return true; + } + } + + public MemberName MethodName { + get { + return MemberName; + } + } + + public TypeSpec[] ParameterTypes { + get { + return ParameterInfo.Types; + } + } + + MethodBase IMethodDefinition.Metadata { + get { + return method_data.MethodBuilder; + } + } + + public abstract ParametersCompiled ParameterInfo { get ; } + public abstract TypeSpec ReturnType { get; } + + #endregion + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.Type == pa.CLSCompliant || a.Type == pa.Obsolete || a.Type == pa.Conditional) { + Report.Error (1667, a.Location, + "Attribute `{0}' is not valid on property or event accessors. It is valid on `{1}' declarations only", + a.Type.GetSignatureForError (), a.GetValidTargets ()); + return; + } + + if (a.IsValidSecurityAttribute ()) { + a.ExtractSecurityPermissionSet (ctor, ref declarative_security); + return; + } + + if (a.Target == AttributeTargets.Method) { + method_data.MethodBuilder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), cdata); + return; + } + + if (a.Target == AttributeTargets.ReturnValue) { + if (return_attributes == null) + return_attributes = new ReturnParameter (this, method_data.MethodBuilder, Location); + + return_attributes.ApplyAttributeBuilder (a, ctor, cdata, pa); + return; + } + + ApplyToExtraTarget (a, ctor, cdata, pa); + } + + protected virtual void ApplyToExtraTarget (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + throw new NotSupportedException ("You forgot to define special attribute target handling"); + } + + // It is not supported for the accessors + public sealed override bool Define() + { + throw new NotSupportedException (); + } + + public virtual void Emit (TypeDefinition parent) + { + method_data.Emit (parent); + + if ((ModFlags & Modifiers.COMPILER_GENERATED) != 0 && !Parent.IsCompilerGenerated) + Module.PredefinedAttributes.CompilerGenerated.EmitAttribute (method_data.MethodBuilder); + if (((ModFlags & Modifiers.DEBUGGER_HIDDEN) != 0)) + Module.PredefinedAttributes.DebuggerHidden.EmitAttribute (method_data.MethodBuilder); + + if (ReturnType.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + return_attributes = new ReturnParameter (this, method_data.MethodBuilder, Location); + Module.PredefinedAttributes.Dynamic.EmitAttribute (return_attributes.Builder); + } else if (ReturnType.HasDynamicElement) { + return_attributes = new ReturnParameter (this, method_data.MethodBuilder, Location); + Module.PredefinedAttributes.Dynamic.EmitAttribute (return_attributes.Builder, ReturnType, Location); + } + + if (OptAttributes != null) + OptAttributes.Emit (); + + if (declarative_security != null) { + foreach (var de in declarative_security) { +#if STATIC + method_data.MethodBuilder.__AddDeclarativeSecurity (de); +#else + method_data.MethodBuilder.AddDeclarativeSecurity (de.Key, de.Value); +#endif + } + } + + block = null; + } + + public override bool EnableOverloadChecks (MemberCore overload) + { + if (overload is MethodCore) { + caching_flags |= Flags.MethodOverloadsExist; + return true; + } + + // This can only happen with indexers and it will + // be catched as indexer difference + if (overload is AbstractPropertyEventMethod) + return true; + + return false; + } + + public override string GetCallerMemberName () + { + return base.GetCallerMemberName ().Substring (prefix.Length); + } + + public override string GetSignatureForDocumentation () + { + // should not be called + throw new NotSupportedException (); + } + + public override bool IsClsComplianceRequired() + { + return false; + } + + public void PrepareEmit () + { + method_data.DefineMethodBuilder (Parent.PartialContainer, ParameterInfo); + } + + public override void WriteDebugSymbol (MonoSymbolFile file) + { + if (method_data != null) + method_data.WriteDebugSymbol (file); + } + + public MethodSpec Spec { get; protected set; } + + // + // Represents header string for documentation comment. + // + public override string DocCommentHeader { + get { throw new InvalidOperationException ("Unexpected attempt to get doc comment from " + this.GetType () + "."); } + } + } + + public class Operator : MethodOrOperator { + + const Modifiers AllowedModifiers = + Modifiers.PUBLIC | + Modifiers.UNSAFE | + Modifiers.EXTERN | + Modifiers.STATIC; + + public enum OpType : byte { + + // Unary operators + LogicalNot, + OnesComplement, + Increment, + Decrement, + True, + False, + + // Unary and Binary operators + Addition, + Subtraction, + + UnaryPlus, + UnaryNegation, + + // Binary operators + Multiply, + Division, + Modulus, + BitwiseAnd, + BitwiseOr, + ExclusiveOr, + LeftShift, + RightShift, + Equality, + Inequality, + GreaterThan, + LessThan, + GreaterThanOrEqual, + LessThanOrEqual, + + // Implicit and Explicit + Implicit, + Explicit, + + // Just because of enum + TOP + }; + + public readonly OpType OperatorType; + + static readonly string [] [] names; + + static Operator () + { + names = new string[(int)OpType.TOP][]; + names [(int) OpType.LogicalNot] = new string [] { "!", "op_LogicalNot" }; + names [(int) OpType.OnesComplement] = new string [] { "~", "op_OnesComplement" }; + names [(int) OpType.Increment] = new string [] { "++", "op_Increment" }; + names [(int) OpType.Decrement] = new string [] { "--", "op_Decrement" }; + names [(int) OpType.True] = new string [] { "true", "op_True" }; + names [(int) OpType.False] = new string [] { "false", "op_False" }; + names [(int) OpType.Addition] = new string [] { "+", "op_Addition" }; + names [(int) OpType.Subtraction] = new string [] { "-", "op_Subtraction" }; + names [(int) OpType.UnaryPlus] = new string [] { "+", "op_UnaryPlus" }; + names [(int) OpType.UnaryNegation] = new string [] { "-", "op_UnaryNegation" }; + names [(int) OpType.Multiply] = new string [] { "*", "op_Multiply" }; + names [(int) OpType.Division] = new string [] { "/", "op_Division" }; + names [(int) OpType.Modulus] = new string [] { "%", "op_Modulus" }; + names [(int) OpType.BitwiseAnd] = new string [] { "&", "op_BitwiseAnd" }; + names [(int) OpType.BitwiseOr] = new string [] { "|", "op_BitwiseOr" }; + names [(int) OpType.ExclusiveOr] = new string [] { "^", "op_ExclusiveOr" }; + names [(int) OpType.LeftShift] = new string [] { "<<", "op_LeftShift" }; + names [(int) OpType.RightShift] = new string [] { ">>", "op_RightShift" }; + names [(int) OpType.Equality] = new string [] { "==", "op_Equality" }; + names [(int) OpType.Inequality] = new string [] { "!=", "op_Inequality" }; + names [(int) OpType.GreaterThan] = new string [] { ">", "op_GreaterThan" }; + names [(int) OpType.LessThan] = new string [] { "<", "op_LessThan" }; + names [(int) OpType.GreaterThanOrEqual] = new string [] { ">=", "op_GreaterThanOrEqual" }; + names [(int) OpType.LessThanOrEqual] = new string [] { "<=", "op_LessThanOrEqual" }; + names [(int) OpType.Implicit] = new string [] { "implicit", "op_Implicit" }; + names [(int) OpType.Explicit] = new string [] { "explicit", "op_Explicit" }; + } + + public Operator (TypeDefinition parent, OpType type, FullNamedExpression ret_type, Modifiers mod_flags, ParametersCompiled parameters, + ToplevelBlock block, Attributes attrs, Location loc) + : base (parent, ret_type, mod_flags, AllowedModifiers, new MemberName (GetMetadataName (type), loc), attrs, parameters) + { + OperatorType = type; + Block = block; + } + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.Type == pa.Conditional) { + Error_ConditionalAttributeIsNotValid (); + return; + } + + base.ApplyAttributeBuilder (a, ctor, cdata, pa); + } + + public override bool Define () + { + const Modifiers RequiredModifiers = Modifiers.PUBLIC | Modifiers.STATIC; + if ((ModFlags & RequiredModifiers) != RequiredModifiers){ + Report.Error (558, Location, "User-defined operator `{0}' must be declared static and public", GetSignatureForError ()); + } + + if (!base.Define ()) + return false; + + if (block != null) { + if (block.IsIterator) { + // + // Current method is turned into automatically generated + // wrapper which creates an instance of iterator + // + Iterator.CreateIterator (this, Parent.PartialContainer, ModFlags); + ModFlags |= Modifiers.DEBUGGER_HIDDEN; + } + + if (Compiler.Settings.WriteMetadataOnly) + block = null; + } + + // imlicit and explicit operator of same types are not allowed + if (OperatorType == OpType.Explicit) + Parent.MemberCache.CheckExistingMembersOverloads (this, GetMetadataName (OpType.Implicit), parameters); + else if (OperatorType == OpType.Implicit) + Parent.MemberCache.CheckExistingMembersOverloads (this, GetMetadataName (OpType.Explicit), parameters); + + TypeSpec declaring_type = Parent.CurrentType; + TypeSpec return_type = MemberType; + TypeSpec first_arg_type = ParameterTypes [0]; + + TypeSpec first_arg_type_unwrap = first_arg_type; + if (first_arg_type.IsNullableType) + first_arg_type_unwrap = Nullable.NullableInfo.GetUnderlyingType (first_arg_type); + + TypeSpec return_type_unwrap = return_type; + if (return_type.IsNullableType) + return_type_unwrap = Nullable.NullableInfo.GetUnderlyingType (return_type); + + // + // Rules for conversion operators + // + if (OperatorType == OpType.Implicit || OperatorType == OpType.Explicit) { + if (first_arg_type_unwrap == return_type_unwrap && first_arg_type_unwrap == declaring_type) { + Report.Error (555, Location, + "User-defined operator cannot take an object of the enclosing type and convert to an object of the enclosing type"); + return false; + } + + TypeSpec conv_type; + if (declaring_type == return_type || declaring_type == return_type_unwrap) { + conv_type = first_arg_type; + } else if (declaring_type == first_arg_type || declaring_type == first_arg_type_unwrap) { + conv_type = return_type; + } else { + Report.Error (556, Location, + "User-defined conversion must convert to or from the enclosing type"); + return false; + } + + if (conv_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + Report.Error (1964, Location, + "User-defined conversion `{0}' cannot convert to or from the dynamic type", + GetSignatureForError ()); + + return false; + } + + if (conv_type.IsInterface) { + Report.Error (552, Location, "User-defined conversion `{0}' cannot convert to or from an interface type", + GetSignatureForError ()); + return false; + } + + if (conv_type.IsClass) { + if (TypeSpec.IsBaseClass (declaring_type, conv_type, true)) { + Report.Error (553, Location, "User-defined conversion `{0}' cannot convert to or from a base class", + GetSignatureForError ()); + return false; + } + + if (TypeSpec.IsBaseClass (conv_type, declaring_type, false)) { + Report.Error (554, Location, "User-defined conversion `{0}' cannot convert to or from a derived class", + GetSignatureForError ()); + return false; + } + } + } else if (OperatorType == OpType.LeftShift || OperatorType == OpType.RightShift) { + if (first_arg_type != declaring_type || parameters.Types[1].BuiltinType != BuiltinTypeSpec.Type.Int) { + Report.Error (564, Location, "Overloaded shift operator must have the type of the first operand be the containing type, and the type of the second operand must be int"); + return false; + } + } else if (parameters.Count == 1) { + // Checks for Unary operators + + if (OperatorType == OpType.Increment || OperatorType == OpType.Decrement) { + if (return_type != declaring_type && !TypeSpec.IsBaseClass (return_type, declaring_type, false)) { + Report.Error (448, Location, + "The return type for ++ or -- operator must be the containing type or derived from the containing type"); + return false; + } + if (first_arg_type != declaring_type) { + Report.Error ( + 559, Location, "The parameter type for ++ or -- operator must be the containing type"); + return false; + } + } + + if (first_arg_type_unwrap != declaring_type) { + Report.Error (562, Location, + "The parameter type of a unary operator must be the containing type"); + return false; + } + + if (OperatorType == OpType.True || OperatorType == OpType.False) { + if (return_type.BuiltinType != BuiltinTypeSpec.Type.Bool) { + Report.Error ( + 215, Location, + "The return type of operator True or False " + + "must be bool"); + return false; + } + } + + } else if (first_arg_type_unwrap != declaring_type) { + // Checks for Binary operators + + var second_arg_type = ParameterTypes[1]; + if (second_arg_type.IsNullableType) + second_arg_type = Nullable.NullableInfo.GetUnderlyingType (second_arg_type); + + if (second_arg_type != declaring_type) { + Report.Error (563, Location, + "One of the parameters of a binary operator must be the containing type"); + return false; + } + } + + return true; + } + + protected override bool ResolveMemberType () + { + if (!base.ResolveMemberType ()) + return false; + + flags |= MethodAttributes.SpecialName | MethodAttributes.HideBySig; + return true; + } + + protected override MemberSpec FindBaseMember (out MemberSpec bestCandidate, ref bool overrides) + { + // Operator cannot be override + bestCandidate = null; + return null; + } + + public static string GetName (OpType ot) + { + return names [(int) ot] [0]; + } + + public static string GetName (string metadata_name) + { + for (int i = 0; i < names.Length; ++i) { + if (names [i] [1] == metadata_name) + return names [i] [0]; + } + return null; + } + + public static string GetMetadataName (OpType ot) + { + return names [(int) ot] [1]; + } + + public static string GetMetadataName (string name) + { + for (int i = 0; i < names.Length; ++i) { + if (names [i] [0] == name) + return names [i] [1]; + } + return null; + } + + public static OpType? GetType (string metadata_name) + { + for (int i = 0; i < names.Length; ++i) { + if (names[i][1] == metadata_name) + return (OpType) i; + } + + return null; + } + + public OpType GetMatchingOperator () + { + switch (OperatorType) { + case OpType.Equality: + return OpType.Inequality; + case OpType.Inequality: + return OpType.Equality; + case OpType.True: + return OpType.False; + case OpType.False: + return OpType.True; + case OpType.GreaterThan: + return OpType.LessThan; + case OpType.LessThan: + return OpType.GreaterThan; + case OpType.GreaterThanOrEqual: + return OpType.LessThanOrEqual; + case OpType.LessThanOrEqual: + return OpType.GreaterThanOrEqual; + default: + return OpType.TOP; + } + } + + public override string GetSignatureForDocumentation () + { + string s = base.GetSignatureForDocumentation (); + if (OperatorType == OpType.Implicit || OperatorType == OpType.Explicit) { + s = s + "~" + ReturnType.GetSignatureForDocumentation (); + } + + return s; + } + + public override string GetSignatureForError () + { + StringBuilder sb = new StringBuilder (); + if (OperatorType == OpType.Implicit || OperatorType == OpType.Explicit) { + sb.AppendFormat ("{0}.{1} operator {2}", + Parent.GetSignatureForError (), GetName (OperatorType), + member_type == null ? type_expr.GetSignatureForError () : member_type.GetSignatureForError ()); + } + else { + sb.AppendFormat ("{0}.operator {1}", Parent.GetSignatureForError (), GetName (OperatorType)); + } + + sb.Append (parameters.GetSignatureForError ()); + return sb.ToString (); + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/modifiers.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/modifiers.cs new file mode 100644 index 000000000..c8c6b73e6 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/modifiers.cs @@ -0,0 +1,282 @@ +// +// modifiers.cs: Modifiers handling +// +// Authors: Miguel de Icaza (miguel@gnu.org) +// Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com) +// Copyright 2004-2010 Novell, Inc +// + +using System; + +#if STATIC +using IKVM.Reflection; +#else +using System.Reflection; +#endif + +namespace Mono.CSharp +{ + [Flags] + public enum Modifiers + { + PROTECTED = 0x0001, + PUBLIC = 0x0002, + PRIVATE = 0x0004, + INTERNAL = 0x0008, + NEW = 0x0010, + ABSTRACT = 0x0020, + SEALED = 0x0040, + STATIC = 0x0080, + READONLY = 0x0100, + VIRTUAL = 0x0200, + OVERRIDE = 0x0400, + EXTERN = 0x0800, + VOLATILE = 0x1000, + UNSAFE = 0x2000, + ASYNC = 0x4000, + TOP = 0x8000, + + // + // Compiler specific flags + // + PROPERTY_CUSTOM = 0x10000, + + PARTIAL = 0x20000, + DEFAULT_ACCESS_MODIFIER = 0x40000, + METHOD_EXTENSION = 0x80000, + COMPILER_GENERATED = 0x100000, + BACKING_FIELD = 0x200000, + DEBUGGER_HIDDEN = 0x400000, + DEBUGGER_STEP_THROUGH = 0x800000, + + AccessibilityMask = PUBLIC | PROTECTED | INTERNAL | PRIVATE, + AllowedExplicitImplFlags = UNSAFE | EXTERN, + } + + static class ModifiersExtensions + { + public static string AccessibilityName (Modifiers mod) + { + switch (mod & Modifiers.AccessibilityMask) { + case Modifiers.PUBLIC: + return "public"; + case Modifiers.PROTECTED: + return "protected"; + case Modifiers.PROTECTED | Modifiers.INTERNAL: + return "protected internal"; + case Modifiers.INTERNAL: + return "internal"; + case Modifiers.PRIVATE: + return "private"; + default: + throw new NotImplementedException (mod.ToString ()); + } + } + + static public string Name (Modifiers i) + { + string s = ""; + + switch (i) { + case Modifiers.NEW: + s = "new"; break; + case Modifiers.PUBLIC: + s = "public"; break; + case Modifiers.PROTECTED: + s = "protected"; break; + case Modifiers.INTERNAL: + s = "internal"; break; + case Modifiers.PRIVATE: + s = "private"; break; + case Modifiers.ABSTRACT: + s = "abstract"; break; + case Modifiers.SEALED: + s = "sealed"; break; + case Modifiers.STATIC: + s = "static"; break; + case Modifiers.READONLY: + s = "readonly"; break; + case Modifiers.VIRTUAL: + s = "virtual"; break; + case Modifiers.OVERRIDE: + s = "override"; break; + case Modifiers.EXTERN: + s = "extern"; break; + case Modifiers.VOLATILE: + s = "volatile"; break; + case Modifiers.UNSAFE: + s = "unsafe"; break; + case Modifiers.ASYNC: + s = "async"; break; + } + + return s; + } + + // + // Used by custom property accessors to check whether @modA is more restrictive than @modB + // + public static bool IsRestrictedModifier (Modifiers modA, Modifiers modB) + { + Modifiers flags = 0; + + if ((modB & Modifiers.PUBLIC) != 0) { + flags = Modifiers.PROTECTED | Modifiers.INTERNAL | Modifiers.PRIVATE; + } else if ((modB & Modifiers.PROTECTED) != 0) { + if ((modB & Modifiers.INTERNAL) != 0) + flags = Modifiers.PROTECTED | Modifiers.INTERNAL; + + flags |= Modifiers.PRIVATE; + } else if ((modB & Modifiers.INTERNAL) != 0) + flags = Modifiers.PRIVATE; + + return modB != modA && (modA & (~flags)) == 0; + } + + public static TypeAttributes TypeAttr (Modifiers mod_flags, bool is_toplevel) + { + TypeAttributes t = 0; + + if (is_toplevel){ + if ((mod_flags & Modifiers.PUBLIC) != 0) + t = TypeAttributes.Public; + else if ((mod_flags & Modifiers.PRIVATE) != 0) + t = TypeAttributes.NotPublic; + } else { + if ((mod_flags & Modifiers.PUBLIC) != 0) + t = TypeAttributes.NestedPublic; + else if ((mod_flags & Modifiers.PRIVATE) != 0) + t = TypeAttributes.NestedPrivate; + else if ((mod_flags & (Modifiers.PROTECTED | Modifiers.INTERNAL)) == (Modifiers.PROTECTED | Modifiers.INTERNAL)) + t = TypeAttributes.NestedFamORAssem; + else if ((mod_flags & Modifiers.PROTECTED) != 0) + t = TypeAttributes.NestedFamily; + else if ((mod_flags & Modifiers.INTERNAL) != 0) + t = TypeAttributes.NestedAssembly; + } + + if ((mod_flags & Modifiers.SEALED) != 0) + t |= TypeAttributes.Sealed; + if ((mod_flags & Modifiers.ABSTRACT) != 0) + t |= TypeAttributes.Abstract; + + return t; + } + + public static FieldAttributes FieldAttr (Modifiers mod_flags) + { + FieldAttributes fa = 0; + + if ((mod_flags & Modifiers.PUBLIC) != 0) + fa |= FieldAttributes.Public; + if ((mod_flags & Modifiers.PRIVATE) != 0) + fa |= FieldAttributes.Private; + if ((mod_flags & Modifiers.PROTECTED) != 0) { + if ((mod_flags & Modifiers.INTERNAL) != 0) + fa |= FieldAttributes.FamORAssem; + else + fa |= FieldAttributes.Family; + } else { + if ((mod_flags & Modifiers.INTERNAL) != 0) + fa |= FieldAttributes.Assembly; + } + + if ((mod_flags & Modifiers.STATIC) != 0) + fa |= FieldAttributes.Static; + if ((mod_flags & Modifiers.READONLY) != 0) + fa |= FieldAttributes.InitOnly; + + return fa; + } + + public static MethodAttributes MethodAttr (Modifiers mod_flags) + { + MethodAttributes ma = MethodAttributes.HideBySig; + + switch (mod_flags & Modifiers.AccessibilityMask) { + case Modifiers.PUBLIC: + ma |= MethodAttributes.Public; + break; + case Modifiers.PRIVATE: + ma |= MethodAttributes.Private; + break; + case Modifiers.PROTECTED | Modifiers.INTERNAL: + ma |= MethodAttributes.FamORAssem; + break; + case Modifiers.PROTECTED: + ma |= MethodAttributes.Family; + break; + case Modifiers.INTERNAL: + ma |= MethodAttributes.Assembly; + break; + default: + throw new NotImplementedException (mod_flags.ToString ()); + } + + if ((mod_flags & Modifiers.STATIC) != 0) + ma |= MethodAttributes.Static; + if ((mod_flags & Modifiers.ABSTRACT) != 0) { + ma |= MethodAttributes.Abstract | MethodAttributes.Virtual; + } + if ((mod_flags & Modifiers.SEALED) != 0) + ma |= MethodAttributes.Final; + + if ((mod_flags & Modifiers.VIRTUAL) != 0) + ma |= MethodAttributes.Virtual; + + if ((mod_flags & Modifiers.OVERRIDE) != 0) { + ma |= MethodAttributes.Virtual; + } else { + if ((ma & MethodAttributes.Virtual) != 0) + ma |= MethodAttributes.NewSlot; + } + + return ma; + } + + // + // Checks the object @mod modifiers to be in @allowed. + // Returns the new mask. Side effect: reports any + // incorrect attributes. + // + public static Modifiers Check (Modifiers allowed, Modifiers mod, Modifiers def_access, Location l, Report Report) + { + int invalid_flags = (~(int) allowed) & ((int) mod & ((int) Modifiers.TOP - 1)); + int i; + + if (invalid_flags == 0){ + // + // If no accessibility bits provided + // then provide the defaults. + // + if ((mod & Modifiers.AccessibilityMask) == 0) { + mod |= def_access; + if (def_access != 0) + mod |= Modifiers.DEFAULT_ACCESS_MODIFIER; + return mod; + } + + return mod; + } + + for (i = 1; i < (int) Modifiers.TOP; i <<= 1) { + if ((i & invalid_flags) == 0) + continue; + + Error_InvalidModifier ((Modifiers)i, l, Report); + } + + return allowed & mod; + } + + static void Error_InvalidModifier (Modifiers mod, Location l, Report Report) + { + Report.Error (106, l, "The modifier `{0}' is not valid for this item", + Name (mod)); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/module.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/module.cs new file mode 100644 index 000000000..68f1b8ece --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/module.cs @@ -0,0 +1,713 @@ +// +// module.cs: keeps a tree representation of the generated code +// +// Authors: Miguel de Icaza (miguel@gnu.org) +// Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) +// Copyright 2003-2008 Novell, Inc. +// Copyright 2011 Xamarin Inc +// + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using Mono.CompilerServices.SymbolWriter; +using System.Linq; + +#if STATIC +using IKVM.Reflection; +using IKVM.Reflection.Emit; +#else +using System.Reflection; +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp +{ + // + // Module (top-level type) container + // + public sealed class ModuleContainer : TypeContainer + { +#if STATIC + // + // Compiler generated container for static data + // + sealed class StaticDataContainer : CompilerGeneratedContainer + { + readonly Dictionary size_types; + int fields; + + public StaticDataContainer (ModuleContainer module) + : base (module, new MemberName ("" + module.builder.ModuleVersionId.ToString ("B"), Location.Null), + Modifiers.STATIC | Modifiers.INTERNAL) + { + size_types = new Dictionary (); + } + + public override void CloseContainer () + { + base.CloseContainer (); + + foreach (var entry in size_types) { + entry.Value.CloseContainer (); + } + } + + public FieldSpec DefineInitializedData (byte[] data, Location loc) + { + Struct size_type; + if (!size_types.TryGetValue (data.Length, out size_type)) { + // + // Build common type for this data length. We cannot use + // DefineInitializedData because it creates public type, + // and its name is not unique among modules + // + size_type = new Struct (this, new MemberName ("$ArrayType=" + data.Length, loc), Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED, null); + size_type.CreateContainer (); + size_type.DefineContainer (); + + size_types.Add (data.Length, size_type); + + // It has to work even if StructLayoutAttribute does not exist + size_type.TypeBuilder.__SetLayout (1, data.Length); + } + + var name = "$field-" + fields.ToString ("X"); + ++fields; + const Modifiers fmod = Modifiers.STATIC | Modifiers.INTERNAL; + var fbuilder = TypeBuilder.DefineField (name, size_type.CurrentType.GetMetaInfo (), ModifiersExtensions.FieldAttr (fmod) | FieldAttributes.HasFieldRVA); + fbuilder.__SetDataAndRVA (data); + + return new FieldSpec (CurrentType, null, size_type.CurrentType, fbuilder, fmod); + } + } + + StaticDataContainer static_data; + + // + // Makes const data field inside internal type container + // + public FieldSpec MakeStaticData (byte[] data, Location loc) + { + if (static_data == null) { + static_data = new StaticDataContainer (this); + static_data.CreateContainer (); + static_data.DefineContainer (); + + AddCompilerGeneratedClass (static_data); + } + + return static_data.DefineInitializedData (data, loc); + } +#endif + + public sealed class PatternMatchingHelper : CompilerGeneratedContainer + { + public PatternMatchingHelper (ModuleContainer module) + : base (module, new MemberName ("", Location.Null), + Modifiers.STATIC | Modifiers.INTERNAL | Modifiers.DEBUGGER_HIDDEN) + { + } + + public Method NumberMatcher { get; private set; } + + protected override bool DoDefineMembers () + { + if (!base.DoDefineMembers ()) + return false; + + NumberMatcher = GenerateNumberMatcher (); + return true; + } + + Method GenerateNumberMatcher () + { + var loc = Location; + var parameters = ParametersCompiled.CreateFullyResolved ( + new [] { + new Parameter (new TypeExpression (Compiler.BuiltinTypes.Object, loc), "obj", 0, null, loc), + new Parameter (new TypeExpression (Compiler.BuiltinTypes.Object, loc), "value", 0, null, loc), + new Parameter (new TypeExpression (Compiler.BuiltinTypes.Bool, loc), "enumType", 0, null, loc), + }, + new [] { + Compiler.BuiltinTypes.Object, + Compiler.BuiltinTypes.Object, + Compiler.BuiltinTypes.Bool + }); + + var m = new Method (this, new TypeExpression (Compiler.BuiltinTypes.Bool, loc), + Modifiers.PUBLIC | Modifiers.STATIC | Modifiers.DEBUGGER_HIDDEN, new MemberName ("NumberMatcher", loc), + parameters, null); + + parameters [0].Resolve (m, 0); + parameters [1].Resolve (m, 1); + parameters [2].Resolve (m, 2); + + ToplevelBlock top_block = new ToplevelBlock (Compiler, parameters, loc); + m.Block = top_block; + + // + // if (enumType) + // return Equals (obj, value); + // + var equals_args = new Arguments (2); + equals_args.Add (new Argument (top_block.GetParameterReference (0, loc))); + equals_args.Add (new Argument (top_block.GetParameterReference (1, loc))); + + var if_type = new If ( + top_block.GetParameterReference (2, loc), + new Return (new Invocation (new SimpleName ("Equals", loc), equals_args), loc), + loc); + + top_block.AddStatement (if_type); + + // + // if (obj is Enum || obj == null) + // return false; + // + + var if_enum = new If ( + new Binary (Binary.Operator.LogicalOr, + new Is (top_block.GetParameterReference (0, loc), new TypeExpression (Compiler.BuiltinTypes.Enum, loc), loc), + new Binary (Binary.Operator.Equality, top_block.GetParameterReference (0, loc), new NullLiteral (loc))), + new Return (new BoolLiteral (Compiler.BuiltinTypes, false, loc), loc), + loc); + + top_block.AddStatement (if_enum); + + + var system_convert = new MemberAccess (new QualifiedAliasMember ("global", "System", loc), "Convert", loc); + + // + // var converted = System.Convert.ChangeType (obj, System.Convert.GetTypeCode (value)); + // + var lv_converted = LocalVariable.CreateCompilerGenerated (Compiler.BuiltinTypes.Object, top_block, loc); + + var arguments_gettypecode = new Arguments (1); + arguments_gettypecode.Add (new Argument (top_block.GetParameterReference (1, loc))); + + var gettypecode = new Invocation (new MemberAccess (system_convert, "GetTypeCode", loc), arguments_gettypecode); + + var arguments_changetype = new Arguments (1); + arguments_changetype.Add (new Argument (top_block.GetParameterReference (0, loc))); + arguments_changetype.Add (new Argument (gettypecode)); + + var changetype = new Invocation (new MemberAccess (system_convert, "ChangeType", loc), arguments_changetype); + + top_block.AddStatement (new StatementExpression (new SimpleAssign (new LocalVariableReference (lv_converted, loc), changetype, loc))); + + + // + // return converted.Equals (value) + // + var equals_arguments = new Arguments (1); + equals_arguments.Add (new Argument (top_block.GetParameterReference (1, loc))); + var equals_invocation = new Invocation (new MemberAccess (new LocalVariableReference (lv_converted, loc), "Equals"), equals_arguments); + top_block.AddStatement (new Return (equals_invocation, loc)); + + m.Define (); + m.PrepareEmit (); + AddMember (m); + + return m; + } + } + + PatternMatchingHelper pmh; + + public PatternMatchingHelper CreatePatterMatchingHelper () + { + if (pmh == null) { + pmh = new PatternMatchingHelper (this); + + pmh.CreateContainer (); + pmh.DefineContainer (); + pmh.Define (); + AddCompilerGeneratedClass (pmh); + } + + return pmh; + } + + public CharSet? DefaultCharSet; + public TypeAttributes DefaultCharSetType = TypeAttributes.AnsiClass; + + readonly Dictionary> anonymous_types; + readonly Dictionary array_types; + readonly Dictionary pointer_types; + readonly Dictionary reference_types; + readonly Dictionary attrs_cache; + readonly Dictionary awaiters; + + AssemblyDefinition assembly; + readonly CompilerContext context; + readonly RootNamespace global_ns; + readonly Dictionary alias_ns; + + ModuleBuilder builder; + + bool has_extenstion_method; + + PredefinedAttributes predefined_attributes; + PredefinedTypes predefined_types; + PredefinedMembers predefined_members; + + public Binary.PredefinedOperator[] OperatorsBinaryEqualityLifted; + public Binary.PredefinedOperator[] OperatorsBinaryLifted; + + static readonly string[] attribute_targets = new string[] { "assembly", "module" }; + + public ModuleContainer (CompilerContext context) + : base (null, MemberName.Null, null, 0) + { + this.context = context; + + caching_flags &= ~(Flags.Obsolete_Undetected | Flags.Excluded_Undetected); + + containers = new List (); + anonymous_types = new Dictionary> (); + global_ns = new GlobalRootNamespace (); + alias_ns = new Dictionary (); + array_types = new Dictionary (); + pointer_types = new Dictionary (); + reference_types = new Dictionary (); + attrs_cache = new Dictionary (); + awaiters = new Dictionary (); + } + + #region Properties + + internal Dictionary ArrayTypesCache { + get { + return array_types; + } + } + + // + // Cache for parameter-less attributes + // + internal Dictionary AttributeConstructorCache { + get { + return attrs_cache; + } + } + + public override AttributeTargets AttributeTargets { + get { + return AttributeTargets.Assembly; + } + } + + public ModuleBuilder Builder { + get { + return builder; + } + } + + public override CompilerContext Compiler { + get { + return context; + } + } + + public int CounterAnonymousTypes { get; set; } + + public AssemblyDefinition DeclaringAssembly { + get { + return assembly; + } + } + + internal DocumentationBuilder DocumentationBuilder { + get; set; + } + + public override string DocCommentHeader { + get { + throw new NotSupportedException (); + } + } + + public Evaluator Evaluator { + get; set; + } + + public bool HasDefaultCharSet { + get { + return DefaultCharSet.HasValue; + } + } + + public bool HasExtensionMethod { + get { + return has_extenstion_method; + } + set { + has_extenstion_method = value; + } + } + + public bool HasTypesFullyDefined { + get; set; + } + + // + // Returns module global:: namespace + // + public RootNamespace GlobalRootNamespace { + get { + return global_ns; + } + } + + public override ModuleContainer Module { + get { + return this; + } + } + + internal Dictionary PointerTypesCache { + get { + return pointer_types; + } + } + + internal PredefinedAttributes PredefinedAttributes { + get { + return predefined_attributes; + } + } + + internal PredefinedMembers PredefinedMembers { + get { + return predefined_members; + } + } + + internal PredefinedTypes PredefinedTypes { + get { + return predefined_types; + } + } + + internal Dictionary ReferenceTypesCache { + get { + return reference_types; + } + } + + public override string[] ValidAttributeTargets { + get { + return attribute_targets; + } + } + + #endregion + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + + public void AddAnonymousType (AnonymousTypeClass type) + { + List existing; + if (!anonymous_types.TryGetValue (type.Parameters.Count, out existing)) + if (existing == null) { + existing = new List (); + anonymous_types.Add (type.Parameters.Count, existing); + } + + existing.Add (type); + } + + public void AddAttribute (Attribute attr, IMemberContext context) + { + attr.AttachTo (this, context); + + if (attributes == null) { + attributes = new Attributes (attr); + return; + } + + attributes.AddAttribute (attr); + } + + public override void AddTypeContainer (TypeContainer tc) + { + AddTypeContainerMember (tc); + } + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.Target == AttributeTargets.Assembly) { + assembly.ApplyAttributeBuilder (a, ctor, cdata, pa); + return; + } + + if (a.Type == pa.DefaultCharset) { + switch (a.GetCharSetValue ()) { + case CharSet.Ansi: + case CharSet.None: + break; + case CharSet.Auto: + DefaultCharSet = CharSet.Auto; + DefaultCharSetType = TypeAttributes.AutoClass; + break; + case CharSet.Unicode: + DefaultCharSet = CharSet.Unicode; + DefaultCharSetType = TypeAttributes.UnicodeClass; + break; + default: + Report.Error (1724, a.Location, "Value specified for the argument to `{0}' is not valid", + a.GetSignatureForError ()); + break; + } + } else if (a.Type == pa.CLSCompliant) { + Attribute cls = DeclaringAssembly.CLSCompliantAttribute; + if (cls == null) { + Report.Warning (3012, 1, a.Location, + "You must specify the CLSCompliant attribute on the assembly, not the module, to enable CLS compliance checking"); + } else if (DeclaringAssembly.IsCLSCompliant != a.GetBoolean ()) { + Report.SymbolRelatedToPreviousError (cls.Location, cls.GetSignatureForError ()); + Report.Warning (3017, 1, a.Location, + "You cannot specify the CLSCompliant attribute on a module that differs from the CLSCompliant attribute on the assembly"); + return; + } + } + + builder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), cdata); + } + + public override void CloseContainer () + { + if (anonymous_types != null) { + foreach (var atypes in anonymous_types) + foreach (var at in atypes.Value) + at.CloseContainer (); + } + + base.CloseContainer (); + } + + public TypeBuilder CreateBuilder (string name, TypeAttributes attr, int typeSize) + { + return builder.DefineType (name, attr, null, typeSize); + } + + // + // Creates alias global namespace + // + public RootNamespace CreateRootNamespace (string alias) + { + if (alias == global_ns.Alias) { + RootNamespace.Error_GlobalNamespaceRedefined (Report, Location.Null); + return global_ns; + } + + RootNamespace rn; + if (!alias_ns.TryGetValue (alias, out rn)) { + rn = new RootNamespace (alias); + alias_ns.Add (alias, rn); + } + + return rn; + } + + public void Create (AssemblyDefinition assembly, ModuleBuilder moduleBuilder) + { + this.assembly = assembly; + builder = moduleBuilder; + } + + public override bool Define () + { + DefineContainer (); + + ExpandBaseInterfaces (); + + base.Define (); + + HasTypesFullyDefined = true; + + return true; + } + + public override bool DefineContainer () + { + DefineNamespace (); + + return base.DefineContainer (); + } + + public void EnableRedefinition () + { + is_defined = false; + } + + public override void EmitContainer () + { + if (OptAttributes != null) + OptAttributes.Emit (); + + if (Compiler.Settings.Unsafe && !assembly.IsSatelliteAssembly) { + var pa = PredefinedAttributes.UnverifiableCode; + if (pa.IsDefined) + pa.EmitAttribute (builder); + } + + foreach (var tc in containers) { + tc.PrepareEmit (); + } + + base.EmitContainer (); + + if (Compiler.Report.Errors == 0 && !Compiler.Settings.WriteMetadataOnly) + VerifyMembers (); + + if (anonymous_types != null) { + foreach (var atypes in anonymous_types) + foreach (var at in atypes.Value) + at.EmitContainer (); + } + } + + internal override void GenerateDocComment (DocumentationBuilder builder) + { + foreach (var tc in containers) + tc.GenerateDocComment (builder); + } + + public AnonymousTypeClass GetAnonymousType (IList parameters) + { + List candidates; + if (!anonymous_types.TryGetValue (parameters.Count, out candidates)) + return null; + + int i; + foreach (AnonymousTypeClass at in candidates) { + for (i = 0; i < parameters.Count; ++i) { + if (!parameters [i].Equals (at.Parameters [i])) + break; + } + + if (i == parameters.Count) + return at; + } + + return null; + } + + // + // Return container with awaiter definition. It never returns null + // but all container member can be null for easier error reporting + // + public AwaiterDefinition GetAwaiter (TypeSpec type) + { + AwaiterDefinition awaiter; + if (awaiters.TryGetValue (type, out awaiter)) + return awaiter; + + awaiter = new AwaiterDefinition (); + + // + // Predefined: bool IsCompleted { get; } + // + awaiter.IsCompleted = MemberCache.FindMember (type, MemberFilter.Property ("IsCompleted", Compiler.BuiltinTypes.Bool), + BindingRestriction.InstanceOnly) as PropertySpec; + + // + // Predefined: GetResult () + // + // The method return type is also result type of await expression + // + awaiter.GetResult = MemberCache.FindMember (type, MemberFilter.Method ("GetResult", 0, + ParametersCompiled.EmptyReadOnlyParameters, null), + BindingRestriction.InstanceOnly) as MethodSpec; + + // + // Predefined: INotifyCompletion.OnCompleted (System.Action) + // + var nc = PredefinedTypes.INotifyCompletion; + awaiter.INotifyCompletion = !nc.Define () || type.ImplementsInterface (nc.TypeSpec, false); + + awaiters.Add (type, awaiter); + return awaiter; + } + + public override void GetCompletionStartingWith (string prefix, List results) + { + var names = Evaluator.GetVarNames (); + results.AddRange (names.Where (l => l.StartsWith (prefix))); + } + + public RootNamespace GetRootNamespace (string name) + { + RootNamespace rn; + alias_ns.TryGetValue (name, out rn); + return rn; + } + + public override string GetSignatureForError () + { + return ""; + } + + public Binary.PredefinedOperator[] GetPredefinedEnumAritmeticOperators (TypeSpec enumType, bool nullable) + { + TypeSpec underlying; + Binary.Operator mask = 0; + + if (nullable) { + underlying = Nullable.NullableInfo.GetEnumUnderlyingType (this, enumType); + mask = Binary.Operator.NullableMask; + } else { + underlying = EnumSpec.GetUnderlyingType (enumType); + } + + var operators = new[] { + new Binary.PredefinedOperator (enumType, underlying, + mask | Binary.Operator.AdditionMask | Binary.Operator.SubtractionMask | Binary.Operator.DecomposedMask, enumType), + new Binary.PredefinedOperator (underlying, enumType, + mask | Binary.Operator.AdditionMask | Binary.Operator.SubtractionMask | Binary.Operator.DecomposedMask, enumType), + new Binary.PredefinedOperator (enumType, mask | Binary.Operator.SubtractionMask, underlying) + }; + + return operators; + } + + public void InitializePredefinedTypes () + { + predefined_attributes = new PredefinedAttributes (this); + predefined_types = new PredefinedTypes (this); + predefined_members = new PredefinedMembers (this); + + OperatorsBinaryEqualityLifted = Binary.CreateEqualityLiftedOperatorsTable (this); + OperatorsBinaryLifted = Binary.CreateStandardLiftedOperatorsTable (this); + } + + public override bool IsClsComplianceRequired () + { + return DeclaringAssembly.IsCLSCompliant; + } + + public Attribute ResolveAssemblyAttribute (PredefinedAttribute a_type) + { + Attribute a = OptAttributes.Search ("assembly", a_type); + if (a != null) { + a.Resolve (); + } + return a; + } + + public void SetDeclaringAssembly (AssemblyDefinition assembly) + { + // TODO: This setter is quite ugly but I have not found a way around it yet + this.assembly = assembly; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/namespace.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/namespace.cs new file mode 100644 index 000000000..f4c5aba5b --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/namespace.cs @@ -0,0 +1,1584 @@ +// +// namespace.cs: Tracks namespaces +// +// Author: +// Miguel de Icaza (miguel@ximian.com) +// Marek Safar (marek.safar@seznam.cz) +// +// Copyright 2001 Ximian, Inc. +// Copyright 2003-2008 Novell, Inc. +// Copyright 2011 Xamarin Inc +// +using System; +using System.Collections.Generic; +using System.Linq; +using Mono.CompilerServices.SymbolWriter; + +namespace Mono.CSharp { + + public class RootNamespace : Namespace { + + readonly string alias_name; + readonly Dictionary all_namespaces; + + public RootNamespace (string alias_name) + : base () + { + this.alias_name = alias_name; + RegisterNamespace (this); + + all_namespaces = new Dictionary (); + all_namespaces.Add ("", this); + } + + public string Alias { + get { + return alias_name; + } + } + + public static void Error_GlobalNamespaceRedefined (Report report, Location loc) + { + report.Error (1681, loc, "The global extern alias cannot be redefined"); + } + + // + // For better error reporting where we try to guess missing using directive + // + public List FindTypeNamespaces (IMemberContext ctx, string name, int arity) + { + List res = null; + + foreach (var ns in all_namespaces) { + var type = ns.Value.LookupType (ctx, name, arity, LookupMode.Normal, Location.Null); + if (type != null) { + if (res == null) + res = new List (); + + res.Add (ns.Key); + } + } + + return res; + } + + // + // For better error reporting where compiler tries to guess missing using directive + // + public List FindExtensionMethodNamespaces (IMemberContext ctx, string name, int arity) + { + List res = null; + + foreach (var ns in all_namespaces) { + var methods = ns.Value.LookupExtensionMethod (ctx, name, arity); + if (methods != null) { + if (res == null) + res = new List (); + + res.Add (ns.Key); + } + } + + return res; + } + + public void RegisterNamespace (Namespace child) + { + if (child != this) + all_namespaces.Add (child.Name, child); + } + + public override string GetSignatureForError () + { + return alias_name + "::"; + } + } + + public sealed class GlobalRootNamespace : RootNamespace + { + public GlobalRootNamespace () + : base ("global") + { + } + } + + // + // Namespace cache for imported and compiled namespaces + // + public class Namespace + { + readonly Namespace parent; + string fullname; + protected Dictionary namespaces; + protected Dictionary> types; + List extension_method_types; + Dictionary cached_types; + bool cls_checked; + + /// + /// Constructor Takes the current namespace and the + /// name. This is bootstrapped with parent == null + /// and name = "" + /// + public Namespace (Namespace parent, string name) + : this () + { + if (name == null) + throw new ArgumentNullException ("name"); + + this.parent = parent; + + string pname = parent != null ? parent.fullname : null; + + if (pname == null) + fullname = name; + else + fullname = pname + "." + name; + + while (parent.parent != null) + parent = parent.parent; + + var root = parent as RootNamespace; + if (root == null) + throw new InternalErrorException ("Root namespaces must be created using RootNamespace"); + + root.RegisterNamespace (this); + } + + protected Namespace () + { + namespaces = new Dictionary (); + cached_types = new Dictionary (); + } + + #region Properties + + /// + /// The qualified name of the current namespace + /// + public string Name { + get { return fullname; } + } + + /// + /// The parent of this namespace, used by the parser to "Pop" + /// the current namespace declaration + /// + public Namespace Parent { + get { return parent; } + } + + #endregion + + public Namespace AddNamespace (MemberName name) + { + var ns_parent = name.Left == null ? this : AddNamespace (name.Left); + return ns_parent.TryAddNamespace (name.Basename); + } + + Namespace TryAddNamespace (string name) + { + Namespace ns; + + if (!namespaces.TryGetValue (name, out ns)) { + ns = new Namespace (this, name); + namespaces.Add (name, ns); + } + + return ns; + } + + public bool TryGetNamespace (string name, out Namespace ns) + { + return namespaces.TryGetValue (name, out ns); + } + + // TODO: Replace with CreateNamespace where MemberName is created for the method call + public Namespace GetNamespace (string name, bool create) + { + int pos = name.IndexOf ('.'); + + Namespace ns; + string first; + if (pos >= 0) + first = name.Substring (0, pos); + else + first = name; + + if (!namespaces.TryGetValue (first, out ns)) { + if (!create) + return null; + + ns = new Namespace (this, first); + namespaces.Add (first, ns); + } + + if (pos >= 0) + ns = ns.GetNamespace (name.Substring (pos + 1), create); + + return ns; + } + + public IList GetAllTypes (string name) + { + IList found; + if (types == null || !types.TryGetValue (name, out found)) + return null; + + return found; + } + + public virtual string GetSignatureForError () + { + return fullname; + } + + public TypeSpec LookupType (IMemberContext ctx, string name, int arity, LookupMode mode, Location loc) + { + if (types == null) + return null; + + TypeSpec best = null; + if (arity == 0 && cached_types.TryGetValue (name, out best)) { + if (best != null || mode != LookupMode.IgnoreAccessibility) + return best; + } + + IList found; + if (!types.TryGetValue (name, out found)) + return null; + + foreach (var ts in found) { + if (ts.Arity == arity || mode == LookupMode.NameOf) { + if (best == null) { + if ((ts.Modifiers & Modifiers.INTERNAL) != 0 && !ts.MemberDefinition.IsInternalAsPublic (ctx.Module.DeclaringAssembly) && mode != LookupMode.IgnoreAccessibility) + continue; + + best = ts; + continue; + } + + if (best.MemberDefinition.IsImported && ts.MemberDefinition.IsImported) { + if (ts.Kind == MemberKind.MissingType) + continue; + + if (best.Kind == MemberKind.MissingType) { + best = ts; + continue; + } + + if (mode == LookupMode.Normal) { + ctx.Module.Compiler.Report.SymbolRelatedToPreviousError (best); + ctx.Module.Compiler.Report.SymbolRelatedToPreviousError (ts); + ctx.Module.Compiler.Report.Error (433, loc, "The imported type `{0}' is defined multiple times", ts.GetSignatureForError ()); + } + + break; + } + + if (best.MemberDefinition.IsImported) + best = ts; + + if ((best.Modifiers & Modifiers.INTERNAL) != 0 && !best.MemberDefinition.IsInternalAsPublic (ctx.Module.DeclaringAssembly)) + continue; + + if (mode != LookupMode.Normal) + continue; + + if (ts.MemberDefinition.IsImported) { + ctx.Module.Compiler.Report.SymbolRelatedToPreviousError (best); + ctx.Module.Compiler.Report.SymbolRelatedToPreviousError (ts); + } + + ctx.Module.Compiler.Report.Warning (436, 2, loc, + "The type `{0}' conflicts with the imported type of same name'. Ignoring the imported type definition", + best.GetSignatureForError ()); + } + + // + // Lookup for the best candidate with the closest arity match + // + if (arity < 0) { + if (best == null) { + best = ts; + } else if (System.Math.Abs (ts.Arity + arity) < System.Math.Abs (best.Arity + arity)) { + best = ts; + } + } + } + + // TODO MemberCache: Cache more + if (arity == 0 && mode == LookupMode.Normal) + cached_types.Add (name, best); + + if (best != null) { + var dep = best.GetMissingDependencies (); + if (dep != null) + ImportedTypeDefinition.Error_MissingDependency (ctx, dep, loc); + } + + return best; + } + + public FullNamedExpression LookupTypeOrNamespace (IMemberContext ctx, string name, int arity, LookupMode mode, Location loc) + { + var texpr = LookupType (ctx, name, arity, mode, loc); + + Namespace ns; + if (arity == 0 && namespaces.TryGetValue (name, out ns)) { + if (texpr == null) + return new NamespaceExpression (ns, loc); + + if (mode != LookupMode.Probing) { + //ctx.Module.Compiler.Report.SymbolRelatedToPreviousError (texpr.Type); + // ctx.Module.Compiler.Report.SymbolRelatedToPreviousError (ns.loc, ""); + ctx.Module.Compiler.Report.Warning (437, 2, loc, + "The type `{0}' conflicts with the imported namespace `{1}'. Using the definition found in the source file", + texpr.GetSignatureForError (), ns.GetSignatureForError ()); + } + + if (texpr.MemberDefinition.IsImported) + return new NamespaceExpression (ns, loc); + } + + if (texpr == null) + return null; + + return new TypeExpression (texpr, loc); + } + + // + // Completes types with the given `prefix' + // + public IEnumerable CompletionGetTypesStartingWith (string prefix) + { + if (types == null) + return Enumerable.Empty (); + + var res = from item in types + where item.Key.StartsWith (prefix) && item.Value.Any (l => (l.Modifiers & Modifiers.PUBLIC) != 0) + select item.Key; + + if (namespaces != null) + res = res.Concat (from item in namespaces where item.Key.StartsWith (prefix) select item.Key); + + return res; + } + + // + // Looks for extension method in this namespace + // + public List LookupExtensionMethod (IMemberContext invocationContext, string name, int arity) + { + if (extension_method_types == null) + return null; + + List found = null; + for (int i = 0; i < extension_method_types.Count; ++i) { + var ts = extension_method_types[i]; + + // + // When the list was built we didn't know what members the type + // contains + // + if ((ts.Modifiers & Modifiers.METHOD_EXTENSION) == 0) { + if (extension_method_types.Count == 1) { + extension_method_types = null; + return found; + } + + extension_method_types.RemoveAt (i--); + continue; + } + + var res = ts.MemberCache.FindExtensionMethods (invocationContext, name, arity); + if (res == null) + continue; + + if (found == null) { + found = res; + } else { + found.AddRange (res); + } + } + + return found; + } + + public void AddType (ModuleContainer module, TypeSpec ts) + { + if (types == null) { + types = new Dictionary> (64); + } + + if (ts.IsClass && ts.Arity == 0) { + var extension_method_allowed = ts.MemberDefinition.IsImported ? (ts.Modifiers & Modifiers.METHOD_EXTENSION) != 0 : (ts.IsStatic || ts.MemberDefinition.IsPartial); + if (extension_method_allowed) { + if (extension_method_types == null) + extension_method_types = new List (); + + extension_method_types.Add (ts); + } + } + + var name = ts.Name; + IList existing; + if (types.TryGetValue (name, out existing)) { + TypeSpec better_type; + TypeSpec found; + if (existing.Count == 1) { + found = existing[0]; + if (ts.Arity == found.Arity) { + better_type = IsImportedTypeOverride (module, ts, found); + if (better_type == found) + return; + + if (better_type != null) { + existing [0] = better_type; + return; + } + } + + existing = new List (); + existing.Add (found); + types[name] = existing; + } else { + for (int i = 0; i < existing.Count; ++i) { + found = existing[i]; + if (ts.Arity != found.Arity) + continue; + + better_type = IsImportedTypeOverride (module, ts, found); + if (better_type == found) + return; + + if (better_type != null) { + existing.RemoveAt (i); + --i; + continue; + } + } + } + + existing.Add (ts); + } else { + types.Add (name, new TypeSpec[] { ts }); + } + } + + // + // We import any types but in the situation there are same types + // but one has better visibility (either public or internal with friend) + // the less visible type is removed from the namespace cache + // + public static TypeSpec IsImportedTypeOverride (ModuleContainer module, TypeSpec ts, TypeSpec found) + { + var ts_accessible = (ts.Modifiers & Modifiers.PUBLIC) != 0 || ts.MemberDefinition.IsInternalAsPublic (module.DeclaringAssembly); + var found_accessible = (found.Modifiers & Modifiers.PUBLIC) != 0 || found.MemberDefinition.IsInternalAsPublic (module.DeclaringAssembly); + + if (ts_accessible && !found_accessible) + return ts; + + // found is better always better for accessible or inaccessible ts + if (!ts_accessible) + return found; + + return null; + } + + public void RemoveContainer (TypeContainer tc) + { + types.Remove (tc.Basename); + cached_types.Remove (tc.Basename); + } + + public void SetBuiltinType (BuiltinTypeSpec pts) + { + var found = types[pts.Name]; + cached_types.Remove (pts.Name); + if (found.Count == 1) { + types[pts.Name][0] = pts; + } else { + throw new NotImplementedException (); + } + } + + public void VerifyClsCompliance () + { + if (types == null || cls_checked) + return; + + cls_checked = true; + + // TODO: This is quite ugly way to check for CLS compliance at namespace level + + var locase_types = new Dictionary> (StringComparer.OrdinalIgnoreCase); + foreach (var tgroup in types.Values) { + foreach (var tm in tgroup) { + if ((tm.Modifiers & Modifiers.PUBLIC) == 0 || !tm.IsCLSCompliant ()) + continue; + + List found; + if (!locase_types.TryGetValue (tm.Name, out found)) { + found = new List (); + locase_types.Add (tm.Name, found); + } + + found.Add (tm); + } + } + + foreach (var locase in locase_types.Values) { + if (locase.Count < 2) + continue; + + bool all_same = true; + foreach (var notcompliant in locase) { + all_same = notcompliant.Name == locase[0].Name; + if (!all_same) + break; + } + + if (all_same) + continue; + + TypeContainer compiled = null; + foreach (var notcompliant in locase) { + if (!notcompliant.MemberDefinition.IsImported) { + if (compiled != null) + compiled.Compiler.Report.SymbolRelatedToPreviousError (compiled); + + compiled = notcompliant.MemberDefinition as TypeContainer; + } else { + compiled.Compiler.Report.SymbolRelatedToPreviousError (notcompliant); + } + } + + compiled.Compiler.Report.Warning (3005, 1, compiled.Location, + "Identifier `{0}' differing only in case is not CLS-compliant", compiled.GetSignatureForError ()); + } + } + } + + public class CompilationSourceFile : NamespaceContainer + { + readonly SourceFile file; + CompileUnitEntry comp_unit; + Dictionary include_files; + Dictionary conditionals; + + public CompilationSourceFile (ModuleContainer parent, SourceFile sourceFile) + : this (parent) + { + this.file = sourceFile; + } + + public CompilationSourceFile (ModuleContainer parent) + : base (parent) + { + } + + public CompileUnitEntry SymbolUnitEntry { + get { + return comp_unit; + } + } + + public IDictionary Conditionals { + get { + return conditionals ?? new Dictionary (); + } + } + + public string FileName { + get { + return file.Name; + } + } + + public SourceFile SourceFile { + get { + return file; + } + } + + public void AddIncludeFile (SourceFile file) + { + if (file == this.file) + return; + + if (include_files == null) + include_files = new Dictionary (); + + if (!include_files.ContainsKey (file.FullPathName)) + include_files.Add (file.FullPathName, file); + } + + public void AddDefine (string value) + { + if (conditionals == null) + conditionals = new Dictionary (2); + + conditionals[value] = true; + } + + public void AddUndefine (string value) + { + if (conditionals == null) + conditionals = new Dictionary (2); + + conditionals[value] = false; + } + + public override void PrepareEmit () + { + var sw = Module.DeclaringAssembly.SymbolWriter; + if (sw != null) { + CreateUnitSymbolInfo (sw); + } + + base.PrepareEmit (); + } + + // + // Creates symbol file index in debug symbol file + // + void CreateUnitSymbolInfo (MonoSymbolFile symwriter) + { + var si = file.CreateSymbolInfo (symwriter); + comp_unit = new CompileUnitEntry (symwriter, si); + + if (include_files != null) { + foreach (SourceFile include in include_files.Values) { + si = include.CreateSymbolInfo (symwriter); + comp_unit.AddFile (si); + } + } + } + + public bool IsConditionalDefined (string value) + { + if (conditionals != null) { + bool res; + if (conditionals.TryGetValue (value, out res)) + return res; + + // When conditional was undefined + if (conditionals.ContainsKey (value)) + return false; + } + + return Compiler.Settings.IsConditionalSymbolDefined (value); + } + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + } + + + // + // Namespace block as created by the parser + // + public class NamespaceContainer : TypeContainer, IMemberContext + { + static readonly Namespace[] empty_namespaces = new Namespace[0]; + + readonly Namespace ns; + + public new readonly NamespaceContainer Parent; + + List clauses; + + // Used by parsed to check for parser errors + public bool DeclarationFound; + + Namespace[] namespace_using_table; + TypeSpec[] types_using_table; + Dictionary aliases; + public readonly MemberName RealMemberName; + + public NamespaceContainer (MemberName name, NamespaceContainer parent) + : base (parent, name, null, MemberKind.Namespace) + { + this.RealMemberName = name; + this.Parent = parent; + this.ns = parent.NS.AddNamespace (name); + + containers = new List (); + } + + protected NamespaceContainer (ModuleContainer parent) + : base (parent, null, null, MemberKind.Namespace) + { + ns = parent.GlobalRootNamespace; + containers = new List (2); + } + + #region Properties + + public override AttributeTargets AttributeTargets { + get { + throw new NotSupportedException (); + } + } + + public override string DocCommentHeader { + get { + throw new NotSupportedException (); + } + } + + public Namespace NS { + get { + return ns; + } + } + + public List Usings { + get { + return clauses; + } + } + + public override string[] ValidAttributeTargets { + get { + throw new NotSupportedException (); + } + } + + #endregion + + public void AddUsing (UsingNamespace un) + { + if (DeclarationFound){ + Compiler.Report.Error (1529, un.Location, "A using clause must precede all other namespace elements except extern alias declarations"); + } + + if (clauses == null) + clauses = new List (); + + clauses.Add (un); + } + + public void AddUsing (UsingAliasNamespace un) + { + if (DeclarationFound){ + Compiler.Report.Error (1529, un.Location, "A using clause must precede all other namespace elements except extern alias declarations"); + } + + AddAlias (un); + } + + void AddAlias (UsingAliasNamespace un) + { + if (clauses == null) { + clauses = new List (); + } else { + foreach (var entry in clauses) { + var a = entry as UsingAliasNamespace; + if (a != null && a.Alias.Value == un.Alias.Value) { + Compiler.Report.SymbolRelatedToPreviousError (a.Location, ""); + Compiler.Report.Error (1537, un.Location, + "The using alias `{0}' appeared previously in this namespace", un.Alias.Value); + } + } + } + + clauses.Add (un); + } + + public override void AddPartial (TypeDefinition next_part) + { + var existing = ns.LookupType (this, next_part.MemberName.Name, next_part.MemberName.Arity, LookupMode.Probing, Location.Null); + var td = existing != null ? existing.MemberDefinition as TypeDefinition : null; + AddPartial (next_part, td); + } + + public override void AddTypeContainer (TypeContainer tc) + { + string name = tc.Basename; + + var mn = tc.MemberName; + while (mn.Left != null) { + mn = mn.Left; + name = mn.Name; + } + + var names_container = Parent == null ? Module : (TypeContainer) this; + + MemberCore mc; + if (names_container.DefinedNames.TryGetValue (name, out mc)) { + if (tc is NamespaceContainer && mc is NamespaceContainer) { + AddTypeContainerMember (tc); + return; + } + + Report.SymbolRelatedToPreviousError (mc); + if ((mc.ModFlags & Modifiers.PARTIAL) != 0 && (tc is ClassOrStruct || tc is Interface)) { + Error_MissingPartialModifier (tc); + } else { + Report.Error (101, tc.Location, "The namespace `{0}' already contains a definition for `{1}'", + GetSignatureForError (), mn.GetSignatureForError ()); + } + } else { + names_container.DefinedNames.Add (name, tc); + + var tdef = tc.PartialContainer; + if (tdef != null) { + // + // Same name conflict in different namespace containers + // + var conflict = ns.GetAllTypes (name); + if (conflict != null) { + foreach (var e in conflict) { + if (e.Arity == mn.Arity) { + mc = (MemberCore) e.MemberDefinition; + break; + } + } + } + + if (mc != null) { + Report.SymbolRelatedToPreviousError (mc); + Report.Error (101, tc.Location, "The namespace `{0}' already contains a definition for `{1}'", + GetSignatureForError (), mn.GetSignatureForError ()); + } else { + ns.AddType (Module, tdef.Definition); + } + } + } + + base.AddTypeContainer (tc); + } + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + throw new NotSupportedException (); + } + + public override void EmitContainer () + { + VerifyClsCompliance (); + + base.EmitContainer (); + } + + public ExtensionMethodCandidates LookupExtensionMethod (IMemberContext invocationContext, TypeSpec extensionType, string name, int arity, int position) + { + // + // Here we try to resume the search for extension method at the point + // where the last bunch of candidates was found. It's more tricky than + // it seems as we have to check both namespace containers and namespace + // in correct order. + // + // Consider: + // + // namespace A { + // using N1; + // namespace B.C.D { + // 0) { + mn = mn.Left; + container_ns = container_ns.Parent; + } + + while (mn != null) { + ++position; + + var methods = container_ns.LookupExtensionMethod (invocationContext, name, arity); + if (methods != null) { + return new ExtensionMethodCandidates (invocationContext, methods, container, position); + } + + mn = mn.Left; + container_ns = container_ns.Parent; + } + + position = 0; + container = container.Parent; + } while (container != null); + + return null; + } + + ExtensionMethodCandidates LookupExtensionMethodCandidates (IMemberContext invocationContext, string name, int arity, ref int position) + { + List candidates = null; + + if (position == 0) { + ++position; + + candidates = ns.LookupExtensionMethod (invocationContext, name, arity); + if (candidates != null) { + return new ExtensionMethodCandidates (invocationContext, candidates, this, position); + } + } + + if (position == 1) { + ++position; + + foreach (Namespace n in namespace_using_table) { + var a = n.LookupExtensionMethod (invocationContext, name, arity); + if (a == null) + continue; + + if (candidates == null) + candidates = a; + else + candidates.AddRange (a); + } + + if (candidates != null) + return new ExtensionMethodCandidates (invocationContext, candidates, this, position); + } + + // LAMESPEC: TODO no spec about priority over normal extension methods yet + if (types_using_table != null) { + foreach (var t in types_using_table) { + + var res = t.MemberCache.FindExtensionMethods (invocationContext, name, arity); + if (res == null) + continue; + + if (candidates == null) + candidates = res; + else + candidates.AddRange (res); + } + + if (candidates != null) + return new ExtensionMethodCandidates (invocationContext, candidates, this, position); + } + + return null; + } + + public override FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc) + { + // + // Only simple names (no dots) will be looked up with this function + // + FullNamedExpression resolved; + for (NamespaceContainer container = this; container != null; container = container.Parent) { + resolved = container.Lookup (name, arity, mode, loc); + if (resolved != null || container.MemberName == null) + return resolved; + + var container_ns = container.ns.Parent; + var mn = container.MemberName.Left; + while (mn != null) { + resolved = container_ns.LookupTypeOrNamespace (this, name, arity, mode, loc); + if (resolved != null) + return resolved; + + mn = mn.Left; + container_ns = container_ns.Parent; + } + } + + return null; + } + + public override void GetCompletionStartingWith (string prefix, List results) + { + if (Usings == null) + return; + + foreach (var un in Usings) { + if (un.Alias != null) + continue; + + var name = un.NamespaceExpression.Name; + if (name.StartsWith (prefix)) + results.Add (name); + } + + + IEnumerable all = Enumerable.Empty (); + + foreach (Namespace using_ns in namespace_using_table) { + if (prefix.StartsWith (using_ns.Name)) { + int ld = prefix.LastIndexOf ('.'); + if (ld != -1) { + string rest = prefix.Substring (ld + 1); + + all = all.Concat (using_ns.CompletionGetTypesStartingWith (rest)); + } + } + all = all.Concat (using_ns.CompletionGetTypesStartingWith (prefix)); + } + + results.AddRange (all); + + base.GetCompletionStartingWith (prefix, results); + } + + + // + // Looks-up a alias named @name in this and surrounding namespace declarations + // + public FullNamedExpression LookupExternAlias (string name) + { + if (aliases == null) + return null; + + UsingAliasNamespace uan; + if (aliases.TryGetValue (name, out uan) && uan is UsingExternAlias) + return uan.ResolvedExpression; + + return null; + } + + // + // Looks-up a alias named @name in this and surrounding namespace declarations + // + public override FullNamedExpression LookupNamespaceAlias (string name) + { + for (NamespaceContainer n = this; n != null; n = n.Parent) { + if (n.aliases == null) + continue; + + UsingAliasNamespace uan; + if (n.aliases.TryGetValue (name, out uan)) + return uan.ResolvedExpression; + } + + return null; + } + + FullNamedExpression Lookup (string name, int arity, LookupMode mode, Location loc) + { + // + // Check whether it's in the namespace. + // + FullNamedExpression fne = ns.LookupTypeOrNamespace (this, name, arity, mode, loc); + + // + // Check aliases. + // + if (aliases != null && arity == 0) { + UsingAliasNamespace uan; + if (aliases.TryGetValue (name, out uan)) { + if (fne != null && mode != LookupMode.Probing) { + // TODO: Namespace has broken location + //Report.SymbolRelatedToPreviousError (fne.Location, null); + Compiler.Report.SymbolRelatedToPreviousError (uan.Location, null); + Compiler.Report.Error (576, loc, + "Namespace `{0}' contains a definition with same name as alias `{1}'", + GetSignatureForError (), name); + } + + return uan.ResolvedExpression; + } + } + + if (fne != null) + return fne; + + // + // Lookup can be called before the namespace is defined from different namespace using alias clause + // + if (namespace_using_table == null) { + DoDefineNamespace (); + } + + // + // Check using entries. + // + FullNamedExpression match = null; + foreach (Namespace using_ns in namespace_using_table) { + // + // A using directive imports only types contained in the namespace, it + // does not import any nested namespaces + // + var t = using_ns.LookupType (this, name, arity, mode, loc); + if (t == null) + continue; + + fne = new TypeExpression (t, loc); + if (match == null) { + match = fne; + continue; + } + + // Prefer types over namespaces + var texpr_fne = fne as TypeExpr; + var texpr_match = match as TypeExpr; + if (texpr_fne != null && texpr_match == null) { + match = fne; + continue; + } else if (texpr_fne == null) { + continue; + } + + // It can be top level accessibility only + var better = Namespace.IsImportedTypeOverride (Module, texpr_match.Type, texpr_fne.Type); + if (better == null) { + if (mode == LookupMode.Normal) { + Compiler.Report.SymbolRelatedToPreviousError (texpr_match.Type); + Compiler.Report.SymbolRelatedToPreviousError (texpr_fne.Type); + Compiler.Report.Error (104, loc, "`{0}' is an ambiguous reference between `{1}' and `{2}'", + name, texpr_match.GetSignatureForError (), texpr_fne.GetSignatureForError ()); + } + + return match; + } + + if (better == texpr_fne.Type) + match = texpr_fne; + } + + return match; + } + + public static MethodGroupExpr LookupStaticUsings (IMemberContext mc, string name, int arity, Location loc) + { + for (var m = mc.CurrentMemberDefinition; m != null; m = m.Parent) { + + var nc = m as NamespaceContainer; + if (nc == null) + continue; + + List candidates = null; + if (nc.types_using_table != null) { + foreach (var using_type in nc.types_using_table) { + var members = MemberCache.FindMembers (using_type, name, true); + if (members != null) { + foreach (var member in members) { + if ((member.Modifiers & Modifiers.METHOD_EXTENSION) != 0) + continue; + + if (arity > 0 && member.Arity != arity) + continue; + + if (candidates == null) + candidates = new List (); + + candidates.Add (member); + } + } + } + } + + if (candidates != null) + return new MethodGroupExpr (candidates, null, loc); + } + + return null; + } + + protected override void DefineNamespace () + { + if (namespace_using_table == null) + DoDefineNamespace (); + + base.DefineNamespace (); + } + + void DoDefineNamespace () + { + namespace_using_table = empty_namespaces; + + if (clauses != null) { + List namespaces = null; + List types = null; + + bool post_process_using_aliases = false; + + for (int i = 0; i < clauses.Count; ++i) { + var entry = clauses[i]; + + if (entry.Alias != null) { + if (aliases == null) + aliases = new Dictionary (); + + // + // Aliases are not available when resolving using section + // except extern aliases + // + if (entry is UsingExternAlias) { + entry.Define (this); + if (entry.ResolvedExpression != null) + aliases.Add (entry.Alias.Value, (UsingExternAlias) entry); + + clauses.RemoveAt (i--); + } else { + post_process_using_aliases = true; + } + + continue; + } + + entry.Define (this); + + // + // It's needed for repl only, when using clause cannot be resolved don't hold it in + // global list which is resolved for each evaluation + // + if (entry.ResolvedExpression == null) { + clauses.RemoveAt (i--); + continue; + } + + var using_ns = entry.ResolvedExpression as NamespaceExpression; + if (using_ns == null) { + + var type = ((TypeExpr)entry.ResolvedExpression).Type; + + if (types == null) + types = new List (); + + if (types.Contains (type)) { + Warning_DuplicateEntry (entry); + } else { + types.Add (type); + } + } else { + if (namespaces == null) + namespaces = new List (); + + if (namespaces.Contains (using_ns.Namespace)) { + // Ensure we don't report the warning multiple times in repl + clauses.RemoveAt (i--); + + Warning_DuplicateEntry (entry); + } else { + namespaces.Add (using_ns.Namespace); + } + } + } + + namespace_using_table = namespaces == null ? new Namespace [0] : namespaces.ToArray (); + if (types != null) + types_using_table = types.ToArray (); + + if (post_process_using_aliases) { + for (int i = 0; i < clauses.Count; ++i) { + var entry = clauses[i]; + if (entry.Alias != null) { + entry.Define (this); + if (entry.ResolvedExpression != null) { + aliases.Add (entry.Alias.Value, (UsingAliasNamespace) entry); + } + + clauses.RemoveAt (i--); + } + } + } + } + } + + public void EnableRedefinition () + { + is_defined = false; + namespace_using_table = null; + } + + internal override void GenerateDocComment (DocumentationBuilder builder) + { + if (containers != null) { + foreach (var tc in containers) + tc.GenerateDocComment (builder); + } + } + + public override string GetSignatureForError () + { + return MemberName == null ? "global::" : base.GetSignatureForError (); + } + + public override void RemoveContainer (TypeContainer cont) + { + base.RemoveContainer (cont); + NS.RemoveContainer (cont); + } + + protected override bool VerifyClsCompliance () + { + if (Module.IsClsComplianceRequired ()) { + if (MemberName != null && MemberName.Name[0] == '_') { + Warning_IdentifierNotCompliant (); + } + + ns.VerifyClsCompliance (); + return true; + } + + return false; + } + + void Warning_DuplicateEntry (UsingNamespace entry) + { + Compiler.Report.Warning (105, 3, entry.Location, + "The using directive for `{0}' appeared previously in this namespace", + entry.ResolvedExpression.GetSignatureForError ()); + } + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + } + + public class UsingNamespace + { + readonly ATypeNameExpression expr; + readonly Location loc; + protected FullNamedExpression resolved; + + public UsingNamespace (ATypeNameExpression expr, Location loc) + { + this.expr = expr; + this.loc = loc; + } + + #region Properties + + public virtual SimpleMemberName Alias { + get { + return null; + } + } + + public Location Location { + get { + return loc; + } + } + + public ATypeNameExpression NamespaceExpression { + get { + return expr; + } + } + + public FullNamedExpression ResolvedExpression { + get { + return resolved; + } + } + + #endregion + + public string GetSignatureForError () + { + return expr.GetSignatureForError (); + } + + public virtual void Define (NamespaceContainer ctx) + { + resolved = expr.ResolveAsTypeOrNamespace (ctx, false); + var ns = resolved as NamespaceExpression; + if (ns != null) + return; + + if (resolved != null) { + var compiler = ctx.Module.Compiler; + var type = resolved.Type; + if (compiler.Settings.Version >= LanguageVersion.V_6) { + if (!type.IsClass || !type.IsStatic) { + compiler.Report.SymbolRelatedToPreviousError (type); + compiler.Report.Error (7007, Location, + "`{0}' is not a static class. A using namespace directive can only be applied to static classes or namespace", + GetSignatureForError ()); + } + + return; + } + + compiler.Report.SymbolRelatedToPreviousError (type); + compiler.Report.Error (138, Location, + "`{0}' is a type not a namespace. A using namespace directive can only be applied to namespaces", + GetSignatureForError ()); + } + } + + public virtual void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + + public override string ToString() + { + return resolved.ToString(); + } + } + + public class UsingExternAlias : UsingAliasNamespace + { + public UsingExternAlias (SimpleMemberName alias, Location loc) + : base (alias, null, loc) + { + } + + public override void Define (NamespaceContainer ctx) + { + var ns = ctx.Module.GetRootNamespace (Alias.Value); + if (ns == null) { + ctx.Module.Compiler.Report.Error (430, Location, + "The extern alias `{0}' was not specified in -reference option", + Alias.Value); + return; + } + + resolved = new NamespaceExpression (ns, Location); + } + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + } + + public class UsingAliasNamespace : UsingNamespace + { + readonly SimpleMemberName alias; + + public struct AliasContext : IMemberContext + { + readonly NamespaceContainer ns; + + public AliasContext (NamespaceContainer ns) + { + this.ns = ns; + } + + public TypeSpec CurrentType { + get { + return null; + } + } + + public TypeParameters CurrentTypeParameters { + get { + return null; + } + } + + public MemberCore CurrentMemberDefinition { + get { + return null; + } + } + + public bool IsObsolete { + get { + return false; + } + } + + public bool IsUnsafe { + get { + throw new NotImplementedException (); + } + } + + public bool IsStatic { + get { + throw new NotImplementedException (); + } + } + + public ModuleContainer Module { + get { + return ns.Module; + } + } + + public string GetSignatureForError () + { + throw new NotImplementedException (); + } + + public ExtensionMethodCandidates LookupExtensionMethod (TypeSpec extensionType, string name, int arity) + { + return null; + } + + public FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc) + { + var fne = ns.NS.LookupTypeOrNamespace (ns, name, arity, mode, loc); + if (fne != null) + return fne; + + // + // Only extern aliases are allowed in this context + // + fne = ns.LookupExternAlias (name); + if (fne != null || ns.MemberName == null) + return fne; + + var container_ns = ns.NS.Parent; + var mn = ns.MemberName.Left; + while (mn != null) { + fne = container_ns.LookupTypeOrNamespace (this, name, arity, mode, loc); + if (fne != null) + return fne; + + mn = mn.Left; + container_ns = container_ns.Parent; + } + + if (ns.Parent != null) + return ns.Parent.LookupNamespaceOrType (name, arity, mode, loc); + + return null; + } + + public FullNamedExpression LookupNamespaceAlias (string name) + { + return ns.LookupNamespaceAlias (name); + } + } + + public UsingAliasNamespace (SimpleMemberName alias, ATypeNameExpression expr, Location loc) + : base (expr, loc) + { + this.alias = alias; + } + + public override SimpleMemberName Alias { + get { + return alias; + } + } + + public override void Define (NamespaceContainer ctx) + { + // + // The namespace-or-type-name of a using-alias-directive is resolved as if + // the immediately containing compilation unit or namespace body had no + // using-directives. A using-alias-directive may however be affected + // by extern-alias-directives in the immediately containing compilation + // unit or namespace body + // + // We achieve that by introducing alias-context which redirect any local + // namespace or type resolve calls to parent namespace + // + resolved = NamespaceExpression.ResolveAsTypeOrNamespace (new AliasContext (ctx), false); + } + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/nullable.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/nullable.cs new file mode 100644 index 000000000..e857e3654 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/nullable.cs @@ -0,0 +1,1380 @@ +// +// nullable.cs: Nullable types support +// +// Authors: Martin Baulig (martin@ximian.com) +// Miguel de Icaza (miguel@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com) +// Copyright 2004-2008 Novell, Inc +// Copyright 2011 Xamarin Inc +// + +using System; +using SLE = System.Linq.Expressions; + +#if STATIC +using IKVM.Reflection.Emit; +#else +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp.Nullable +{ + public class NullableType : TypeExpr + { + readonly TypeSpec underlying; + + public NullableType (TypeSpec type, Location loc) + { + this.underlying = type; + this.loc = loc; + } + + public override TypeSpec ResolveAsType (IMemberContext ec, bool allowUnboundTypeArguments = false) + { + eclass = ExprClass.Type; + + var otype = ec.Module.PredefinedTypes.Nullable.Resolve (); + if (otype == null) + return null; + + TypeArguments args = new TypeArguments (new TypeExpression (underlying, loc)); + GenericTypeExpr ctype = new GenericTypeExpr (otype, args, loc); + + type = ctype.ResolveAsType (ec); + return type; + } + } + + static class NullableInfo + { + public static MethodSpec GetConstructor (TypeSpec nullableType) + { + return (MethodSpec) MemberCache.FindMember (nullableType, + MemberFilter.Constructor (ParametersCompiled.CreateFullyResolved (GetUnderlyingType (nullableType))), BindingRestriction.DeclaredOnly); + } + + public static MethodSpec GetHasValue (TypeSpec nullableType) + { + return (MethodSpec) MemberCache.FindMember (nullableType, + MemberFilter.Method ("get_HasValue", 0, ParametersCompiled.EmptyReadOnlyParameters, null), BindingRestriction.None); + } + + public static MethodSpec GetGetValueOrDefault (TypeSpec nullableType) + { + return (MethodSpec) MemberCache.FindMember (nullableType, + MemberFilter.Method ("GetValueOrDefault", 0, ParametersCompiled.EmptyReadOnlyParameters, null), BindingRestriction.None); + } + + // + // Don't use unless really required for correctness, see Unwrap::Emit + // + public static MethodSpec GetValue (TypeSpec nullableType) + { + return (MethodSpec) MemberCache.FindMember (nullableType, + MemberFilter.Method ("get_Value", 0, ParametersCompiled.EmptyReadOnlyParameters, null), BindingRestriction.None); + } + + public static TypeSpec GetUnderlyingType (TypeSpec nullableType) + { + return ((InflatedTypeSpec) nullableType).TypeArguments[0]; + } + + public static TypeSpec GetEnumUnderlyingType (ModuleContainer module, TypeSpec nullableEnum) + { + return MakeType (module, EnumSpec.GetUnderlyingType (GetUnderlyingType (nullableEnum))); + } + + public static TypeSpec MakeType (ModuleContainer module, TypeSpec underlyingType) + { + return module.PredefinedTypes.Nullable.TypeSpec.MakeGenericType (module, + new[] { underlyingType }); + + } + } + + public class Unwrap : Expression, IMemoryLocation + { + Expression expr; + + LocalTemporary temp; + Expression temp_field; + readonly bool useDefaultValue; + + public Unwrap (Expression expr, bool useDefaultValue = true) + { + this.expr = expr; + this.loc = expr.Location; + this.useDefaultValue = useDefaultValue; + + type = NullableInfo.GetUnderlyingType (expr.Type); + eclass = expr.eclass; + } + + public override bool ContainsEmitWithAwait () + { + return expr.ContainsEmitWithAwait (); + } + + // TODO: REMOVE + public static Expression Create (Expression expr) + { + // + // Avoid unwraping and wraping of same type + // + Wrap wrap = expr as Wrap; + if (wrap != null) + return wrap.Child; + + return Create (expr, false); + } + + public static Expression CreateUnwrapped (Expression expr) + { + // + // Avoid unwraping and wraping of same type + // + Wrap wrap = expr as Wrap; + if (wrap != null) + return wrap.Child; + + return Create (expr, true); + } + + public static Unwrap Create (Expression expr, bool useDefaultValue) + { + return new Unwrap (expr, useDefaultValue); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + return expr.CreateExpressionTree (ec); + } + + protected override Expression DoResolve (ResolveContext ec) + { + return this; + } + + public override Expression DoResolveLValue (ResolveContext ec, Expression right_side) + { + expr = expr.DoResolveLValue (ec, right_side); + return this; + } + + public override void Emit (EmitContext ec) + { + Store (ec); + + var call = new CallEmitter (); + call.InstanceExpression = this; + + // + // Using GetGetValueOrDefault is prefered because JIT can possibly + // inline it whereas Value property contains a throw which is very + // unlikely to be inlined + // + if (useDefaultValue) + call.EmitPredefined (ec, NullableInfo.GetGetValueOrDefault (expr.Type), null); + else + call.EmitPredefined (ec, NullableInfo.GetValue (expr.Type), null); + } + + public void EmitCheck (EmitContext ec) + { + Store (ec); + + var call = new CallEmitter (); + call.InstanceExpression = this; + + call.EmitPredefined (ec, NullableInfo.GetHasValue (expr.Type), null); + } + + public override void EmitSideEffect (EmitContext ec) + { + expr.EmitSideEffect (ec); + } + + public override Expression EmitToField (EmitContext ec) + { + if (temp_field == null) + temp_field = this.expr.EmitToField (ec); + + return this; + } + + public override bool Equals (object obj) + { + Unwrap uw = obj as Unwrap; + return uw != null && expr.Equals (uw.expr); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + expr.FlowAnalysis (fc); + } + + public Expression Original { + get { + return expr; + } + } + + public override int GetHashCode () + { + return expr.GetHashCode (); + } + + public override bool IsNull { + get { + return expr.IsNull; + } + } + + public void Store (EmitContext ec) + { + if (temp != null || temp_field != null) + return; + + if (expr is VariableReference) + return; + + expr.Emit (ec); + LocalVariable.Store (ec); + } + + public void Load (EmitContext ec) + { + if (temp_field != null) + temp_field.Emit (ec); + else if (expr is VariableReference) + expr.Emit (ec); + else + LocalVariable.Emit (ec); + } + + public override SLE.Expression MakeExpression (BuilderContext ctx) + { + return expr.MakeExpression (ctx); + } + + public void AddressOf (EmitContext ec, AddressOp mode) + { + IMemoryLocation ml; + + if (temp_field != null) { + ml = temp_field as IMemoryLocation; + if (ml == null) { + var lt = new LocalTemporary (temp_field.Type); + temp_field.Emit (ec); + lt.Store (ec); + ml = lt; + } + } else { + ml = expr as VariableReference; + } + + if (ml != null) + ml.AddressOf (ec, mode); + else + LocalVariable.AddressOf (ec, mode); + } + + // + // Keeps result of non-variable expression + // + LocalTemporary LocalVariable { + get { + if (temp == null && temp_field == null) + temp = new LocalTemporary (expr.Type); + return temp; + } + } + } + + // + // Calls get_Value method on nullable expression + // + public class UnwrapCall : CompositeExpression + { + public UnwrapCall (Expression expr) + : base (expr) + { + } + + protected override Expression DoResolve (ResolveContext rc) + { + base.DoResolve (rc); + + if (type != null) + type = NullableInfo.GetUnderlyingType (type); + + return this; + } + + public override void Emit (EmitContext ec) + { + var call = new CallEmitter (); + call.InstanceExpression = Child; + call.EmitPredefined (ec, NullableInfo.GetValue (Child.Type), null); + } + } + + public class Wrap : TypeCast + { + private Wrap (Expression expr, TypeSpec type) + : base (expr, type) + { + eclass = ExprClass.Value; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + TypeCast child_cast = child as TypeCast; + if (child_cast != null) { + child.Type = type; + return child_cast.CreateExpressionTree (ec); + } + + var user_cast = child as UserCast; + if (user_cast != null) { + child.Type = type; + return user_cast.CreateExpressionTree (ec); + } + + return base.CreateExpressionTree (ec); + } + + public static Expression Create (Expression expr, TypeSpec type) + { + // + // Avoid unwraping and wraping of the same type + // + Unwrap unwrap = expr as Unwrap; + if (unwrap != null && expr.Type == NullableInfo.GetUnderlyingType (type)) + return unwrap.Original; + + return new Wrap (expr, type); + } + + public override void Emit (EmitContext ec) + { + child.Emit (ec); + ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type)); + } + } + + // + // Represents null literal lifted to nullable type + // + public class LiftedNull : NullConstant, IMemoryLocation + { + private LiftedNull (TypeSpec nullable_type, Location loc) + : base (nullable_type, loc) + { + eclass = ExprClass.Value; + } + + public static Constant Create (TypeSpec nullable, Location loc) + { + return new LiftedNull (nullable, loc); + } + + public static Constant CreateFromExpression (ResolveContext rc, Expression e) + { + if (!rc.HasSet (ResolveContext.Options.ExpressionTreeConversion)) { + rc.Report.Warning (458, 2, e.Location, "The result of the expression is always `null' of type `{0}'", + e.Type.GetSignatureForError ()); + } + + return ReducedExpression.Create (Create (e.Type, e.Location), e); + } + + public override void Emit (EmitContext ec) + { + // TODO: generate less temporary variables + LocalTemporary value_target = new LocalTemporary (type); + + value_target.AddressOf (ec, AddressOp.Store); + ec.Emit (OpCodes.Initobj, type); + value_target.Emit (ec); + value_target.Release (ec); + } + + public void AddressOf (EmitContext ec, AddressOp Mode) + { + LocalTemporary value_target = new LocalTemporary (type); + + value_target.AddressOf (ec, AddressOp.Store); + ec.Emit (OpCodes.Initobj, type); + value_target.AddressOf (ec, Mode); + } + } + + // + // Generic lifting expression, supports all S/S? -> T/T? cases + // + public class LiftedConversion : Expression, IMemoryLocation + { + Expression expr, null_value; + Unwrap unwrap; + + public LiftedConversion (Expression expr, Unwrap unwrap, TypeSpec type) + { + this.expr = expr; + this.unwrap = unwrap; + this.loc = expr.Location; + this.type = type; + } + + public LiftedConversion (Expression expr, Expression unwrap, TypeSpec type) + : this (expr, unwrap as Unwrap, type) + { + } + + public override bool IsNull { + get { + return expr.IsNull; + } + } + + public override bool ContainsEmitWithAwait () + { + return unwrap.ContainsEmitWithAwait (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + return expr.CreateExpressionTree (ec); + } + + protected override Expression DoResolve (ResolveContext ec) + { + // + // It's null when lifting non-nullable type + // + if (unwrap == null) { + // S -> T? is wrap only + if (type.IsNullableType) + return Wrap.Create (expr, type); + + // S -> T can be simplified + return expr; + } + + // Wrap target for T? + if (type.IsNullableType) { + if (!expr.Type.IsNullableType) { + expr = Wrap.Create (expr, type); + if (expr == null) + return null; + } + + null_value = LiftedNull.Create (type, loc); + } else if (TypeSpec.IsValueType (type)) { + null_value = LiftedNull.Create (type, loc); + } else { + null_value = new NullConstant (type, loc); + } + + eclass = ExprClass.Value; + return this; + } + + public override void Emit (EmitContext ec) + { + Label is_null_label = ec.DefineLabel (); + Label end_label = ec.DefineLabel (); + + unwrap.EmitCheck (ec); + ec.Emit (OpCodes.Brfalse, is_null_label); + + expr.Emit (ec); + + ec.Emit (OpCodes.Br, end_label); + ec.MarkLabel (is_null_label); + + null_value.Emit (ec); + + ec.MarkLabel (end_label); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + expr.FlowAnalysis (fc); + } + + public void AddressOf (EmitContext ec, AddressOp mode) + { + unwrap.AddressOf (ec, mode); + } + } + + public class LiftedUnaryOperator : Unary, IMemoryLocation + { + Unwrap unwrap; + Expression user_operator; + + public LiftedUnaryOperator (Unary.Operator op, Expression expr, Location loc) + : base (op, expr, loc) + { + } + + public void AddressOf (EmitContext ec, AddressOp mode) + { + unwrap.AddressOf (ec, mode); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + if (user_operator != null) + return user_operator.CreateExpressionTree (ec); + + if (Oper == Operator.UnaryPlus) + return Expr.CreateExpressionTree (ec); + + return base.CreateExpressionTree (ec); + } + + protected override Expression DoResolve (ResolveContext ec) + { + unwrap = Unwrap.Create (Expr, false); + if (unwrap == null) + return null; + + Expression res = base.ResolveOperator (ec, unwrap); + if (res == null) { + Error_OperatorCannotBeApplied (ec, loc, OperName (Oper), Expr.Type); + return null; + } + + if (res != this) { + if (user_operator == null) + return res; + } else { + res = Expr = LiftExpression (ec, Expr); + } + + if (res == null) + return null; + + eclass = ExprClass.Value; + type = res.Type; + return this; + } + + public override void Emit (EmitContext ec) + { + Label is_null_label = ec.DefineLabel (); + Label end_label = ec.DefineLabel (); + + unwrap.EmitCheck (ec); + ec.Emit (OpCodes.Brfalse, is_null_label); + + if (user_operator != null) { + user_operator.Emit (ec); + } else { + EmitOperator (ec, NullableInfo.GetUnderlyingType (type)); + } + + ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type)); + ec.Emit (OpCodes.Br_S, end_label); + + ec.MarkLabel (is_null_label); + LiftedNull.Create (type, loc).Emit (ec); + + ec.MarkLabel (end_label); + } + + static Expression LiftExpression (ResolveContext ec, Expression expr) + { + var lifted_type = new NullableType (expr.Type, expr.Location); + if (lifted_type.ResolveAsType (ec) == null) + return null; + + expr.Type = lifted_type.Type; + return expr; + } + + protected override Expression ResolveEnumOperator (ResolveContext ec, Expression expr, TypeSpec[] predefined) + { + expr = base.ResolveEnumOperator (ec, expr, predefined); + if (expr == null) + return null; + + Expr = LiftExpression (ec, Expr); + return LiftExpression (ec, expr); + } + + protected override Expression ResolveUserOperator (ResolveContext ec, Expression expr) + { + expr = base.ResolveUserOperator (ec, expr); + if (expr == null) + return null; + + // + // When a user operator is of non-nullable type + // + if (Expr is Unwrap) { + user_operator = LiftExpression (ec, expr); + return user_operator; + } + + return expr; + } + } + + // + // Lifted version of binary operators + // + class LiftedBinaryOperator : Expression + { + public LiftedBinaryOperator (Binary b) + { + this.Binary = b; + this.loc = b.Location; + } + + public Binary Binary { get; private set; } + + public Expression Left { get; set; } + + public Expression Right { get; set; } + + public Unwrap UnwrapLeft { get; set; } + + public Unwrap UnwrapRight { get; set; } + + public MethodSpec UserOperator { get; set; } + + bool IsBitwiseBoolean { + get { + return (Binary.Oper == Binary.Operator.BitwiseAnd || Binary.Oper == Binary.Operator.BitwiseOr) && + ((UnwrapLeft != null && UnwrapLeft.Type.BuiltinType == BuiltinTypeSpec.Type.Bool) || + (UnwrapRight != null && UnwrapRight.Type.BuiltinType == BuiltinTypeSpec.Type.Bool)); + } + } + + public override bool ContainsEmitWithAwait () + { + return Left.ContainsEmitWithAwait () || Right.ContainsEmitWithAwait (); + } + + public override Expression CreateExpressionTree (ResolveContext rc) + { + if (UserOperator != null) { + Arguments args = new Arguments (2); + args.Add (new Argument (Binary.Left)); + args.Add (new Argument (Binary.Right)); + + var method = new UserOperatorCall (UserOperator, args, Binary.CreateExpressionTree, loc); + return method.CreateExpressionTree (rc); + } + + return Binary.CreateExpressionTree (rc); + } + + protected override Expression DoResolve (ResolveContext rc) + { + if (rc.IsRuntimeBinder) { + if (UnwrapLeft == null && !Left.Type.IsNullableType) + Left = LiftOperand (rc, Left); + + if (UnwrapRight == null && !Right.Type.IsNullableType) + Right = LiftOperand (rc, Right); + } else { + if (UnwrapLeft == null && Left != null && Left.Type.IsNullableType) { + Left = Unwrap.CreateUnwrapped (Left); + UnwrapLeft = Left as Unwrap; + } + + if (UnwrapRight == null && Right != null && Right.Type.IsNullableType) { + Right = Unwrap.CreateUnwrapped (Right); + UnwrapRight = Right as Unwrap; + } + } + + type = Binary.Type; + eclass = Binary.eclass; + + return this; + } + + Expression LiftOperand (ResolveContext rc, Expression expr) + { + TypeSpec type; + if (expr.IsNull) { + type = Left.IsNull ? Right.Type : Left.Type; + } else { + type = expr.Type; + } + + if (!type.IsNullableType) + type = NullableInfo.MakeType (rc.Module, type); + + return Wrap.Create (expr, type); + } + + public override void Emit (EmitContext ec) + { + if (IsBitwiseBoolean && UserOperator == null) { + EmitBitwiseBoolean (ec); + return; + } + + if ((Binary.Oper & Binary.Operator.EqualityMask) != 0) { + EmitEquality (ec); + return; + } + + Label is_null_label = ec.DefineLabel (); + Label end_label = ec.DefineLabel (); + + if (ec.HasSet (BuilderContext.Options.AsyncBody) && Right.ContainsEmitWithAwait ()) { + Left = Left.EmitToField (ec); + Right = Right.EmitToField (ec); + } + + if (UnwrapLeft != null) { + UnwrapLeft.EmitCheck (ec); + } + + // + // Don't emit HasValue check when left and right expressions are same + // + if (UnwrapRight != null && !Binary.Left.Equals (Binary.Right)) { + UnwrapRight.EmitCheck (ec); + if (UnwrapLeft != null) { + ec.Emit (OpCodes.And); + } + } + + ec.Emit (OpCodes.Brfalse, is_null_label); + + if (UserOperator != null) { + var args = new Arguments (2); + args.Add (new Argument (Left)); + args.Add (new Argument (Right)); + + var call = new CallEmitter (); + call.EmitPredefined (ec, UserOperator, args); + } else { + Binary.EmitOperator (ec, Left, Right); + } + + // + // Wrap the result when the operator return type is nullable type + // + if (type.IsNullableType) + ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type)); + + ec.Emit (OpCodes.Br_S, end_label); + ec.MarkLabel (is_null_label); + + if ((Binary.Oper & Binary.Operator.ComparisonMask) != 0) { + ec.EmitInt (0); + } else { + LiftedNull.Create (type, loc).Emit (ec); + } + + ec.MarkLabel (end_label); + } + + void EmitBitwiseBoolean (EmitContext ec) + { + Label load_left = ec.DefineLabel (); + Label load_right = ec.DefineLabel (); + Label end_label = ec.DefineLabel (); + Label is_null_label = ec.DefineLabel (); + + bool or = Binary.Oper == Binary.Operator.BitwiseOr; + + // + // Both operands are bool? types + // + if (UnwrapLeft != null && UnwrapRight != null) { + if (ec.HasSet (BuilderContext.Options.AsyncBody) && Binary.Right.ContainsEmitWithAwait ()) { + Left = Left.EmitToField (ec); + Right = Right.EmitToField (ec); + } else { + UnwrapLeft.Store (ec); + UnwrapRight.Store (ec); + } + + Left.Emit (ec); + ec.Emit (OpCodes.Brtrue_S, load_right); + + Right.Emit (ec); + ec.Emit (OpCodes.Brtrue_S, load_left); + + UnwrapLeft.EmitCheck (ec); + ec.Emit (OpCodes.Brfalse_S, load_right); + + // load left + ec.MarkLabel (load_left); + if (or) + UnwrapRight.Load (ec); + else + UnwrapLeft.Load (ec); + + ec.Emit (OpCodes.Br_S, end_label); + + // load right + ec.MarkLabel (load_right); + if (or) + UnwrapLeft.Load (ec); + else + UnwrapRight.Load (ec); + + ec.MarkLabel (end_label); + return; + } + + // + // Faster version when one operand is bool + // + if (UnwrapLeft == null) { + // + // (bool, bool?) + // + // Optimizes remaining (false & bool?), (true | bool?) which are not easy to handle + // in binary expression reduction + // + var c = Left as BoolConstant; + if (c != null) { + // Keep evaluation order + UnwrapRight.Store (ec); + + ec.EmitInt (or ? 1 : 0); + ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type)); + } else if (Left.IsNull) { + UnwrapRight.Emit (ec); + ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, is_null_label); + + UnwrapRight.Load (ec); + ec.Emit (OpCodes.Br_S, end_label); + + ec.MarkLabel (is_null_label); + LiftedNull.Create (type, loc).Emit (ec); + } else { + Left.Emit (ec); + ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, load_right); + + ec.EmitInt (or ? 1 : 0); + ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type)); + + ec.Emit (OpCodes.Br_S, end_label); + + ec.MarkLabel (load_right); + UnwrapRight.Original.Emit (ec); + } + } else { + // + // (bool?, bool) + // + // Keep left-right evaluation order + UnwrapLeft.Store (ec); + + // + // Optimizes remaining (bool? & false), (bool? | true) which are not easy to handle + // in binary expression reduction + // + var c = Right as BoolConstant; + if (c != null) { + ec.EmitInt (or ? 1 : 0); + ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type)); + } else if (Right.IsNull) { + UnwrapLeft.Emit (ec); + ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, is_null_label); + + UnwrapLeft.Load (ec); + ec.Emit (OpCodes.Br_S, end_label); + + ec.MarkLabel (is_null_label); + LiftedNull.Create (type, loc).Emit (ec); + } else { + Right.Emit (ec); + ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, load_right); + + ec.EmitInt (or ? 1 : 0); + ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type)); + + ec.Emit (OpCodes.Br_S, end_label); + + ec.MarkLabel (load_right); + + UnwrapLeft.Load (ec); + } + } + + ec.MarkLabel (end_label); + } + + // + // Emits optimized equality or inequality operator when possible + // + void EmitEquality (EmitContext ec) + { + // + // Either left or right is null + // + if (UnwrapLeft != null && Binary.Right.IsNull) { // TODO: Optimize for EmitBranchable + // + // left.HasValue == false + // + UnwrapLeft.EmitCheck (ec); + if (Binary.Oper == Binary.Operator.Equality) { + ec.EmitInt (0); + ec.Emit (OpCodes.Ceq); + } + return; + } + + if (UnwrapRight != null && Binary.Left.IsNull) { + // + // right.HasValue == false + // + UnwrapRight.EmitCheck (ec); + if (Binary.Oper == Binary.Operator.Equality) { + ec.EmitInt (0); + ec.Emit (OpCodes.Ceq); + } + return; + } + + Label dissimilar_label = ec.DefineLabel (); + Label end_label = ec.DefineLabel (); + + if (UserOperator != null) { + var left = Left; + + if (UnwrapLeft != null) { + UnwrapLeft.EmitCheck (ec); + } else { + // Keep evaluation order same + if (!(Left is VariableReference)) { + Left.Emit (ec); + var lt = new LocalTemporary (Left.Type); + lt.Store (ec); + left = lt; + } + } + + if (UnwrapRight != null) { + UnwrapRight.EmitCheck (ec); + + if (UnwrapLeft != null) { + ec.Emit (OpCodes.Bne_Un, dissimilar_label); + + Label compare_label = ec.DefineLabel (); + UnwrapLeft.EmitCheck (ec); + ec.Emit (OpCodes.Brtrue, compare_label); + + if (Binary.Oper == Binary.Operator.Equality) + ec.EmitInt (1); + else + ec.EmitInt (0); + + ec.Emit (OpCodes.Br, end_label); + + ec.MarkLabel (compare_label); + } else { + ec.Emit (OpCodes.Brfalse, dissimilar_label); + } + } else { + ec.Emit (OpCodes.Brfalse, dissimilar_label); + } + + var args = new Arguments (2); + args.Add (new Argument (left)); + args.Add (new Argument (Right)); + + var call = new CallEmitter (); + call.EmitPredefined (ec, UserOperator, args); + } else { + if (ec.HasSet (BuilderContext.Options.AsyncBody) && Binary.Right.ContainsEmitWithAwait ()) { + Left = Left.EmitToField (ec); + Right = Right.EmitToField (ec); + } + + // + // Emit underlying value comparison first. + // + // For this code: int? a = 1; bool b = a == 1; + // + // We emit something similar to this. Expressions with side effects have local + // variable created by Unwrap expression + // + // left.GetValueOrDefault () + // right + // bne.un.s dissimilar_label + // left.HasValue + // br.s end_label + // dissimilar_label: + // ldc.i4.0 + // end_label: + // + + Left.Emit (ec); + Right.Emit (ec); + + ec.Emit (OpCodes.Bne_Un_S, dissimilar_label); + + // + // Check both left and right expressions for Unwrap call in which + // case we need to run get_HasValue() check because the type is + // nullable and could have null value + // + if (UnwrapLeft != null) + UnwrapLeft.EmitCheck (ec); + + if (UnwrapRight != null) + UnwrapRight.EmitCheck (ec); + + if (UnwrapLeft != null && UnwrapRight != null) { + if (Binary.Oper == Binary.Operator.Inequality) + ec.Emit (OpCodes.Xor); + else + ec.Emit (OpCodes.Ceq); + } else { + if (Binary.Oper == Binary.Operator.Inequality) { + ec.EmitInt (0); + ec.Emit (OpCodes.Ceq); + } + } + } + + ec.Emit (OpCodes.Br_S, end_label); + + ec.MarkLabel (dissimilar_label); + if (Binary.Oper == Binary.Operator.Inequality) + ec.EmitInt (1); + else + ec.EmitInt (0); + + ec.MarkLabel (end_label); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + Binary.FlowAnalysis (fc); + } + + public override SLE.Expression MakeExpression (BuilderContext ctx) + { + return Binary.MakeExpression (ctx, Left, Right); + } + } + + public class NullCoalescingOperator : Expression + { + Expression left, right; + Unwrap unwrap; + + public NullCoalescingOperator (Expression left, Expression right) + { + this.left = left; + this.right = right; + this.loc = left.Location; + } + + public Expression LeftExpression { + get { + return left; + } + } + + public Expression RightExpression { + get { + return right; + } + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + if (left is NullLiteral) + ec.Report.Error (845, loc, "An expression tree cannot contain a coalescing operator with null left side"); + + UserCast uc = left as UserCast; + Expression conversion = null; + if (uc != null) { + left = uc.Source; + + Arguments c_args = new Arguments (2); + c_args.Add (new Argument (uc.CreateExpressionTree (ec))); + c_args.Add (new Argument (left.CreateExpressionTree (ec))); + conversion = CreateExpressionFactoryCall (ec, "Lambda", c_args); + } + + Arguments args = new Arguments (3); + args.Add (new Argument (left.CreateExpressionTree (ec))); + args.Add (new Argument (right.CreateExpressionTree (ec))); + if (conversion != null) + args.Add (new Argument (conversion)); + + return CreateExpressionFactoryCall (ec, "Coalesce", args); + } + + Expression ConvertExpression (ResolveContext ec) + { + // TODO: ImplicitConversionExists should take care of this + if (left.eclass == ExprClass.MethodGroup) + return null; + + TypeSpec ltype = left.Type; + + // + // If left is a nullable type and an implicit conversion exists from right to underlying type of left, + // the result is underlying type of left + // + if (ltype.IsNullableType) { + unwrap = Unwrap.Create (left, false); + if (unwrap == null) + return null; + + // + // Reduce (left ?? null) to left + // + if (right.IsNull) + return ReducedExpression.Create (left, this); + + Expression conv; + if (right.Type.IsNullableType) { + conv = right.Type == ltype ? right : Convert.ImplicitNulableConversion (ec, right, ltype); + if (conv != null) { + right = conv; + type = ltype; + return this; + } + } else { + conv = Convert.ImplicitConversion (ec, right, unwrap.Type, loc); + if (conv != null) { + left = unwrap; + ltype = left.Type; + + // + // If right is a dynamic expression, the result type is dynamic + // + if (right.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + type = right.Type; + + // Need to box underlying value type + left = Convert.ImplicitBoxingConversion (left, ltype, type); + return this; + } + + right = conv; + type = ltype; + return this; + } + } + } else if (TypeSpec.IsReferenceType (ltype)) { + if (Convert.ImplicitConversionExists (ec, right, ltype)) { + // + // If right is a dynamic expression, the result type is dynamic + // + if (right.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + type = right.Type; + return this; + } + + // + // Reduce ("foo" ?? expr) to expression + // + Constant lc = left as Constant; + if (lc != null && !lc.IsDefaultValue) + return ReducedExpression.Create (lc, this, false); + + // + // Reduce (left ?? null) to left OR (null-constant ?? right) to right + // + if (right.IsNull || lc != null) { + // + // Special case null ?? null + // + if (right.IsNull && ltype == right.Type) + return null; + + return ReducedExpression.Create (lc != null ? right : left, this, false); + } + + right = Convert.ImplicitConversion (ec, right, ltype, loc); + type = ltype; + return this; + } + } else { + return null; + } + + TypeSpec rtype = right.Type; + if (!Convert.ImplicitConversionExists (ec, unwrap ?? left, rtype) || right.eclass == ExprClass.MethodGroup) + return null; + + // + // Reduce (null ?? right) to right + // + if (left.IsNull) + return ReducedExpression.Create (right, this, false).Resolve (ec); + + left = Convert.ImplicitConversion (ec, unwrap ?? left, rtype, loc); + + if (TypeSpec.IsValueType (left.Type) && !left.Type.IsNullableType) { + Warning_UnreachableExpression (ec, right.Location); + return ReducedExpression.Create (left, this, false).Resolve (ec); + } + + type = rtype; + return this; + } + + public override bool ContainsEmitWithAwait () + { + if (unwrap != null) + return unwrap.ContainsEmitWithAwait () || right.ContainsEmitWithAwait (); + + return left.ContainsEmitWithAwait () || right.ContainsEmitWithAwait (); + } + + protected override Expression DoResolve (ResolveContext ec) + { + left = left.Resolve (ec); + right = right.Resolve (ec); + + if (left == null || right == null) + return null; + + eclass = ExprClass.Value; + + Expression e = ConvertExpression (ec); + if (e == null) { + Binary.Error_OperatorCannotBeApplied (ec, left, right, "??", loc); + return null; + } + + return e; + } + + public override void Emit (EmitContext ec) + { + Label end_label = ec.DefineLabel (); + + if (unwrap != null) { + Label is_null_label = ec.DefineLabel (); + + unwrap.EmitCheck (ec); + ec.Emit (OpCodes.Brfalse, is_null_label); + + // + // When both expressions are nullable the unwrap + // is needed only for null check not for value uwrap + // + if (type.IsNullableType && TypeSpecComparer.IsEqual (NullableInfo.GetUnderlyingType (type), unwrap.Type)) + unwrap.Load (ec); + else + left.Emit (ec); + + ec.Emit (OpCodes.Br, end_label); + + ec.MarkLabel (is_null_label); + right.Emit (ec); + + ec.MarkLabel (end_label); + return; + } + + left.Emit (ec); + ec.Emit (OpCodes.Dup); + + // Only to make verifier happy + if (left.Type.IsGenericParameter) + ec.Emit (OpCodes.Box, left.Type); + + ec.Emit (OpCodes.Brtrue, end_label); + + ec.Emit (OpCodes.Pop); + right.Emit (ec); + + ec.MarkLabel (end_label); + } + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + left.FlowAnalysis (fc); + var left_da = fc.BranchDefiniteAssignment (); + right.FlowAnalysis (fc); + fc.DefiniteAssignment = left_da; + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + NullCoalescingOperator target = (NullCoalescingOperator) t; + + target.left = left.Clone (clonectx); + target.right = right.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + class LiftedUnaryMutator : UnaryMutator + { + public LiftedUnaryMutator (Mode mode, Expression expr, Location loc) + : base (mode, expr, loc) + { + } + + protected override Expression DoResolve (ResolveContext ec) + { + var orig_expr = expr; + + expr = Unwrap.Create (expr); + + var res = base.DoResolveOperation (ec); + + expr = orig_expr; + type = expr.Type; + + return res; + } + + protected override void EmitOperation (EmitContext ec) + { + Label is_null_label = ec.DefineLabel (); + Label end_label = ec.DefineLabel (); + + LocalTemporary lt = new LocalTemporary (type); + + // Value is on the stack + lt.Store (ec); + + var call = new CallEmitter (); + call.InstanceExpression = lt; + call.EmitPredefined (ec, NullableInfo.GetHasValue (expr.Type), null); + + ec.Emit (OpCodes.Brfalse, is_null_label); + + call = new CallEmitter (); + call.InstanceExpression = lt; + call.EmitPredefined (ec, NullableInfo.GetGetValueOrDefault (expr.Type), null); + + lt.Release (ec); + + base.EmitOperation (ec); + + ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type)); + ec.Emit (OpCodes.Br_S, end_label); + + ec.MarkLabel (is_null_label); + LiftedNull.Create (type, loc).Emit (ec); + + ec.MarkLabel (end_label); + } + } +} + diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/outline.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/outline.cs new file mode 100644 index 000000000..1104b1c11 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/outline.cs @@ -0,0 +1,1038 @@ +// +// outline -- support for rendering in monop +// Some code stolen from updater.cs in monodoc. +// +// Authors: +// Ben Maurer (bmaurer@users.sourceforge.net) +// +// (C) 2004 Ben Maurer +// + +// +// 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.Reflection; +using System.Collections; +using System.CodeDom.Compiler; +using System.IO; +using System.Text; + +namespace Mono.CSharp { +public class Outline { + + bool declared_only; + bool show_private; + bool filter_obsolete; + + IndentedTextWriter o; + Type t; + + public Outline (Type t, TextWriter output, bool declared_only, bool show_private, bool filter_obsolete) + { + this.t = t; + this.o = new IndentedTextWriter (output, "\t"); + this.declared_only = declared_only; + this.show_private = show_private; + this.filter_obsolete = filter_obsolete; + } + + public void OutlineType () + { + bool first; + + OutlineAttributes (); + o.Write (GetTypeVisibility (t)); + + if (t.IsClass && !t.IsSubclassOf (typeof (System.MulticastDelegate))) { + if (t.IsSealed) + o.Write (t.IsAbstract ? " static" : " sealed"); + else if (t.IsAbstract) + o.Write (" abstract"); + } + + o.Write (" "); + o.Write (GetTypeKind (t)); + o.Write (" "); + + Type [] interfaces = (Type []) Comparer.Sort (TypeGetInterfaces (t, declared_only)); + Type parent = t.BaseType; + + if (t.IsSubclassOf (typeof (System.MulticastDelegate))) { + MethodInfo method; + + method = t.GetMethod ("Invoke"); + + o.Write (FormatType (method.ReturnType)); + o.Write (" "); + o.Write (GetTypeName (t)); + o.Write (" ("); + OutlineParams (method.GetParameters ()); + o.Write (")"); + +#if NET_2_0 + WriteGenericConstraints (t.GetGenericArguments ()); +#endif + + o.WriteLine (";"); + return; + } + + o.Write (GetTypeName (t)); + if (((parent != null && parent != typeof (object) && parent != typeof (ValueType)) || interfaces.Length != 0) && ! t.IsEnum) { + first = true; + o.Write (" : "); + + if (parent != null && parent != typeof (object) && parent != typeof (ValueType)) { + o.Write (FormatType (parent)); + first = false; + } + + foreach (Type intf in interfaces) { + if (!first) o.Write (", "); + first = false; + + o.Write (FormatType (intf)); + } + } + + if (t.IsEnum) { + Type underlyingType = System.Enum.GetUnderlyingType (t); + if (underlyingType != typeof (int)) + o.Write (" : {0}", FormatType (underlyingType)); + } +#if NET_2_0 + WriteGenericConstraints (t.GetGenericArguments ()); +#endif + o.WriteLine (" {"); + o.Indent++; + + if (t.IsEnum) { + bool is_first = true; + foreach (FieldInfo fi in t.GetFields (BindingFlags.Public | BindingFlags.Static)) { + + if (! is_first) + o.WriteLine (","); + is_first = false; + o.Write (fi.Name); + } + o.WriteLine (); + o.Indent--; o.WriteLine ("}"); + return; + } + + first = true; + + foreach (ConstructorInfo ci in t.GetConstructors (DefaultFlags)) { + if (! ShowMember (ci)) + continue; + + if (first) + o.WriteLine (); + first = false; + + OutlineMemberAttribute (ci); + OutlineConstructor (ci); + + o.WriteLine (); + } + + + first = true; + + foreach (MethodInfo m in Comparer.Sort (t.GetMethods (DefaultFlags))) { + + if (! ShowMember (m)) + continue; + + if ((m.Attributes & MethodAttributes.SpecialName) != 0) + continue; + + if (first) + o.WriteLine (); + first = false; + + OutlineMemberAttribute (m); + OutlineMethod (m); + + o.WriteLine (); + } + + first = true; + + foreach (MethodInfo m in t.GetMethods (DefaultFlags)) { + + if (! ShowMember (m)) + continue; + + if ((m.Attributes & MethodAttributes.SpecialName) == 0) + continue; + if (!(m.Name.StartsWith ("op_"))) + continue; + + if (first) + o.WriteLine (); + first = false; + + OutlineMemberAttribute (m); + OutlineOperator (m); + + o.WriteLine (); + } + + first = true; + + foreach (PropertyInfo pi in Comparer.Sort (t.GetProperties (DefaultFlags))) { + + if (! ((pi.CanRead && ShowMember (pi.GetGetMethod (true))) || + (pi.CanWrite && ShowMember (pi.GetSetMethod (true))))) + continue; + + if (first) + o.WriteLine (); + first = false; + + OutlineMemberAttribute (pi); + OutlineProperty (pi); + + o.WriteLine (); + } + + first = true; + + foreach (FieldInfo fi in t.GetFields (DefaultFlags)) { + + if (! ShowMember (fi)) + continue; + + if (first) + o.WriteLine (); + first = false; + + OutlineMemberAttribute (fi); + OutlineField (fi); + + o.WriteLine (); + } + + first = true; + + foreach (EventInfo ei in Comparer.Sort (t.GetEvents (DefaultFlags))) { + + if (! ShowMember (ei.GetAddMethod (true))) + continue; + + if (first) + o.WriteLine (); + first = false; + + OutlineMemberAttribute (ei); + OutlineEvent (ei); + + o.WriteLine (); + } + + first = true; + + foreach (Type ntype in Comparer.Sort (t.GetNestedTypes (DefaultFlags))) { + + if (! ShowMember (ntype)) + continue; + + if (first) + o.WriteLine (); + first = false; + + new Outline (ntype, o, declared_only, show_private, filter_obsolete).OutlineType (); + } + + o.Indent--; o.WriteLine ("}"); + } + + BindingFlags DefaultFlags { + get { + BindingFlags f = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; + + if (declared_only) + f |= BindingFlags.DeclaredOnly; + + return f; + } + } + + // FIXME: add other interesting attributes? + void OutlineAttributes () + { + if (t.IsSerializable) + o.WriteLine ("[Serializable]"); + + if (t.IsDefined (typeof (System.FlagsAttribute), true)) + o.WriteLine ("[Flags]"); + + if (t.IsDefined (typeof (System.ObsoleteAttribute), true)) + o.WriteLine ("[Obsolete]"); + } + + void OutlineMemberAttribute (MemberInfo mi) + { + if (!mi.IsDefined (typeof (System.ObsoleteAttribute), false)) + return; + var oa = mi.GetCustomAttributes (typeof (System.ObsoleteAttribute), false) [0] as ObsoleteAttribute; + var msg = oa.Message; + o.WriteLine ("[Obsolete{0}]", msg == null || msg == "" ? "" : string.Format ("(\"{0}\")", msg)); + } + + void OutlineEvent (EventInfo ei) + { + MethodBase accessor = ei.GetAddMethod (true); + + o.Write (GetMethodVisibility (accessor)); + o.Write ("event "); + o.Write (FormatType (ei.EventHandlerType)); + o.Write (" "); + o.Write (ei.Name); + o.Write (";"); + } + + void OutlineConstructor (ConstructorInfo ci) + { + o.Write (GetMethodVisibility (ci)); + o.Write (RemoveGenericArity (t.Name)); + o.Write (" ("); + OutlineParams (ci.GetParameters ()); + o.Write (");"); + } + + + void OutlineProperty (PropertyInfo pi) + { + ParameterInfo [] idxp = pi.GetIndexParameters (); + MethodBase g = pi.GetGetMethod (true); + MethodBase s = pi.GetSetMethod (true); + MethodBase accessor = g != null ? g : s; + + if (pi.CanRead && pi.CanWrite) { + + + // Get the more accessible accessor + if ((g.Attributes & MethodAttributes.MemberAccessMask) != + (s.Attributes & MethodAttributes.MemberAccessMask)) { + + if (g.IsPublic) accessor = g; + else if (s.IsPublic) accessor = s; + else if (g.IsFamilyOrAssembly) accessor = g; + else if (s.IsFamilyOrAssembly) accessor = s; + else if (g.IsAssembly || g.IsFamily) accessor = g; + else if (s.IsAssembly || s.IsFamily) accessor = s; + } + } + + o.Write (GetMethodVisibility (accessor)); + o.Write (GetMethodModifiers (accessor)); + o.Write (FormatType (pi.PropertyType)); + o.Write (" "); + + if (idxp.Length == 0) + o.Write (pi.Name); + else { + o.Write ("this ["); + OutlineParams (idxp); + o.Write ("]"); + } + + o.WriteLine (" {"); + o.Indent ++; + + if (g != null && ShowMember (g)) { + if ((g.Attributes & MethodAttributes.MemberAccessMask) != + (accessor.Attributes & MethodAttributes.MemberAccessMask)) + o.Write (GetMethodVisibility (g)); + o.WriteLine ("get;"); + } + + if (s != null && ShowMember (s)) { + if ((s.Attributes & MethodAttributes.MemberAccessMask) != + (accessor.Attributes & MethodAttributes.MemberAccessMask)) + o.Write (GetMethodVisibility (s)); + o.WriteLine ("set;"); + } + + o.Indent --; + o.Write ("}"); + } + + void OutlineMethod (MethodInfo mi) + { + if (MethodIsExplicitIfaceImpl (mi)) { + o.Write (FormatType (mi.ReturnType)); + o.Write (" "); + // MSFT has no way to get the method that we are overriding + // from the interface. this would allow us to pretty print + // the type name (and be more correct if there compiler + // were to do some strange naming thing). + } else { + o.Write (GetMethodVisibility (mi)); + o.Write (GetMethodModifiers (mi)); + o.Write (FormatType (mi.ReturnType)); + o.Write (" "); + } + + o.Write (mi.Name); +#if NET_2_0 + o.Write (FormatGenericParams (mi.GetGenericArguments ())); +#endif + o.Write (" ("); + OutlineParams (mi.GetParameters ()); + o.Write (")"); +#if NET_2_0 + WriteGenericConstraints (mi.GetGenericArguments ()); +#endif + o.Write (";"); + } + + void OutlineOperator (MethodInfo mi) + { + o.Write (GetMethodVisibility (mi)); + o.Write (GetMethodModifiers (mi)); + if (mi.Name == "op_Explicit" || mi.Name == "op_Implicit") { + o.Write (mi.Name.Substring (3).ToLower ()); + o.Write (" operator "); + o.Write (FormatType (mi.ReturnType)); + } else { + o.Write (FormatType (mi.ReturnType)); + o.Write (" operator "); + o.Write (OperatorFromName (mi.Name)); + } + o.Write (" ("); + OutlineParams (mi.GetParameters ()); + o.Write (");"); + } + + void OutlineParams (ParameterInfo [] pi) + { + int i = 0; + foreach (ParameterInfo p in pi) { + if (p.ParameterType.IsByRef) { + o.Write (p.IsOut ? "out " : "ref "); + o.Write (FormatType (p.ParameterType.GetElementType ())); + } else if (p.IsDefined (typeof (ParamArrayAttribute), false)) { + o.Write ("params "); + o.Write (FormatType (p.ParameterType)); + } else { + o.Write (FormatType (p.ParameterType)); + } + + o.Write (" "); + o.Write (p.Name); + if (i + 1 < pi.Length) + o.Write (", "); + i++; + } + } + + void OutlineField (FieldInfo fi) + { + if (fi.IsPublic) o.Write ("public "); + if (fi.IsFamily) o.Write ("protected "); + if (fi.IsPrivate) o.Write ("private "); + if (fi.IsAssembly) o.Write ("internal "); + if (fi.IsLiteral) o.Write ("const "); + else if (fi.IsStatic) o.Write ("static "); + if (fi.IsInitOnly) o.Write ("readonly "); + + o.Write (FormatType (fi.FieldType)); + o.Write (" "); + o.Write (fi.Name); + if (fi.IsLiteral) { + object v = fi.GetValue (this); + + // TODO: Escape values here + o.Write (" = "); + if (v is char) + o.Write ("'{0}'", v); + else if (v is string) + o.Write ("\"{0}\"", v); + else + o.Write (fi.GetValue (this)); + } + o.Write (";"); + } + + static string GetMethodVisibility (MethodBase m) + { + // itnerfaces have no modifiers here + if (m.DeclaringType.IsInterface) + return ""; + + if (m.IsPublic) return "public "; + if (m.IsFamily) return "protected "; + if (m.IsPrivate) return "private "; + if (m.IsAssembly) return "internal "; + + return null; + } + + static string GetMethodModifiers (MethodBase method) + { + if (method.IsStatic) + return "static "; + + if (method.IsFinal) { + // This will happen if you have + // class X : IA { + // public void A () {} + // static void Main () {} + // } + // interface IA { + // void A (); + // } + // + // A needs to be virtual (the CLR requires + // methods implementing an iface be virtual), + // but can not be inherited. It also can not + // be inherited. In C# this is represented + // with no special modifiers + + if (method.IsVirtual) + return null; + return "sealed "; + } + + // all interface methods are "virtual" but we don't say that in c# + if (method.IsVirtual && !method.DeclaringType.IsInterface) { + if (method.IsAbstract) + return "abstract "; + + return ((method.Attributes & MethodAttributes.NewSlot) != 0) ? + "virtual " : + "override "; + } + + return null; + } + + static string GetTypeKind (Type t) + { + if (t.IsEnum) + return "enum"; + if (t.IsClass) { + if (t.IsSubclassOf (typeof (System.MulticastDelegate))) + return "delegate"; + else + return "class"; + } + if (t.IsInterface) + return "interface"; + if (t.IsValueType) + return "struct"; + return "class"; + } + + static string GetTypeVisibility (Type t) + { + switch (t.Attributes & TypeAttributes.VisibilityMask){ + case TypeAttributes.Public: + case TypeAttributes.NestedPublic: + return "public"; + + case TypeAttributes.NestedFamily: + case TypeAttributes.NestedFamANDAssem: + case TypeAttributes.NestedFamORAssem: + return "protected"; + + default: + return "internal"; + } + } + +#if NET_2_0 + string FormatGenericParams (Type [] args) + { + StringBuilder sb = new StringBuilder (); + if (args.Length == 0) + return ""; + + sb.Append ("<"); + for (int i = 0; i < args.Length; i++) { + if (i > 0) + sb.Append (","); + sb.Append (FormatType (args [i])); + } + sb.Append (">"); + return sb.ToString (); + } +#endif + + // TODO: fine tune this so that our output is less verbose. We need to figure + // out a way to do this while not making things confusing. + string FormatType (Type t) + { + if (t == null) + return ""; + + string type = GetFullName (t); + if (type == null) + return t.ToString (); + + if (!type.StartsWith ("System.")) { + if (t.Namespace == this.t.Namespace) + return t.Name; + return type; + } + + if (t.HasElementType) { + Type et = t.GetElementType (); + if (t.IsArray) + return FormatType (et) + " []"; + if (t.IsPointer) + return FormatType (et) + " *"; + if (t.IsByRef) + return "ref " + FormatType (et); + } + + switch (type) { + case "System.Byte": return "byte"; + case "System.SByte": return "sbyte"; + case "System.Int16": return "short"; + case "System.Int32": return "int"; + case "System.Int64": return "long"; + + case "System.UInt16": return "ushort"; + case "System.UInt32": return "uint"; + case "System.UInt64": return "ulong"; + + case "System.Single": return "float"; + case "System.Double": return "double"; + case "System.Decimal": return "decimal"; + case "System.Boolean": return "bool"; + case "System.Char": return "char"; + case "System.String": return "string"; + + case "System.Object": return "object"; + case "System.Void": return "void"; + } + + if (type.LastIndexOf(".") == 6) + return type.Substring(7); + + // + // If the namespace of the type is the namespace of what + // we are printing (or is a member of one if its children + // don't print it. This basically means that in C# we would + // automatically get the namespace imported by virtue of the + // namespace {} block. + // + if (this.t.Namespace.StartsWith (t.Namespace + ".") || t.Namespace == this.t.Namespace) + return type.Substring (t.Namespace.Length + 1); + + return type; + } + + public static string RemoveGenericArity (string name) + { + int start = 0; + StringBuilder sb = new StringBuilder (); + while (start < name.Length) { + int pos = name.IndexOf ('`', start); + if (pos < 0) { + sb.Append (name.Substring (start)); + break; + } + sb.Append (name.Substring (start, pos-start)); + + pos++; + + while ((pos < name.Length) && Char.IsNumber (name [pos])) + pos++; + + start = pos; + } + + return sb.ToString (); + } + + string GetTypeName (Type t) + { + StringBuilder sb = new StringBuilder (); + GetTypeName (sb, t); + return sb.ToString (); + } + + void GetTypeName (StringBuilder sb, Type t) + { + sb.Append (RemoveGenericArity (t.Name)); +#if NET_2_0 + sb.Append (FormatGenericParams (t.GetGenericArguments ())); +#endif + } + + string GetFullName (Type t) + { + StringBuilder sb = new StringBuilder (); + GetFullName_recursed (sb, t, false); + return sb.ToString (); + } + + void GetFullName_recursed (StringBuilder sb, Type t, bool recursed) + { +#if NET_2_0 + if (t.IsGenericParameter) { + sb.Append (t.Name); + return; + } +#endif + + if (t.DeclaringType != null) { + GetFullName_recursed (sb, t.DeclaringType, true); + sb.Append ("."); + } + + if (!recursed) { + string ns = t.Namespace; + if ((ns != null) && (ns != "")) { + sb.Append (ns); + sb.Append ("."); + } + } + + GetTypeName (sb, t); + } + +#if NET_2_0 + void WriteGenericConstraints (Type [] args) + { + + foreach (Type t in args) { + bool first = true; + Type[] ifaces = TypeGetInterfaces (t, true); + + GenericParameterAttributes attrs = t.GenericParameterAttributes & GenericParameterAttributes.SpecialConstraintMask; + GenericParameterAttributes [] interesting = { + GenericParameterAttributes.ReferenceTypeConstraint, + GenericParameterAttributes.NotNullableValueTypeConstraint, + GenericParameterAttributes.DefaultConstructorConstraint + }; + + if (t.BaseType != typeof (object) || ifaces.Length != 0 || attrs != 0) { + o.Write (" where "); + o.Write (FormatType (t)); + o.Write (" : "); + } + + if (t.BaseType != typeof (object)) { + o.Write (FormatType (t.BaseType)); + first = false; + } + + foreach (Type iface in ifaces) { + if (!first) + o.Write (", "); + first = false; + + o.Write (FormatType (iface)); + } + + foreach (GenericParameterAttributes a in interesting) { + if ((attrs & a) == 0) + continue; + + if (!first) + o.Write (", "); + first = false; + + switch (a) { + case GenericParameterAttributes.ReferenceTypeConstraint: + o.Write ("class"); + break; + case GenericParameterAttributes.NotNullableValueTypeConstraint: + o.Write ("struct"); + break; + case GenericParameterAttributes.DefaultConstructorConstraint: + o.Write ("new ()"); + break; + } + } + } + } +#endif + + string OperatorFromName (string name) + { + switch (name) { + case "op_UnaryPlus": return "+"; + case "op_UnaryNegation": return "-"; + case "op_LogicalNot": return "!"; + case "op_OnesComplement": return "~"; + case "op_Increment": return "++"; + case "op_Decrement": return "--"; + case "op_True": return "true"; + case "op_False": return "false"; + case "op_Addition": return "+"; + case "op_Subtraction": return "-"; + case "op_Multiply": return "*"; + case "op_Division": return "/"; + case "op_Modulus": return "%"; + case "op_BitwiseAnd": return "&"; + case "op_BitwiseOr": return "|"; + case "op_ExclusiveOr": return "^"; + case "op_LeftShift": return "<<"; + case "op_RightShift": return ">>"; + case "op_Equality": return "=="; + case "op_Inequality": return "!="; + case "op_GreaterThan": return ">"; + case "op_LessThan": return "<"; + case "op_GreaterThanOrEqual": return ">="; + case "op_LessThanOrEqual": return "<="; + default: return name; + } + } + + bool MethodIsExplicitIfaceImpl (MethodBase mb) + { + if (!(mb.IsFinal && mb.IsVirtual && mb.IsPrivate)) + return false; + + // UGH msft has no way to get the info about what method is + // getting overriden. Another reason to use cecil :-) + // + //MethodInfo mi = mb as MethodInfo; + //if (mi == null) + // return false; + // + //Console.WriteLine (mi.GetBaseDefinition ().DeclaringType); + //return mi.GetBaseDefinition ().DeclaringType.IsInterface; + + // So, we guess that virtual final private methods only come + // from ifaces :-) + return true; + } + + bool ShowMember (MemberInfo mi) + { + if (mi.MemberType == MemberTypes.Constructor && ((MethodBase) mi).IsStatic) + return false; + + if (show_private) + return true; + + if (filter_obsolete && mi.IsDefined (typeof (ObsoleteAttribute), false)) + return false; + + switch (mi.MemberType) { + case MemberTypes.Constructor: + case MemberTypes.Method: + MethodBase mb = mi as MethodBase; + + if (mb.IsFamily || mb.IsPublic || mb.IsFamilyOrAssembly) + return true; + + if (MethodIsExplicitIfaceImpl (mb)) + return true; + + return false; + + + case MemberTypes.Field: + FieldInfo fi = mi as FieldInfo; + + if (fi.IsFamily || fi.IsPublic || fi.IsFamilyOrAssembly) + return true; + + return false; + + + case MemberTypes.NestedType: + case MemberTypes.TypeInfo: + Type t = mi as Type; + + switch (t.Attributes & TypeAttributes.VisibilityMask){ + case TypeAttributes.Public: + case TypeAttributes.NestedPublic: + case TypeAttributes.NestedFamily: + case TypeAttributes.NestedFamORAssem: + return true; + } + + return false; + } + + // What am I !!! + return true; + } + + static Type [] TypeGetInterfaces (Type t, bool declonly) + { + if (t.IsGenericParameter) + return new Type [0]; + + Type [] ifaces = t.GetInterfaces (); + if (! declonly) + return ifaces; + + // Handle Object. Also, optimize for no interfaces + if (t.BaseType == null || ifaces.Length == 0) + return ifaces; + + ArrayList ar = new ArrayList (); + + foreach (Type i in ifaces) + if (! i.IsAssignableFrom (t.BaseType)) + ar.Add (i); + + return (Type []) ar.ToArray (typeof (Type)); + } +} + +public class Comparer : IComparer { + delegate int ComparerFunc (object a, object b); + + ComparerFunc cmp; + + Comparer (ComparerFunc f) + { + this.cmp = f; + } + + public int Compare (object a, object b) + { + return cmp (a, b); + } + + static int CompareType (object a, object b) + { + Type type1 = (Type) a; + Type type2 = (Type) b; + + if (type1.IsSubclassOf (typeof (System.MulticastDelegate)) != type2.IsSubclassOf (typeof (System.MulticastDelegate))) + return (type1.IsSubclassOf (typeof (System.MulticastDelegate)))? -1:1; + return string.Compare (type1.Name, type2.Name); + + } + +// static Comparer TypeComparer = new Comparer (new ComparerFunc (CompareType)); + +// static Type [] Sort (Type [] types) +// { +// Array.Sort (types, TypeComparer); +// return types; +// } + + static int CompareMemberInfo (object a, object b) + { + return string.Compare (((MemberInfo) a).Name, ((MemberInfo) b).Name); + } + + static Comparer MemberInfoComparer = new Comparer (new ComparerFunc (CompareMemberInfo)); + + public static MemberInfo [] Sort (MemberInfo [] inf) + { + Array.Sort (inf, MemberInfoComparer); + return inf; + } + + static int CompareMethodBase (object a, object b) + { + MethodBase aa = (MethodBase) a, bb = (MethodBase) b; + + if (aa.IsStatic == bb.IsStatic) { + int c = CompareMemberInfo (a, b); + if (c != 0) + return c; + ParameterInfo [] ap, bp; + + // + // Sort overloads by the names of their types + // put methods with fewer params first. + // + + ap = aa.GetParameters (); + bp = bb.GetParameters (); + int n = System.Math.Min (ap.Length, bp.Length); + + for (int i = 0; i < n; i ++) + if ((c = CompareType (ap [i].ParameterType, bp [i].ParameterType)) != 0) + return c; + + return ap.Length.CompareTo (bp.Length); + } + if (aa.IsStatic) + return -1; + + return 1; + } + + static Comparer MethodBaseComparer = new Comparer (new ComparerFunc (CompareMethodBase)); + + public static MethodBase [] Sort (MethodBase [] inf) + { + Array.Sort (inf, MethodBaseComparer); + return inf; + } + + static int ComparePropertyInfo (object a, object b) + { + PropertyInfo aa = (PropertyInfo) a, bb = (PropertyInfo) b; + + bool astatic = (aa.CanRead ? aa.GetGetMethod (true) : aa.GetSetMethod (true)).IsStatic; + bool bstatic = (bb.CanRead ? bb.GetGetMethod (true) : bb.GetSetMethod (true)).IsStatic; + + if (astatic == bstatic) + return CompareMemberInfo (a, b); + + if (astatic) + return -1; + + return 1; + } + + static Comparer PropertyInfoComparer = new Comparer (new ComparerFunc (ComparePropertyInfo)); + + public static PropertyInfo [] Sort (PropertyInfo [] inf) + { + Array.Sort (inf, PropertyInfoComparer); + return inf; + } + + static int CompareEventInfo (object a, object b) + { + EventInfo aa = (EventInfo) a, bb = (EventInfo) b; + + bool astatic = aa.GetAddMethod (true).IsStatic; + bool bstatic = bb.GetAddMethod (true).IsStatic; + + if (astatic == bstatic) + return CompareMemberInfo (a, b); + + if (astatic) + return -1; + + return 1; + } + + static Comparer EventInfoComparer = new Comparer (new ComparerFunc (CompareEventInfo)); + + public static EventInfo [] Sort (EventInfo [] inf) + { + Array.Sort (inf, EventInfoComparer); + return inf; + } +} +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/parameter.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/parameter.cs new file mode 100644 index 000000000..1a30ae384 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/parameter.cs @@ -0,0 +1,1472 @@ +// +// parameter.cs: Parameter definition. +// +// Author: Miguel de Icaza (miguel@gnu.org) +// Marek Safar (marek.safar@seznam.cz) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) +// Copyright 2003-2008 Novell, Inc. +// Copyright 2011 Xamarin Inc +// +// +using System; +using System.Text; + +#if STATIC +using MetaType = IKVM.Reflection.Type; +using IKVM.Reflection; +using IKVM.Reflection.Emit; +#else +using MetaType = System.Type; +using System.Reflection; +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp { + + /// + /// Abstract Base class for parameters of a method. + /// + public abstract class ParameterBase : Attributable + { + protected ParameterBuilder builder; + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { +#if false + if (a.Type == pa.MarshalAs) { + UnmanagedMarshal marshal = a.GetMarshal (this); + if (marshal != null) { + builder.SetMarshal (marshal); + } + return; + } +#endif + if (a.HasSecurityAttribute) { + a.Error_InvalidSecurityParent (); + return; + } + + if (a.Type == pa.Dynamic) { + a.Error_MisusedDynamicAttribute (); + return; + } + + builder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), cdata); + } + + public ParameterBuilder Builder { + get { + return builder; + } + } + + public override bool IsClsComplianceRequired() + { + return false; + } + } + + /// + /// Class for applying custom attributes on the return type + /// + public class ReturnParameter : ParameterBase + { + MemberCore method; + + // TODO: merge method and mb + public ReturnParameter (MemberCore method, MethodBuilder mb, Location location) + { + this.method = method; + try { + builder = mb.DefineParameter (0, ParameterAttributes.None, ""); + } + catch (ArgumentOutOfRangeException) { + method.Compiler.Report.RuntimeMissingSupport (location, "custom attributes on the return type"); + } + } + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.Type == pa.CLSCompliant) { + method.Compiler.Report.Warning (3023, 1, a.Location, + "CLSCompliant attribute has no meaning when applied to return types. Try putting it on the method instead"); + } + + // This occurs after Warning -28 + if (builder == null) + return; + + base.ApplyAttributeBuilder (a, ctor, cdata, pa); + } + + public override AttributeTargets AttributeTargets { + get { + return AttributeTargets.ReturnValue; + } + } + + /// + /// Is never called + /// + public override string[] ValidAttributeTargets { + get { + return null; + } + } + } + + public class ImplicitLambdaParameter : Parameter + { + public ImplicitLambdaParameter (string name, Location loc) + : base (null, name, Modifier.NONE, null, loc) + { + } + + public override TypeSpec Resolve (IMemberContext ec, int index) + { + if (parameter_type == null) + throw new InternalErrorException ("A type of implicit lambda parameter `{0}' is not set", + Name); + + base.idx = index; + return parameter_type; + } + + public void SetParameterType (TypeSpec type) + { + parameter_type = type; + } + } + + public class ParamsParameter : Parameter { + public ParamsParameter (FullNamedExpression type, string name, Attributes attrs, Location loc): + base (type, name, Parameter.Modifier.PARAMS, attrs, loc) + { + } + + public override TypeSpec Resolve (IMemberContext ec, int index) + { + if (base.Resolve (ec, index) == null) + return null; + + var ac = parameter_type as ArrayContainer; + if (ac == null || ac.Rank != 1) { + ec.Module.Compiler.Report.Error (225, Location, "The params parameter must be a single dimensional array"); + return null; + } + + return parameter_type; + } + + public override void ApplyAttributes (MethodBuilder mb, ConstructorBuilder cb, int index, PredefinedAttributes pa) + { + base.ApplyAttributes (mb, cb, index, pa); + pa.ParamArray.EmitAttribute (builder); + } + } + + public class ArglistParameter : Parameter { + // Doesn't have proper type because it's never chosen for better conversion + public ArglistParameter (Location loc) : + base (null, String.Empty, Parameter.Modifier.NONE, null, loc) + { + parameter_type = InternalType.Arglist; + } + + public override void ApplyAttributes (MethodBuilder mb, ConstructorBuilder cb, int index, PredefinedAttributes pa) + { + // Nothing to do + } + + public override bool CheckAccessibility (InterfaceMemberBase member) + { + return true; + } + + public override TypeSpec Resolve (IMemberContext ec, int index) + { + return parameter_type; + } + } + + public interface IParameterData + { + Expression DefaultValue { get; } + bool HasExtensionMethodModifier { get; } + bool HasDefaultValue { get; } + Parameter.Modifier ModFlags { get; } + string Name { get; } + } + + // + // Parameter information created by parser + // + public class Parameter : ParameterBase, IParameterData, ILocalVariable // TODO: INamedBlockVariable + { + [Flags] + public enum Modifier : byte { + NONE = 0, + PARAMS = 1 << 0, + REF = 1 << 1, + OUT = 1 << 2, + This = 1 << 3, + CallerMemberName = 1 << 4, + CallerLineNumber = 1 << 5, + CallerFilePath = 1 << 6, + + RefOutMask = REF | OUT, + ModifierMask = PARAMS | REF | OUT | This, + CallerMask = CallerMemberName | CallerLineNumber | CallerFilePath + } + + static readonly string[] attribute_targets = new [] { "param" }; + + FullNamedExpression texpr; + Modifier modFlags; + string name; + Expression default_expr; + protected TypeSpec parameter_type; + readonly Location loc; + protected int idx; + public bool HasAddressTaken; + + TemporaryVariableReference expr_tree_variable; + + HoistedParameter hoisted_variant; + + public Parameter (FullNamedExpression type, string name, Modifier mod, Attributes attrs, Location loc) + { + this.name = name; + modFlags = mod; + this.loc = loc; + texpr = type; + + // Only assign, attributes will be attached during resolve + base.attributes = attrs; + } + + #region Properties + + public Expression DefaultExpression { + get { + return default_expr; + } + } + + public DefaultParameterValueExpression DefaultValue { + get { + return default_expr as DefaultParameterValueExpression; + } + set { + default_expr = value; + } + } + + Expression IParameterData.DefaultValue { + get { + var expr = default_expr as DefaultParameterValueExpression; + return expr == null ? default_expr : expr.Child; + } + } + + bool HasOptionalExpression { + get { + return default_expr is DefaultParameterValueExpression; + } + } + + public Location Location { + get { + return loc; + } + } + + public Modifier ParameterModifier { + get { + return modFlags; + } + } + + public TypeSpec Type { + get { + return parameter_type; + } + set { + parameter_type = value; + } + } + + public FullNamedExpression TypeExpression { + get { + return texpr; + } + } + + public override string[] ValidAttributeTargets { + get { + return attribute_targets; + } + } + + #endregion + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.Type == pa.In && ModFlags == Modifier.OUT) { + a.Report.Error (36, a.Location, "An out parameter cannot have the `In' attribute"); + return; + } + + if (a.Type == pa.ParamArray) { + a.Report.Error (674, a.Location, "Do not use `System.ParamArrayAttribute'. Use the `params' keyword instead"); + return; + } + + if (a.Type == pa.Out && (ModFlags & Modifier.REF) != 0 && + !OptAttributes.Contains (pa.In)) { + a.Report.Error (662, a.Location, + "Cannot specify only `Out' attribute on a ref parameter. Use both `In' and `Out' attributes or neither"); + return; + } + + if (a.Type == pa.CLSCompliant) { + a.Report.Warning (3022, 1, a.Location, "CLSCompliant attribute has no meaning when applied to parameters. Try putting it on the method instead"); + } else if (a.Type == pa.DefaultParameterValue || a.Type == pa.OptionalParameter) { + if (HasOptionalExpression) { + a.Report.Error (1745, a.Location, + "Cannot specify `{0}' attribute on optional parameter `{1}'", + a.Type.GetSignatureForError ().Replace ("Attribute", ""), Name); + } + + if (a.Type == pa.DefaultParameterValue) + return; + } else if (a.Type == pa.CallerMemberNameAttribute) { + if ((modFlags & Modifier.CallerMemberName) == 0) { + a.Report.Error (4022, a.Location, + "The CallerMemberName attribute can only be applied to parameters with default value"); + } + } else if (a.Type == pa.CallerLineNumberAttribute) { + if ((modFlags & Modifier.CallerLineNumber) == 0) { + a.Report.Error (4020, a.Location, + "The CallerLineNumber attribute can only be applied to parameters with default value"); + } + } else if (a.Type == pa.CallerFilePathAttribute) { + if ((modFlags & Modifier.CallerFilePath) == 0) { + a.Report.Error (4021, a.Location, + "The CallerFilePath attribute can only be applied to parameters with default value"); + } + } + + base.ApplyAttributeBuilder (a, ctor, cdata, pa); + } + + public virtual bool CheckAccessibility (InterfaceMemberBase member) + { + if (parameter_type == null) + return true; + + return member.IsAccessibleAs (parameter_type); + } + + bool IsValidCallerContext (MemberCore memberContext) + { + var m = memberContext as Method; + if (m != null) + return !m.IsPartialImplementation; + + return true; + } + + // + // Resolve is used in method definitions + // + public virtual TypeSpec Resolve (IMemberContext rc, int index) + { + if (parameter_type != null) + return parameter_type; + + if (attributes != null) + attributes.AttachTo (this, rc); + + parameter_type = texpr.ResolveAsType (rc); + if (parameter_type == null) + return null; + + this.idx = index; + + if ((modFlags & Parameter.Modifier.RefOutMask) != 0 && parameter_type.IsSpecialRuntimeType) { + rc.Module.Compiler.Report.Error (1601, Location, "Method or delegate parameter cannot be of type `{0}'", + GetSignatureForError ()); + return null; + } + + VarianceDecl.CheckTypeVariance (parameter_type, + (modFlags & Parameter.Modifier.RefOutMask) != 0 ? Variance.None : Variance.Contravariant, + rc); + + if (parameter_type.IsStatic) { + rc.Module.Compiler.Report.Error (721, Location, "`{0}': static types cannot be used as parameters", + texpr.GetSignatureForError ()); + return parameter_type; + } + + if ((modFlags & Modifier.This) != 0 && (parameter_type.IsPointer || parameter_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic)) { + rc.Module.Compiler.Report.Error (1103, Location, "The extension method cannot be of type `{0}'", + parameter_type.GetSignatureForError ()); + } + + return parameter_type; + } + + void ResolveCallerAttributes (ResolveContext rc) + { + var pa = rc.Module.PredefinedAttributes; + TypeSpec caller_type; + Attribute callerMemberName = null, callerFilePath = null; + + foreach (var attr in attributes.Attrs) { + var atype = attr.ResolveTypeForComparison (); + if (atype == null) + continue; + + if (atype == pa.CallerMemberNameAttribute) { + caller_type = rc.BuiltinTypes.String; + if (caller_type != parameter_type && !Convert.ImplicitReferenceConversionExists (caller_type, parameter_type)) { + rc.Report.Error (4019, attr.Location, + "The CallerMemberName attribute cannot be applied because there is no standard conversion from `{0}' to `{1}'", + caller_type.GetSignatureForError (), parameter_type.GetSignatureForError ()); + } + + if (!IsValidCallerContext (rc.CurrentMemberDefinition)) { + rc.Report.Warning (4026, 1, attr.Location, + "The CallerMemberName applied to parameter `{0}' will have no effect because it applies to a member that is used in context that do not allow optional arguments", + name); + } + + modFlags |= Modifier.CallerMemberName; + callerMemberName = attr; + continue; + } + + if (atype == pa.CallerLineNumberAttribute) { + caller_type = rc.BuiltinTypes.Int; + if (caller_type != parameter_type && !Convert.ImplicitStandardConversionExists (new IntConstant (caller_type, int.MaxValue, Location.Null), parameter_type)) { + rc.Report.Error (4017, attr.Location, + "The CallerLineNumberAttribute attribute cannot be applied because there is no standard conversion from `{0}' to `{1}'", + caller_type.GetSignatureForError (), parameter_type.GetSignatureForError ()); + } + + if (!IsValidCallerContext (rc.CurrentMemberDefinition)) { + rc.Report.Warning (4024, 1, attr.Location, + "The CallerLineNumberAttribute applied to parameter `{0}' will have no effect because it applies to a member that is used in context that do not allow optional arguments", + name); + } + + modFlags |= Modifier.CallerLineNumber; + continue; + } + + if (atype == pa.CallerFilePathAttribute) { + caller_type = rc.BuiltinTypes.String; + if (caller_type != parameter_type && !Convert.ImplicitReferenceConversionExists (caller_type, parameter_type)) { + rc.Report.Error (4018, attr.Location, + "The CallerFilePath attribute cannot be applied because there is no standard conversion from `{0}' to `{1}'", + caller_type.GetSignatureForError (), parameter_type.GetSignatureForError ()); + } + + if (!IsValidCallerContext (rc.CurrentMemberDefinition)) { + rc.Report.Warning (4025, 1, attr.Location, + "The CallerFilePath applied to parameter `{0}' will have no effect because it applies to a member that is used in context that do not allow optional arguments", + name); + } + + modFlags |= Modifier.CallerFilePath; + callerFilePath = attr; + continue; + } + } + + if ((modFlags & Modifier.CallerLineNumber) != 0) { + if (callerMemberName != null) { + rc.Report.Warning (7081, 1, callerMemberName.Location, + "The CallerMemberNameAttribute applied to parameter `{0}' will have no effect. It is overridden by the CallerLineNumberAttribute", + Name); + } + + if (callerFilePath != null) { + rc.Report.Warning (7082, 1, callerFilePath.Location, + "The CallerFilePathAttribute applied to parameter `{0}' will have no effect. It is overridden by the CallerLineNumberAttribute", + name); + } + } + + if ((modFlags & Modifier.CallerMemberName) != 0) { + if (callerFilePath != null) { + rc.Report.Warning (7080, 1, callerFilePath.Location, + "The CallerMemberNameAttribute applied to parameter `{0}' will have no effect. It is overridden by the CallerFilePathAttribute", + name); + } + + } + } + + public void ResolveDefaultValue (ResolveContext rc) + { + // + // Default value was specified using an expression + // + if (default_expr != null) { + ((DefaultParameterValueExpression)default_expr).Resolve (rc, this); + if (attributes != null) + ResolveCallerAttributes (rc); + + return; + } + + if (attributes == null) + return; + + var pa = rc.Module.PredefinedAttributes; + var def_attr = attributes.Search (pa.DefaultParameterValue); + if (def_attr != null) { + if (def_attr.Resolve () == null) + return; + + var default_expr_attr = def_attr.GetParameterDefaultValue (); + if (default_expr_attr == null) + return; + + var dpa_rc = def_attr.CreateResolveContext (); + default_expr = default_expr_attr.Resolve (dpa_rc); + + if (default_expr is BoxedCast) + default_expr = ((BoxedCast) default_expr).Child; + + Constant c = default_expr as Constant; + if (c == null) { + if (parameter_type.BuiltinType == BuiltinTypeSpec.Type.Object) { + rc.Report.Error (1910, default_expr.Location, + "Argument of type `{0}' is not applicable for the DefaultParameterValue attribute", + default_expr.Type.GetSignatureForError ()); + } else { + rc.Report.Error (1909, default_expr.Location, + "The DefaultParameterValue attribute is not applicable on parameters of type `{0}'", + default_expr.Type.GetSignatureForError ()); + } + + default_expr = null; + return; + } + + if (TypeSpecComparer.IsEqual (default_expr.Type, parameter_type) || + (default_expr is NullConstant && TypeSpec.IsReferenceType (parameter_type) && !parameter_type.IsGenericParameter) || + parameter_type.BuiltinType == BuiltinTypeSpec.Type.Object) { + return; + } + + // + // LAMESPEC: Some really weird csc behaviour which we have to mimic + // User operators returning same type as parameter type are considered + // valid for this attribute only + // + // struct S { public static implicit operator S (int i) {} } + // + // void M ([DefaultParameterValue (3)]S s) + // + var expr = Convert.ImplicitUserConversion (dpa_rc, default_expr, parameter_type, loc); + if (expr != null && TypeSpecComparer.IsEqual (expr.Type, parameter_type)) { + return; + } + + rc.Report.Error (1908, default_expr.Location, "The type of the default value should match the type of the parameter"); + return; + } + + var opt_attr = attributes.Search (pa.OptionalParameter); + if (opt_attr != null) { + default_expr = EmptyExpression.MissingValue; + } + } + + public bool HasDefaultValue { + get { return default_expr != null; } + } + + public bool HasExtensionMethodModifier { + get { return (modFlags & Modifier.This) != 0; } + } + + // + // Hoisted parameter variant + // + public HoistedParameter HoistedVariant { + get { + return hoisted_variant; + } + set { + hoisted_variant = value; + } + } + + public Modifier ModFlags { + get { return modFlags & ~Modifier.This; } + } + + public string Name { + get { return name; } + set { name = value; } + } + + public override AttributeTargets AttributeTargets { + get { + return AttributeTargets.Parameter; + } + } + + public void Error_DuplicateName (Report r) + { + r.Error (100, Location, "The parameter name `{0}' is a duplicate", Name); + } + + public virtual string GetSignatureForError () + { + string type_name; + if (parameter_type != null) + type_name = parameter_type.GetSignatureForError (); + else + type_name = texpr.GetSignatureForError (); + + string mod = GetModifierSignature (modFlags); + if (mod.Length > 0) + return String.Concat (mod, " ", type_name); + + return type_name; + } + + public static string GetModifierSignature (Modifier mod) + { + switch (mod) { + case Modifier.OUT: + return "out"; + case Modifier.PARAMS: + return "params"; + case Modifier.REF: + return "ref"; + case Modifier.This: + return "this"; + default: + return ""; + } + } + + public void IsClsCompliant (IMemberContext ctx) + { + if (parameter_type.IsCLSCompliant ()) + return; + + ctx.Module.Compiler.Report.Warning (3001, 1, Location, + "Argument type `{0}' is not CLS-compliant", parameter_type.GetSignatureForError ()); + } + + public virtual void ApplyAttributes (MethodBuilder mb, ConstructorBuilder cb, int index, PredefinedAttributes pa) + { + if (builder != null) + throw new InternalErrorException ("builder already exists"); + + var pattrs = ParametersCompiled.GetParameterAttribute (modFlags); + if (HasOptionalExpression) + pattrs |= ParameterAttributes.Optional; + + if (mb == null) + builder = cb.DefineParameter (index, pattrs, Name); + else + builder = mb.DefineParameter (index, pattrs, Name); + + if (OptAttributes != null) + OptAttributes.Emit (); + + if (HasDefaultValue && default_expr.Type != null) { + // + // Emit constant values for true constants only, the other + // constant-like expressions will rely on default value expression + // + var def_value = DefaultValue; + Constant c = def_value != null ? def_value.Child as Constant : default_expr as Constant; + if (c != null) { + if (c.Type.BuiltinType == BuiltinTypeSpec.Type.Decimal) { + pa.DecimalConstant.EmitAttribute (builder, (decimal) c.GetValue (), c.Location); + } else { + builder.SetConstant (c.GetValue ()); + } + } else if (default_expr.Type.IsStruct || default_expr.Type.IsGenericParameter) { + // + // Handles special case where default expression is used with value-type or type parameter + // + // void Foo (S s = default (S)) {} + // + builder.SetConstant (null); + } + } + + if (parameter_type != null) { + if (parameter_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + pa.Dynamic.EmitAttribute (builder); + } else if (parameter_type.HasDynamicElement) { + pa.Dynamic.EmitAttribute (builder, parameter_type, Location); + } + } + } + + public Parameter Clone () + { + Parameter p = (Parameter) MemberwiseClone (); + if (attributes != null) + p.attributes = attributes.Clone (); + + return p; + } + + public ExpressionStatement CreateExpressionTreeVariable (BlockContext ec) + { + if ((modFlags & Modifier.RefOutMask) != 0) + ec.Report.Error (1951, Location, "An expression tree parameter cannot use `ref' or `out' modifier"); + + expr_tree_variable = TemporaryVariableReference.Create (ResolveParameterExpressionType (ec, Location).Type, ec.CurrentBlock.ParametersBlock, Location); + expr_tree_variable = (TemporaryVariableReference) expr_tree_variable.Resolve (ec); + + Arguments arguments = new Arguments (2); + arguments.Add (new Argument (new TypeOf (parameter_type, Location))); + arguments.Add (new Argument (new StringConstant (ec.BuiltinTypes, Name, Location))); + return new SimpleAssign (ExpressionTreeVariableReference (), + Expression.CreateExpressionFactoryCall (ec, "Parameter", null, arguments, Location)); + } + + public void Emit (EmitContext ec) + { + ec.EmitArgumentLoad (idx); + } + + public void EmitAssign (EmitContext ec) + { + ec.EmitArgumentStore (idx); + } + + public void EmitAddressOf (EmitContext ec) + { + if ((ModFlags & Modifier.RefOutMask) != 0) { + ec.EmitArgumentLoad (idx); + } else { + ec.EmitArgumentAddress (idx); + } + } + + public TemporaryVariableReference ExpressionTreeVariableReference () + { + return expr_tree_variable; + } + + // + // System.Linq.Expressions.ParameterExpression type + // + public static TypeExpr ResolveParameterExpressionType (IMemberContext ec, Location location) + { + TypeSpec p_type = ec.Module.PredefinedTypes.ParameterExpression.Resolve (); + return new TypeExpression (p_type, location); + } + + public void SetIndex (int index) + { + idx = index; + } + + public void Warning_UselessOptionalParameter (Report Report) + { + Report.Warning (1066, 1, Location, + "The default value specified for optional parameter `{0}' will never be used", + Name); + } + } + + // + // Imported or resolved parameter information + // + public class ParameterData : IParameterData + { + readonly string name; + readonly Parameter.Modifier modifiers; + readonly Expression default_value; + + public ParameterData (string name, Parameter.Modifier modifiers) + { + this.name = name; + this.modifiers = modifiers; + } + + public ParameterData (string name, Parameter.Modifier modifiers, Expression defaultValue) + : this (name, modifiers) + { + this.default_value = defaultValue; + } + + #region IParameterData Members + + public Expression DefaultValue { + get { return default_value; } + } + + public bool HasExtensionMethodModifier { + get { return (modifiers & Parameter.Modifier.This) != 0; } + } + + public bool HasDefaultValue { + get { return default_value != null; } + } + + public Parameter.Modifier ModFlags { + get { return modifiers; } + } + + public string Name { + get { return name; } + } + + #endregion + } + + public abstract class AParametersCollection + { + protected bool has_arglist; + protected bool has_params; + + // Null object pattern + protected IParameterData [] parameters; + protected TypeSpec [] types; + + public CallingConventions CallingConvention { + get { + return has_arglist ? + CallingConventions.VarArgs : + CallingConventions.Standard; + } + } + + public int Count { + get { return parameters.Length; } + } + + public TypeSpec ExtensionMethodType { + get { + if (Count == 0) + return null; + + return FixedParameters [0].HasExtensionMethodModifier ? + types [0] : null; + } + } + + public IParameterData [] FixedParameters { + get { + return parameters; + } + } + + public static ParameterAttributes GetParameterAttribute (Parameter.Modifier modFlags) + { + return (modFlags & Parameter.Modifier.OUT) != 0 ? + ParameterAttributes.Out : ParameterAttributes.None; + } + + // Very expensive operation + public MetaType[] GetMetaInfo () + { + MetaType[] types; + if (has_arglist) { + if (Count == 1) + return MetaType.EmptyTypes; + + types = new MetaType[Count - 1]; + } else { + if (Count == 0) + return MetaType.EmptyTypes; + + types = new MetaType[Count]; + } + + for (int i = 0; i < types.Length; ++i) { + types[i] = Types[i].GetMetaInfo (); + + if ((FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask) == 0) + continue; + + // TODO MemberCache: Should go to MetaInfo getter + types [i] = types [i].MakeByRefType (); + } + + return types; + } + + // + // Returns the parameter information based on the name + // + public int GetParameterIndexByName (string name) + { + for (int idx = 0; idx < Count; ++idx) { + if (parameters [idx].Name == name) + return idx; + } + + return -1; + } + + public string GetSignatureForDocumentation () + { + if (IsEmpty) + return string.Empty; + + StringBuilder sb = new StringBuilder ("("); + for (int i = 0; i < Count; ++i) { + if (i != 0) + sb.Append (","); + + sb.Append (types [i].GetSignatureForDocumentation ()); + + if ((parameters[i].ModFlags & Parameter.Modifier.RefOutMask) != 0) + sb.Append ("@"); + } + sb.Append (")"); + + return sb.ToString (); + } + + public string GetSignatureForError () + { + return GetSignatureForError ("(", ")", Count); + } + + public string GetSignatureForError (string start, string end, int count) + { + StringBuilder sb = new StringBuilder (start); + for (int i = 0; i < count; ++i) { + if (i != 0) + sb.Append (", "); + sb.Append (ParameterDesc (i)); + } + sb.Append (end); + return sb.ToString (); + } + + public bool HasArglist { + get { return has_arglist; } + } + + public bool HasExtensionMethodType { + get { + if (Count == 0) + return false; + + return FixedParameters [0].HasExtensionMethodModifier; + } + } + + public bool HasParams { + get { return has_params; } + } + + public bool IsEmpty { + get { return parameters.Length == 0; } + } + + public AParametersCollection Inflate (TypeParameterInflator inflator) + { + TypeSpec[] inflated_types = null; + bool default_value = false; + + for (int i = 0; i < Count; ++i) { + var inflated_param = inflator.Inflate (types[i]); + if (inflated_types == null) { + if (inflated_param == types[i]) + continue; + + default_value |= FixedParameters[i].HasDefaultValue; + inflated_types = new TypeSpec[types.Length]; + Array.Copy (types, inflated_types, types.Length); + } else { + if (inflated_param == types[i]) + continue; + + default_value |= FixedParameters[i].HasDefaultValue; + } + + inflated_types[i] = inflated_param; + } + + if (inflated_types == null) + return this; + + var clone = (AParametersCollection) MemberwiseClone (); + clone.types = inflated_types; + + // + // Default expression is original expression from the parameter + // declaration context which can be of nested enum in generic class type. + // In such case we end up with expression type of G.E and e.g. parameter + // type of G.E and conversion would fail without inflate in this + // context. + // + if (default_value) { + clone.parameters = new IParameterData[Count]; + for (int i = 0; i < Count; ++i) { + var fp = FixedParameters[i]; + clone.FixedParameters[i] = fp; + + if (!fp.HasDefaultValue) + continue; + + var expr = fp.DefaultValue; + + if (inflated_types[i] == expr.Type) + continue; + + var c = expr as Constant; + if (c != null) { + // + // It may fail we are inflating before type validation is done + // + c = Constant.ExtractConstantFromValue (inflated_types[i], c.GetValue (), expr.Location); + if (c == null) + expr = new DefaultValueExpression (new TypeExpression (inflated_types[i], expr.Location), expr.Location); + else + expr = c; + } else if (expr is DefaultValueExpression) + expr = new DefaultValueExpression (new TypeExpression (inflated_types[i], expr.Location), expr.Location); + + clone.FixedParameters[i] = new ParameterData (fp.Name, fp.ModFlags, expr); + } + } + + return clone; + } + + public string ParameterDesc (int pos) + { + if (types == null || types [pos] == null) + return ((Parameter)FixedParameters [pos]).GetSignatureForError (); + + string type = types [pos].GetSignatureForError (); + if (FixedParameters [pos].HasExtensionMethodModifier) + return "this " + type; + + var mod = FixedParameters[pos].ModFlags & Parameter.Modifier.ModifierMask; + if (mod == 0) + return type; + + return Parameter.GetModifierSignature (mod) + " " + type; + } + + public TypeSpec[] Types { + get { return types; } + set { types = value; } + } + } + + // + // A collection of imported or resolved parameters + // + public class ParametersImported : AParametersCollection + { + public ParametersImported (IParameterData [] parameters, TypeSpec [] types, bool hasArglist, bool hasParams) + { + this.parameters = parameters; + this.types = types; + this.has_arglist = hasArglist; + this.has_params = hasParams; + } + + public ParametersImported (IParameterData[] param, TypeSpec[] types, bool hasParams) + { + this.parameters = param; + this.types = types; + this.has_params = hasParams; + } + } + + /// + /// Represents the methods parameters + /// + public class ParametersCompiled : AParametersCollection + { + public static readonly ParametersCompiled EmptyReadOnlyParameters = new ParametersCompiled (); + + // Used by C# 2.0 delegates + public static readonly ParametersCompiled Undefined = new ParametersCompiled (); + + private ParametersCompiled () + { + parameters = new Parameter [0]; + types = TypeSpec.EmptyTypes; + } + + private ParametersCompiled (IParameterData[] parameters, TypeSpec[] types) + { + this.parameters = parameters; + this.types = types; + } + + public ParametersCompiled (params Parameter[] parameters) + { + if (parameters == null || parameters.Length == 0) + throw new ArgumentException ("Use EmptyReadOnlyParameters"); + + this.parameters = parameters; + int count = parameters.Length; + + for (int i = 0; i < count; i++){ + has_params |= (parameters [i].ModFlags & Parameter.Modifier.PARAMS) != 0; + } + } + + public ParametersCompiled (Parameter [] parameters, bool has_arglist) : + this (parameters) + { + this.has_arglist = has_arglist; + } + + public static ParametersCompiled CreateFullyResolved (Parameter p, TypeSpec type) + { + return new ParametersCompiled (new Parameter [] { p }, new TypeSpec [] { type }); + } + + public static ParametersCompiled CreateFullyResolved (Parameter[] parameters, TypeSpec[] types) + { + return new ParametersCompiled (parameters, types); + } + + public static ParametersCompiled Prefix (ParametersCompiled parameters, Parameter p, TypeSpec type) + { + var ptypes = new TypeSpec [parameters.Count + 1]; + ptypes [0] = type; + Array.Copy (parameters.Types, 0, ptypes, 1, parameters.Count); + + var param = new Parameter [ptypes.Length]; + param [0] = p; + for (int i = 0; i < parameters.Count; ++i) { + var pi = parameters [i]; + param [i + 1] = pi; + pi.SetIndex (i + 1); + } + + return ParametersCompiled.CreateFullyResolved (param, ptypes); + } + + // + // TODO: This does not fit here, it should go to different version of AParametersCollection + // as the underlying type is not Parameter and some methods will fail to cast + // + public static AParametersCollection CreateFullyResolved (params TypeSpec[] types) + { + var pd = new ParameterData [types.Length]; + for (int i = 0; i < pd.Length; ++i) + pd[i] = new ParameterData (null, Parameter.Modifier.NONE, null); + + return new ParametersCompiled (pd, types); + } + + public static ParametersCompiled CreateImplicitParameter (FullNamedExpression texpr, Location loc) + { + return new ParametersCompiled ( + new[] { new Parameter (texpr, "value", Parameter.Modifier.NONE, null, loc) }, + null); + } + + public void CheckConstraints (IMemberContext mc) + { + foreach (Parameter p in parameters) { + // + // It's null for compiler generated types or special types like __arglist + // + if (p.TypeExpression != null) + ConstraintChecker.Check (mc, p.Type, p.TypeExpression.Location); + } + } + + // + // Returns non-zero value for equal CLS parameter signatures + // + public static int IsSameClsSignature (AParametersCollection a, AParametersCollection b) + { + int res = 0; + + for (int i = 0; i < a.Count; ++i) { + var a_type = a.Types[i]; + var b_type = b.Types[i]; + if (TypeSpecComparer.Override.IsEqual (a_type, b_type)) { + if ((a.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask) != (b.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask)) + res |= 1; + + continue; + } + + var ac_a = a_type as ArrayContainer; + if (ac_a == null) + return 0; + + var ac_b = b_type as ArrayContainer; + if (ac_b == null) + return 0; + + if (ac_a.Element is ArrayContainer || ac_b.Element is ArrayContainer) { + res |= 2; + continue; + } + + if (ac_a.Rank != ac_b.Rank && TypeSpecComparer.Override.IsEqual (ac_a.Element, ac_b.Element)) { + res |= 1; + continue; + } + + return 0; + } + + return res; + } + + public static ParametersCompiled MergeGenerated (CompilerContext ctx, ParametersCompiled userParams, bool checkConflicts, Parameter compilerParams, TypeSpec compilerTypes) + { + return MergeGenerated (ctx, userParams, checkConflicts, + new Parameter [] { compilerParams }, + new TypeSpec [] { compilerTypes }); + } + + // + // Use this method when you merge compiler generated parameters with user parameters + // + public static ParametersCompiled MergeGenerated (CompilerContext ctx, ParametersCompiled userParams, bool checkConflicts, Parameter[] compilerParams, TypeSpec[] compilerTypes) + { + Parameter[] all_params = new Parameter [userParams.Count + compilerParams.Length]; + userParams.FixedParameters.CopyTo(all_params, 0); + + TypeSpec [] all_types; + if (userParams.types != null) { + all_types = new TypeSpec [all_params.Length]; + userParams.Types.CopyTo (all_types, 0); + } else { + all_types = null; + } + + int last_filled = userParams.Count; + int index = 0; + foreach (Parameter p in compilerParams) { + for (int i = 0; i < last_filled; ++i) { + while (p.Name == all_params [i].Name) { + if (checkConflicts && i < userParams.Count) { + ctx.Report.Error (316, userParams[i].Location, + "The parameter name `{0}' conflicts with a compiler generated name", p.Name); + } + p.Name = '_' + p.Name; + } + } + all_params [last_filled] = p; + if (all_types != null) + all_types [last_filled] = compilerTypes [index++]; + ++last_filled; + } + + ParametersCompiled parameters = new ParametersCompiled (all_params, all_types); + parameters.has_params = userParams.has_params; + return parameters; + } + + // + // Parameters checks for members which don't have a block + // + public void CheckParameters (MemberCore member) + { + for (int i = 0; i < parameters.Length; ++i) { + var name = parameters[i].Name; + for (int ii = i + 1; ii < parameters.Length; ++ii) { + if (parameters[ii].Name == name) + this[ii].Error_DuplicateName (member.Compiler.Report); + } + } + } + + public bool Resolve (IMemberContext ec) + { + if (types != null) + return true; + + types = new TypeSpec [Count]; + + bool ok = true; + Parameter p; + for (int i = 0; i < FixedParameters.Length; ++i) { + p = this [i]; + TypeSpec t = p.Resolve (ec, i); + if (t == null) { + ok = false; + continue; + } + + types [i] = t; + } + + return ok; + } + + public void ResolveDefaultValues (MemberCore m) + { + ResolveContext rc = null; + for (int i = 0; i < parameters.Length; ++i) { + Parameter p = (Parameter) parameters [i]; + + // + // Try not to enter default values resolution if there are is not any default value possible + // + if (p.HasDefaultValue || p.OptAttributes != null) { + if (rc == null) + rc = new ResolveContext (m); + + p.ResolveDefaultValue (rc); + } + } + } + + // Define each type attribute (in/out/ref) and + // the argument names. + public void ApplyAttributes (IMemberContext mc, MethodBase builder) + { + if (Count == 0) + return; + + MethodBuilder mb = builder as MethodBuilder; + ConstructorBuilder cb = builder as ConstructorBuilder; + var pa = mc.Module.PredefinedAttributes; + + for (int i = 0; i < Count; i++) { + this [i].ApplyAttributes (mb, cb, i + 1, pa); + } + } + + public void VerifyClsCompliance (IMemberContext ctx) + { + foreach (Parameter p in FixedParameters) + p.IsClsCompliant (ctx); + } + + public Parameter this [int pos] { + get { return (Parameter) parameters [pos]; } + } + + public Expression CreateExpressionTree (BlockContext ec, Location loc) + { + var initializers = new ArrayInitializer (Count, loc); + foreach (Parameter p in FixedParameters) { + // + // Each parameter expression is stored to local variable + // to save some memory when referenced later. + // + StatementExpression se = new StatementExpression (p.CreateExpressionTreeVariable (ec), Location.Null); + if (se.Resolve (ec)) { + ec.CurrentBlock.AddScopeStatement (new TemporaryVariableReference.Declarator (p.ExpressionTreeVariableReference ())); + ec.CurrentBlock.AddScopeStatement (se); + } + + initializers.Add (p.ExpressionTreeVariableReference ()); + } + + return new ArrayCreation ( + Parameter.ResolveParameterExpressionType (ec, loc), + initializers, loc); + } + + public ParametersCompiled Clone () + { + ParametersCompiled p = (ParametersCompiled) MemberwiseClone (); + + p.parameters = new IParameterData [parameters.Length]; + for (int i = 0; i < Count; ++i) + p.parameters [i] = this [i].Clone (); + + return p; + } + } + + // + // Default parameter value expression. We need this wrapper to handle + // default parameter values of folded constants (e.g. indexer parameters). + // The expression is resolved only once but applied to two methods which + // both share reference to this expression and we ensure that resolving + // this expression always returns same instance + // + public class DefaultParameterValueExpression : CompositeExpression + { + public DefaultParameterValueExpression (Expression expr) + : base (expr) + { + } + + public void Resolve (ResolveContext rc, Parameter p) + { + var expr = Resolve (rc); + if (expr == null) { + this.expr = ErrorExpression.Instance; + return; + } + + expr = Child; + + if (!(expr is Constant || expr is DefaultValueExpression || (expr is New && ((New) expr).IsDefaultStruct))) { + if (!(expr is ErrorExpression)) { + rc.Report.Error (1736, Location, + "The expression being assigned to optional parameter `{0}' must be a constant or default value", + p.Name); + } + + return; + } + + var parameter_type = p.Type; + if (type == parameter_type) + return; + + var res = Convert.ImplicitConversionStandard (rc, expr, parameter_type, Location); + if (res != null) { + if (parameter_type.IsNullableType && res is Nullable.Wrap) { + Nullable.Wrap wrap = (Nullable.Wrap) res; + res = wrap.Child; + if (!(res is Constant)) { + rc.Report.Error (1770, Location, + "The expression being assigned to nullable optional parameter `{0}' must be default value", + p.Name); + return; + } + } + + if (!expr.IsNull && TypeSpec.IsReferenceType (parameter_type) && parameter_type.BuiltinType != BuiltinTypeSpec.Type.String) { + rc.Report.Error (1763, Location, + "Optional parameter `{0}' of type `{1}' can only be initialized with `null'", + p.Name, parameter_type.GetSignatureForError ()); + + return; + } + + this.expr = res; + return; + } + + rc.Report.Error (1750, Location, + "Optional parameter expression of type `{0}' cannot be converted to parameter type `{1}'", + type.GetSignatureForError (), parameter_type.GetSignatureForError ()); + + this.expr = ErrorExpression.Instance; + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/pending.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/pending.cs new file mode 100644 index 000000000..0f863a7bc --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/pending.cs @@ -0,0 +1,734 @@ +// +// pending.cs: Pending method implementation +// +// Authors: +// Miguel de Icaza (miguel@gnu.org) +// Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2001, 2002 Ximian, Inc (http://www.ximian.com) +// Copyright 2003-2008 Novell, Inc. +// Copyright 2011 Xamarin Inc +// + +using System; +using System.Collections.Generic; +using System.Linq; + +#if STATIC +using IKVM.Reflection; +using IKVM.Reflection.Emit; +#else +using System.Reflection; +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp { + + struct TypeAndMethods { + public TypeSpec type; + public IList methods; + + // + // Whether it is optional, this is used to allow the explicit/implicit + // implementation when a base class already implements an interface. + // + // For example: + // + // class X : IA { } class Y : X, IA { IA.Explicit (); } + // + public bool optional; + + // + // This flag on the method says `We found a match, but + // because it was private, we could not use the match + // + public MethodData [] found; + + // If a method is defined here, then we always need to + // create a proxy for it. This is used when implementing + // an interface's indexer with a different IndexerName. + public MethodSpec [] need_proxy; + } + + struct ProxyMethodContext : IMemberContext + { + readonly TypeContainer container; + + public ProxyMethodContext (TypeContainer container) + { + this.container = container; + } + + public TypeSpec CurrentType { + get { + throw new NotImplementedException (); + } + } + + public TypeParameters CurrentTypeParameters { + get { + throw new NotImplementedException (); + } + } + + public MemberCore CurrentMemberDefinition { + get { + throw new NotImplementedException (); + } + } + + public bool IsObsolete { + get { + return false; + } + } + + public bool IsUnsafe { + get { + throw new NotImplementedException (); + } + } + + public bool IsStatic { + get { + return false; + } + } + + public ModuleContainer Module { + get { + return container.Module; + } + } + + public string GetSignatureForError () + { + throw new NotImplementedException (); + } + + public ExtensionMethodCandidates LookupExtensionMethod (TypeSpec extensionType, string name, int arity) + { + throw new NotImplementedException (); + } + + public FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc) + { + throw new NotImplementedException (); + } + + public FullNamedExpression LookupNamespaceAlias (string name) + { + throw new NotImplementedException (); + } + } + + public class PendingImplementation + { + /// + /// The container for this PendingImplementation + /// + readonly TypeDefinition container; + + /// + /// This is the array of TypeAndMethods that describes the pending implementations + /// (both interfaces and abstract methods in base class) + /// + TypeAndMethods [] pending_implementations; + + PendingImplementation (TypeDefinition container, MissingInterfacesInfo[] missing_ifaces, MethodSpec[] abstract_methods, int total) + { + var type_builder = container.Definition; + + this.container = container; + pending_implementations = new TypeAndMethods [total]; + + int i = 0; + if (abstract_methods != null) { + int count = abstract_methods.Length; + pending_implementations [i].methods = new MethodSpec [count]; + pending_implementations [i].need_proxy = new MethodSpec [count]; + + pending_implementations [i].methods = abstract_methods; + pending_implementations [i].found = new MethodData [count]; + pending_implementations [i].type = type_builder; + ++i; + } + + foreach (MissingInterfacesInfo missing in missing_ifaces) { + var iface = missing.Type; + var mi = MemberCache.GetInterfaceMethods (iface); + + int count = mi.Count; + pending_implementations [i].type = iface; + pending_implementations [i].optional = missing.Optional; + pending_implementations [i].methods = mi; + pending_implementations [i].found = new MethodData [count]; + pending_implementations [i].need_proxy = new MethodSpec [count]; + i++; + } + } + + Report Report { + get { + return container.Module.Compiler.Report; + } + } + + struct MissingInterfacesInfo { + public TypeSpec Type; + public bool Optional; + + public MissingInterfacesInfo (TypeSpec t) + { + Type = t; + Optional = false; + } + } + + static readonly MissingInterfacesInfo [] EmptyMissingInterfacesInfo = new MissingInterfacesInfo [0]; + + static MissingInterfacesInfo [] GetMissingInterfaces (TypeDefinition container) + { + // + // Interfaces will return all interfaces that the container + // implements including any inherited interfaces + // + var impl = container.Definition.Interfaces; + + if (impl == null || impl.Count == 0) + return EmptyMissingInterfacesInfo; + + var ret = new MissingInterfacesInfo[impl.Count]; + + for (int i = 0; i < ret.Length; i++) + ret [i] = new MissingInterfacesInfo (impl [i]); + + // we really should not get here because Object doesnt implement any + // interfaces. But it could implement something internal, so we have + // to handle that case. + if (container.BaseType == null) + return ret; + + var base_impls = container.BaseType.Interfaces; + if (base_impls != null) { + foreach (TypeSpec t in base_impls) { + for (int i = 0; i < ret.Length; i++) { + if (t == ret[i].Type) { + ret[i].Optional = true; + break; + } + } + } + } + + return ret; + } + + // + // Factory method: if there are pending implementation methods, we return a PendingImplementation + // object, otherwise we return null. + // + // Register method implementations are either abstract methods + // flagged as such on the base class or interface methods + // + static public PendingImplementation GetPendingImplementations (TypeDefinition container) + { + TypeSpec b = container.BaseType; + + var missing_interfaces = GetMissingInterfaces (container); + + // + // If we are implementing an abstract class, and we are not + // ourselves abstract, and there are abstract methods (C# allows + // abstract classes that have no abstract methods), then allocate + // one slot. + // + // We also pre-compute the methods. + // + bool implementing_abstract = ((b != null) && b.IsAbstract && (container.ModFlags & Modifiers.ABSTRACT) == 0); + MethodSpec[] abstract_methods = null; + + if (implementing_abstract){ + var am = MemberCache.GetNotImplementedAbstractMethods (b); + + if (am == null) { + implementing_abstract = false; + } else { + abstract_methods = new MethodSpec[am.Count]; + am.CopyTo (abstract_methods, 0); + } + } + + int total = missing_interfaces.Length + (implementing_abstract ? 1 : 0); + if (total == 0) + return null; + + var pending = new PendingImplementation (container, missing_interfaces, abstract_methods, total); + + // + // check for inherited conflicting methods + // + foreach (var p in pending.pending_implementations) { + // + // It can happen for generic interfaces only + // + if (!p.type.IsGeneric) + continue; + + // + // CLR does not distinguishes between ref and out + // + for (int i = 0; i < p.methods.Count; ++i) { + MethodSpec compared_method = p.methods[i]; + if (compared_method.Parameters.IsEmpty) + continue; + + for (int ii = i + 1; ii < p.methods.Count; ++ii) { + MethodSpec tested_method = p.methods[ii]; + if (compared_method.Name != tested_method.Name) + continue; + + if (p.type != tested_method.DeclaringType) + continue; + + if (!TypeSpecComparer.Override.IsSame (compared_method.Parameters.Types, tested_method.Parameters.Types)) + continue; + + bool exact_match = true; + bool ref_only_difference = false; + var cp = compared_method.Parameters.FixedParameters; + var tp = tested_method.Parameters.FixedParameters; + + for (int pi = 0; pi < cp.Length; ++pi) { + // + // First check exact modifiers match + // + if ((cp[pi].ModFlags & Parameter.Modifier.RefOutMask) == (tp[pi].ModFlags & Parameter.Modifier.RefOutMask)) + continue; + + if (((cp[pi].ModFlags | tp[pi].ModFlags) & Parameter.Modifier.RefOutMask) == Parameter.Modifier.RefOutMask) { + ref_only_difference = true; + continue; + } + + exact_match = false; + break; + } + + if (!exact_match || !ref_only_difference) + continue; + + pending.Report.SymbolRelatedToPreviousError (compared_method); + pending.Report.SymbolRelatedToPreviousError (tested_method); + pending.Report.Error (767, container.Location, + "Cannot implement interface `{0}' with the specified type parameters because it causes method `{1}' to differ on parameter modifiers only", + p.type.GetDefinition().GetSignatureForError (), compared_method.GetSignatureForError ()); + + break; + } + } + } + + return pending; + } + + public enum Operation { + // + // If you change this, review the whole InterfaceMethod routine as there + // are a couple of assumptions on these three states + // + Lookup, ClearOne, ClearAll + } + + /// + /// Whether the specified method is an interface method implementation + /// + public MethodSpec IsInterfaceMethod (MemberName name, TypeSpec ifaceType, MethodData method, out MethodSpec ambiguousCandidate, ref bool optional) + { + return InterfaceMethod (name, ifaceType, method, Operation.Lookup, out ambiguousCandidate, ref optional); + } + + public void ImplementMethod (MemberName name, TypeSpec ifaceType, MethodData method, bool clear_one, out MethodSpec ambiguousCandidate, ref bool optional) + { + InterfaceMethod (name, ifaceType, method, clear_one ? Operation.ClearOne : Operation.ClearAll, out ambiguousCandidate, ref optional); + } + + /// + /// If a method in Type `t' (or null to look in all interfaces + /// and the base abstract class) with name `Name', return type `ret_type' and + /// arguments `args' implements an interface, this method will + /// return the MethodInfo that this method implements. + /// + /// If `name' is null, we operate solely on the method's signature. This is for + /// instance used when implementing indexers. + /// + /// The `Operation op' controls whether to lookup, clear the pending bit, or clear + /// all the methods with the given signature. + /// + /// The `MethodInfo need_proxy' is used when we're implementing an interface's + /// indexer in a class. If the new indexer's IndexerName does not match the one + /// that was used in the interface, then we always need to create a proxy for it. + /// + /// + public MethodSpec InterfaceMethod (MemberName name, TypeSpec iType, MethodData method, Operation op, out MethodSpec ambiguousCandidate, ref bool optional) + { + ambiguousCandidate = null; + + if (pending_implementations == null) + return null; + + TypeSpec ret_type = method.method.ReturnType; + ParametersCompiled args = method.method.ParameterInfo; + bool is_indexer = method.method is Indexer.SetIndexerMethod || method.method is Indexer.GetIndexerMethod; + MethodSpec m; + + foreach (TypeAndMethods tm in pending_implementations){ + if (!(iType == null || tm.type == iType)) + continue; + + int method_count = tm.methods.Count; + for (int i = 0; i < method_count; i++){ + m = tm.methods [i]; + + if (m == null) + continue; + + if (is_indexer) { + if (!m.IsAccessor || m.Parameters.IsEmpty) + continue; + } else { + if (name.Name != m.Name) + continue; + + if (m.Arity != name.Arity) + continue; + } + + if (!TypeSpecComparer.Override.IsEqual (m.Parameters, args)) + continue; + + if (!TypeSpecComparer.Override.IsEqual (m.ReturnType, ret_type)) { + tm.found[i] = method; + continue; + } + + // + // `need_proxy' is not null when we're implementing an + // interface indexer and this is Clear(One/All) operation. + // + // If `name' is null, then we do a match solely based on the + // signature and not on the name (this is done in the Lookup + // for an interface indexer). + // + if (op != Operation.Lookup) { + if (m.IsAccessor != method.method.IsAccessor) + continue; + + // If `t != null', then this is an explicitly interface + // implementation and we can always clear the method. + // `need_proxy' is not null if we're implementing an + // interface indexer. In this case, we need to create + // a proxy if the implementation's IndexerName doesn't + // match the IndexerName in the interface. + if (m.DeclaringType.IsInterface && iType == null && name.Name != m.Name) { // TODO: This is very expensive comparison + tm.need_proxy[i] = method.method.Spec; + } else { + tm.methods[i] = null; + } + } else { + tm.found [i] = method; + optional = tm.optional; + } + + if (op == Operation.Lookup && name.ExplicitInterface != null && ambiguousCandidate == null) { + ambiguousCandidate = m; + continue; + } + + // + // Lookups and ClearOne return + // + if (op != Operation.ClearAll) + return m; + } + + // If a specific type was requested, we can stop now. + if (tm.type == iType) + break; + } + + m = ambiguousCandidate; + ambiguousCandidate = null; + return m; + } + + /// + /// C# allows this kind of scenarios: + /// interface I { void M (); } + /// class X { public void M (); } + /// class Y : X, I { } + /// + /// For that case, we create an explicit implementation function + /// I.M in Y. + /// + void DefineProxy (TypeSpec iface, MethodSpec base_method, MethodSpec iface_method) + { + // TODO: Handle nested iface names + string proxy_name; + var ns = iface.MemberDefinition.Namespace; + if (string.IsNullOrEmpty (ns)) + proxy_name = iface.MemberDefinition.Name + "." + iface_method.Name; + else + proxy_name = ns + "." + iface.MemberDefinition.Name + "." + iface_method.Name; + + var param = iface_method.Parameters; + + MethodBuilder proxy = container.TypeBuilder.DefineMethod ( + proxy_name, + MethodAttributes.Private | + MethodAttributes.HideBySig | + MethodAttributes.NewSlot | + MethodAttributes.CheckAccessOnOverride | + MethodAttributes.Virtual | MethodAttributes.Final, + CallingConventions.Standard | CallingConventions.HasThis, + base_method.ReturnType.GetMetaInfo (), param.GetMetaInfo ()); + + if (iface_method.IsGeneric) { + var gnames = iface_method.GenericDefinition.TypeParameters.Select (l => l.Name).ToArray (); + proxy.DefineGenericParameters (gnames); + } + + for (int i = 0; i < param.Count; i++) { + string name = param.FixedParameters [i].Name; + ParameterAttributes attr = ParametersCompiled.GetParameterAttribute (param.FixedParameters [i].ModFlags); + proxy.DefineParameter (i + 1, attr, name); + } + + int top = param.Count; + var ec = new EmitContext (new ProxyMethodContext (container), proxy.GetILGenerator (), null, null); + ec.EmitThis (); + // TODO: GetAllParametersArguments + for (int i = 0; i < top; i++) + ec.EmitArgumentLoad (i); + + ec.Emit (OpCodes.Call, base_method); + ec.Emit (OpCodes.Ret); + + container.TypeBuilder.DefineMethodOverride (proxy, (MethodInfo) iface_method.GetMetaInfo ()); + } + + /// + /// This function tells whether one of our base classes implements + /// the given method (which turns out, it is valid to have an interface + /// implementation in a base + /// + bool BaseImplements (TypeSpec iface_type, MethodSpec mi, out MethodSpec base_method) + { + base_method = null; + var base_type = container.BaseType; + + // + // Setup filter with no return type to give better error message + // about mismatch at return type when the check bellow rejects them + // + var parameters = mi.Parameters; + MethodSpec close_match = null; + + while (true) { + var candidates = MemberCache.FindMembers (base_type, mi.Name, false); + if (candidates == null) { + base_method = close_match; + return false; + } + + MethodSpec similar_candidate = null; + foreach (var candidate in candidates) { + if (candidate.Kind != MemberKind.Method) + continue; + + if (candidate.Arity != mi.Arity) + continue; + + var candidate_param = ((MethodSpec) candidate).Parameters; + if (!TypeSpecComparer.Override.IsEqual (parameters.Types, candidate_param.Types)) + continue; + + bool modifiers_match = true; + for (int i = 0; i < parameters.Count; ++i) { + // + // First check exact ref/out match + // + if ((parameters.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask) == (candidate_param.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask)) + continue; + + modifiers_match = false; + + // + // Different in ref/out only + // + if ((parameters.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask) != (candidate_param.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask)) { + if (similar_candidate == null) { + if (!candidate.IsPublic) + break; + + if (!TypeSpecComparer.Override.IsEqual (mi.ReturnType, ((MethodSpec) candidate).ReturnType)) + break; + + // It's used for ref/out ambiguity overload check + similar_candidate = (MethodSpec) candidate; + } + + continue; + } + + similar_candidate = null; + break; + } + + if (!modifiers_match) + continue; + + // + // From this point the candidate is used for detailed error reporting + // because it's very close match to what we are looking for + // + var m = (MethodSpec) candidate; + + if (!m.IsPublic) { + if (close_match == null) + close_match = m; + + continue; + } + + if (!TypeSpecComparer.Override.IsEqual (mi.ReturnType, m.ReturnType)) { + if (close_match == null) + close_match = m; + + continue; + } + + base_method = m; + + if (mi.IsGeneric && !Method.CheckImplementingMethodConstraints (container, m, mi)) { + return true; + } + } + + if (base_method != null) { + if (similar_candidate != null) { + Report.SymbolRelatedToPreviousError (similar_candidate); + Report.SymbolRelatedToPreviousError (mi); + Report.SymbolRelatedToPreviousError (container); + Report.Warning (1956, 1, ((MemberCore) base_method.MemberDefinition).Location, + "The interface method `{0}' implementation is ambiguous between following methods: `{1}' and `{2}' in type `{3}'", + mi.GetSignatureForError (), base_method.GetSignatureForError (), similar_candidate.GetSignatureForError (), container.GetSignatureForError ()); + } + + break; + } + + base_type = candidates[0].DeclaringType.BaseType; + if (base_type == null) { + base_method = close_match; + return false; + } + } + + if (!base_method.IsVirtual) { +#if STATIC + var base_builder = base_method.GetMetaInfo () as MethodBuilder; + if (base_builder != null) { + // + // We can avoid creating a proxy if base_method can be marked 'final virtual'. This can + // be done for all methods from compiled assembly + // + base_builder.__SetAttributes (base_builder.Attributes | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.NewSlot); + return true; + } +#endif + DefineProxy (iface_type, base_method, mi); + } + + return true; + } + + /// + /// Verifies that any pending abstract methods or interface methods + /// were implemented. + /// + public bool VerifyPendingMethods () + { + int top = pending_implementations.Length; + bool errors = false; + int i; + + for (i = 0; i < top; i++){ + TypeSpec type = pending_implementations [i].type; + + bool base_implements_type = type.IsInterface && + container.BaseType != null && + container.BaseType.ImplementsInterface (type, false); + + for (int j = 0; j < pending_implementations [i].methods.Count; ++j) { + var mi = pending_implementations[i].methods[j]; + if (mi == null) + continue; + + if (type.IsInterface){ + var need_proxy = + pending_implementations [i].need_proxy [j]; + + if (need_proxy != null) { + DefineProxy (type, need_proxy, mi); + continue; + } + + if (pending_implementations [i].optional) + continue; + + MethodSpec candidate; + if (base_implements_type || BaseImplements (type, mi, out candidate)) + continue; + + if (candidate == null) { + MethodData md = pending_implementations [i].found [j]; + if (md != null) + candidate = md.method.Spec; + } + + Report.SymbolRelatedToPreviousError (mi); + if (candidate != null) { + Report.SymbolRelatedToPreviousError (candidate); + if (candidate.IsStatic) { + Report.Error (736, container.Location, + "`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' is static", + container.GetSignatureForError (), mi.GetSignatureForError (), candidate.GetSignatureForError ()); + } else if ((candidate.Modifiers & Modifiers.PUBLIC) == 0) { + Report.Error (737, container.Location, + "`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' is not public", + container.GetSignatureForError (), mi.GetSignatureForError (), candidate.GetSignatureForError ()); + } else { + Report.Error (738, container.Location, + "`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' return type `{3}' does not match interface member return type `{4}'", + container.GetSignatureForError (), mi.GetSignatureForError (), candidate.GetSignatureForError (), + candidate.ReturnType.GetSignatureForError (), mi.ReturnType.GetSignatureForError ()); + } + } else { + Report.Error (535, container.Location, "`{0}' does not implement interface member `{1}'", + container.GetSignatureForError (), mi.GetSignatureForError ()); + } + } else { + Report.SymbolRelatedToPreviousError (mi); + Report.Error (534, container.Location, "`{0}' does not implement inherited abstract member `{1}'", + container.GetSignatureForError (), mi.GetSignatureForError ()); + } + errors = true; + } + } + return errors; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/property.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/property.cs new file mode 100644 index 000000000..979a8e83f --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/property.cs @@ -0,0 +1,1807 @@ +// +// property.cs: Property based handlers +// +// Authors: Miguel de Icaza (miguel@gnu.org) +// Martin Baulig (martin@ximian.com) +// Marek Safar (marek.safar@seznam.cz) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com) +// Copyright 2004-2008 Novell, Inc +// Copyright 2011 Xamarin Inc +// + +using System; +using System.Collections.Generic; +using System.Text; +using Mono.CompilerServices.SymbolWriter; + +#if NET_2_1 +using XmlElement = System.Object; +#endif + +#if STATIC +using IKVM.Reflection; +using IKVM.Reflection.Emit; +#else +using System.Reflection; +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp +{ + // It is used as a base class for all property based members + // This includes properties, indexers, and events + public abstract class PropertyBasedMember : InterfaceMemberBase + { + protected PropertyBasedMember (TypeDefinition parent, FullNamedExpression type, Modifiers mod, Modifiers allowed_mod, MemberName name, Attributes attrs) + : base (parent, type, mod, allowed_mod, name, attrs) + { + } + + protected void CheckReservedNameConflict (string prefix, MethodSpec accessor) + { + string name; + AParametersCollection parameters; + if (accessor != null) { + name = accessor.Name; + parameters = accessor.Parameters; + } else { + name = prefix + ShortName; + if (IsExplicitImpl) + name = MemberName.Left + "." + name; + + if (this is Indexer) { + parameters = ((Indexer) this).ParameterInfo; + if (prefix[0] == 's') { + var data = new IParameterData[parameters.Count + 1]; + Array.Copy (parameters.FixedParameters, data, data.Length - 1); + data[data.Length - 1] = new ParameterData ("value", Parameter.Modifier.NONE); + var types = new TypeSpec[data.Length]; + Array.Copy (parameters.Types, types, data.Length - 1); + types[data.Length - 1] = member_type; + + parameters = new ParametersImported (data, types, false); + } + } else { + if (prefix[0] == 's') + parameters = ParametersCompiled.CreateFullyResolved (new[] { member_type }); + else + parameters = ParametersCompiled.EmptyReadOnlyParameters; + } + } + + var conflict = MemberCache.FindMember (Parent.Definition, + new MemberFilter (name, 0, MemberKind.Method, parameters, null), + BindingRestriction.DeclaredOnly | BindingRestriction.NoAccessors); + + if (conflict != null) { + Report.SymbolRelatedToPreviousError (conflict); + Report.Error (82, Location, "A member `{0}' is already reserved", conflict.GetSignatureForError ()); + } + } + + public abstract void PrepareEmit (); + + protected override bool VerifyClsCompliance () + { + if (!base.VerifyClsCompliance ()) + return false; + + if (!MemberType.IsCLSCompliant ()) { + Report.Warning (3003, 1, Location, "Type of `{0}' is not CLS-compliant", + GetSignatureForError ()); + } + return true; + } + + } + + public class PropertySpec : MemberSpec, IInterfaceMemberSpec + { + PropertyInfo info; + TypeSpec memberType; + MethodSpec set, get; + + public PropertySpec (MemberKind kind, TypeSpec declaringType, IMemberDefinition definition, TypeSpec memberType, PropertyInfo info, Modifiers modifiers) + : base (kind, declaringType, definition, modifiers) + { + this.info = info; + this.memberType = memberType; + } + + #region Properties + + public MethodSpec Get { + get { + return get; + } + set { + get = value; + get.IsAccessor = true; + } + } + + public MethodSpec Set { + get { + return set; + } + set { + set = value; + set.IsAccessor = true; + } + } + + public bool HasDifferentAccessibility { + get { + return HasGet && HasSet && + (Get.Modifiers & Modifiers.AccessibilityMask) != (Set.Modifiers & Modifiers.AccessibilityMask); + } + } + + public bool HasGet { + get { + return Get != null; + } + } + + public bool HasSet { + get { + return Set != null; + } + } + + public PropertyInfo MetaInfo { + get { + if ((state & StateFlags.PendingMetaInflate) != 0) + throw new NotSupportedException (); + + return info; + } + } + + public TypeSpec MemberType { + get { + return memberType; + } + } + + #endregion + + public override MemberSpec InflateMember (TypeParameterInflator inflator) + { + var ps = (PropertySpec) base.InflateMember (inflator); + ps.memberType = inflator.Inflate (memberType); + return ps; + } + + public override List ResolveMissingDependencies (MemberSpec caller) + { + return memberType.ResolveMissingDependencies (this); + } + } + + // + // Properties and Indexers both generate PropertyBuilders, we use this to share + // their common bits. + // + abstract public class PropertyBase : PropertyBasedMember { + + public class GetMethod : PropertyMethod + { + static readonly string[] attribute_targets = new string [] { "method", "return" }; + + internal const string Prefix = "get_"; + + public GetMethod (PropertyBase method, Modifiers modifiers, Attributes attrs, Location loc) + : base (method, Prefix, modifiers, attrs, loc) + { + } + + public override void Define (TypeContainer parent) + { + base.Define (parent); + + Spec = new MethodSpec (MemberKind.Method, parent.PartialContainer.Definition, this, ReturnType, ParameterInfo, ModFlags); + + method_data = new MethodData (method, ModFlags, flags, this); + + method_data.Define (parent.PartialContainer, method.GetFullName (MemberName)); + } + + public override TypeSpec ReturnType { + get { + return method.MemberType; + } + } + + public override ParametersCompiled ParameterInfo { + get { + return ParametersCompiled.EmptyReadOnlyParameters; + } + } + + public override string[] ValidAttributeTargets { + get { + return attribute_targets; + } + } + } + + public class SetMethod : PropertyMethod { + + static readonly string[] attribute_targets = new string[] { "method", "param", "return" }; + + internal const string Prefix = "set_"; + + protected ParametersCompiled parameters; + + public SetMethod (PropertyBase method, Modifiers modifiers, ParametersCompiled parameters, Attributes attrs, Location loc) + : base (method, Prefix, modifiers, attrs, loc) + { + this.parameters = parameters; + } + + protected override void ApplyToExtraTarget (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.Target == AttributeTargets.Parameter) { + parameters[0].ApplyAttributeBuilder (a, ctor, cdata, pa); + return; + } + + base.ApplyToExtraTarget (a, ctor, cdata, pa); + } + + public override ParametersCompiled ParameterInfo { + get { + return parameters; + } + } + + public override void Define (TypeContainer parent) + { + parameters.Resolve (this); + + base.Define (parent); + + Spec = new MethodSpec (MemberKind.Method, parent.PartialContainer.Definition, this, ReturnType, ParameterInfo, ModFlags); + + method_data = new MethodData (method, ModFlags, flags, this); + + method_data.Define (parent.PartialContainer, method.GetFullName (MemberName)); + } + + public override TypeSpec ReturnType { + get { + return Parent.Compiler.BuiltinTypes.Void; + } + } + + public override string[] ValidAttributeTargets { + get { + return attribute_targets; + } + } + } + + static readonly string[] attribute_targets = new string[] { "property" }; + + public abstract class PropertyMethod : AbstractPropertyEventMethod + { + const Modifiers AllowedModifiers = + Modifiers.PUBLIC | + Modifiers.PROTECTED | + Modifiers.INTERNAL | + Modifiers.PRIVATE; + + protected readonly PropertyBase method; + protected MethodAttributes flags; + + public PropertyMethod (PropertyBase method, string prefix, Modifiers modifiers, Attributes attrs, Location loc) + : base (method, prefix, attrs, loc) + { + this.method = method; + this.ModFlags = ModifiersExtensions.Check (AllowedModifiers, modifiers, 0, loc, Report); + this.ModFlags |= (method.ModFlags & (Modifiers.STATIC | Modifiers.UNSAFE)); + } + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.Type == pa.MethodImpl) { + method.is_external_implementation = a.IsInternalCall (); + } + + base.ApplyAttributeBuilder (a, ctor, cdata, pa); + } + + public override AttributeTargets AttributeTargets { + get { + return AttributeTargets.Method; + } + } + + public override bool IsClsComplianceRequired () + { + return method.IsClsComplianceRequired (); + } + + public virtual void Define (TypeContainer parent) + { + var container = parent.PartialContainer; + + // + // Check for custom access modifier + // + if ((ModFlags & Modifiers.AccessibilityMask) == 0) { + ModFlags |= method.ModFlags; + flags = method.flags; + } else { + if (container.Kind == MemberKind.Interface) + Report.Error (275, Location, "`{0}': accessibility modifiers may not be used on accessors in an interface", + GetSignatureForError ()); + else if ((method.ModFlags & Modifiers.ABSTRACT) != 0 && (ModFlags & Modifiers.PRIVATE) != 0) { + Report.Error (442, Location, "`{0}': abstract properties cannot have private accessors", GetSignatureForError ()); + } + + CheckModifiers (ModFlags); + ModFlags |= (method.ModFlags & (~Modifiers.AccessibilityMask)); + ModFlags |= Modifiers.PROPERTY_CUSTOM; + flags = ModifiersExtensions.MethodAttr (ModFlags); + flags |= (method.flags & (~MethodAttributes.MemberAccessMask)); + } + + CheckAbstractAndExtern (block != null); + CheckProtectedModifier (); + + if (block != null) { + if (block.IsIterator) + Iterator.CreateIterator (this, Parent.PartialContainer, ModFlags); + + if (Compiler.Settings.WriteMetadataOnly) + block = null; + } + } + + public bool HasCustomAccessModifier { + get { + return (ModFlags & Modifiers.PROPERTY_CUSTOM) != 0; + } + } + + public PropertyBase Property { + get { + return method; + } + } + + public override ObsoleteAttribute GetAttributeObsolete () + { + return method.GetAttributeObsolete (); + } + + public override string GetSignatureForError() + { + return method.GetSignatureForError () + "." + prefix.Substring (0, 3); + } + + void CheckModifiers (Modifiers modflags) + { + if (!ModifiersExtensions.IsRestrictedModifier (modflags & Modifiers.AccessibilityMask, method.ModFlags & Modifiers.AccessibilityMask)) { + Report.Error (273, Location, + "The accessibility modifier of the `{0}' accessor must be more restrictive than the modifier of the property or indexer `{1}'", + GetSignatureForError (), method.GetSignatureForError ()); + } + } + } + + PropertyMethod get, set, first; + PropertyBuilder PropertyBuilder; + + protected PropertyBase (TypeDefinition parent, FullNamedExpression type, Modifiers mod_flags, Modifiers allowed_mod, MemberName name, Attributes attrs) + : base (parent, type, mod_flags, allowed_mod, name, attrs) + { + } + + #region Properties + + public override AttributeTargets AttributeTargets { + get { + return AttributeTargets.Property; + } + } + + public PropertyMethod AccessorFirst { + get { + return first; + } + } + + public PropertyMethod AccessorSecond { + get { + return first == get ? set : get; + } + } + + public override Variance ExpectedMemberTypeVariance { + get { + return (get != null && set != null) ? + Variance.None : set == null ? + Variance.Covariant : + Variance.Contravariant; + } + } + + public PropertyMethod Get { + get { + return get; + } + set { + get = value; + if (first == null) + first = value; + + Parent.AddNameToContainer (get, get.MemberName.Basename); + } + } + + public PropertyMethod Set { + get { + return set; + } + set { + set = value; + if (first == null) + first = value; + + Parent.AddNameToContainer (set, set.MemberName.Basename); + } + } + + public override string[] ValidAttributeTargets { + get { + return attribute_targets; + } + } + + #endregion + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.HasSecurityAttribute) { + a.Error_InvalidSecurityParent (); + return; + } + + if (a.Type == pa.Dynamic) { + a.Error_MisusedDynamicAttribute (); + return; + } + + PropertyBuilder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), cdata); + } + + void CheckMissingAccessor (MemberKind kind, ParametersCompiled parameters, bool get) + { + if (IsExplicitImpl) { + MemberFilter filter; + if (kind == MemberKind.Indexer) + filter = new MemberFilter (MemberCache.IndexerNameAlias, 0, kind, parameters, null); + else + filter = new MemberFilter (MemberName.Name, 0, kind, null, null); + + var implementing = MemberCache.FindMember (InterfaceType, filter, BindingRestriction.DeclaredOnly) as PropertySpec; + + if (implementing == null) + return; + + var accessor = get ? implementing.Get : implementing.Set; + if (accessor != null) { + Report.SymbolRelatedToPreviousError (accessor); + Report.Error (551, Location, "Explicit interface implementation `{0}' is missing accessor `{1}'", + GetSignatureForError (), accessor.GetSignatureForError ()); + } + } + } + + protected override bool CheckOverrideAgainstBase (MemberSpec base_member) + { + var ok = base.CheckOverrideAgainstBase (base_member); + + // + // Check base property accessors conflict + // + var base_prop = (PropertySpec) base_member; + if (Get == null) { + if ((ModFlags & Modifiers.SEALED) != 0 && base_prop.HasGet && !base_prop.Get.IsAccessible (this)) { + // TODO: Should be different error code but csc uses for some reason same + Report.SymbolRelatedToPreviousError (base_prop); + Report.Error (545, Location, + "`{0}': cannot override because `{1}' does not have accessible get accessor", + GetSignatureForError (), base_prop.GetSignatureForError ()); + ok = false; + } + } else { + if (!base_prop.HasGet) { + if (ok) { + Report.SymbolRelatedToPreviousError (base_prop); + Report.Error (545, Get.Location, + "`{0}': cannot override because `{1}' does not have an overridable get accessor", + Get.GetSignatureForError (), base_prop.GetSignatureForError ()); + ok = false; + } + } else if (Get.HasCustomAccessModifier || base_prop.HasDifferentAccessibility) { + if (!CheckAccessModifiers (Get, base_prop.Get)) { + Error_CannotChangeAccessModifiers (Get, base_prop.Get); + ok = false; + } + } + } + + if (Set == null) { + if ((ModFlags & Modifiers.SEALED) != 0 && base_prop.HasSet && !base_prop.Set.IsAccessible (this)) { + // TODO: Should be different error code but csc uses for some reason same + Report.SymbolRelatedToPreviousError (base_prop); + Report.Error (546, Location, + "`{0}': cannot override because `{1}' does not have accessible set accessor", + GetSignatureForError (), base_prop.GetSignatureForError ()); + ok = false; + } + } else { + if (!base_prop.HasSet) { + if (ok) { + Report.SymbolRelatedToPreviousError (base_prop); + Report.Error (546, Set.Location, + "`{0}': cannot override because `{1}' does not have an overridable set accessor", + Set.GetSignatureForError (), base_prop.GetSignatureForError ()); + ok = false; + } + } else if (Set.HasCustomAccessModifier || base_prop.HasDifferentAccessibility) { + if (!CheckAccessModifiers (Set, base_prop.Set)) { + Error_CannotChangeAccessModifiers (Set, base_prop.Set); + ok = false; + } + } + } + + if ((Set == null || !Set.HasCustomAccessModifier) && (Get == null || !Get.HasCustomAccessModifier)) { + if (!CheckAccessModifiers (this, base_prop)) { + Error_CannotChangeAccessModifiers (this, base_prop); + ok = false; + } + } + + return ok; + } + + protected override void DoMemberTypeDependentChecks () + { + base.DoMemberTypeDependentChecks (); + + IsTypePermitted (); + + if (MemberType.IsStatic) + Error_StaticReturnType (); + } + + protected override void DoMemberTypeIndependentChecks () + { + base.DoMemberTypeIndependentChecks (); + + // + // Accessors modifiers check + // + if (AccessorSecond != null) { + if ((Get.ModFlags & Modifiers.AccessibilityMask) != 0 && (Set.ModFlags & Modifiers.AccessibilityMask) != 0) { + Report.Error (274, Location, "`{0}': Cannot specify accessibility modifiers for both accessors of the property or indexer", + GetSignatureForError ()); + } + } else if ((ModFlags & Modifiers.OVERRIDE) == 0 && + (Get == null && (Set.ModFlags & Modifiers.AccessibilityMask) != 0) || + (Set == null && (Get.ModFlags & Modifiers.AccessibilityMask) != 0)) { + Report.Error (276, Location, + "`{0}': accessibility modifiers on accessors may only be used if the property or indexer has both a get and a set accessor", + GetSignatureForError ()); + } + } + + protected bool DefineAccessors () + { + first.Define (Parent); + if (AccessorSecond != null) + AccessorSecond.Define (Parent); + + return true; + } + + protected void DefineBuilders (MemberKind kind, ParametersCompiled parameters) + { + PropertyBuilder = Parent.TypeBuilder.DefineProperty ( + GetFullName (MemberName), PropertyAttributes.None, +#if !BOOTSTRAP_BASIC // Requires trunk version mscorlib + IsStatic ? 0 : CallingConventions.HasThis, +#endif + MemberType.GetMetaInfo (), null, null, + parameters.GetMetaInfo (), null, null); + + PropertySpec spec; + if (kind == MemberKind.Indexer) + spec = new IndexerSpec (Parent.Definition, this, MemberType, parameters, PropertyBuilder, ModFlags); + else + spec = new PropertySpec (kind, Parent.Definition, this, MemberType, PropertyBuilder, ModFlags); + + if (Get != null) { + spec.Get = Get.Spec; + Parent.MemberCache.AddMember (this, Get.Spec.Name, Get.Spec); + } else { + CheckMissingAccessor (kind, parameters, true); + } + + if (Set != null) { + spec.Set = Set.Spec; + Parent.MemberCache.AddMember (this, Set.Spec.Name, Set.Spec); + } else { + CheckMissingAccessor (kind, parameters, false); + } + + Parent.MemberCache.AddMember (this, PropertyBuilder.Name, spec); + } + + public override void Emit () + { + CheckReservedNameConflict (GetMethod.Prefix, get == null ? null : get.Spec); + CheckReservedNameConflict (SetMethod.Prefix, set == null ? null : set.Spec); + + if (OptAttributes != null) + OptAttributes.Emit (); + + if (member_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + Module.PredefinedAttributes.Dynamic.EmitAttribute (PropertyBuilder); + } else if (member_type.HasDynamicElement) { + Module.PredefinedAttributes.Dynamic.EmitAttribute (PropertyBuilder, member_type, Location); + } + + ConstraintChecker.Check (this, member_type, type_expr.Location); + + first.Emit (Parent); + if (AccessorSecond != null) + AccessorSecond.Emit (Parent); + + base.Emit (); + } + + public override bool IsUsed { + get { + if (IsExplicitImpl) + return true; + + return Get.IsUsed | Set.IsUsed; + } + } + + public override void PrepareEmit () + { + AccessorFirst.PrepareEmit (); + if (AccessorSecond != null) + AccessorSecond.PrepareEmit (); + + if (get != null) { + var method = Get.Spec.GetMetaInfo () as MethodBuilder; + if (method != null) + PropertyBuilder.SetGetMethod (method); + } + + if (set != null) { + var method = Set.Spec.GetMetaInfo () as MethodBuilder; + if (method != null) + PropertyBuilder.SetSetMethod (method); + } + } + + protected override void SetMemberName (MemberName new_name) + { + base.SetMemberName (new_name); + + if (Get != null) + Get.UpdateName (this); + + if (Set != null) + Set.UpdateName (this); + } + + public override void WriteDebugSymbol (MonoSymbolFile file) + { + if (get != null) + get.WriteDebugSymbol (file); + + if (set != null) + set.WriteDebugSymbol (file); + } + + // + // Represents header string for documentation comment. + // + public override string DocCommentHeader { + get { return "P:"; } + } + } + + public class Property : PropertyBase + { + public sealed class BackingField : Field + { + readonly Property property; + const Modifiers DefaultModifiers = Modifiers.BACKING_FIELD | Modifiers.COMPILER_GENERATED | Modifiers.PRIVATE | Modifiers.DEBUGGER_HIDDEN; + + public BackingField (Property p, bool readOnly) + : base (p.Parent, p.type_expr, DefaultModifiers | (p.ModFlags & (Modifiers.STATIC | Modifiers.UNSAFE)), + new MemberName ("<" + p.GetFullName (p.MemberName) + ">k__BackingField", p.Location), null) + { + this.property = p; + if (readOnly) + ModFlags |= Modifiers.READONLY; + } + + public Property OriginalProperty { + get { + return property; + } + } + + public override string GetSignatureForError () + { + return property.GetSignatureForError (); + } + } + + static readonly string[] attribute_target_auto = new string[] { "property", "field" }; + + Field backing_field; + + public Property (TypeDefinition parent, FullNamedExpression type, Modifiers mod, + MemberName name, Attributes attrs) + : base (parent, type, mod, + parent.PartialContainer.Kind == MemberKind.Interface ? AllowedModifiersInterface : + parent.PartialContainer.Kind == MemberKind.Struct ? AllowedModifiersStruct : + AllowedModifiersClass, + name, attrs) + { + } + + public Expression Initializer { get; set; } + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.Target == AttributeTargets.Field) { + backing_field.ApplyAttributeBuilder (a, ctor, cdata, pa); + return; + } + + base.ApplyAttributeBuilder (a, ctor, cdata, pa); + } + + void CreateAutomaticProperty () + { + // Create backing field + backing_field = new BackingField (this, Initializer != null && Set == null); + if (!backing_field.Define ()) + return; + + if (Initializer != null) { + backing_field.Initializer = Initializer; + Parent.RegisterFieldForInitialization (backing_field, new FieldInitializer (backing_field, Initializer, Location)); + backing_field.ModFlags |= Modifiers.READONLY; + } + + Parent.PartialContainer.Members.Add (backing_field); + + FieldExpr fe = new FieldExpr (backing_field, Location); + if ((backing_field.ModFlags & Modifiers.STATIC) == 0) + fe.InstanceExpression = new CompilerGeneratedThis (Parent.CurrentType, Location); + + // + // Create get block but we careful with location to + // emit only single sequence point per accessor. This allow + // to set a breakpoint on it even with no user code + // + Get.Block = new ToplevelBlock (Compiler, ParametersCompiled.EmptyReadOnlyParameters, Location.Null); + Return r = new Return (fe, Get.Location); + Get.Block.AddStatement (r); + Get.ModFlags |= Modifiers.COMPILER_GENERATED; + + // Create set block + if (Set != null) { + Set.Block = new ToplevelBlock (Compiler, Set.ParameterInfo, Location.Null); + Assign a = new SimpleAssign (fe, new SimpleName ("value", Location.Null), Location.Null); + Set.Block.AddStatement (new StatementExpression (a, Set.Location)); + Set.ModFlags |= Modifiers.COMPILER_GENERATED; + } + } + + public override bool Define () + { + if (!base.Define ()) + return false; + + flags |= MethodAttributes.HideBySig | MethodAttributes.SpecialName; + + bool auto = AccessorFirst.Block == null && (AccessorSecond == null || AccessorSecond.Block == null) && + (ModFlags & (Modifiers.ABSTRACT | Modifiers.EXTERN)) == 0; + + if (Initializer != null) { + if (!auto) + Report.Error (8050, Location, "`{0}': Only auto-implemented properties can have initializers", + GetSignatureForError ()); + + if (IsInterface) + Report.Error (8053, Location, "`{0}': Properties inside interfaces cannot have initializers", + GetSignatureForError ()); + + if (Compiler.Settings.Version < LanguageVersion.V_6) + Report.FeatureIsNotAvailable (Compiler, Location, "auto-implemented property initializer"); + } + + if (auto) { + if (Get == null) { + Report.Error (8052, Location, "Auto-implemented property `{0}' must have get accessor", + GetSignatureForError ()); + return false; + } + + if (Initializer == null && AccessorSecond == null) { + Report.Error (8051, Location, "Auto-implemented property `{0}' must have set accessor or initializer", + GetSignatureForError ()); + } + + if (Compiler.Settings.Version < LanguageVersion.V_3 && Initializer == null) + Report.FeatureIsNotAvailable (Compiler, Location, "auto-implemented properties"); + + CreateAutomaticProperty (); + } + + if (!DefineAccessors ()) + return false; + + if (AccessorSecond == null) { + PropertyMethod pm; + if (AccessorFirst is GetMethod) + pm = new SetMethod (this, 0, ParametersCompiled.EmptyReadOnlyParameters, null, Location); + else + pm = new GetMethod (this, 0, null, Location); + + Parent.AddNameToContainer (pm, pm.MemberName.Basename); + } + + if (!CheckBase ()) + return false; + + DefineBuilders (MemberKind.Property, ParametersCompiled.EmptyReadOnlyParameters); + return true; + } + + public override void Emit () + { + if ((AccessorFirst.ModFlags & (Modifiers.STATIC | Modifiers.COMPILER_GENERATED)) == Modifiers.COMPILER_GENERATED && Parent.PartialContainer.HasExplicitLayout) { + Report.Error (842, Location, + "Automatically implemented property `{0}' cannot be used inside a type with an explicit StructLayout attribute", + GetSignatureForError ()); + } + + base.Emit (); + } + + public override string[] ValidAttributeTargets { + get { + return Get != null && ((Get.ModFlags & Modifiers.COMPILER_GENERATED) != 0) ? + attribute_target_auto : base.ValidAttributeTargets; + } + } + } + + /// + /// For case when event is declared like property (with add and remove accessors). + /// + public class EventProperty: Event { + public abstract class AEventPropertyAccessor : AEventAccessor + { + protected AEventPropertyAccessor (EventProperty method, string prefix, Attributes attrs, Location loc) + : base (method, prefix, attrs, loc) + { + } + + public override void Define (TypeContainer ds) + { + CheckAbstractAndExtern (block != null); + base.Define (ds); + } + + public override string GetSignatureForError () + { + return method.GetSignatureForError () + "." + prefix.Substring (0, prefix.Length - 1); + } + } + + public sealed class AddDelegateMethod: AEventPropertyAccessor + { + public AddDelegateMethod (EventProperty method, Attributes attrs, Location loc) + : base (method, AddPrefix, attrs, loc) + { + } + } + + public sealed class RemoveDelegateMethod: AEventPropertyAccessor + { + public RemoveDelegateMethod (EventProperty method, Attributes attrs, Location loc) + : base (method, RemovePrefix, attrs, loc) + { + } + } + + static readonly string[] attribute_targets = new string [] { "event" }; + + public EventProperty (TypeDefinition parent, FullNamedExpression type, Modifiers mod_flags, MemberName name, Attributes attrs) + : base (parent, type, mod_flags, name, attrs) + { + } + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + + public override bool Define() + { + if (!base.Define ()) + return false; + + SetIsUsed (); + return true; + } + + public override string[] ValidAttributeTargets { + get { + return attribute_targets; + } + } + } + + /// + /// Event is declared like field. + /// + public class EventField : Event + { + abstract class EventFieldAccessor : AEventAccessor + { + protected EventFieldAccessor (EventField method, string prefix) + : base (method, prefix, null, method.Location) + { + } + + protected abstract MethodSpec GetOperation (Location loc); + + public override void Emit (TypeDefinition parent) + { + if ((method.ModFlags & (Modifiers.ABSTRACT | Modifiers.EXTERN)) == 0 && !Compiler.Settings.WriteMetadataOnly) { + block = new ToplevelBlock (Compiler, ParameterInfo, Location) { + IsCompilerGenerated = true + }; + FabricateBodyStatement (); + } + + base.Emit (parent); + } + + void FabricateBodyStatement () + { + // + // Delegate obj1 = backing_field + // do { + // Delegate obj2 = obj1; + // obj1 = Interlocked.CompareExchange (ref backing_field, Delegate.Combine|Remove(obj2, value), obj1); + // } while ((object)obj1 != (object)obj2) + // + + var field_info = ((EventField) method).backing_field; + FieldExpr f_expr = new FieldExpr (field_info, Location); + if (!IsStatic) + f_expr.InstanceExpression = new CompilerGeneratedThis (Parent.CurrentType, Location); + + var obj1 = LocalVariable.CreateCompilerGenerated (field_info.MemberType, block, Location); + var obj2 = LocalVariable.CreateCompilerGenerated (field_info.MemberType, block, Location); + + block.AddStatement (new StatementExpression (new SimpleAssign (new LocalVariableReference (obj1, Location), f_expr))); + + var cond = new BooleanExpression (new Binary (Binary.Operator.Inequality, + new Cast (new TypeExpression (Module.Compiler.BuiltinTypes.Object, Location), new LocalVariableReference (obj1, Location), Location), + new Cast (new TypeExpression (Module.Compiler.BuiltinTypes.Object, Location), new LocalVariableReference (obj2, Location), Location))); + + var body = new ExplicitBlock (block, Location, Location); + block.AddStatement (new Do (body, cond, Location, Location)); + + body.AddStatement (new StatementExpression ( + new SimpleAssign (new LocalVariableReference (obj2, Location), new LocalVariableReference (obj1, Location)))); + + var args_oper = new Arguments (2); + args_oper.Add (new Argument (new LocalVariableReference (obj2, Location))); + args_oper.Add (new Argument (block.GetParameterReference (0, Location))); + + var op_method = GetOperation (Location); + + var args = new Arguments (3); + args.Add (new Argument (f_expr, Argument.AType.Ref)); + args.Add (new Argument (new Cast ( + new TypeExpression (field_info.MemberType, Location), + new Invocation (MethodGroupExpr.CreatePredefined (op_method, op_method.DeclaringType, Location), args_oper), + Location))); + args.Add (new Argument (new LocalVariableReference (obj1, Location))); + + var cas = Module.PredefinedMembers.InterlockedCompareExchange_T.Get (); + if (cas == null) { + if (Module.PredefinedMembers.MonitorEnter_v4.Get () != null || Module.PredefinedMembers.MonitorEnter.Get () != null) { + // Workaround for cripled (e.g. microframework) mscorlib without CompareExchange + body.AddStatement (new Lock ( + block.GetParameterReference (0, Location), + new StatementExpression (new SimpleAssign ( + f_expr, args [1].Expr, Location), Location), Location)); + } else { + Module.PredefinedMembers.InterlockedCompareExchange_T.Resolve (Location); + } + } else { + body.AddStatement (new StatementExpression (new SimpleAssign ( + new LocalVariableReference (obj1, Location), + new Invocation (MethodGroupExpr.CreatePredefined (cas, cas.DeclaringType, Location), args)))); + } + } + } + + sealed class AddDelegateMethod: EventFieldAccessor + { + public AddDelegateMethod (EventField method): + base (method, AddPrefix) + { + } + + protected override MethodSpec GetOperation (Location loc) + { + return Module.PredefinedMembers.DelegateCombine.Resolve (loc); + } + } + + sealed class RemoveDelegateMethod: EventFieldAccessor + { + public RemoveDelegateMethod (EventField method): + base (method, RemovePrefix) + { + } + + protected override MethodSpec GetOperation (Location loc) + { + return Module.PredefinedMembers.DelegateRemove.Resolve (loc); + } + } + + + static readonly string[] attribute_targets = new string [] { "event", "field", "method" }; + static readonly string[] attribute_targets_interface = new string[] { "event", "method" }; + + Expression initializer; + Field backing_field; + List declarators; + + public EventField (TypeDefinition parent, FullNamedExpression type, Modifiers mod_flags, MemberName name, Attributes attrs) + : base (parent, type, mod_flags, name, attrs) + { + Add = new AddDelegateMethod (this); + Remove = new RemoveDelegateMethod (this); + } + + #region Properties + + public List Declarators { + get { + return this.declarators; + } + } + + bool HasBackingField { + get { + return !IsInterface && (ModFlags & Modifiers.ABSTRACT) == 0; + } + } + + public Expression Initializer { + get { + return initializer; + } + set { + initializer = value; + } + } + + public override string[] ValidAttributeTargets { + get { + return HasBackingField ? attribute_targets : attribute_targets_interface; + } + } + + #endregion + + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + + public void AddDeclarator (FieldDeclarator declarator) + { + if (declarators == null) + declarators = new List (2); + + declarators.Add (declarator); + + Parent.AddNameToContainer (this, declarator.Name.Value); + } + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.Target == AttributeTargets.Field) { + backing_field.ApplyAttributeBuilder (a, ctor, cdata, pa); + return; + } + + if (a.Target == AttributeTargets.Method) { + int errors = Report.Errors; + Add.ApplyAttributeBuilder (a, ctor, cdata, pa); + if (errors == Report.Errors) + Remove.ApplyAttributeBuilder (a, ctor, cdata, pa); + return; + } + + base.ApplyAttributeBuilder (a, ctor, cdata, pa); + } + + public override bool Define() + { + var mod_flags_src = ModFlags; + + if (!base.Define ()) + return false; + + if (declarators != null) { + if ((mod_flags_src & Modifiers.DEFAULT_ACCESS_MODIFIER) != 0) + mod_flags_src &= ~(Modifiers.AccessibilityMask | Modifiers.DEFAULT_ACCESS_MODIFIER); + + var t = new TypeExpression (MemberType, TypeExpression.Location); + foreach (var d in declarators) { + var ef = new EventField (Parent, t, mod_flags_src, new MemberName (d.Name.Value, d.Name.Location), OptAttributes); + + if (d.Initializer != null) + ef.initializer = d.Initializer; + + ef.Define (); + Parent.PartialContainer.Members.Add (ef); + } + } + + if (!HasBackingField) { + SetIsUsed (); + return true; + } + + backing_field = new Field (Parent, + new TypeExpression (MemberType, Location), + Modifiers.BACKING_FIELD | Modifiers.COMPILER_GENERATED | Modifiers.PRIVATE | (ModFlags & (Modifiers.STATIC | Modifiers.UNSAFE)), + MemberName, null); + + Parent.PartialContainer.Members.Add (backing_field); + backing_field.Initializer = Initializer; + backing_field.ModFlags &= ~Modifiers.COMPILER_GENERATED; + + // Call define because we passed fields definition + backing_field.Define (); + + // Set backing field for event fields + spec.BackingField = backing_field.Spec; + + return true; + } + } + + public abstract class Event : PropertyBasedMember + { + public abstract class AEventAccessor : AbstractPropertyEventMethod + { + protected readonly Event method; + readonly ParametersCompiled parameters; + + static readonly string[] attribute_targets = new string [] { "method", "param", "return" }; + + public const string AddPrefix = "add_"; + public const string RemovePrefix = "remove_"; + + protected AEventAccessor (Event method, string prefix, Attributes attrs, Location loc) + : base (method, prefix, attrs, loc) + { + this.method = method; + this.ModFlags = method.ModFlags; + this.parameters = ParametersCompiled.CreateImplicitParameter (method.TypeExpression, loc); + } + + public bool IsInterfaceImplementation { + get { return method_data.implementing != null; } + } + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.Type == pa.MethodImpl) { + method.is_external_implementation = a.IsInternalCall (); + } + + base.ApplyAttributeBuilder (a, ctor, cdata, pa); + } + + protected override void ApplyToExtraTarget (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.Target == AttributeTargets.Parameter) { + parameters[0].ApplyAttributeBuilder (a, ctor, cdata, pa); + return; + } + + base.ApplyToExtraTarget (a, ctor, cdata, pa); + } + + public override AttributeTargets AttributeTargets { + get { + return AttributeTargets.Method; + } + } + + public override bool IsClsComplianceRequired () + { + return method.IsClsComplianceRequired (); + } + + public virtual void Define (TypeContainer parent) + { + // Fill in already resolved event type to speed things up and + // avoid confusing duplicate errors + ((Parameter) parameters.FixedParameters[0]).Type = method.member_type; + parameters.Types = new TypeSpec[] { method.member_type }; + + method_data = new MethodData (method, method.ModFlags, + method.flags | MethodAttributes.HideBySig | MethodAttributes.SpecialName, this); + + if (!method_data.Define (parent.PartialContainer, method.GetFullName (MemberName))) + return; + + if (Compiler.Settings.WriteMetadataOnly) + block = null; + + Spec = new MethodSpec (MemberKind.Method, parent.PartialContainer.Definition, this, ReturnType, ParameterInfo, method.ModFlags); + Spec.IsAccessor = true; + } + + public override TypeSpec ReturnType { + get { + return Parent.Compiler.BuiltinTypes.Void; + } + } + + public override ObsoleteAttribute GetAttributeObsolete () + { + return method.GetAttributeObsolete (); + } + + public MethodData MethodData { + get { + return method_data; + } + } + + public override string[] ValidAttributeTargets { + get { + return attribute_targets; + } + } + + public override ParametersCompiled ParameterInfo { + get { + return parameters; + } + } + } + + AEventAccessor add, remove; + EventBuilder EventBuilder; + protected EventSpec spec; + + protected Event (TypeDefinition parent, FullNamedExpression type, Modifiers mod_flags, MemberName name, Attributes attrs) + : base (parent, type, mod_flags, + parent.PartialContainer.Kind == MemberKind.Interface ? AllowedModifiersInterface : + parent.PartialContainer.Kind == MemberKind.Struct ? AllowedModifiersStruct : + AllowedModifiersClass, + name, attrs) + { + } + + #region Properties + + public override AttributeTargets AttributeTargets { + get { + return AttributeTargets.Event; + } + } + + public AEventAccessor Add { + get { + return this.add; + } + set { + add = value; + Parent.AddNameToContainer (value, value.MemberName.Basename); + } + } + + public override Variance ExpectedMemberTypeVariance { + get { + return Variance.Contravariant; + } + } + + public AEventAccessor Remove { + get { + return this.remove; + } + set { + remove = value; + Parent.AddNameToContainer (value, value.MemberName.Basename); + } + } + #endregion + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if ((a.HasSecurityAttribute)) { + a.Error_InvalidSecurityParent (); + return; + } + + EventBuilder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), cdata); + } + + protected override bool CheckOverrideAgainstBase (MemberSpec base_member) + { + var ok = base.CheckOverrideAgainstBase (base_member); + + if (!CheckAccessModifiers (this, base_member)) { + Error_CannotChangeAccessModifiers (this, base_member); + ok = false; + } + + return ok; + } + + public override bool Define () + { + if (!base.Define ()) + return false; + + if (!MemberType.IsDelegate) { + Report.Error (66, Location, "`{0}': event must be of a delegate type", GetSignatureForError ()); + } + + if (!CheckBase ()) + return false; + + // + // Now define the accessors + // + add.Define (Parent); + remove.Define (Parent); + + EventBuilder = Parent.TypeBuilder.DefineEvent (GetFullName (MemberName), EventAttributes.None, MemberType.GetMetaInfo ()); + + spec = new EventSpec (Parent.Definition, this, MemberType, ModFlags, Add.Spec, remove.Spec); + + Parent.MemberCache.AddMember (this, GetFullName (MemberName), spec); + Parent.MemberCache.AddMember (this, Add.Spec.Name, Add.Spec); + Parent.MemberCache.AddMember (this, Remove.Spec.Name, remove.Spec); + + return true; + } + + public override void Emit () + { + CheckReservedNameConflict (null, add.Spec); + CheckReservedNameConflict (null, remove.Spec); + + if (OptAttributes != null) { + OptAttributes.Emit (); + } + + ConstraintChecker.Check (this, member_type, type_expr.Location); + + Add.Emit (Parent); + Remove.Emit (Parent); + + base.Emit (); + } + + public override void PrepareEmit () + { + add.PrepareEmit (); + remove.PrepareEmit (); + + EventBuilder.SetAddOnMethod (add.MethodData.MethodBuilder); + EventBuilder.SetRemoveOnMethod (remove.MethodData.MethodBuilder); + } + + public override void WriteDebugSymbol (MonoSymbolFile file) + { + add.WriteDebugSymbol (file); + remove.WriteDebugSymbol (file); + } + + // + // Represents header string for documentation comment. + // + public override string DocCommentHeader { + get { return "E:"; } + } + } + + public class EventSpec : MemberSpec, IInterfaceMemberSpec + { + MethodSpec add, remove; + FieldSpec backing_field; + + public EventSpec (TypeSpec declaringType, IMemberDefinition definition, TypeSpec eventType, Modifiers modifiers, MethodSpec add, MethodSpec remove) + : base (MemberKind.Event, declaringType, definition, modifiers) + { + this.AccessorAdd = add; + this.AccessorRemove = remove; + this.MemberType = eventType; + } + + #region Properties + + public MethodSpec AccessorAdd { + get { + return add; + } + set { + add = value; + } + } + + public MethodSpec AccessorRemove { + get { + return remove; + } + set { + remove = value; + } + } + + public FieldSpec BackingField { + get { + return backing_field; + } + set { + backing_field = value; + } + } + + public TypeSpec MemberType { get; private set; } + + #endregion + + public override MemberSpec InflateMember (TypeParameterInflator inflator) + { + var es = (EventSpec) base.InflateMember (inflator); + es.MemberType = inflator.Inflate (MemberType); + + if (backing_field != null) + es.backing_field = (FieldSpec) backing_field.InflateMember (inflator); + + return es; + } + + public override List ResolveMissingDependencies (MemberSpec caller) + { + return MemberType.ResolveMissingDependencies (this); + } + } + + public class Indexer : PropertyBase, IParametersMember + { + public class GetIndexerMethod : GetMethod, IParametersMember + { + ParametersCompiled parameters; + + public GetIndexerMethod (PropertyBase property, Modifiers modifiers, ParametersCompiled parameters, Attributes attrs, Location loc) + : base (property, modifiers, attrs, loc) + { + this.parameters = parameters; + } + + public override void Define (TypeContainer parent) + { + // Disable reporting, parameters are resolved twice + Report.DisableReporting (); + try { + parameters.Resolve (this); + } finally { + Report.EnableReporting (); + } + + base.Define (parent); + } + + public override ParametersCompiled ParameterInfo { + get { + return parameters; + } + } + + #region IParametersMember Members + + AParametersCollection IParametersMember.Parameters { + get { + return parameters; + } + } + + TypeSpec IInterfaceMemberSpec.MemberType { + get { + return ReturnType; + } + } + + #endregion + } + + public class SetIndexerMethod : SetMethod, IParametersMember + { + public SetIndexerMethod (PropertyBase property, Modifiers modifiers, ParametersCompiled parameters, Attributes attrs, Location loc) + : base (property, modifiers, parameters, attrs, loc) + { + } + + #region IParametersMember Members + + AParametersCollection IParametersMember.Parameters { + get { + return parameters; + } + } + + TypeSpec IInterfaceMemberSpec.MemberType { + get { + return ReturnType; + } + } + + #endregion + } + + const Modifiers AllowedModifiers = + Modifiers.NEW | + Modifiers.PUBLIC | + Modifiers.PROTECTED | + Modifiers.INTERNAL | + Modifiers.PRIVATE | + Modifiers.VIRTUAL | + Modifiers.SEALED | + Modifiers.OVERRIDE | + Modifiers.UNSAFE | + Modifiers.EXTERN | + Modifiers.ABSTRACT; + + const Modifiers AllowedInterfaceModifiers = + Modifiers.NEW; + + readonly ParametersCompiled parameters; + + public Indexer (TypeDefinition parent, FullNamedExpression type, MemberName name, Modifiers mod, ParametersCompiled parameters, Attributes attrs) + : base (parent, type, mod, + parent.PartialContainer.Kind == MemberKind.Interface ? AllowedInterfaceModifiers : AllowedModifiers, + name, attrs) + { + this.parameters = parameters; + } + + #region Properties + + AParametersCollection IParametersMember.Parameters { + get { + return parameters; + } + } + + public ParametersCompiled ParameterInfo { + get { + return parameters; + } + } + + #endregion + + + public override void Accept (StructuralVisitor visitor) + { + visitor.Visit (this); + } + + public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) + { + if (a.Type == pa.IndexerName) { + // Attribute was copied to container + return; + } + + base.ApplyAttributeBuilder (a, ctor, cdata, pa); + } + + protected override bool CheckForDuplications () + { + return Parent.MemberCache.CheckExistingMembersOverloads (this, parameters); + } + + public override bool Define () + { + if (!base.Define ()) + return false; + + if (!DefineParameters (parameters)) + return false; + + if (OptAttributes != null) { + Attribute indexer_attr = OptAttributes.Search (Module.PredefinedAttributes.IndexerName); + if (indexer_attr != null) { + var compiling = indexer_attr.Type.MemberDefinition as TypeContainer; + if (compiling != null) + compiling.Define (); + + if (IsExplicitImpl) { + Report.Error (415, indexer_attr.Location, + "The `{0}' attribute is valid only on an indexer that is not an explicit interface member declaration", + indexer_attr.Type.GetSignatureForError ()); + } else if ((ModFlags & Modifiers.OVERRIDE) != 0) { + Report.Error (609, indexer_attr.Location, + "Cannot set the `IndexerName' attribute on an indexer marked override"); + } else { + string name = indexer_attr.GetIndexerAttributeValue (); + + if (!string.IsNullOrEmpty (name)) { + SetMemberName (new MemberName (MemberName.Left, name, Location)); + } + } + } + } + + if (InterfaceType != null) { + string base_IndexerName = InterfaceType.MemberDefinition.GetAttributeDefaultMember (); + if (base_IndexerName != ShortName) { + SetMemberName (new MemberName (MemberName.Left, base_IndexerName, new TypeExpression (InterfaceType, Location), Location)); + } + } + + Parent.AddNameToContainer (this, MemberName.Basename); + + flags |= MethodAttributes.HideBySig | MethodAttributes.SpecialName; + + if (!DefineAccessors ()) + return false; + + if (!CheckBase ()) + return false; + + DefineBuilders (MemberKind.Indexer, parameters); + return true; + } + + public override bool EnableOverloadChecks (MemberCore overload) + { + if (overload is Indexer) { + caching_flags |= Flags.MethodOverloadsExist; + return true; + } + + return base.EnableOverloadChecks (overload); + } + + public override void Emit () + { + parameters.CheckConstraints (this); + + base.Emit (); + } + + public override string GetSignatureForError () + { + StringBuilder sb = new StringBuilder (Parent.GetSignatureForError ()); + if (MemberName.ExplicitInterface != null) { + sb.Append ("."); + sb.Append (MemberName.ExplicitInterface.GetSignatureForError ()); + } + + sb.Append (".this"); + sb.Append (parameters.GetSignatureForError ("[", "]", parameters.Count)); + return sb.ToString (); + } + + public override string GetSignatureForDocumentation () + { + return base.GetSignatureForDocumentation () + parameters.GetSignatureForDocumentation (); + } + + protected override bool VerifyClsCompliance () + { + if (!base.VerifyClsCompliance ()) + return false; + + parameters.VerifyClsCompliance (this); + return true; + } + } + + public class IndexerSpec : PropertySpec, IParametersMember + { + AParametersCollection parameters; + + public IndexerSpec (TypeSpec declaringType, IMemberDefinition definition, TypeSpec memberType, AParametersCollection parameters, PropertyInfo info, Modifiers modifiers) + : base (MemberKind.Indexer, declaringType, definition, memberType, info, modifiers) + { + this.parameters = parameters; + } + + #region Properties + public AParametersCollection Parameters { + get { + return parameters; + } + } + #endregion + + public override string GetSignatureForDocumentation () + { + return base.GetSignatureForDocumentation () + parameters.GetSignatureForDocumentation (); + } + + public override string GetSignatureForError () + { + return DeclaringType.GetSignatureForError () + ".this" + parameters.GetSignatureForError ("[", "]", parameters.Count); + } + + public override MemberSpec InflateMember (TypeParameterInflator inflator) + { + var spec = (IndexerSpec) base.InflateMember (inflator); + spec.parameters = parameters.Inflate (inflator); + return spec; + } + + public override List ResolveMissingDependencies (MemberSpec caller) + { + var missing = base.ResolveMissingDependencies (caller); + + foreach (var pt in parameters.Types) { + var m = pt.GetMissingDependencies (caller); + if (m == null) + continue; + + if (missing == null) + missing = new List (); + + missing.AddRange (m); + } + + return missing; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/reflection.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/reflection.cs new file mode 100644 index 000000000..b233b9439 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/reflection.cs @@ -0,0 +1,550 @@ +// +// reflection.cs: System.Reflection and System.Reflection.Emit specific implementations +// +// Author: Marek Safar (marek.safar@gmail.com) +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2009-2010 Novell, Inc. +// +// + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.IO; +using System.Runtime.CompilerServices; +using System.Reflection.Emit; +using System.Security; + +namespace Mono.CSharp +{ +#if STATIC + public class ReflectionImporter + { + public ReflectionImporter (ModuleContainer module, BuiltinTypes builtin) + { + throw new NotSupportedException (); + } + + public void ImportAssembly (Assembly assembly, RootNamespace targetNamespace) + { + throw new NotSupportedException (); + } + + public ImportedModuleDefinition ImportModule (Module module, RootNamespace targetNamespace) + { + throw new NotSupportedException (); + } + + public TypeSpec ImportType (Type type) + { + throw new NotSupportedException (); + } + } +#else + public sealed class ReflectionImporter : MetadataImporter + { + public ReflectionImporter (ModuleContainer module, BuiltinTypes builtin) + : base (module) + { + Initialize (builtin); + } + + public override void AddCompiledType (TypeBuilder builder, TypeSpec spec) + { + } + + protected override MemberKind DetermineKindFromBaseType (Type baseType) + { + if (baseType == typeof (ValueType)) + return MemberKind.Struct; + + if (baseType == typeof (System.Enum)) + return MemberKind.Enum; + + if (baseType == typeof (MulticastDelegate)) + return MemberKind.Delegate; + + return MemberKind.Class; + } + + protected override bool HasVolatileModifier (Type[] modifiers) + { + foreach (var t in modifiers) { + if (t == typeof (IsVolatile)) + return true; + } + + return false; + } + + public void ImportAssembly (Assembly assembly, RootNamespace targetNamespace) + { + // It can be used more than once when importing same assembly + // into 2 or more global aliases + GetAssemblyDefinition (assembly); + + // + // This part tries to simulate loading of top-level + // types only, any missing dependencies are ignores here. + // Full error report is reported later when the type is + // actually used + // + Type[] all_types; + try { + all_types = assembly.GetTypes (); + } catch (ReflectionTypeLoadException e) { + all_types = e.Types; + } + + ImportTypes (all_types, targetNamespace, true); + } + + public ImportedModuleDefinition ImportModule (Module module, RootNamespace targetNamespace) + { + var module_definition = new ImportedModuleDefinition (module); + module_definition.ReadAttributes (); + + Type[] all_types; + try { + all_types = module.GetTypes (); + } catch (ReflectionTypeLoadException e) { + all_types = e.Types; + } + + ImportTypes (all_types, targetNamespace, false); + + return module_definition; + } + + void Initialize (BuiltinTypes builtin) + { + // + // Setup mapping for build-in types to avoid duplication of their definition + // + compiled_types.Add (typeof (object), builtin.Object); + compiled_types.Add (typeof (System.ValueType), builtin.ValueType); + compiled_types.Add (typeof (System.Attribute), builtin.Attribute); + + compiled_types.Add (typeof (int), builtin.Int); + compiled_types.Add (typeof (long), builtin.Long); + compiled_types.Add (typeof (uint), builtin.UInt); + compiled_types.Add (typeof (ulong), builtin.ULong); + compiled_types.Add (typeof (byte), builtin.Byte); + compiled_types.Add (typeof (sbyte), builtin.SByte); + compiled_types.Add (typeof (short), builtin.Short); + compiled_types.Add (typeof (ushort), builtin.UShort); + + compiled_types.Add (typeof (System.Collections.IEnumerator), builtin.IEnumerator); + compiled_types.Add (typeof (System.Collections.IEnumerable), builtin.IEnumerable); + compiled_types.Add (typeof (System.IDisposable), builtin.IDisposable); + + compiled_types.Add (typeof (char), builtin.Char); + compiled_types.Add (typeof (string), builtin.String); + compiled_types.Add (typeof (float), builtin.Float); + compiled_types.Add (typeof (double), builtin.Double); + compiled_types.Add (typeof (decimal), builtin.Decimal); + compiled_types.Add (typeof (bool), builtin.Bool); + compiled_types.Add (typeof (System.IntPtr), builtin.IntPtr); + compiled_types.Add (typeof (System.UIntPtr), builtin.UIntPtr); + + compiled_types.Add (typeof (System.MulticastDelegate), builtin.MulticastDelegate); + compiled_types.Add (typeof (System.Delegate), builtin.Delegate); + compiled_types.Add (typeof (System.Enum), builtin.Enum); + compiled_types.Add (typeof (System.Array), builtin.Array); + compiled_types.Add (typeof (void), builtin.Void); + compiled_types.Add (typeof (System.Type), builtin.Type); + compiled_types.Add (typeof (System.Exception), builtin.Exception); + compiled_types.Add (typeof (System.RuntimeFieldHandle), builtin.RuntimeFieldHandle); + compiled_types.Add (typeof (System.RuntimeTypeHandle), builtin.RuntimeTypeHandle); + } + } + + [System.Runtime.InteropServices.StructLayout (System.Runtime.InteropServices.LayoutKind.Explicit)] + struct SingleConverter + { + [System.Runtime.InteropServices.FieldOffset (0)] + int i; + +#pragma warning disable 414 + [System.Runtime.InteropServices.FieldOffset (0)] + float f; +#pragma warning restore 414 + + public static int SingleToInt32Bits (float v) + { + SingleConverter c = new SingleConverter (); + c.f = v; + return c.i; + } + } + +#endif + + public class AssemblyDefinitionDynamic : AssemblyDefinition + { + // + // In-memory only assembly container + // + public AssemblyDefinitionDynamic (ModuleContainer module, string name) + : base (module, name) + { + } + + // + // Assembly container with file output + // + public AssemblyDefinitionDynamic (ModuleContainer module, string name, string fileName) + : base (module, name, fileName) + { + } + + public Module IncludeModule (string moduleFile) + { + return builder_extra.AddModule (moduleFile); + } + +#if !STATIC + public override ModuleBuilder CreateModuleBuilder () + { + if (file_name == null) + return Builder.DefineDynamicModule (Name, false); + + return base.CreateModuleBuilder (); + } +#endif + // + // Initializes the code generator + // + public bool Create (AppDomain domain, AssemblyBuilderAccess access) + { +#if STATIC || FULL_AOT_RUNTIME + throw new NotSupportedException (); +#else + ResolveAssemblySecurityAttributes (); + var an = CreateAssemblyName (); + + Builder = file_name == null ? + domain.DefineDynamicAssembly (an, access) : + domain.DefineDynamicAssembly (an, access, Dirname (file_name)); + + module.Create (this, CreateModuleBuilder ()); + builder_extra = new AssemblyBuilderMonoSpecific (Builder, Compiler); + return true; +#endif + } + + static string Dirname (string name) + { + int pos = name.LastIndexOf ('/'); + + if (pos != -1) + return name.Substring (0, pos); + + pos = name.LastIndexOf ('\\'); + if (pos != -1) + return name.Substring (0, pos); + + return "."; + } + +#if !STATIC + protected override void SaveModule (PortableExecutableKinds pekind, ImageFileMachine machine) + { + try { + var module_only = typeof (AssemblyBuilder).GetProperty ("IsModuleOnly", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + var set_module_only = module_only.GetSetMethod (true); + + set_module_only.Invoke (Builder, new object[] { true }); + } catch { + base.SaveModule (pekind, machine); + } + + Builder.Save (file_name, pekind, machine); + } +#endif + } + + // + // Extension to System.Reflection.Emit.AssemblyBuilder to have fully compatible + // compiler + // + class AssemblyBuilderMonoSpecific : AssemblyBuilderExtension + { + static MethodInfo adder_method; + static MethodInfo add_permission; + static MethodInfo add_type_forwarder; + static MethodInfo win32_icon_define; + static FieldInfo assembly_version; + static FieldInfo assembly_algorithm; + static FieldInfo assembly_culture; + static FieldInfo assembly_flags; + + AssemblyBuilder builder; + + public AssemblyBuilderMonoSpecific (AssemblyBuilder ab, CompilerContext ctx) + : base (ctx) + { + this.builder = ab; + } + + public override Module AddModule (string module) + { + try { + if (adder_method == null) + adder_method = typeof (AssemblyBuilder).GetMethod ("AddModule", BindingFlags.Instance | BindingFlags.NonPublic); + + return (Module) adder_method.Invoke (builder, new object[] { module }); + } catch { + return base.AddModule (module); + } + } + + public override void AddPermissionRequests (PermissionSet[] permissions) + { + try { + if (add_permission == null) + add_permission = typeof (AssemblyBuilder).GetMethod ("AddPermissionRequests", BindingFlags.Instance | BindingFlags.NonPublic); + + add_permission.Invoke (builder, permissions); + } catch { + base.AddPermissionRequests (permissions); + } + } + + public override void AddTypeForwarder (TypeSpec type, Location loc) + { + try { + if (add_type_forwarder == null) { + add_type_forwarder = typeof (AssemblyBuilder).GetMethod ("AddTypeForwarder", BindingFlags.NonPublic | BindingFlags.Instance); + } + + add_type_forwarder.Invoke (builder, new object[] { type.GetMetaInfo () }); + } catch { + base.AddTypeForwarder (type, loc); + } + } + + public override void DefineWin32IconResource (string fileName) + { + try { + if (win32_icon_define == null) + win32_icon_define = typeof (AssemblyBuilder).GetMethod ("DefineIconResource", BindingFlags.Instance | BindingFlags.NonPublic); + + win32_icon_define.Invoke (builder, new object[] { fileName }); + } catch { + base.DefineWin32IconResource (fileName); + } + } + + public override void SetAlgorithmId (uint value, Location loc) + { + try { + if (assembly_algorithm == null) + assembly_algorithm = typeof (AssemblyBuilder).GetField ("algid", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.SetField); + + assembly_algorithm.SetValue (builder, value); + } catch { + base.SetAlgorithmId (value, loc); + } + } + + public override void SetCulture (string culture, Location loc) + { + try { + if (assembly_culture == null) + assembly_culture = typeof (AssemblyBuilder).GetField ("culture", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.SetField); + + assembly_culture.SetValue (builder, culture); + } catch { + base.SetCulture (culture, loc); + } + } + + public override void SetFlags (uint flags, Location loc) + { + try { + if (assembly_flags == null) + assembly_flags = typeof (AssemblyBuilder).GetField ("flags", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.SetField); + + assembly_flags.SetValue (builder, flags); + } catch { + base.SetFlags (flags, loc); + } + } + + public override void SetVersion (Version version, Location loc) + { + try { + if (assembly_version == null) + assembly_version = typeof (AssemblyBuilder).GetField ("version", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.SetField); + + assembly_version.SetValue (builder, version.ToString (4)); + } catch { + base.SetVersion (version, loc); + } + } + } + + // + // Reflection based references loader + // + class DynamicLoader : AssemblyReferencesLoader + { + readonly ReflectionImporter importer; + + public DynamicLoader (ReflectionImporter importer, CompilerContext compiler) + : base (compiler) + { + paths.Add (GetSystemDir ()); + + this.importer = importer; + } + + public ReflectionImporter Importer { + get { + return importer; + } + } + + protected override string[] GetDefaultReferences () + { + // + // For now the "default config" is harcoded into the compiler + // we can move this outside later + // + var default_references = new List (8); + + default_references.Add ("System"); + default_references.Add ("System.Xml"); +#if NET_2_1 + default_references.Add ("System.Net"); + default_references.Add ("System.Windows"); + default_references.Add ("System.Windows.Browser"); +#endif + + if (compiler.Settings.Version > LanguageVersion.ISO_2) + default_references.Add ("System.Core"); + if (compiler.Settings.Version > LanguageVersion.V_3) + default_references.Add ("Microsoft.CSharp"); + + return default_references.ToArray (); + } + + // + // Returns the directory where the system assemblies are installed + // + static string GetSystemDir () + { + return Path.GetDirectoryName (typeof (object).Assembly.Location); + } + + public override bool HasObjectType (Assembly assembly) + { + return assembly.GetType (compiler.BuiltinTypes.Object.FullName) != null; + } + + public override Assembly LoadAssemblyFile (string assembly, bool isImplicitReference) + { + Assembly a = null; + + try { + try { + char[] path_chars = { '/', '\\' }; + + if (assembly.IndexOfAny (path_chars) != -1) { + a = Assembly.LoadFrom (assembly); + } else { + string ass = assembly; + if (ass.EndsWith (".dll") || ass.EndsWith (".exe")) + ass = assembly.Substring (0, assembly.Length - 4); + a = Assembly.Load (ass); + } + } catch (FileNotFoundException) { + bool err = !isImplicitReference; + foreach (string dir in paths) { + string full_path = Path.Combine (dir, assembly); + if (!assembly.EndsWith (".dll") && !assembly.EndsWith (".exe")) + full_path += ".dll"; + + try { + a = Assembly.LoadFrom (full_path); + err = false; + break; + } catch (FileNotFoundException) { + } + } + + if (err) { + Error_FileNotFound (assembly); + return a; + } + } + } catch (BadImageFormatException) { + Error_FileCorrupted (assembly); + } + + return a; + } + + Module LoadModuleFile (AssemblyDefinitionDynamic assembly, string module) + { + string total_log = ""; + + try { + try { + return assembly.IncludeModule (module); + } catch (FileNotFoundException) { + bool err = true; + foreach (string dir in paths) { + string full_path = Path.Combine (dir, module); + if (!module.EndsWith (".netmodule")) + full_path += ".netmodule"; + + try { + return assembly.IncludeModule (full_path); + } catch (FileNotFoundException ff) { + total_log += ff.FusionLog; + } + } + if (err) { + Error_FileNotFound (module); + return null; + } + } + } catch (BadImageFormatException) { + Error_FileCorrupted (module); + } + + return null; + } + + public void LoadModules (AssemblyDefinitionDynamic assembly, RootNamespace targetNamespace) + { + foreach (var moduleName in compiler.Settings.Modules) { + var m = LoadModuleFile (assembly, moduleName); + if (m == null) + continue; + + var md = importer.ImportModule (m, targetNamespace); + assembly.AddModule (md); + } + } + + public override void LoadReferences (ModuleContainer module) + { + Assembly corlib; + List> loaded; + base.LoadReferencesCore (module, out corlib, out loaded); + + if (corlib == null) + return; + + importer.ImportAssembly (corlib, module.GlobalRootNamespace); + foreach (var entry in loaded) { + importer.ImportAssembly (entry.Item2, entry.Item1); + } + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/report.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/report.cs new file mode 100644 index 000000000..e9b16d1c0 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/report.cs @@ -0,0 +1,1144 @@ +// +// report.cs: report errors and warnings. +// +// Author: Miguel de Icaza (miguel@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// Copyright 2001 Ximian, Inc. (http://www.ximian.com) +// Copyright 2011 Xamarin, Inc (http://www.xamarin.com) +// + +using System; +using System.IO; +using System.Text; +using System.Collections.Generic; +using System.Diagnostics; + +namespace Mono.CSharp { + + // + // Errors and warnings manager + // + public class Report + { + public const int RuntimeErrorId = 10000; + + Dictionary warning_regions_table; + + ReportPrinter printer; + + int reporting_disabled; + + readonly CompilerSettings settings; + + /// + /// List of symbols related to reported error/warning. You have to fill it before error/warning is reported. + /// + List extra_information = new List (); + + // + // IF YOU ADD A NEW WARNING YOU HAVE TO ADD ITS ID HERE + // + public static readonly int[] AllWarnings = new int[] { + 28, 67, 78, + 105, 108, 109, 114, 162, 164, 168, 169, 183, 184, 197, + 219, 251, 252, 253, 278, 282, + 402, 414, 419, 420, 429, 436, 437, 440, 458, 464, 465, 467, 469, 472, 473, + 612, 618, 626, 628, 642, 649, 652, 657, 658, 659, 660, 661, 665, 672, 675, 693, + 728, + 809, 824, + 1030, 1058, 1060, 1066, + 1522, 1570, 1571, 1572, 1573, 1574, 1580, 1581, 1584, 1587, 1589, 1590, 1591, 1592, + 1607, 1616, 1633, 1634, 1635, 1685, 1690, 1691, 1692, 1695, 1696, 1697, 1699, + 1700, 1701, 1702, 1709, 1711, 1717, 1718, 1720, 1735, + 1901, 1956, 1981, 1998, + 2002, 2023, 2029, + 3000, 3001, 3002, 3003, 3005, 3006, 3007, 3008, 3009, + 3010, 3011, 3012, 3013, 3014, 3015, 3016, 3017, 3018, 3019, + 3021, 3022, 3023, 3024, 3026, 3027, + 4014, 4024, 4025, 4026, + 7035, 7080, 7081, 7082, 7095, + 8009, + }; + + static HashSet AllWarningsHashSet; + + public Report (CompilerContext context, ReportPrinter printer) + { + if (context == null) + throw new ArgumentNullException ("settings"); + if (printer == null) + throw new ArgumentNullException ("printer"); + + this.settings = context.Settings; + this.printer = printer; + } + + public void DisableReporting () + { + ++reporting_disabled; + } + + public void EnableReporting () + { + --reporting_disabled; + } + + public void FeatureIsNotAvailable (CompilerContext compiler, Location loc, string feature) + { + string version; + switch (compiler.Settings.Version) { + case LanguageVersion.ISO_1: + version = "1.0"; + break; + case LanguageVersion.ISO_2: + version = "2.0"; + break; + case LanguageVersion.V_3: + version = "3.0"; + break; + case LanguageVersion.V_4: + version = "4.0"; + break; + case LanguageVersion.V_5: + version = "5.0"; + break; + case LanguageVersion.V_6: + version = "6.0"; + break; + default: + throw new InternalErrorException ("Invalid feature version", compiler.Settings.Version); + } + + Error (1644, loc, + "Feature `{0}' cannot be used because it is not part of the C# {1} language specification", + feature, version); + } + + public void FeatureIsNotSupported (Location loc, string feature) + { + Error (1644, loc, + "Feature `{0}' is not supported in Mono mcs1 compiler. Consider using the `gmcs' compiler instead", + feature); + } + + public void RuntimeMissingSupport (Location loc, string feature) + { + Error (-88, loc, "Your .NET Runtime does not support `{0}'. Please use the latest Mono runtime instead.", feature); + } + + /// + /// In most error cases is very useful to have information about symbol that caused the error. + /// Call this method before you call Report.Error when it makes sense. + /// + public void SymbolRelatedToPreviousError (Location loc, string symbol) + { + SymbolRelatedToPreviousError (loc.ToString ()); + } + + public void SymbolRelatedToPreviousError (MemberSpec ms) + { + if (reporting_disabled > 0 || !printer.HasRelatedSymbolSupport) + return; + + var mc = ms.MemberDefinition as MemberCore; + while (ms is ElementTypeSpec) { + ms = ((ElementTypeSpec) ms).Element; + mc = ms.MemberDefinition as MemberCore; + } + + if (mc != null) { + SymbolRelatedToPreviousError (mc); + } else { + if (ms.DeclaringType != null) + ms = ms.DeclaringType; + + var imported_type = ms.MemberDefinition as ImportedTypeDefinition; + if (imported_type != null) { + var iad = imported_type.DeclaringAssembly as ImportedAssemblyDefinition; + SymbolRelatedToPreviousError (iad.Location); + } + } + } + + public void SymbolRelatedToPreviousError (MemberCore mc) + { + SymbolRelatedToPreviousError (mc.Location, mc.GetSignatureForError ()); + } + + public void SymbolRelatedToPreviousError (string loc) + { + string msg = String.Format ("{0} (Location of the symbol related to previous ", loc); + if (extra_information.Contains (msg)) + return; + + extra_information.Add (msg); + } + + public bool CheckWarningCode (int code, Location loc) + { + if (AllWarningsHashSet == null) + AllWarningsHashSet = new HashSet (AllWarnings); + + if (AllWarningsHashSet.Contains (code)) + return true; + + Warning (1691, 1, loc, "`{0}' is not a valid warning number", code); + return false; + } + + public void ExtraInformation (Location loc, string msg) + { + extra_information.Add (String.Format ("{0} {1}", loc, msg)); + } + + public WarningRegions RegisterWarningRegion (Location location) + { + WarningRegions regions; + if (warning_regions_table == null) { + regions = null; + warning_regions_table = new Dictionary (); + } else { + warning_regions_table.TryGetValue (location.File, out regions); + } + + if (regions == null) { + regions = new WarningRegions (); + warning_regions_table.Add (location.File, regions); + } + + return regions; + } + + public void Warning (int code, int level, Location loc, string message) + { + if (reporting_disabled > 0) + return; + + if (!settings.IsWarningEnabled (code, level)) + return; + + if (warning_regions_table != null && !loc.IsNull) { + WarningRegions regions; + if (warning_regions_table.TryGetValue (loc.File, out regions) && !regions.IsWarningEnabled (code, loc.Row)) + return; + } + + AbstractMessage msg; + if (settings.IsWarningAsError (code)) { + message = "Warning as Error: " + message; + msg = new ErrorMessage (code, loc, message, extra_information); + } else { + msg = new WarningMessage (code, loc, message, extra_information); + } + + extra_information.Clear (); + printer.Print (msg, settings.ShowFullPaths); + } + + public void Warning (int code, int level, Location loc, string format, string arg) + { + Warning (code, level, loc, String.Format (format, arg)); + } + + public void Warning (int code, int level, Location loc, string format, string arg1, string arg2) + { + Warning (code, level, loc, String.Format (format, arg1, arg2)); + } + + public void Warning (int code, int level, Location loc, string format, params object[] args) + { + Warning (code, level, loc, String.Format (format, args)); + } + + public void Warning (int code, int level, string message) + { + Warning (code, level, Location.Null, message); + } + + public void Warning (int code, int level, string format, string arg) + { + Warning (code, level, Location.Null, format, arg); + } + + public void Warning (int code, int level, string format, string arg1, string arg2) + { + Warning (code, level, Location.Null, format, arg1, arg2); + } + + public void Warning (int code, int level, string format, params string[] args) + { + Warning (code, level, Location.Null, String.Format (format, args)); + } + + // + // Warnings encountered so far + // + public int Warnings { + get { return printer.WarningsCount; } + } + + public void Error (int code, Location loc, string error) + { + if (reporting_disabled > 0) + return; + + ErrorMessage msg = new ErrorMessage (code, loc, error, extra_information); + extra_information.Clear (); + + printer.Print (msg, settings.ShowFullPaths); + + if (settings.Stacktrace) + Console.WriteLine (FriendlyStackTrace (new StackTrace (true))); + + if (printer.ErrorsCount == settings.FatalCounter) + throw new FatalException (msg.Text); + } + + public void Error (int code, Location loc, string format, string arg) + { + Error (code, loc, String.Format (format, arg)); + } + + public void Error (int code, Location loc, string format, string arg1, string arg2) + { + Error (code, loc, String.Format (format, arg1, arg2)); + } + + public void Error (int code, Location loc, string format, params string[] args) + { + Error (code, loc, String.Format (format, args)); + } + + public void Error (int code, string error) + { + Error (code, Location.Null, error); + } + + public void Error (int code, string format, string arg) + { + Error (code, Location.Null, format, arg); + } + + public void Error (int code, string format, string arg1, string arg2) + { + Error (code, Location.Null, format, arg1, arg2); + } + + public void Error (int code, string format, params string[] args) + { + Error (code, Location.Null, String.Format (format, args)); + } + + // + // Errors encountered so far + // + public int Errors { + get { return printer.ErrorsCount; } + } + + public bool IsDisabled { + get { + return reporting_disabled > 0; + } + } + + public ReportPrinter Printer { + get { return printer; } + } + + public ReportPrinter SetPrinter (ReportPrinter printer) + { + ReportPrinter old = this.printer; + this.printer = printer; + return old; + } + + [Conditional ("MCS_DEBUG")] + static public void Debug (string message, params object[] args) + { + Debug (4, message, args); + } + + [Conditional ("MCS_DEBUG")] + static public void Debug (int category, string message, params object[] args) + { +// if ((category & DebugFlags) == 0) +// return; + + StringBuilder sb = new StringBuilder (message); + + if ((args != null) && (args.Length > 0)) { + sb.Append (": "); + + bool first = true; + foreach (object arg in args) { + if (first) + first = false; + else + sb.Append (", "); + if (arg == null) + sb.Append ("null"); +// else if (arg is ICollection) +// sb.Append (PrintCollection ((ICollection) arg)); + else + sb.Append (arg); + } + } + + Console.WriteLine (sb.ToString ()); + } +/* + static public string PrintCollection (ICollection collection) + { + StringBuilder sb = new StringBuilder (); + + sb.Append (collection.GetType ()); + sb.Append ("("); + + bool first = true; + foreach (object o in collection) { + if (first) + first = false; + else + sb.Append (", "); + sb.Append (o); + } + + sb.Append (")"); + return sb.ToString (); + } +*/ + static string FriendlyStackTrace (StackTrace t) + { + StringBuilder sb = new StringBuilder (); + + bool foundUserCode = false; + + for (int i = 0; i < t.FrameCount; i++) { + StackFrame f = t.GetFrame (i); + var mb = f.GetMethod (); + + if (!foundUserCode && mb.ReflectedType == typeof (Report)) + continue; + + foundUserCode = true; + + sb.Append ("\tin "); + + if (f.GetFileLineNumber () > 0) + sb.AppendFormat ("(at {0}:{1}) ", f.GetFileName (), f.GetFileLineNumber ()); + + sb.AppendFormat ("{0}.{1} (", mb.ReflectedType.Name, mb.Name); + + bool first = true; + foreach (var pi in mb.GetParameters ()) { + if (!first) + sb.Append (", "); + first = false; + + sb.Append (pi.ParameterType.FullName); + } + sb.Append (")\n"); + } + + return sb.ToString (); + } + } + + public abstract class AbstractMessage + { + readonly string[] extra_info; + protected readonly int code; + protected readonly Location location; + readonly string message; + + protected AbstractMessage (int code, Location loc, string msg, List extraInfo) + { + this.code = code; + if (code < 0) + this.code = 8000 - code; + + this.location = loc; + this.message = msg; + if (extraInfo.Count != 0) { + this.extra_info = extraInfo.ToArray (); + } + } + + protected AbstractMessage (AbstractMessage aMsg) + { + this.code = aMsg.code; + this.location = aMsg.location; + this.message = aMsg.message; + this.extra_info = aMsg.extra_info; + } + + public int Code { + get { return code; } + } + + public override bool Equals (object obj) + { + AbstractMessage msg = obj as AbstractMessage; + if (msg == null) + return false; + + return code == msg.code && location.Equals (msg.location) && message == msg.message; + } + + public override int GetHashCode () + { + return code.GetHashCode (); + } + + public abstract bool IsWarning { get; } + + public Location Location { + get { return location; } + } + + public abstract string MessageType { get; } + + public string[] RelatedSymbols { + get { return extra_info; } + } + + public string Text { + get { return message; } + } + } + + sealed class WarningMessage : AbstractMessage + { + public WarningMessage (int code, Location loc, string message, List extra_info) + : base (code, loc, message, extra_info) + { + } + + public override bool IsWarning { + get { return true; } + } + + public override string MessageType { + get { + return "warning"; + } + } + } + + sealed class ErrorMessage : AbstractMessage + { + public ErrorMessage (int code, Location loc, string message, List extraInfo) + : base (code, loc, message, extraInfo) + { + } + + public ErrorMessage (AbstractMessage aMsg) + : base (aMsg) + { + } + + public override bool IsWarning { + get { return false; } + } + + public override string MessageType { + get { + return "error"; + } + } + } + + // + // Generic base for any message writer + // + public abstract class ReportPrinter + { + protected HashSet reported_missing_definitions; + + #region Properties + + public int ErrorsCount { get; protected set; } + + public int WarningsCount { get; private set; } + + // + // When (symbols related to previous ...) can be used + // + public virtual bool HasRelatedSymbolSupport { + get { return true; } + } + + #endregion + + + protected virtual string FormatText (string txt) + { + return txt; + } + + public virtual void Print (AbstractMessage msg, bool showFullPath) + { + if (msg.IsWarning) { + ++WarningsCount; + } else { + ++ErrorsCount; + } + } + + protected void Print (AbstractMessage msg, TextWriter output, bool showFullPath) + { + StringBuilder txt = new StringBuilder (); + if (!msg.Location.IsNull) { + if (showFullPath) + txt.Append (msg.Location.ToStringFullName ()); + else + txt.Append (msg.Location.ToString ()); + + txt.Append (" "); + } + + txt.AppendFormat ("{0} CS{1:0000}: {2}", msg.MessageType, msg.Code, msg.Text); + + if (!msg.IsWarning) + output.WriteLine (FormatText (txt.ToString ())); + else + output.WriteLine (txt.ToString ()); + + if (msg.RelatedSymbols != null) { + foreach (string s in msg.RelatedSymbols) + output.WriteLine (s + msg.MessageType + ")"); + } + } + + // + // Tracks reported missing types. It needs to be session specific + // because we can run in probing mode + // + public bool MissingTypeReported (ITypeDefinition typeDefinition) + { + if (reported_missing_definitions == null) + reported_missing_definitions = new HashSet (); + + if (reported_missing_definitions.Contains (typeDefinition)) + return true; + + reported_missing_definitions.Add (typeDefinition); + return false; + } + + public void Reset () + { + // HACK: Temporary hack for broken repl flow + ErrorsCount = WarningsCount = 0; + } + } + + sealed class NullReportPrinter : ReportPrinter + { + } + + // + // Default message recorder, it uses two types of message groups. + // Common messages: messages reported in all sessions. + // Merged messages: union of all messages in all sessions. + // + // Used by the Lambda expressions to compile the code with various + // parameter values, or by attribute resolver + // + class SessionReportPrinter : ReportPrinter + { + List session_messages; + // + // A collection of exactly same messages reported in all sessions + // + List common_messages; + + // + // A collection of unique messages reported in all sessions + // + List merged_messages; + + bool showFullPaths; + + public void ClearSession () + { + session_messages = null; + } + + public override void Print (AbstractMessage msg, bool showFullPath) + { + // + // This line is useful when debugging recorded messages + // + // Console.WriteLine ("RECORDING: {0}", msg.Text); + + if (session_messages == null) + session_messages = new List (); + + session_messages.Add (msg); + + this.showFullPaths = showFullPath; + base.Print (msg, showFullPath); + } + + public void EndSession () + { + if (session_messages == null) + return; + + // + // Handles the first session + // + if (common_messages == null) { + common_messages = new List (session_messages); + merged_messages = session_messages; + session_messages = null; + return; + } + + // + // Store common messages if any + // + for (int i = 0; i < common_messages.Count; ++i) { + AbstractMessage cmsg = common_messages[i]; + bool common_msg_found = false; + foreach (AbstractMessage msg in session_messages) { + if (cmsg.Equals (msg)) { + common_msg_found = true; + break; + } + } + + if (!common_msg_found) + common_messages.RemoveAt (i); + } + + // + // Merge session and previous messages + // + for (int i = 0; i < session_messages.Count; ++i) { + AbstractMessage msg = session_messages[i]; + bool msg_found = false; + for (int ii = 0; ii < merged_messages.Count; ++ii) { + if (msg.Equals (merged_messages[ii])) { + msg_found = true; + break; + } + } + + if (!msg_found) + merged_messages.Add (msg); + } + } + + public bool IsEmpty { + get { + return merged_messages == null && common_messages == null; + } + } + + // + // Prints collected messages, common messages have a priority + // + public bool Merge (ReportPrinter dest) + { + var messages_to_print = merged_messages; + if (common_messages != null && common_messages.Count > 0) { + messages_to_print = common_messages; + } + + if (messages_to_print == null) + return false; + + bool error_msg = false; + foreach (AbstractMessage msg in messages_to_print) { + dest.Print (msg, showFullPaths); + error_msg |= !msg.IsWarning; + } + + if (reported_missing_definitions != null) { + foreach (var missing in reported_missing_definitions) + dest.MissingTypeReported (missing); + } + + return error_msg; + } + } + + public class StreamReportPrinter : ReportPrinter + { + readonly TextWriter writer; + + public StreamReportPrinter (TextWriter writer) + { + this.writer = writer; + } + + public override void Print (AbstractMessage msg, bool showFullPath) + { + Print (msg, writer, showFullPath); + base.Print (msg, showFullPath); + } + } + + public class ConsoleReportPrinter : StreamReportPrinter + { + static readonly string prefix, postfix; + + static ConsoleReportPrinter () + { + string term = Environment.GetEnvironmentVariable ("TERM"); + bool xterm_colors = false; + + switch (term){ + case "xterm": + case "rxvt": + case "rxvt-unicode": + if (Environment.GetEnvironmentVariable ("COLORTERM") != null){ + xterm_colors = true; + } + break; + + case "xterm-color": + case "xterm-256color": + xterm_colors = true; + break; + } + if (!xterm_colors) + return; + + if (!(UnixUtils.isatty (1) && UnixUtils.isatty (2))) + return; + + string config = Environment.GetEnvironmentVariable ("MCS_COLORS"); + if (config == null){ + config = "errors=red"; + //config = "brightwhite,red"; + } + + if (config == "disable") + return; + + if (!config.StartsWith ("errors=")) + return; + + config = config.Substring (7); + + int p = config.IndexOf (","); + if (p == -1) + prefix = GetForeground (config); + else + prefix = GetBackground (config.Substring (p+1)) + GetForeground (config.Substring (0, p)); + postfix = "\x001b[0m"; + } + + public ConsoleReportPrinter () + : base (Console.Error) + { + } + + public ConsoleReportPrinter (TextWriter writer) + : base (writer) + { + } + + static int NameToCode (string s) + { + switch (s) { + case "black": + return 0; + case "red": + return 1; + case "green": + return 2; + case "yellow": + return 3; + case "blue": + return 4; + case "magenta": + return 5; + case "cyan": + return 6; + case "grey": + case "white": + return 7; + } + return 7; + } + + // + // maps a color name to its xterm color code + // + static string GetForeground (string s) + { + string highcode; + + if (s.StartsWith ("bright")) { + highcode = "1;"; + s = s.Substring (6); + } else + highcode = ""; + + return "\x001b[" + highcode + (30 + NameToCode (s)).ToString () + "m"; + } + + static string GetBackground (string s) + { + return "\x001b[" + (40 + NameToCode (s)).ToString () + "m"; + } + + protected override string FormatText (string txt) + { + if (prefix != null) + return prefix + txt + postfix; + + return txt; + } + } + + class TimeReporter + { + public enum TimerType + { + ParseTotal, + AssemblyBuilderSetup, + CreateTypeTotal, + ReferencesLoading, + ReferencesImporting, + PredefinedTypesInit, + ModuleDefinitionTotal, + EmitTotal, + CloseTypes, + Resouces, + OutputSave, + DebugSave, + } + + readonly Stopwatch[] timers; + Stopwatch total; + + public TimeReporter (bool enabled) + { + if (!enabled) + return; + + timers = new Stopwatch[System.Enum.GetValues(typeof (TimerType)).Length]; + } + + public void Start (TimerType type) + { + if (timers != null) { + var sw = new Stopwatch (); + timers[(int) type] = sw; + sw.Start (); + } + } + + public void StartTotal () + { + total = new Stopwatch (); + total.Start (); + } + + public void Stop (TimerType type) + { + if (timers != null) { + timers[(int) type].Stop (); + } + } + + public void StopTotal () + { + total.Stop (); + } + + public void ShowStats () + { + if (timers == null) + return; + + Dictionary timer_names = new Dictionary { + { TimerType.ParseTotal, "Parsing source files" }, + { TimerType.AssemblyBuilderSetup, "Assembly builder setup" }, + { TimerType.CreateTypeTotal, "Compiled types created" }, + { TimerType.ReferencesLoading, "Referenced assemblies loading" }, + { TimerType.ReferencesImporting, "Referenced assemblies importing" }, + { TimerType.PredefinedTypesInit, "Predefined types initialization" }, + { TimerType.ModuleDefinitionTotal, "Module definition" }, + { TimerType.EmitTotal, "Resolving and emitting members blocks" }, + { TimerType.CloseTypes, "Module types closed" }, + { TimerType.Resouces, "Embedding resources" }, + { TimerType.OutputSave, "Writing output file" }, + { TimerType.DebugSave, "Writing debug symbols file" }, + }; + + int counter = 0; + double percentage = (double) total.ElapsedMilliseconds / 100; + long subtotal = total.ElapsedMilliseconds; + foreach (var timer in timers) { + string msg = timer_names[(TimerType) counter++]; + var ms = timer == null ? 0 : timer.ElapsedMilliseconds; + Console.WriteLine ("{0,4:0.0}% {1,5}ms {2}", ms / percentage, ms, msg); + subtotal -= ms; + } + + Console.WriteLine ("{0,4:0.0}% {1,5}ms Other tasks", subtotal / percentage, subtotal); + Console.WriteLine (); + Console.WriteLine ("Total elapsed time: {0}", total.Elapsed); + } + } + + public class InternalErrorException : Exception { + public InternalErrorException (MemberCore mc, Exception e) + : base (mc.Location + " " + mc.GetSignatureForError (), e) + { + } + + public InternalErrorException () + : base ("Internal error") + { + } + + public InternalErrorException (string message) + : base (message) + { + } + + public InternalErrorException (string message, params object[] args) + : base (String.Format (message, args)) + { + } + + public InternalErrorException (Exception exception, string message, params object[] args) + : base (String.Format (message, args), exception) + { + } + + public InternalErrorException (Exception e, Location loc) + : base (loc.ToString (), e) + { + } + } + + class FatalException : Exception + { + public FatalException (string message) + : base (message) + { + } + } + + /// + /// Handles #pragma warning + /// + public class WarningRegions { + + abstract class PragmaCmd + { + public int Line; + + protected PragmaCmd (int line) + { + Line = line; + } + + public abstract bool IsEnabled (int code, bool previous); + } + + class Disable : PragmaCmd + { + int code; + public Disable (int line, int code) + : base (line) + { + this.code = code; + } + + public override bool IsEnabled (int code, bool previous) + { + return this.code != code && previous; + } + } + + class DisableAll : PragmaCmd + { + public DisableAll (int line) + : base (line) {} + + public override bool IsEnabled(int code, bool previous) + { + return false; + } + } + + class Enable : PragmaCmd + { + int code; + public Enable (int line, int code) + : base (line) + { + this.code = code; + } + + public override bool IsEnabled(int code, bool previous) + { + return this.code == code || previous; + } + } + + class EnableAll : PragmaCmd + { + public EnableAll (int line) + : base (line) {} + + public override bool IsEnabled(int code, bool previous) + { + return true; + } + } + + + List regions = new List (); + + public void WarningDisable (int line) + { + regions.Add (new DisableAll (line)); + } + + public void WarningDisable (Location location, int code, Report Report) + { + if (Report.CheckWarningCode (code, location)) + regions.Add (new Disable (location.Row, code)); + } + + public void WarningEnable (int line) + { + regions.Add (new EnableAll (line)); + } + + public void WarningEnable (Location location, int code, CompilerContext context) + { + if (!context.Report.CheckWarningCode (code, location)) + return; + + if (context.Settings.IsWarningDisabledGlobally (code)) + context.Report.Warning (1635, 1, location, "Cannot restore warning `CS{0:0000}' because it was disabled globally", code); + + regions.Add (new Enable (location.Row, code)); + } + + public bool IsWarningEnabled (int code, int src_line) + { + bool result = true; + foreach (PragmaCmd pragma in regions) { + if (src_line < pragma.Line) + break; + + result = pragma.IsEnabled (code, result); + } + return result; + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/settings.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/settings.cs new file mode 100644 index 000000000..9ba66c161 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/settings.cs @@ -0,0 +1,1601 @@ +// +// settings.cs: All compiler settings +// +// Author: Miguel de Icaza (miguel@ximian.com) +// Ravi Pratap (ravi@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2001 Ximian, Inc (http://www.ximian.com) +// Copyright 2004-2008 Novell, Inc +// Copyright 2011 Xamarin, Inc (http://www.xamarin.com) +// + +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Globalization; +using System; + +namespace Mono.CSharp { + + public enum LanguageVersion + { + ISO_1 = 1, + ISO_2 = 2, + V_3 = 3, + V_4 = 4, + V_5 = 5, + V_6 = 6, + Experimental = 100, + + Default = LanguageVersion.V_6, + } + + public enum RuntimeVersion + { + v1, + v2, + v4 + } + + public enum Target + { + Library, Exe, Module, WinExe + } + + public enum Platform + { + AnyCPU, + AnyCPU32Preferred, + Arm, + X86, + X64, + IA64 + } + + public class CompilerSettings + { + public Target Target; + public Platform Platform; + public string TargetExt; + public bool VerifyClsCompliance; + public bool Optimize; + public LanguageVersion Version; + public bool EnhancedWarnings; + public bool LoadDefaultReferences; + public string SdkVersion; + + public string StrongNameKeyFile; + public string StrongNameKeyContainer; + public bool StrongNameDelaySign; + + public int TabSize; + + public bool WarningsAreErrors; + public int WarningLevel; + + // + // Assemblies references to be loaded + // + public List AssemblyReferences; + + // + // External aliases for assemblies + // + public List> AssemblyReferencesAliases; + + // + // Modules to be embedded + // + public List Modules; + + // + // Lookup paths for referenced assemblies + // + public List ReferencesLookupPaths; + + // + // Encoding. + // + public Encoding Encoding; + + // + // If set, enable XML documentation generation + // + public string DocumentationFile; + + public string MainClass; + + // + // Output file + // + public string OutputFile; + + // + // The default compiler checked state + // + public bool Checked; + + // + // If true, the compiler is operating in statement mode, + // this currently turns local variable declaration into + // static variables of a class + // + public bool StatementMode; // TODO: SUPER UGLY + + // + // Whether to allow Unsafe code + // + public bool Unsafe; + + public string Win32ResourceFile; + public string Win32IconFile; + + // + // A list of resource files for embedding + // + public List Resources; + + public bool GenerateDebugInfo; + + #region Compiler debug flags only + public bool ParseOnly, TokenizeOnly, Timestamps; + public int DebugFlags; + public int VerboseParserFlag; + public int FatalCounter; + public bool Stacktrace; + public bool BreakOnInternalError; + #endregion + + public bool ShowFullPaths; + + // + // Whether we are being linked against the standard libraries. + // This is only used to tell whether `System.Object' should + // have a base class or not. + // + public bool StdLib; + + public RuntimeVersion StdLibRuntimeVersion; + + public string RuntimeMetadataVersion; + + public bool WriteMetadataOnly; + + readonly List conditional_symbols; + + readonly List source_files; + + List warnings_as_error; + List warnings_only; + HashSet warning_ignore_table; + + public CompilerSettings () + { + StdLib = true; + Target = Target.Exe; + TargetExt = ".exe"; + Platform = Platform.AnyCPU; + Version = LanguageVersion.Default; + VerifyClsCompliance = true; + Encoding = Encoding.UTF8; + LoadDefaultReferences = true; + StdLibRuntimeVersion = RuntimeVersion.v4; + WarningLevel = 4; + + // Default to 1 or mdb files would be platform speficic + TabSize = 1; + + AssemblyReferences = new List (); + AssemblyReferencesAliases = new List> (); + Modules = new List (); + ReferencesLookupPaths = new List (); + + conditional_symbols = new List (); + // + // Add default mcs define + // + conditional_symbols.Add ("__MonoCS__"); + + source_files = new List (); + } + + #region Properties + + public SourceFile FirstSourceFile { + get { + return source_files.Count > 0 ? source_files [0] : null; + } + } + + public bool HasKeyFileOrContainer { + get { + return StrongNameKeyFile != null || StrongNameKeyContainer != null; + } + } + + public bool NeedsEntryPoint { + get { + return Target == Target.Exe || Target == Target.WinExe; + } + } + + public List SourceFiles { + get { + return source_files; + } + } + + #endregion + + public void AddConditionalSymbol (string symbol) + { + if (!conditional_symbols.Contains (symbol)) + conditional_symbols.Add (symbol); + } + + public void AddWarningAsError (int id) + { + if (warnings_as_error == null) + warnings_as_error = new List (); + + warnings_as_error.Add (id); + } + + public void AddWarningOnly (int id) + { + if (warnings_only == null) + warnings_only = new List (); + + warnings_only.Add (id); + } + + public bool IsConditionalSymbolDefined (string symbol) + { + return conditional_symbols.Contains (symbol); + } + + public bool IsWarningAsError (int code) + { + bool is_error = WarningsAreErrors; + + // Check specific list + if (warnings_as_error != null) + is_error |= warnings_as_error.Contains (code); + + // Ignore excluded warnings + if (warnings_only != null && warnings_only.Contains (code)) + is_error = false; + + return is_error; + } + + public bool IsWarningEnabled (int code, int level) + { + if (WarningLevel < level) + return false; + + return !IsWarningDisabledGlobally (code); + } + + public bool IsWarningDisabledGlobally (int code) + { + return warning_ignore_table != null && warning_ignore_table.Contains (code); + } + + public void SetIgnoreWarning (int code) + { + if (warning_ignore_table == null) + warning_ignore_table = new HashSet (); + + warning_ignore_table.Add (code); + } + } + + public class CommandLineParser + { + enum ParseResult + { + Success, + Error, + Stop, + UnknownOption + } + + static readonly char[] argument_value_separator = { ';', ',' }; + static readonly char[] numeric_value_separator = { ';', ',', ' ' }; + + readonly TextWriter output; + readonly Report report; + bool stop_argument; + + Dictionary source_file_index; + + public event Func UnknownOptionHandler; + + CompilerSettings parser_settings; + + public CommandLineParser (TextWriter errorOutput) + : this (errorOutput, Console.Out) + { + } + + public CommandLineParser (TextWriter errorOutput, TextWriter messagesOutput) + { + var rp = new StreamReportPrinter (errorOutput); + + parser_settings = new CompilerSettings (); + report = new Report (new CompilerContext (parser_settings, rp), rp); + this.output = messagesOutput; + } + + public bool HasBeenStopped { + get { + return stop_argument; + } + } + + void About () + { + output.WriteLine ( + "The Mono C# compiler is Copyright 2001-2011, Novell, Inc.\n\n" + + "The compiler source code is released under the terms of the \n" + + "MIT X11 or GNU GPL licenses\n\n" + + + "For more information on Mono, visit the project Web site\n" + + " http://www.mono-project.com\n\n" + + + "The compiler was written by Miguel de Icaza, Ravi Pratap, Martin Baulig, Marek Safar, Raja R Harinath, Atushi Enomoto"); + } + + public CompilerSettings ParseArguments (string[] args) + { + CompilerSettings settings = new CompilerSettings (); + if (!ParseArguments (settings, args)) + return null; + + return settings; + } + + public bool ParseArguments (CompilerSettings settings, string[] args) + { + if (settings == null) + throw new ArgumentNullException ("settings"); + + List response_file_list = null; + bool parsing_options = true; + stop_argument = false; + source_file_index = new Dictionary (); + + for (int i = 0; i < args.Length; i++) { + string arg = args[i]; + if (arg.Length == 0) + continue; + + if (arg[0] == '@') { + string[] extra_args; + string response_file = arg.Substring (1); + + if (response_file_list == null) + response_file_list = new List (); + + if (response_file_list.Contains (response_file)) { + report.Error (1515, "Response file `{0}' specified multiple times", response_file); + return false; + } + + response_file_list.Add (response_file); + + extra_args = LoadArgs (response_file); + if (extra_args == null) { + report.Error (2011, "Unable to open response file: " + response_file); + return false; + } + + args = AddArgs (args, extra_args); + continue; + } + + if (parsing_options) { + if (arg == "--") { + parsing_options = false; + continue; + } + + bool dash_opt = arg[0] == '-'; + bool slash_opt = arg[0] == '/'; + if (dash_opt) { + switch (ParseOptionUnix (arg, ref args, ref i, settings)) { + case ParseResult.Error: + case ParseResult.Success: + continue; + case ParseResult.Stop: + stop_argument = true; + return true; + case ParseResult.UnknownOption: + if (UnknownOptionHandler != null) { + var ret = UnknownOptionHandler (args, i); + if (ret != -1) { + i = ret; + continue; + } + } + break; + } + } + + if (dash_opt || slash_opt) { + // Try a -CSCOPTION + string csc_opt = dash_opt ? "/" + arg.Substring (1) : arg; + switch (ParseOption (csc_opt, ref args, settings)) { + case ParseResult.Error: + case ParseResult.Success: + continue; + case ParseResult.UnknownOption: + // Need to skip `/home/test.cs' however /test.cs is considered as error + if ((slash_opt && arg.Length > 3 && arg.IndexOf ('/', 2) > 0)) + break; + + if (UnknownOptionHandler != null) { + var ret = UnknownOptionHandler (args, i); + if (ret != -1) { + i = ret; + continue; + } + } + + Error_WrongOption (arg); + return false; + + case ParseResult.Stop: + stop_argument = true; + return true; + } + } + } + + ProcessSourceFiles (arg, false, settings.SourceFiles); + } + + return report.Errors == 0; + } + + void ProcessSourceFiles (string spec, bool recurse, List sourceFiles) + { + string path, pattern; + + SplitPathAndPattern (spec, out path, out pattern); + if (pattern.IndexOf ('*') == -1) { + AddSourceFile (spec, sourceFiles); + return; + } + + string[] files; + try { + files = Directory.GetFiles (path, pattern); + } catch (System.IO.DirectoryNotFoundException) { + report.Error (2001, "Source file `" + spec + "' could not be found"); + return; + } catch (System.IO.IOException) { + report.Error (2001, "Source file `" + spec + "' could not be found"); + return; + } + foreach (string f in files) { + AddSourceFile (f, sourceFiles); + } + + if (!recurse) + return; + + string[] dirs = null; + + try { + dirs = Directory.GetDirectories (path); + } catch { + } + + foreach (string d in dirs) { + + // Don't include path in this string, as each + // directory entry already does + ProcessSourceFiles (d + "/" + pattern, true, sourceFiles); + } + } + + static string[] AddArgs (string[] args, string[] extra_args) + { + string[] new_args; + new_args = new string[extra_args.Length + args.Length]; + + // if args contains '--' we have to take that into account + // split args into first half and second half based on '--' + // and add the extra_args before -- + int split_position = Array.IndexOf (args, "--"); + if (split_position != -1) { + Array.Copy (args, new_args, split_position); + extra_args.CopyTo (new_args, split_position); + Array.Copy (args, split_position, new_args, split_position + extra_args.Length, args.Length - split_position); + } else { + args.CopyTo (new_args, 0); + extra_args.CopyTo (new_args, args.Length); + } + + return new_args; + } + + void AddAssemblyReference (string alias, string assembly, CompilerSettings settings) + { + if (assembly.Length == 0) { + report.Error (1680, "Invalid reference alias `{0}='. Missing filename", alias); + return; + } + + if (!IsExternAliasValid (alias)) { + report.Error (1679, "Invalid extern alias for -reference. Alias `{0}' is not a valid identifier", alias); + return; + } + + settings.AssemblyReferencesAliases.Add (Tuple.Create (alias, assembly)); + } + + void AddResource (AssemblyResource res, CompilerSettings settings) + { + if (settings.Resources == null) { + settings.Resources = new List (); + settings.Resources.Add (res); + return; + } + + if (settings.Resources.Contains (res)) { + report.Error (1508, "The resource identifier `{0}' has already been used in this assembly", res.Name); + return; + } + + settings.Resources.Add (res); + } + + void AddSourceFile (string fileName, List sourceFiles) + { + string path = Path.GetFullPath (fileName); + + int index; + if (source_file_index.TryGetValue (path, out index)) { + string other_name = sourceFiles[index - 1].Name; + if (fileName.Equals (other_name)) + report.Warning (2002, 1, "Source file `{0}' specified multiple times", other_name); + else + report.Warning (2002, 1, "Source filenames `{0}' and `{1}' both refer to the same file: {2}", fileName, other_name, path); + + return; + } + + var unit = new SourceFile (fileName, path, sourceFiles.Count + 1); + sourceFiles.Add (unit); + source_file_index.Add (path, unit.Index); + } + + public bool ProcessWarningsList (string text, Action action) + { + bool valid = true; + foreach (string wid in text.Split (numeric_value_separator, StringSplitOptions.RemoveEmptyEntries)) { + int id; + if (!int.TryParse (wid, NumberStyles.AllowLeadingWhite, CultureInfo.InvariantCulture, out id)) { + report.Error (1904, "`{0}' is not a valid warning number", wid); + valid = false; + continue; + } + + if (report.CheckWarningCode (id, Location.Null)) + action (id); + } + + return valid; + } + + void Error_RequiresArgument (string option) + { + report.Error (2006, "Missing argument for `{0}' option", option); + } + + void Error_RequiresFileName (string option) + { + report.Error (2005, "Missing file specification for `{0}' option", option); + } + + void Error_WrongOption (string option) + { + report.Error (2007, "Unrecognized command-line option: `{0}'", option); + } + + static bool IsExternAliasValid (string identifier) + { + return Tokenizer.IsValidIdentifier (identifier); + } + + static string[] LoadArgs (string file) + { + StreamReader f; + var args = new List (); + string line; + try { + f = new StreamReader (file); + } catch { + return null; + } + + StringBuilder sb = new StringBuilder (); + + while ((line = f.ReadLine ()) != null) { + int t = line.Length; + + for (int i = 0; i < t; i++) { + char c = line[i]; + + if (c == '"' || c == '\'') { + char end = c; + + for (i++; i < t; i++) { + c = line[i]; + + if (c == end) + break; + sb.Append (c); + } + } else if (c == ' ') { + if (sb.Length > 0) { + args.Add (sb.ToString ()); + sb.Length = 0; + } + } else + sb.Append (c); + } + if (sb.Length > 0) { + args.Add (sb.ToString ()); + sb.Length = 0; + } + } + + return args.ToArray (); + } + + void OtherFlags () + { + output.WriteLine ( + "Other flags in the compiler\n" + + " --fatal[=COUNT] Makes error after COUNT fatal\n" + + " --lint Enhanced warnings\n" + + " --metadata-only Produced assembly will contain metadata only\n" + + " --parse Only parses the source file\n" + + " --runtime:VERSION Sets mscorlib.dll metadata version: v1, v2, v4\n" + + " --stacktrace Shows stack trace at error location\n" + + " --timestamp Displays time stamps of various compiler events\n" + + " -v Verbose parsing (for debugging the parser)\n" + + " --mcs-debug X Sets MCS debugging level to X\n" + + " --break-on-ice Breaks compilation on internal compiler error"); + } + + // + // This parses the -arg and /arg options to the compiler, even if the strings + // in the following text use "/arg" on the strings. + // + ParseResult ParseOption (string option, ref string[] args, CompilerSettings settings) + { + int idx = option.IndexOf (':'); + string arg, value; + + if (idx == -1) { + arg = option; + value = ""; + } else { + arg = option.Substring (0, idx); + + value = option.Substring (idx + 1); + } + + switch (arg.ToLowerInvariant ()) { + case "/nologo": + return ParseResult.Success; + + case "/t": + case "/target": + switch (value) { + case "exe": + settings.Target = Target.Exe; + break; + + case "winexe": + settings.Target = Target.WinExe; + break; + + case "library": + settings.Target = Target.Library; + settings.TargetExt = ".dll"; + break; + + case "module": + settings.Target = Target.Module; + settings.TargetExt = ".netmodule"; + break; + + default: + report.Error (2019, "Invalid target type for -target. Valid options are `exe', `winexe', `library' or `module'"); + return ParseResult.Error; + } + return ParseResult.Success; + + case "/out": + if (value.Length == 0) { + Error_RequiresFileName (option); + return ParseResult.Error; + } + settings.OutputFile = value; + return ParseResult.Success; + + case "/o": + case "/o+": + case "/optimize": + case "/optimize+": + settings.Optimize = true; + return ParseResult.Success; + + case "/o-": + case "/optimize-": + settings.Optimize = false; + return ParseResult.Success; + + // TODO: Not supported by csc 3.5+ + case "/incremental": + case "/incremental+": + case "/incremental-": + // nothing. + return ParseResult.Success; + + case "/d": + case "/define": { + if (value.Length == 0) { + Error_RequiresArgument (option); + return ParseResult.Error; + } + + foreach (string d in value.Split (argument_value_separator)) { + string conditional = d.Trim (); + if (!Tokenizer.IsValidIdentifier (conditional)) { + report.Warning (2029, 1, "Invalid conditional define symbol `{0}'", conditional); + continue; + } + + settings.AddConditionalSymbol (conditional); + } + return ParseResult.Success; + } + + case "/bugreport": + // + // We should collect data, runtime, etc and store in the file specified + // + output.WriteLine ("To file bug reports, please visit: http://www.mono-project.com/Bugs"); + return ParseResult.Success; + + case "/pkg": { + string packages; + + if (value.Length == 0) { + Error_RequiresArgument (option); + return ParseResult.Error; + } + packages = String.Join (" ", value.Split (new Char[] { ';', ',', '\n', '\r' })); + string pkgout = Driver.GetPackageFlags (packages, report); + + if (pkgout == null) + return ParseResult.Error; + + string[] xargs = pkgout.Trim (new Char[] { ' ', '\n', '\r', '\t' }).Split (new Char[] { ' ', '\t' }); + args = AddArgs (args, xargs); + return ParseResult.Success; + } + + case "/linkres": + case "/linkresource": + case "/res": + case "/resource": + AssemblyResource res = null; + string[] s = value.Split (argument_value_separator, StringSplitOptions.RemoveEmptyEntries); + switch (s.Length) { + case 1: + if (s[0].Length == 0) + goto default; + res = new AssemblyResource (s[0], Path.GetFileName (s[0])); + break; + case 2: + res = new AssemblyResource (s[0], s[1]); + break; + case 3: + if (s[2] != "public" && s[2] != "private") { + report.Error (1906, "Invalid resource visibility option `{0}'. Use either `public' or `private' instead", s[2]); + return ParseResult.Error; + } + res = new AssemblyResource (s[0], s[1], s[2] == "private"); + break; + default: + report.Error (-2005, "Wrong number of arguments for option `{0}'", option); + return ParseResult.Error; + } + + if (res != null) { + res.IsEmbeded = arg[1] == 'r' || arg[1] == 'R'; + AddResource (res, settings); + } + + return ParseResult.Success; + + case "/recurse": + if (value.Length == 0) { + Error_RequiresFileName (option); + return ParseResult.Error; + } + ProcessSourceFiles (value, true, settings.SourceFiles); + return ParseResult.Success; + + case "/r": + case "/reference": { + if (value.Length == 0) { + Error_RequiresFileName (option); + return ParseResult.Error; + } + + string[] refs = value.Split (argument_value_separator); + foreach (string r in refs) { + if (r.Length == 0) + continue; + + string val = r; + int index = val.IndexOf ('='); + if (index > -1) { + string alias = r.Substring (0, index); + string assembly = r.Substring (index + 1); + AddAssemblyReference (alias, assembly, settings); + if (refs.Length != 1) { + report.Error (2034, "Cannot specify multiple aliases using single /reference option"); + return ParseResult.Error; + } + } else { + settings.AssemblyReferences.Add (val); + } + } + return ParseResult.Success; + } + case "/addmodule": { + if (value.Length == 0) { + Error_RequiresFileName (option); + return ParseResult.Error; + } + + string[] refs = value.Split (argument_value_separator); + foreach (string r in refs) { + settings.Modules.Add (r); + } + return ParseResult.Success; + } + case "/win32res": { + if (value.Length == 0) { + Error_RequiresFileName (option); + return ParseResult.Error; + } + + if (settings.Win32IconFile != null) + report.Error (1565, "Cannot specify the `win32res' and the `win32ico' compiler option at the same time"); + + settings.Win32ResourceFile = value; + return ParseResult.Success; + } + case "/win32icon": { + if (value.Length == 0) { + Error_RequiresFileName (option); + return ParseResult.Error; + } + + if (settings.Win32ResourceFile != null) + report.Error (1565, "Cannot specify the `win32res' and the `win32ico' compiler option at the same time"); + + settings.Win32IconFile = value; + return ParseResult.Success; + } + case "/doc": { + if (value.Length == 0) { + Error_RequiresFileName (option); + return ParseResult.Error; + } + + settings.DocumentationFile = value; + return ParseResult.Success; + } + case "/lib": { + string[] libdirs; + + if (value.Length == 0) { + return ParseResult.Error; + } + + libdirs = value.Split (argument_value_separator); + foreach (string dir in libdirs) + settings.ReferencesLookupPaths.Add (dir); + return ParseResult.Success; + } + + case "/debug-": + settings.GenerateDebugInfo = false; + return ParseResult.Success; + + case "/debug": + if (value.Equals ("full", StringComparison.OrdinalIgnoreCase) || value.Equals ("pdbonly", StringComparison.OrdinalIgnoreCase) || idx < 0) { + settings.GenerateDebugInfo = true; + return ParseResult.Success; + } + + if (value.Length > 0) { + report.Error (1902, "Invalid debug option `{0}'. Valid options are `full' or `pdbonly'", value); + } else { + Error_RequiresArgument (option); + } + + return ParseResult.Error; + + case "/debug+": + settings.GenerateDebugInfo = true; + return ParseResult.Success; + + case "/checked": + case "/checked+": + settings.Checked = true; + return ParseResult.Success; + + case "/checked-": + settings.Checked = false; + return ParseResult.Success; + + case "/clscheck": + case "/clscheck+": + settings.VerifyClsCompliance = true; + return ParseResult.Success; + + case "/clscheck-": + settings.VerifyClsCompliance = false; + return ParseResult.Success; + + case "/unsafe": + case "/unsafe+": + settings.Unsafe = true; + return ParseResult.Success; + + case "/unsafe-": + settings.Unsafe = false; + return ParseResult.Success; + + case "/warnaserror": + case "/warnaserror+": + if (value.Length == 0) { + settings.WarningsAreErrors = true; + parser_settings.WarningsAreErrors = true; + } else { + if (!ProcessWarningsList (value, settings.AddWarningAsError)) + return ParseResult.Error; + } + return ParseResult.Success; + + case "/warnaserror-": + if (value.Length == 0) { + settings.WarningsAreErrors = false; + } else { + if (!ProcessWarningsList (value, settings.AddWarningOnly)) + return ParseResult.Error; + } + return ParseResult.Success; + + case "/warn": + case "/w": + if (value.Length == 0) { + Error_RequiresArgument (option); + return ParseResult.Error; + } + + SetWarningLevel (value, settings); + return ParseResult.Success; + + case "/nowarn": + if (value.Length == 0) { + Error_RequiresArgument (option); + return ParseResult.Error; + } + + if (!ProcessWarningsList (value, settings.SetIgnoreWarning)) + return ParseResult.Error; + + return ParseResult.Success; + + case "/noconfig": + settings.LoadDefaultReferences = false; + return ParseResult.Success; + + case "/platform": + if (value.Length == 0) { + Error_RequiresArgument (option); + return ParseResult.Error; + } + + switch (value.ToLowerInvariant ()) { + case "arm": + settings.Platform = Platform.Arm; + break; + case "anycpu": + settings.Platform = Platform.AnyCPU; + break; + case "x86": + settings.Platform = Platform.X86; + break; + case "x64": + settings.Platform = Platform.X64; + break; + case "itanium": + settings.Platform = Platform.IA64; + break; + case "anycpu32bitpreferred": + settings.Platform = Platform.AnyCPU32Preferred; + break; + default: + report.Error (1672, "Invalid -platform option `{0}'. Valid options are `anycpu', `anycpu32bitpreferred', `arm', `x86', `x64' or `itanium'", + value); + return ParseResult.Error; + } + + return ParseResult.Success; + + case "/sdk": + if (value.Length == 0) { + Error_RequiresArgument (option); + return ParseResult.Error; + } + + settings.SdkVersion = value; + return ParseResult.Success; + + // We just ignore this. + case "/errorreport": + case "/filealign": + if (value.Length == 0) { + Error_RequiresArgument (option); + return ParseResult.Error; + } + + return ParseResult.Success; + + case "/helpinternal": + OtherFlags (); + return ParseResult.Stop; + + case "/help": + case "/?": + Usage (); + return ParseResult.Stop; + + case "/main": + case "/m": + if (value.Length == 0) { + Error_RequiresArgument (option); + return ParseResult.Error; + } + settings.MainClass = value; + return ParseResult.Success; + + case "/nostdlib": + case "/nostdlib+": + settings.StdLib = false; + return ParseResult.Success; + + case "/nostdlib-": + settings.StdLib = true; + return ParseResult.Success; + + case "/fullpaths": + settings.ShowFullPaths = true; + return ParseResult.Success; + + case "/keyfile": + if (value.Length == 0) { + Error_RequiresFileName (option); + return ParseResult.Error; + } + + settings.StrongNameKeyFile = value; + return ParseResult.Success; + + case "/keycontainer": + if (value.Length == 0) { + Error_RequiresArgument (option); + return ParseResult.Error; + } + + settings.StrongNameKeyContainer = value; + return ParseResult.Success; + + case "/delaysign+": + case "/delaysign": + settings.StrongNameDelaySign = true; + return ParseResult.Success; + + case "/delaysign-": + settings.StrongNameDelaySign = false; + return ParseResult.Success; + + case "/langversion": + if (value.Length == 0) { + Error_RequiresArgument (option); + return ParseResult.Error; + } + + switch (value.ToLowerInvariant ()) { + case "iso-1": + case "1": + settings.Version = LanguageVersion.ISO_1; + return ParseResult.Success; + case "default": + settings.Version = LanguageVersion.Default; + return ParseResult.Success; + case "2": + case "iso-2": + settings.Version = LanguageVersion.ISO_2; + return ParseResult.Success; + case "3": + settings.Version = LanguageVersion.V_3; + return ParseResult.Success; + case "4": + settings.Version = LanguageVersion.V_4; + return ParseResult.Success; + case "5": + settings.Version = LanguageVersion.V_5; + return ParseResult.Success; + case "6": + settings.Version = LanguageVersion.V_6; + return ParseResult.Success; + case "experimental": + settings.Version = LanguageVersion.Experimental; + return ParseResult.Success; + case "future": + report.Warning (8000, 1, "Language version `future' is no longer supported"); + goto case "6"; + } + + report.Error (1617, "Invalid -langversion option `{0}'. It must be `ISO-1', `ISO-2', Default or value in range 1 to 6", value); + return ParseResult.Error; + + case "/codepage": + if (value.Length == 0) { + Error_RequiresArgument (option); + return ParseResult.Error; + } + + switch (value) { + case "utf8": + settings.Encoding = Encoding.UTF8; + break; + case "reset": + settings.Encoding = Encoding.Default; + break; + default: + try { + settings.Encoding = Encoding.GetEncoding (int.Parse (value)); + } catch { + report.Error (2016, "Code page `{0}' is invalid or not installed", value); + } + return ParseResult.Error; + } + return ParseResult.Success; + + case "runtimemetadataversion": + if (value.Length == 0) { + Error_RequiresArgument (option); + return ParseResult.Error; + } + + settings.RuntimeMetadataVersion = value; + return ParseResult.Success; + + default: + return ParseResult.UnknownOption; + } + } + + // + // Currently handles the Unix-like command line options, but will be + // deprecated in favor of the CSCParseOption, which will also handle the + // options that start with a dash in the future. + // + ParseResult ParseOptionUnix (string arg, ref string[] args, ref int i, CompilerSettings settings) + { + switch (arg){ + case "-v": + settings.VerboseParserFlag++; + return ParseResult.Success; + + case "--version": + Version (); + return ParseResult.Stop; + + case "--parse": + settings.ParseOnly = true; + return ParseResult.Success; + + case "--main": case "-m": + report.Warning (-29, 1, "Compatibility: Use -main:CLASS instead of --main CLASS or -m CLASS"); + if ((i + 1) >= args.Length){ + Error_RequiresArgument (arg); + return ParseResult.Error; + } + settings.MainClass = args[++i]; + return ParseResult.Success; + + case "--unsafe": + report.Warning (-29, 1, "Compatibility: Use -unsafe instead of --unsafe"); + settings.Unsafe = true; + return ParseResult.Success; + + case "/?": case "/h": case "/help": + case "--help": + Usage (); + return ParseResult.Stop; + + case "--define": + report.Warning (-29, 1, "Compatibility: Use -d:SYMBOL instead of --define SYMBOL"); + if ((i + 1) >= args.Length){ + Error_RequiresArgument (arg); + return ParseResult.Error; + } + + settings.AddConditionalSymbol (args [++i]); + return ParseResult.Success; + + case "--tokenize": + settings.TokenizeOnly = true; + return ParseResult.Success; + + case "-o": + case "--output": + report.Warning (-29, 1, "Compatibility: Use -out:FILE instead of --output FILE or -o FILE"); + if ((i + 1) >= args.Length){ + Error_RequiresArgument (arg); + return ParseResult.Error; + } + settings.OutputFile = args[++i]; + return ParseResult.Success; + + case "--checked": + report.Warning (-29, 1, "Compatibility: Use -checked instead of --checked"); + settings.Checked = true; + return ParseResult.Success; + + case "--stacktrace": + settings.Stacktrace = true; + return ParseResult.Success; + + case "--linkresource": + case "--linkres": + report.Warning (-29, 1, "Compatibility: Use -linkres:VALUE instead of --linkres VALUE"); + if ((i + 1) >= args.Length){ + Error_RequiresArgument (arg); + return ParseResult.Error; + } + + AddResource (new AssemblyResource (args[++i], args[i]), settings); + return ParseResult.Success; + + case "--resource": + case "--res": + report.Warning (-29, 1, "Compatibility: Use -res:VALUE instead of --res VALUE"); + if ((i + 1) >= args.Length){ + Error_RequiresArgument (arg); + return ParseResult.Error; + } + + AddResource (new AssemblyResource (args[++i], args[i], true), settings); + return ParseResult.Success; + + case "--target": + report.Warning (-29, 1, "Compatibility: Use -target:KIND instead of --target KIND"); + if ((i + 1) >= args.Length){ + Error_RequiresArgument (arg); + return ParseResult.Error; + } + + string type = args [++i]; + switch (type){ + case "library": + settings.Target = Target.Library; + settings.TargetExt = ".dll"; + break; + + case "exe": + settings.Target = Target.Exe; + break; + + case "winexe": + settings.Target = Target.WinExe; + break; + + case "module": + settings.Target = Target.Module; + settings.TargetExt = ".dll"; + break; + default: + report.Error (2019, "Invalid target type for -target. Valid options are `exe', `winexe', `library' or `module'"); + break; + } + return ParseResult.Success; + + case "-r": + report.Warning (-29, 1, "Compatibility: Use -r:LIBRARY instead of -r library"); + if ((i + 1) >= args.Length){ + Error_RequiresArgument (arg); + return ParseResult.Error; + } + + string val = args [++i]; + int idx = val.IndexOf ('='); + if (idx > -1) { + string alias = val.Substring (0, idx); + string assembly = val.Substring (idx + 1); + AddAssemblyReference (alias, assembly, settings); + return ParseResult.Success; + } + + settings.AssemblyReferences.Add (val); + return ParseResult.Success; + + case "-L": + report.Warning (-29, 1, "Compatibility: Use -lib:ARG instead of --L arg"); + if ((i + 1) >= args.Length){ + Error_RequiresArgument (arg); + return ParseResult.Error; + } + settings.ReferencesLookupPaths.Add (args [++i]); + return ParseResult.Success; + + case "--lint": + settings.EnhancedWarnings = true; + return ParseResult.Success; + + case "--nostdlib": + report.Warning (-29, 1, "Compatibility: Use -nostdlib instead of --nostdlib"); + settings.StdLib = false; + return ParseResult.Success; + + case "--nowarn": + report.Warning (-29, 1, "Compatibility: Use -nowarn instead of --nowarn"); + if ((i + 1) >= args.Length){ + Error_RequiresArgument (arg); + return ParseResult.Error; + } + int warn = 0; + + try { + warn = int.Parse (args [++i]); + } catch { + Usage (); + Environment.Exit (1); + } + settings.SetIgnoreWarning (warn); + return ParseResult.Success; + + case "--wlevel": + report.Warning (-29, 1, "Compatibility: Use -warn:LEVEL instead of --wlevel LEVEL"); + if ((i + 1) >= args.Length){ + Error_RequiresArgument (arg); + return ParseResult.Error; + } + + SetWarningLevel (args [++i], settings); + return ParseResult.Success; + + case "--mcs-debug": + if ((i + 1) >= args.Length){ + Error_RequiresArgument (arg); + return ParseResult.Error; + } + + try { + settings.DebugFlags = int.Parse (args [++i]); + } catch { + Error_RequiresArgument (arg); + return ParseResult.Error; + } + + return ParseResult.Success; + + case "--about": + About (); + return ParseResult.Stop; + + case "--recurse": + report.Warning (-29, 1, "Compatibility: Use -recurse:PATTERN option instead --recurse PATTERN"); + if ((i + 1) >= args.Length){ + Error_RequiresArgument (arg); + return ParseResult.Error; + } + ProcessSourceFiles (args [++i], true, settings.SourceFiles); + return ParseResult.Success; + + case "--timestamp": + settings.Timestamps = true; + return ParseResult.Success; + + case "--debug": case "-g": + report.Warning (-29, 1, "Compatibility: Use -debug option instead of -g or --debug"); + settings.GenerateDebugInfo = true; + return ParseResult.Success; + + case "--noconfig": + report.Warning (-29, 1, "Compatibility: Use -noconfig option instead of --noconfig"); + settings.LoadDefaultReferences = false; + return ParseResult.Success; + + case "--metadata-only": + settings.WriteMetadataOnly = true; + return ParseResult.Success; + + case "--break-on-ice": + settings.BreakOnInternalError = true; + return ParseResult.Success; + + default: + if (arg.StartsWith ("--fatal", StringComparison.Ordinal)){ + int fatal = 1; + if (arg.StartsWith ("--fatal=", StringComparison.Ordinal)) + int.TryParse (arg.Substring (8), out fatal); + + settings.FatalCounter = fatal; + return ParseResult.Success; + } + if (arg.StartsWith ("--runtime:", StringComparison.Ordinal)) { + string version = arg.Substring (10); + + switch (version) { + case "v1": + case "V1": + settings.StdLibRuntimeVersion = RuntimeVersion.v1; + break; + case "v2": + case "V2": + settings.StdLibRuntimeVersion = RuntimeVersion.v2; + break; + case "v4": + case "V4": + settings.StdLibRuntimeVersion = RuntimeVersion.v4; + break; + } + return ParseResult.Success; + } + + return ParseResult.UnknownOption; + } + } + + void SetWarningLevel (string s, CompilerSettings settings) + { + int level = -1; + + try { + level = int.Parse (s); + } catch { + } + if (level < 0 || level > 4) { + report.Error (1900, "Warning level must be in the range 0-4"); + return; + } + settings.WarningLevel = level; + } + + // + // Given a path specification, splits the path from the file/pattern + // + static void SplitPathAndPattern (string spec, out string path, out string pattern) + { + int p = spec.LastIndexOf ('/'); + if (p != -1) { + // + // Windows does not like /file.cs, switch that to: + // "\", "file.cs" + // + if (p == 0) { + path = "\\"; + pattern = spec.Substring (1); + } else { + path = spec.Substring (0, p); + pattern = spec.Substring (p + 1); + } + return; + } + + p = spec.LastIndexOf ('\\'); + if (p != -1) { + path = spec.Substring (0, p); + pattern = spec.Substring (p + 1); + return; + } + + path = "."; + pattern = spec; + } + + void Usage () + { + output.WriteLine ( + "Mono C# compiler, Copyright 2001-2011 Novell, Inc., Copyright 2011-2012 Xamarin, Inc\n" + + "mcs [options] source-files\n" + + " --about About the Mono C# compiler\n" + + " -addmodule:M1[,Mn] Adds the module to the generated assembly\n" + + " -checked[+|-] Sets default aritmetic overflow context\n" + + " -clscheck[+|-] Disables CLS Compliance verifications\n" + + " -codepage:ID Sets code page to the one in ID (number, utf8, reset)\n" + + " -define:S1[;S2] Defines one or more conditional symbols (short: -d)\n" + + " -debug[+|-], -g Generate debugging information\n" + + " -delaysign[+|-] Only insert the public key into the assembly (no signing)\n" + + " -doc:FILE Process documentation comments to XML file\n" + + " -fullpaths Any issued error or warning uses absolute file path\n" + + " -help Lists all compiler options (short: -?)\n" + + " -keycontainer:NAME The key pair container used to sign the output assembly\n" + + " -keyfile:FILE The key file used to strongname the ouput assembly\n" + + " -langversion:TEXT Specifies language version: ISO-1, ISO-2, 3, 4, 5, Default or Future\n" + + " -lib:PATH1[,PATHn] Specifies the location of referenced assemblies\n" + + " -main:CLASS Specifies the class with the Main method (short: -m)\n" + + " -noconfig Disables implicitly referenced assemblies\n" + + " -nostdlib[+|-] Does not reference mscorlib.dll library\n" + + " -nowarn:W1[,Wn] Suppress one or more compiler warnings\n" + + " -optimize[+|-] Enables advanced compiler optimizations (short: -o)\n" + + " -out:FILE Specifies output assembly name\n" + + " -pkg:P1[,Pn] References packages P1..Pn\n" + + " -platform:ARCH Specifies the target platform of the output assembly\n" + + " ARCH can be one of: anycpu, anycpu32bitpreferred, arm,\n" + + " x86, x64 or itanium. The default is anycpu.\n" + + " -recurse:SPEC Recursively compiles files according to SPEC pattern\n" + + " -reference:A1[,An] Imports metadata from the specified assembly (short: -r)\n" + + " -reference:ALIAS=A Imports metadata using specified extern alias (short: -r)\n" + + " -sdk:VERSION Specifies SDK version of referenced assemblies\n" + + " VERSION can be one of: 2, 4, 4.5 (default) or a custom value\n" + + " -target:KIND Specifies the format of the output assembly (short: -t)\n" + + " KIND can be one of: exe, winexe, library, module\n" + + " -unsafe[+|-] Allows to compile code which uses unsafe keyword\n" + + " -warnaserror[+|-] Treats all warnings as errors\n" + + " -warnaserror[+|-]:W1[,Wn] Treats one or more compiler warnings as errors\n" + + " -warn:0-4 Sets warning level, the default is 4 (short -w:)\n" + + " -helpinternal Shows internal and advanced compiler options\n" + + "\n" + + "Resources:\n" + + " -linkresource:FILE[,ID] Links FILE as a resource (short: -linkres)\n" + + " -resource:FILE[,ID] Embed FILE as a resource (short: -res)\n" + + " -win32res:FILE Specifies Win32 resource file (.res)\n" + + " -win32icon:FILE Use this icon for the output\n" + + " @file Read response file for more options\n\n" + + "Options can be of the form -option or /option"); + } + + void Version () + { + string version = System.Reflection.MethodBase.GetCurrentMethod ().DeclaringType.Assembly.GetName ().Version.ToString (); + output.WriteLine ("Mono C# compiler version {0}", version); + } + } + + public class RootContext + { + // + // Contains the parsed tree + // + static ModuleContainer root; + + static public ModuleContainer ToplevelTypes { + get { return root; } + set { root = value; } + } + } +} diff --git a/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/statement.cs b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/statement.cs new file mode 100644 index 000000000..18249431b --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/statement.cs @@ -0,0 +1,8074 @@ +// +// statement.cs: Statement representation for the IL tree. +// +// Authors: +// Miguel de Icaza (miguel@ximian.com) +// Martin Baulig (martin@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// Copyright 2001, 2002, 2003 Ximian, Inc. +// Copyright 2003, 2004 Novell, Inc. +// Copyright 2011 Xamarin Inc. +// + +using System; +using System.Collections.Generic; + +#if STATIC +using IKVM.Reflection.Emit; +#else +using System.Reflection.Emit; +#endif + +namespace Mono.CSharp { + + public abstract class Statement { + public Location loc; + protected bool reachable; + + public bool IsUnreachable { + get { + return !reachable; + } + } + + /// + /// Resolves the statement, true means that all sub-statements + /// did resolve ok. + /// + public virtual bool Resolve (BlockContext bc) + { + return true; + } + + /// + /// Return value indicates whether all code paths emitted return. + /// + protected abstract void DoEmit (EmitContext ec); + + public virtual void Emit (EmitContext ec) + { + ec.Mark (loc); + DoEmit (ec); + + if (ec.StatementEpilogue != null) { + ec.EmitEpilogue (); + } + } + + // + // This routine must be overrided in derived classes and make copies + // of all the data that might be modified if resolved + // + protected abstract void CloneTo (CloneContext clonectx, Statement target); + + public Statement Clone (CloneContext clonectx) + { + Statement s = (Statement) this.MemberwiseClone (); + CloneTo (clonectx, s); + return s; + } + + public virtual Expression CreateExpressionTree (ResolveContext ec) + { + ec.Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree"); + return null; + } + + public virtual object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + + // + // Return value indicates whether statement has unreachable end + // + protected abstract bool DoFlowAnalysis (FlowAnalysisContext fc); + + public bool FlowAnalysis (FlowAnalysisContext fc) + { + if (reachable) { + fc.UnreachableReported = false; + var res = DoFlowAnalysis (fc); + return res; + } + + // + // Special handling cases + // + if (this is Block) { + return DoFlowAnalysis (fc); + } + + if (this is EmptyStatement || loc.IsNull) + return true; + + if (fc.UnreachableReported) + return true; + + fc.Report.Warning (162, 2, loc, "Unreachable code detected"); + fc.UnreachableReported = true; + return true; + } + + public virtual Reachability MarkReachable (Reachability rc) + { + if (!rc.IsUnreachable) + reachable = true; + + return rc; + } + + protected void CheckExitBoundaries (BlockContext bc, Block scope) + { + if (bc.CurrentBlock.ParametersBlock.Original != scope.ParametersBlock.Original) { + bc.Report.Error (1632, loc, "Control cannot leave the body of an anonymous method"); + return; + } + + for (var b = bc.CurrentBlock; b != null && b != scope; b = b.Parent) { + if (b.IsFinallyBlock) { + Error_FinallyClauseExit (bc); + break; + } + } + } + + protected void Error_FinallyClauseExit (BlockContext bc) + { + bc.Report.Error (157, loc, "Control cannot leave the body of a finally clause"); + } + } + + public sealed class EmptyStatement : Statement + { + public EmptyStatement (Location loc) + { + this.loc = loc; + } + + public override bool Resolve (BlockContext ec) + { + return true; + } + + public override void Emit (EmitContext ec) + { + } + + protected override void DoEmit (EmitContext ec) + { + throw new NotSupportedException (); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + return false; + } + + protected override void CloneTo (CloneContext clonectx, Statement target) + { + // nothing needed. + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class If : Statement { + Expression expr; + public Statement TrueStatement; + public Statement FalseStatement; + + bool true_returns, false_returns; + + public If (Expression bool_expr, Statement true_statement, Location l) + : this (bool_expr, true_statement, null, l) + { + } + + public If (Expression bool_expr, + Statement true_statement, + Statement false_statement, + Location l) + { + this.expr = bool_expr; + TrueStatement = true_statement; + FalseStatement = false_statement; + loc = l; + } + + public Expression Expr { + get { + return this.expr; + } + } + + public override bool Resolve (BlockContext ec) + { + expr = expr.Resolve (ec); + + var ok = TrueStatement.Resolve (ec); + + if (FalseStatement != null) { + ok &= FalseStatement.Resolve (ec); + } + + return ok; + } + + protected override void DoEmit (EmitContext ec) + { + Label false_target = ec.DefineLabel (); + Label end; + + // + // If we're a boolean constant, Resolve() already + // eliminated dead code for us. + // + Constant c = expr as Constant; + if (c != null){ + c.EmitSideEffect (ec); + + if (!c.IsDefaultValue) + TrueStatement.Emit (ec); + else if (FalseStatement != null) + FalseStatement.Emit (ec); + + return; + } + + expr.EmitBranchable (ec, false_target, false); + + TrueStatement.Emit (ec); + + if (FalseStatement != null){ + bool branch_emitted = false; + + end = ec.DefineLabel (); + if (!true_returns){ + ec.Emit (OpCodes.Br, end); + branch_emitted = true; + } + + ec.MarkLabel (false_target); + FalseStatement.Emit (ec); + + if (branch_emitted) + ec.MarkLabel (end); + } else { + ec.MarkLabel (false_target); + } + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + expr.FlowAnalysisConditional (fc); + + var da_false = new DefiniteAssignmentBitSet (fc.DefiniteAssignmentOnFalse); + + fc.DefiniteAssignment = fc.DefiniteAssignmentOnTrue; + + var res = TrueStatement.FlowAnalysis (fc); + + if (FalseStatement == null) { + var c = expr as Constant; + if (c != null && !c.IsDefaultValue) + return true_returns; + + if (true_returns) + fc.DefiniteAssignment = da_false; + else + fc.DefiniteAssignment &= da_false; + + return false; + } + + if (true_returns) { + fc.DefiniteAssignment = da_false; + return FalseStatement.FlowAnalysis (fc); + } + + var da_true = fc.DefiniteAssignment; + + fc.DefiniteAssignment = da_false; + res &= FalseStatement.FlowAnalysis (fc); + + if (!TrueStatement.IsUnreachable) { + if (false_returns || FalseStatement.IsUnreachable) + fc.DefiniteAssignment = da_true; + else + fc.DefiniteAssignment &= da_true; + } + + return res; + } + + public override Reachability MarkReachable (Reachability rc) + { + if (rc.IsUnreachable) + return rc; + + base.MarkReachable (rc); + + var c = expr as Constant; + if (c != null) { + bool take = !c.IsDefaultValue; + if (take) { + rc = TrueStatement.MarkReachable (rc); + } else { + if (FalseStatement != null) + rc = FalseStatement.MarkReachable (rc); + } + + return rc; + } + + var true_rc = TrueStatement.MarkReachable (rc); + true_returns = true_rc.IsUnreachable; + + if (FalseStatement == null) + return rc; + + var false_rc = FalseStatement.MarkReachable (rc); + false_returns = false_rc.IsUnreachable; + + return true_rc & false_rc; + } + + protected override void CloneTo (CloneContext clonectx, Statement t) + { + If target = (If) t; + + target.expr = expr.Clone (clonectx); + target.TrueStatement = TrueStatement.Clone (clonectx); + if (FalseStatement != null) + target.FalseStatement = FalseStatement.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class Do : LoopStatement + { + public Expression expr; + bool iterator_reachable, end_reachable; + + public Do (Statement statement, BooleanExpression bool_expr, Location doLocation, Location whileLocation) + : base (statement) + { + expr = bool_expr; + loc = doLocation; + WhileLocation = whileLocation; + } + + public Location WhileLocation { + get; private set; + } + + public override bool Resolve (BlockContext bc) + { + var ok = base.Resolve (bc); + + expr = expr.Resolve (bc); + + return ok; + } + + protected override void DoEmit (EmitContext ec) + { + Label loop = ec.DefineLabel (); + Label old_begin = ec.LoopBegin; + Label old_end = ec.LoopEnd; + + ec.LoopBegin = ec.DefineLabel (); + ec.LoopEnd = ec.DefineLabel (); + + ec.MarkLabel (loop); + Statement.Emit (ec); + ec.MarkLabel (ec.LoopBegin); + + // Mark start of while condition + ec.Mark (WhileLocation); + + // + // Dead code elimination + // + if (expr is Constant) { + bool res = !((Constant) expr).IsDefaultValue; + + expr.EmitSideEffect (ec); + if (res) + ec.Emit (OpCodes.Br, loop); + } else { + expr.EmitBranchable (ec, loop, true); + } + + ec.MarkLabel (ec.LoopEnd); + + ec.LoopBegin = old_begin; + ec.LoopEnd = old_end; + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + var res = Statement.FlowAnalysis (fc); + + expr.FlowAnalysisConditional (fc); + + fc.DefiniteAssignment = fc.DefiniteAssignmentOnFalse; + + if (res && !iterator_reachable) + return !end_reachable; + + if (!end_reachable) { + var c = expr as Constant; + if (c != null && !c.IsDefaultValue) + return true; + } + + return false; + } + + public override Reachability MarkReachable (Reachability rc) + { + base.MarkReachable (rc); + + var body_rc = Statement.MarkReachable (rc); + + if (body_rc.IsUnreachable && !iterator_reachable) { + expr = new UnreachableExpression (expr); + return end_reachable ? rc : Reachability.CreateUnreachable (); + } + + if (!end_reachable) { + var c = expr as Constant; + if (c != null && !c.IsDefaultValue) + return Reachability.CreateUnreachable (); + } + + return rc; + } + + protected override void CloneTo (CloneContext clonectx, Statement t) + { + Do target = (Do) t; + + target.Statement = Statement.Clone (clonectx); + target.expr = expr.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + + public override void SetEndReachable () + { + end_reachable = true; + } + + public override void SetIteratorReachable () + { + iterator_reachable = true; + } + } + + public class While : LoopStatement + { + public Expression expr; + bool empty, infinite, end_reachable; + List end_reachable_das; + + public While (BooleanExpression bool_expr, Statement statement, Location l) + : base (statement) + { + this.expr = bool_expr; + loc = l; + } + + public override bool Resolve (BlockContext bc) + { + bool ok = true; + + expr = expr.Resolve (bc); + if (expr == null) + ok = false; + + var c = expr as Constant; + if (c != null) { + empty = c.IsDefaultValue; + infinite = !empty; + } + + ok &= base.Resolve (bc); + return ok; + } + + protected override void DoEmit (EmitContext ec) + { + if (empty) { + expr.EmitSideEffect (ec); + return; + } + + Label old_begin = ec.LoopBegin; + Label old_end = ec.LoopEnd; + + ec.LoopBegin = ec.DefineLabel (); + ec.LoopEnd = ec.DefineLabel (); + + // + // Inform whether we are infinite or not + // + if (expr is Constant) { + // expr is 'true', since the 'empty' case above handles the 'false' case + ec.MarkLabel (ec.LoopBegin); + + if (ec.EmitAccurateDebugInfo) + ec.Emit (OpCodes.Nop); + + expr.EmitSideEffect (ec); + Statement.Emit (ec); + ec.Emit (OpCodes.Br, ec.LoopBegin); + + // + // Inform that we are infinite (ie, `we return'), only + // if we do not `break' inside the code. + // + ec.MarkLabel (ec.LoopEnd); + } else { + Label while_loop = ec.DefineLabel (); + + ec.Emit (OpCodes.Br, ec.LoopBegin); + ec.MarkLabel (while_loop); + + Statement.Emit (ec); + + ec.MarkLabel (ec.LoopBegin); + + ec.Mark (loc); + expr.EmitBranchable (ec, while_loop, true); + + ec.MarkLabel (ec.LoopEnd); + } + + ec.LoopBegin = old_begin; + ec.LoopEnd = old_end; + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + expr.FlowAnalysisConditional (fc); + + fc.DefiniteAssignment = fc.DefiniteAssignmentOnTrue; + var da_false = new DefiniteAssignmentBitSet (fc.DefiniteAssignmentOnFalse); + + Statement.FlowAnalysis (fc); + + // + // Special case infinite while with breaks + // + if (end_reachable_das != null) { + da_false = DefiniteAssignmentBitSet.And (end_reachable_das); + end_reachable_das = null; + } + + fc.DefiniteAssignment = da_false; + + if (infinite && !end_reachable) + return true; + + return false; + } + + public override Reachability MarkReachable (Reachability rc) + { + if (rc.IsUnreachable) + return rc; + + base.MarkReachable (rc); + + // + // Special case unreachable while body + // + if (empty) { + Statement.MarkReachable (Reachability.CreateUnreachable ()); + return rc; + } + + Statement.MarkReachable (rc); + + // + // When infinite while end is unreachable via break anything what follows is unreachable too + // + if (infinite && !end_reachable) + return Reachability.CreateUnreachable (); + + return rc; + } + + protected override void CloneTo (CloneContext clonectx, Statement t) + { + While target = (While) t; + + target.expr = expr.Clone (clonectx); + target.Statement = Statement.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + + public override void AddEndDefiniteAssignment (FlowAnalysisContext fc) + { + if (!infinite) + return; + + if (end_reachable_das == null) + end_reachable_das = new List (); + + end_reachable_das.Add (fc.DefiniteAssignment); + } + + public override void SetEndReachable () + { + end_reachable = true; + } + } + + public class For : LoopStatement + { + bool infinite, empty, iterator_reachable, end_reachable; + List end_reachable_das; + + public For (Location l) + : base (null) + { + loc = l; + } + + public Statement Initializer { + get; set; + } + + public Expression Condition { + get; set; + } + + public Statement Iterator { + get; set; + } + + public override bool Resolve (BlockContext bc) + { + Initializer.Resolve (bc); + + if (Condition != null) { + Condition = Condition.Resolve (bc); + var condition_constant = Condition as Constant; + if (condition_constant != null) { + if (condition_constant.IsDefaultValue) { + empty = true; + } else { + infinite = true; + } + } + } else { + infinite = true; + } + + return base.Resolve (bc) && Iterator.Resolve (bc); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + Initializer.FlowAnalysis (fc); + + DefiniteAssignmentBitSet da_false; + if (Condition != null) { + Condition.FlowAnalysisConditional (fc); + fc.DefiniteAssignment = fc.DefiniteAssignmentOnTrue; + da_false = new DefiniteAssignmentBitSet (fc.DefiniteAssignmentOnFalse); + } else { + da_false = fc.BranchDefiniteAssignment (); + } + + Statement.FlowAnalysis (fc); + + Iterator.FlowAnalysis (fc); + + // + // Special case infinite for with breaks + // + if (end_reachable_das != null) { + da_false = DefiniteAssignmentBitSet.And (end_reachable_das); + end_reachable_das = null; + } + + fc.DefiniteAssignment = da_false; + + if (infinite && !end_reachable) + return true; + + return false; + } + + public override Reachability MarkReachable (Reachability rc) + { + base.MarkReachable (rc); + + Initializer.MarkReachable (rc); + + var body_rc = Statement.MarkReachable (rc); + if (!body_rc.IsUnreachable || iterator_reachable) { + Iterator.MarkReachable (rc); + } + + // + // When infinite for end is unreachable via break anything what follows is unreachable too + // + if (infinite && !end_reachable) { + return Reachability.CreateUnreachable (); + } + + return rc; + } + + protected override void DoEmit (EmitContext ec) + { + if (Initializer != null) + Initializer.Emit (ec); + + if (empty) { + Condition.EmitSideEffect (ec); + return; + } + + Label old_begin = ec.LoopBegin; + Label old_end = ec.LoopEnd; + Label loop = ec.DefineLabel (); + Label test = ec.DefineLabel (); + + ec.LoopBegin = ec.DefineLabel (); + ec.LoopEnd = ec.DefineLabel (); + + ec.Emit (OpCodes.Br, test); + ec.MarkLabel (loop); + Statement.Emit (ec); + + ec.MarkLabel (ec.LoopBegin); + Iterator.Emit (ec); + + ec.MarkLabel (test); + // + // If test is null, there is no test, and we are just + // an infinite loop + // + if (Condition != null) { + ec.Mark (Condition.Location); + + // + // The Resolve code already catches the case for + // Test == Constant (false) so we know that + // this is true + // + if (Condition is Constant) { + Condition.EmitSideEffect (ec); + ec.Emit (OpCodes.Br, loop); + } else { + Condition.EmitBranchable (ec, loop, true); + } + + } else + ec.Emit (OpCodes.Br, loop); + ec.MarkLabel (ec.LoopEnd); + + ec.LoopBegin = old_begin; + ec.LoopEnd = old_end; + } + + protected override void CloneTo (CloneContext clonectx, Statement t) + { + For target = (For) t; + + if (Initializer != null) + target.Initializer = Initializer.Clone (clonectx); + if (Condition != null) + target.Condition = Condition.Clone (clonectx); + if (Iterator != null) + target.Iterator = Iterator.Clone (clonectx); + target.Statement = Statement.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + + public override void AddEndDefiniteAssignment (FlowAnalysisContext fc) + { + if (!infinite) + return; + + if (end_reachable_das == null) + end_reachable_das = new List (); + + end_reachable_das.Add (fc.DefiniteAssignment); + } + + public override void SetEndReachable () + { + end_reachable = true; + } + + public override void SetIteratorReachable () + { + iterator_reachable = true; + } + } + + public abstract class LoopStatement : Statement + { + protected LoopStatement (Statement statement) + { + Statement = statement; + } + + public Statement Statement { get; set; } + + public override bool Resolve (BlockContext bc) + { + var prev_loop = bc.EnclosingLoop; + var prev_los = bc.EnclosingLoopOrSwitch; + bc.EnclosingLoopOrSwitch = bc.EnclosingLoop = this; + var ok = Statement.Resolve (bc); + bc.EnclosingLoopOrSwitch = prev_los; + bc.EnclosingLoop = prev_loop; + + return ok; + } + + // + // Needed by possibly infinite loops statements (for, while) and switch statment + // + public virtual void AddEndDefiniteAssignment (FlowAnalysisContext fc) + { + } + + public virtual void SetEndReachable () + { + } + + public virtual void SetIteratorReachable () + { + } + } + + public class StatementExpression : Statement + { + ExpressionStatement expr; + + public StatementExpression (ExpressionStatement expr) + { + this.expr = expr; + loc = expr.StartLocation; + } + + public StatementExpression (ExpressionStatement expr, Location loc) + { + this.expr = expr; + this.loc = loc; + } + + public ExpressionStatement Expr { + get { + return this.expr; + } + } + + protected override void CloneTo (CloneContext clonectx, Statement t) + { + StatementExpression target = (StatementExpression) t; + target.expr = (ExpressionStatement) expr.Clone (clonectx); + } + + protected override void DoEmit (EmitContext ec) + { + expr.EmitStatement (ec); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + expr.FlowAnalysis (fc); + return false; + } + + public override Reachability MarkReachable (Reachability rc) + { + base.MarkReachable (rc); + expr.MarkReachable (rc); + return rc; + } + + public override bool Resolve (BlockContext ec) + { + expr = expr.ResolveStatement (ec); + return expr != null; + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class StatementErrorExpression : Statement + { + Expression expr; + + public StatementErrorExpression (Expression expr) + { + this.expr = expr; + this.loc = expr.StartLocation; + } + + public Expression Expr { + get { + return expr; + } + } + + public override bool Resolve (BlockContext bc) + { + expr.Error_InvalidExpressionStatement (bc); + return true; + } + + protected override void DoEmit (EmitContext ec) + { + throw new NotSupportedException (); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + return false; + } + + protected override void CloneTo (CloneContext clonectx, Statement target) + { + var t = (StatementErrorExpression) target; + + t.expr = expr.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + // + // Simple version of statement list not requiring a block + // + public class StatementList : Statement + { + List statements; + + public StatementList (Statement first, Statement second) + { + statements = new List { first, second }; + } + + #region Properties + public IList Statements { + get { + return statements; + } + } + #endregion + + public void Add (Statement statement) + { + statements.Add (statement); + } + + public override bool Resolve (BlockContext ec) + { + foreach (var s in statements) + s.Resolve (ec); + + return true; + } + + protected override void DoEmit (EmitContext ec) + { + foreach (var s in statements) + s.Emit (ec); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + foreach (var s in statements) + s.FlowAnalysis (fc); + + return false; + } + + public override Reachability MarkReachable (Reachability rc) + { + base.MarkReachable (rc); + + Reachability res = rc; + foreach (var s in statements) + res = s.MarkReachable (rc); + + return res; + } + + protected override void CloneTo (CloneContext clonectx, Statement target) + { + StatementList t = (StatementList) target; + + t.statements = new List (statements.Count); + foreach (Statement s in statements) + t.statements.Add (s.Clone (clonectx)); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + // + // For statements which require special handling when inside try or catch block + // + public abstract class ExitStatement : Statement + { + protected bool unwind_protect; + + protected abstract bool DoResolve (BlockContext bc); + protected abstract bool IsLocalExit { get; } + + public override bool Resolve (BlockContext bc) + { + var res = DoResolve (bc); + + if (!IsLocalExit) { + // + // We are inside finally scope but is it the scope we are exiting + // + if (bc.HasSet (ResolveContext.Options.FinallyScope)) { + + for (var b = bc.CurrentBlock; b != null; b = b.Parent) { + if (b.IsFinallyBlock) { + Error_FinallyClauseExit (bc); + break; + } + + if (b is ParametersBlock) + break; + } + } + } + + unwind_protect = bc.HasAny (ResolveContext.Options.TryScope | ResolveContext.Options.CatchScope); + return res; + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + if (IsLocalExit) + return true; + + if (fc.TryFinally != null) { + fc.TryFinally.RegisterForControlExitCheck (new DefiniteAssignmentBitSet (fc.DefiniteAssignment)); + } else { + fc.ParametersBlock.CheckControlExit (fc); + } + + return true; + } + } + + /// + /// Implements the return statement + /// + public class Return : ExitStatement + { + Expression expr; + + public Return (Expression expr, Location l) + { + this.expr = expr; + loc = l; + } + + #region Properties + + public Expression Expr { + get { + return expr; + } + protected set { + expr = value; + } + } + + protected override bool IsLocalExit { + get { + return false; + } + } + + #endregion + + protected override bool DoResolve (BlockContext ec) + { + var block_return_type = ec.ReturnType; + + if (expr == null) { + if (block_return_type.Kind == MemberKind.Void) + return true; + + // + // Return must not be followed by an expression when + // the method return type is Task + // + if (ec.CurrentAnonymousMethod is AsyncInitializer) { + var storey = (AsyncTaskStorey) ec.CurrentAnonymousMethod.Storey; + if (storey.ReturnType == ec.Module.PredefinedTypes.Task.TypeSpec) { + // + // Extra trick not to emit ret/leave inside awaiter body + // + expr = EmptyExpression.Null; + return true; + } + + if (storey.ReturnType.IsGenericTask) + block_return_type = storey.ReturnType.TypeArguments[0]; + } + + if (ec.CurrentIterator != null) { + Error_ReturnFromIterator (ec); + } else if (block_return_type != InternalType.ErrorType) { + ec.Report.Error (126, loc, + "An object of a type convertible to `{0}' is required for the return statement", + block_return_type.GetSignatureForError ()); + } + + return false; + } + + expr = expr.Resolve (ec); + + AnonymousExpression am = ec.CurrentAnonymousMethod; + if (am == null) { + if (block_return_type.Kind == MemberKind.Void) { + ec.Report.Error (127, loc, + "`{0}': A return keyword must not be followed by any expression when method returns void", + ec.GetSignatureForError ()); + + return false; + } + } else { + if (am.IsIterator) { + Error_ReturnFromIterator (ec); + return false; + } + + var async_block = am as AsyncInitializer; + if (async_block != null) { + if (expr != null) { + var storey = (AsyncTaskStorey) am.Storey; + var async_type = storey.ReturnType; + + if (async_type == null && async_block.ReturnTypeInference != null) { + if (expr.Type.Kind == MemberKind.Void && !(this is ContextualReturn)) + ec.Report.Error (4029, loc, "Cannot return an expression of type `void'"); + else + async_block.ReturnTypeInference.AddCommonTypeBoundAsync (expr.Type); + return true; + } + + if (async_type.Kind == MemberKind.Void) { + ec.Report.Error (8030, loc, + "Anonymous function or lambda expression converted to a void returning delegate cannot return a value"); + return false; + } + + if (!async_type.IsGenericTask) { + if (this is ContextualReturn) + return true; + + if (async_block.DelegateType != null) { + ec.Report.Error (8031, loc, + "Async lambda expression or anonymous method converted to a `Task' cannot return a value. Consider returning `Task'"); + } else { + ec.Report.Error (1997, loc, + "`{0}': A return keyword must not be followed by an expression when async method returns `Task'. Consider using `Task' return type", + ec.GetSignatureForError ()); + } + return false; + } + + // + // The return type is actually Task type argument + // + if (expr.Type == async_type) { + ec.Report.Error (4016, loc, + "`{0}': The return expression type of async method must be `{1}' rather than `Task<{1}>'", + ec.GetSignatureForError (), async_type.TypeArguments[0].GetSignatureForError ()); + } else { + block_return_type = async_type.TypeArguments[0]; + } + } + } else { + if (block_return_type.Kind == MemberKind.Void) { + ec.Report.Error (8030, loc, + "Anonymous function or lambda expression converted to a void returning delegate cannot return a value"); + return false; + } + + var l = am as AnonymousMethodBody; + if (l != null && expr != null) { + if (l.ReturnTypeInference != null) { + l.ReturnTypeInference.AddCommonTypeBound (expr.Type); + return true; + } + + // + // Try to optimize simple lambda. Only when optimizations are enabled not to cause + // unexpected debugging experience + // + if (this is ContextualReturn && !ec.IsInProbingMode && ec.Module.Compiler.Settings.Optimize) { + l.DirectMethodGroupConversion = expr.CanReduceLambda (l); + } + } + } + } + + if (expr == null) + return false; + + if (expr.Type != block_return_type && expr.Type != InternalType.ErrorType) { + expr = Convert.ImplicitConversionRequired (ec, expr, block_return_type, loc); + + if (expr == null) { + if (am != null && block_return_type == ec.ReturnType) { + ec.Report.Error (1662, loc, + "Cannot convert `{0}' to delegate type `{1}' because some of the return types in the block are not implicitly convertible to the delegate return type", + am.ContainerType, am.GetSignatureForError ()); + } + return false; + } + } + + return true; + } + + protected override void DoEmit (EmitContext ec) + { + if (expr != null) { + + var async_body = ec.CurrentAnonymousMethod as AsyncInitializer; + if (async_body != null) { + var storey = (AsyncTaskStorey)async_body.Storey; + Label exit_label = async_body.BodyEnd; + + // + // It's null for await without async + // + if (storey.HoistedReturnValue != null) { + // + // Special case hoisted return value (happens in try/finally scenario) + // + if (ec.TryFinallyUnwind != null) { + if (storey.HoistedReturnValue is VariableReference) { + storey.HoistedReturnValue = ec.GetTemporaryField (storey.HoistedReturnValue.Type); + } + + exit_label = TryFinally.EmitRedirectedReturn (ec, async_body); + } + + var async_return = (IAssignMethod)storey.HoistedReturnValue; + async_return.EmitAssign (ec, expr, false, false); + ec.EmitEpilogue (); + } else { + expr.Emit (ec); + + if (ec.TryFinallyUnwind != null) + exit_label = TryFinally.EmitRedirectedReturn (ec, async_body); + } + + ec.Emit (OpCodes.Leave, exit_label); + return; + } + + expr.Emit (ec); + ec.EmitEpilogue (); + + if (unwind_protect || ec.EmitAccurateDebugInfo) + ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ()); + } + + if (unwind_protect) { + ec.Emit (OpCodes.Leave, ec.CreateReturnLabel ()); + } else if (ec.EmitAccurateDebugInfo) { + ec.Emit (OpCodes.Br, ec.CreateReturnLabel ()); + } else { + ec.Emit (OpCodes.Ret); + } + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + if (expr != null) + expr.FlowAnalysis (fc); + + base.DoFlowAnalysis (fc); + return true; + } + + void Error_ReturnFromIterator (ResolveContext rc) + { + rc.Report.Error (1622, loc, + "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration"); + } + + public override Reachability MarkReachable (Reachability rc) + { + base.MarkReachable (rc); + return Reachability.CreateUnreachable (); + } + + protected override void CloneTo (CloneContext clonectx, Statement t) + { + Return target = (Return) t; + // It's null for simple return; + if (expr != null) + target.expr = expr.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class Goto : ExitStatement + { + string target; + LabeledStatement label; + TryFinally try_finally; + + public Goto (string label, Location l) + { + loc = l; + target = label; + } + + public string Target { + get { return target; } + } + + protected override bool IsLocalExit { + get { + return true; + } + } + + protected override bool DoResolve (BlockContext bc) + { + label = bc.CurrentBlock.LookupLabel (target); + if (label == null) { + Error_UnknownLabel (bc, target, loc); + return false; + } + + try_finally = bc.CurrentTryBlock as TryFinally; + + CheckExitBoundaries (bc, label.Block); + + return true; + } + + public static void Error_UnknownLabel (BlockContext bc, string label, Location loc) + { + bc.Report.Error (159, loc, "The label `{0}:' could not be found within the scope of the goto statement", + label); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + if (fc.LabelStack == null) { + fc.LabelStack = new List (); + } else if (fc.LabelStack.Contains (label)) { + return true; + } + + fc.LabelStack.Add (label); + label.Block.ScanGotoJump (label, fc); + fc.LabelStack.Remove (label); + return true; + } + + public override Reachability MarkReachable (Reachability rc) + { + if (rc.IsUnreachable) + return rc; + + base.MarkReachable (rc); + + if (try_finally != null) { + if (try_finally.FinallyBlock.HasReachableClosingBrace) { + label.AddGotoReference (rc, false); + } else { + label.AddGotoReference (rc, true); + } + } else { + label.AddGotoReference (rc, false); + } + + return Reachability.CreateUnreachable (); + } + + protected override void CloneTo (CloneContext clonectx, Statement target) + { + // Nothing to clone + } + + protected override void DoEmit (EmitContext ec) + { + if (label == null) + throw new InternalErrorException ("goto emitted before target resolved"); + + Label l = label.LabelTarget (ec); + + if (ec.TryFinallyUnwind != null && IsLeavingFinally (label.Block)) { + var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod; + l = TryFinally.EmitRedirectedJump (ec, async_body, l, label.Block); + } + + ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l); + } + + bool IsLeavingFinally (Block labelBlock) + { + var b = try_finally.Statement as Block; + while (b != null) { + if (b == labelBlock) + return true; + + b = b.Parent; + } + + return false; + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class LabeledStatement : Statement { + string name; + bool defined; + bool referenced; + bool finalTarget; + Label label; + Block block; + + public LabeledStatement (string name, Block block, Location l) + { + this.name = name; + this.block = block; + this.loc = l; + } + + public Label LabelTarget (EmitContext ec) + { + if (defined) + return label; + + label = ec.DefineLabel (); + defined = true; + return label; + } + + public Block Block { + get { + return block; + } + } + + public string Name { + get { return name; } + } + + protected override void CloneTo (CloneContext clonectx, Statement target) + { + var t = (LabeledStatement) target; + + t.block = clonectx.RemapBlockCopy (block); + } + + public override bool Resolve (BlockContext bc) + { + return true; + } + + protected override void DoEmit (EmitContext ec) + { + LabelTarget (ec); + ec.MarkLabel (label); + + if (finalTarget) + ec.Emit (OpCodes.Br_S, label); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + if (!referenced) { + fc.Report.Warning (164, 2, loc, "This label has not been referenced"); + } + + return false; + } + + public override Reachability MarkReachable (Reachability rc) + { + base.MarkReachable (rc); + + if (referenced) + rc = new Reachability (); + + return rc; + } + + public void AddGotoReference (Reachability rc, bool finalTarget) + { + if (referenced) + return; + + referenced = true; + MarkReachable (rc); + + // + // Label is final target when goto jumps out of try block with + // finally clause. In that case we need leave with target but in C# + // terms the label is unreachable. Using finalTarget we emit + // explicit label not just marker + // + if (finalTarget) { + this.finalTarget = true; + return; + } + + block.ScanGotoJump (this); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + + /// + /// `goto default' statement + /// + public class GotoDefault : SwitchGoto + { + public GotoDefault (Location l) + : base (l) + { + } + + public override bool Resolve (BlockContext bc) + { + if (bc.Switch == null) { + Error_GotoCaseRequiresSwitchBlock (bc); + return false; + } + + bc.Switch.RegisterGotoCase (null, null); + base.Resolve (bc); + + return true; + } + + protected override void DoEmit (EmitContext ec) + { + ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.Switch.DefaultLabel.GetILLabel (ec)); + } + + public override Reachability MarkReachable (Reachability rc) + { + if (!rc.IsUnreachable) { + var label = switch_statement.DefaultLabel; + if (label.IsUnreachable) { + label.MarkReachable (rc); + switch_statement.Block.ScanGotoJump (label); + } + } + + return base.MarkReachable (rc); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + /// + /// `goto case' statement + /// + public class GotoCase : SwitchGoto + { + Expression expr; + + public GotoCase (Expression e, Location l) + : base (l) + { + expr = e; + } + + public Expression Expr { + get { + return expr; + } + } + + public SwitchLabel Label { get; set; } + + public override bool Resolve (BlockContext ec) + { + if (ec.Switch == null) { + Error_GotoCaseRequiresSwitchBlock (ec); + return false; + } + + Constant c = expr.ResolveLabelConstant (ec); + if (c == null) { + return false; + } + + Constant res; + if (ec.Switch.IsNullable && c is NullLiteral) { + res = c; + } else { + TypeSpec type = ec.Switch.SwitchType; + res = c.Reduce (ec, type); + if (res == null) { + c.Error_ValueCannotBeConverted (ec, type, true); + return false; + } + + if (!Convert.ImplicitStandardConversionExists (c, type)) + ec.Report.Warning (469, 2, loc, + "The `goto case' value is not implicitly convertible to type `{0}'", + type.GetSignatureForError ()); + + } + + ec.Switch.RegisterGotoCase (this, res); + base.Resolve (ec); + expr = res; + + return true; + } + + protected override void DoEmit (EmitContext ec) + { + ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, Label.GetILLabel (ec)); + } + + protected override void CloneTo (CloneContext clonectx, Statement t) + { + GotoCase target = (GotoCase) t; + + target.expr = expr.Clone (clonectx); + } + + public override Reachability MarkReachable (Reachability rc) + { + if (!rc.IsUnreachable) { + var label = switch_statement.FindLabel ((Constant) expr); + if (label.IsUnreachable) { + label.MarkReachable (rc); + switch_statement.Block.ScanGotoJump (label); + } + } + + return base.MarkReachable (rc); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public abstract class SwitchGoto : Statement + { + protected bool unwind_protect; + protected Switch switch_statement; + + protected SwitchGoto (Location loc) + { + this.loc = loc; + } + + protected override void CloneTo (CloneContext clonectx, Statement target) + { + // Nothing to clone + } + + public override bool Resolve (BlockContext bc) + { + CheckExitBoundaries (bc, bc.Switch.Block); + + unwind_protect = bc.HasAny (ResolveContext.Options.TryScope | ResolveContext.Options.CatchScope); + switch_statement = bc.Switch; + + return true; + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + return true; + } + + public override Reachability MarkReachable (Reachability rc) + { + base.MarkReachable (rc); + return Reachability.CreateUnreachable (); + } + + protected void Error_GotoCaseRequiresSwitchBlock (BlockContext bc) + { + bc.Report.Error (153, loc, "A goto case is only valid inside a switch statement"); + } + } + + public class Throw : Statement { + Expression expr; + + public Throw (Expression expr, Location l) + { + this.expr = expr; + loc = l; + } + + public Expression Expr { + get { + return this.expr; + } + } + + public override bool Resolve (BlockContext ec) + { + if (expr == null) { + if (!ec.HasSet (ResolveContext.Options.CatchScope)) { + ec.Report.Error (156, loc, "A throw statement with no arguments is not allowed outside of a catch clause"); + } else if (ec.HasSet (ResolveContext.Options.FinallyScope)) { + for (var b = ec.CurrentBlock; b != null && !b.IsCatchBlock; b = b.Parent) { + if (b.IsFinallyBlock) { + ec.Report.Error (724, loc, + "A throw statement with no arguments is not allowed inside of a finally clause nested inside of the innermost catch clause"); + break; + } + } + } + + return true; + } + + expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue); + + if (expr == null) + return false; + + var et = ec.BuiltinTypes.Exception; + if (Convert.ImplicitConversionExists (ec, expr, et)) + expr = Convert.ImplicitConversion (ec, expr, et, loc); + else + ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception"); + + return true; + } + + protected override void DoEmit (EmitContext ec) + { + if (expr == null) { + var atv = ec.AsyncThrowVariable; + if (atv != null) { + if (atv.HoistedVariant != null) { + atv.HoistedVariant.Emit (ec); + } else { + atv.Emit (ec); + } + + ec.Emit (OpCodes.Throw); + } else { + ec.Emit (OpCodes.Rethrow); + } + } else { + expr.Emit (ec); + + ec.Emit (OpCodes.Throw); + } + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + if (expr != null) + expr.FlowAnalysis (fc); + + return true; + } + + public override Reachability MarkReachable (Reachability rc) + { + base.MarkReachable (rc); + return Reachability.CreateUnreachable (); + } + + protected override void CloneTo (CloneContext clonectx, Statement t) + { + Throw target = (Throw) t; + + if (expr != null) + target.expr = expr.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class Break : LocalExitStatement + { + public Break (Location l) + : base (l) + { + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + + protected override void DoEmit (EmitContext ec) + { + var l = ec.LoopEnd; + + if (ec.TryFinallyUnwind != null) { + var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod; + l = TryFinally.EmitRedirectedJump (ec, async_body, l, enclosing_loop.Statement as Block); + } + + ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + enclosing_loop.AddEndDefiniteAssignment (fc); + return true; + } + + protected override bool DoResolve (BlockContext bc) + { + enclosing_loop = bc.EnclosingLoopOrSwitch; + return base.DoResolve (bc); + } + + public override Reachability MarkReachable (Reachability rc) + { + base.MarkReachable (rc); + + if (!rc.IsUnreachable) + enclosing_loop.SetEndReachable (); + + return Reachability.CreateUnreachable (); + } + } + + public class Continue : LocalExitStatement + { + public Continue (Location l) + : base (l) + { + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + + + protected override void DoEmit (EmitContext ec) + { + var l = ec.LoopBegin; + + if (ec.TryFinallyUnwind != null) { + var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod; + l = TryFinally.EmitRedirectedJump (ec, async_body, l, enclosing_loop.Statement as Block); + } + + ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l); + } + + protected override bool DoResolve (BlockContext bc) + { + enclosing_loop = bc.EnclosingLoop; + return base.DoResolve (bc); + } + + public override Reachability MarkReachable (Reachability rc) + { + base.MarkReachable (rc); + + if (!rc.IsUnreachable) + enclosing_loop.SetIteratorReachable (); + + return Reachability.CreateUnreachable (); + } + } + + public abstract class LocalExitStatement : ExitStatement + { + protected LoopStatement enclosing_loop; + + protected LocalExitStatement (Location loc) + { + this.loc = loc; + } + + protected override bool IsLocalExit { + get { + return true; + } + } + + protected override void CloneTo (CloneContext clonectx, Statement t) + { + // nothing needed. + } + + protected override bool DoResolve (BlockContext bc) + { + if (enclosing_loop == null) { + bc.Report.Error (139, loc, "No enclosing loop out of which to break or continue"); + return false; + } + + var block = enclosing_loop.Statement as Block; + + // Don't need to do extra checks for simple statements loops + if (block != null) { + CheckExitBoundaries (bc, block); + } + + return true; + } + } + + public interface ILocalVariable + { + void Emit (EmitContext ec); + void EmitAssign (EmitContext ec); + void EmitAddressOf (EmitContext ec); + } + + public interface INamedBlockVariable + { + Block Block { get; } + Expression CreateReferenceExpression (ResolveContext rc, Location loc); + bool IsDeclared { get; } + bool IsParameter { get; } + Location Location { get; } + } + + public class BlockVariableDeclarator + { + LocalVariable li; + Expression initializer; + + public BlockVariableDeclarator (LocalVariable li, Expression initializer) + { + if (li.Type != null) + throw new ArgumentException ("Expected null variable type"); + + this.li = li; + this.initializer = initializer; + } + + #region Properties + + public LocalVariable Variable { + get { + return li; + } + } + + public Expression Initializer { + get { + return initializer; + } + set { + initializer = value; + } + } + + #endregion + + public virtual BlockVariableDeclarator Clone (CloneContext cloneCtx) + { + var t = (BlockVariableDeclarator) MemberwiseClone (); + if (initializer != null) + t.initializer = initializer.Clone (cloneCtx); + + return t; + } + } + + public class BlockVariable : Statement + { + Expression initializer; + protected FullNamedExpression type_expr; + protected LocalVariable li; + protected List declarators; + TypeSpec type; + + public BlockVariable (FullNamedExpression type, LocalVariable li) + { + this.type_expr = type; + this.li = li; + this.loc = type_expr.Location; + } + + protected BlockVariable (LocalVariable li) + { + this.li = li; + } + + #region Properties + + public List Declarators { + get { + return declarators; + } + } + + public Expression Initializer { + get { + return initializer; + } + set { + initializer = value; + } + } + + public FullNamedExpression TypeExpression { + get { + return type_expr; + } + } + + public LocalVariable Variable { + get { + return li; + } + } + + #endregion + + public void AddDeclarator (BlockVariableDeclarator decl) + { + if (declarators == null) + declarators = new List (); + + declarators.Add (decl); + } + + static void CreateEvaluatorVariable (BlockContext bc, LocalVariable li) + { + if (bc.Report.Errors != 0) + return; + + var container = bc.CurrentMemberDefinition.Parent.PartialContainer; + + Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC, + new MemberName (li.Name, li.Location), null); + + container.AddField (f); + f.Define (); + + li.HoistedVariant = new HoistedEvaluatorVariable (f); + li.SetIsUsed (); + } + + public override bool Resolve (BlockContext bc) + { + return Resolve (bc, true); + } + + public bool Resolve (BlockContext bc, bool resolveDeclaratorInitializers) + { + if (type == null && !li.IsCompilerGenerated) { + var vexpr = type_expr as VarExpr; + + // + // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with + // same name exists or as a keyword when no type was found + // + if (vexpr != null && !vexpr.IsPossibleTypeOrNamespace (bc)) { + if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3) + bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, loc, "implicitly typed local variable"); + + if (li.IsFixed) { + bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable"); + return false; + } + + if (li.IsConstant) { + bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant"); + return false; + } + + if (Initializer == null) { + bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer"); + return false; + } + + if (declarators != null) { + bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators"); + declarators = null; + } + + Initializer = Initializer.Resolve (bc); + if (Initializer != null) { + ((VarExpr) type_expr).InferType (bc, Initializer); + type = type_expr.Type; + } else { + // Set error type to indicate the var was placed correctly but could + // not be infered + // + // var a = missing (); + // + type = InternalType.ErrorType; + } + } + + if (type == null) { + type = type_expr.ResolveAsType (bc); + if (type == null) + return false; + + if (li.IsConstant && !type.IsConstantCompatible) { + Const.Error_InvalidConstantType (type, loc, bc.Report); + } + } + + if (type.IsStatic) + FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report); + + li.Type = type; + } + + bool eval_global = bc.Module.Compiler.Settings.StatementMode && bc.CurrentBlock is ToplevelBlock; + if (eval_global) { + CreateEvaluatorVariable (bc, li); + } else if (type != InternalType.ErrorType) { + li.PrepareAssignmentAnalysis (bc); + } + + if (initializer != null) { + initializer = ResolveInitializer (bc, li, initializer); + // li.Variable.DefinitelyAssigned + } + + if (declarators != null) { + foreach (var d in declarators) { + d.Variable.Type = li.Type; + if (eval_global) { + CreateEvaluatorVariable (bc, d.Variable); + } else if (type != InternalType.ErrorType) { + d.Variable.PrepareAssignmentAnalysis (bc); + } + + if (d.Initializer != null && resolveDeclaratorInitializers) { + d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer); + // d.Variable.DefinitelyAssigned + } + } + } + + return true; + } + + protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer) + { + var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location); + return a.ResolveStatement (bc); + } + + protected override void DoEmit (EmitContext ec) + { + li.CreateBuilder (ec); + + if (Initializer != null && !IsUnreachable) + ((ExpressionStatement) Initializer).EmitStatement (ec); + + if (declarators != null) { + foreach (var d in declarators) { + d.Variable.CreateBuilder (ec); + if (d.Initializer != null && !IsUnreachable) { + ec.Mark (d.Variable.Location); + ((ExpressionStatement) d.Initializer).EmitStatement (ec); + } + } + } + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + if (Initializer != null) + Initializer.FlowAnalysis (fc); + + if (declarators != null) { + foreach (var d in declarators) { + if (d.Initializer != null) + d.Initializer.FlowAnalysis (fc); + } + } + + return false; + } + + public override Reachability MarkReachable (Reachability rc) + { + var init = initializer as ExpressionStatement; + if (init != null) + init.MarkReachable (rc); + + return base.MarkReachable (rc); + } + + protected override void CloneTo (CloneContext clonectx, Statement target) + { + BlockVariable t = (BlockVariable) target; + + if (type_expr != null) + t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx); + + if (initializer != null) + t.initializer = initializer.Clone (clonectx); + + if (declarators != null) { + t.declarators = null; + foreach (var d in declarators) + t.AddDeclarator (d.Clone (clonectx)); + } + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class BlockConstant : BlockVariable + { + public BlockConstant (FullNamedExpression type, LocalVariable li) + : base (type, li) + { + } + + public override void Emit (EmitContext ec) + { + // Nothing to emit, not even sequence point + } + + protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer) + { + initializer = initializer.Resolve (bc); + if (initializer == null) + return null; + + var c = initializer as Constant; + if (c == null) { + initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name); + return null; + } + + c = c.ConvertImplicitly (li.Type); + if (c == null) { + if (TypeSpec.IsReferenceType (li.Type)) + initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name); + else + initializer.Error_ValueCannotBeConverted (bc, li.Type, false); + + return null; + } + + li.ConstantValue = c; + return initializer; + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + // + // The information about a user-perceived local variable + // + public sealed class LocalVariable : INamedBlockVariable, ILocalVariable + { + [Flags] + public enum Flags + { + Used = 1, + IsThis = 1 << 1, + AddressTaken = 1 << 2, + CompilerGenerated = 1 << 3, + Constant = 1 << 4, + ForeachVariable = 1 << 5, + FixedVariable = 1 << 6, + UsingVariable = 1 << 7, + IsLocked = 1 << 8, + + ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable + } + + TypeSpec type; + readonly string name; + readonly Location loc; + readonly Block block; + Flags flags; + Constant const_value; + + public VariableInfo VariableInfo; + HoistedVariable hoisted_variant; + + LocalBuilder builder; + + public LocalVariable (Block block, string name, Location loc) + { + this.block = block; + this.name = name; + this.loc = loc; + } + + public LocalVariable (Block block, string name, Flags flags, Location loc) + : this (block, name, loc) + { + this.flags = flags; + } + + // + // Used by variable declarators + // + public LocalVariable (LocalVariable li, string name, Location loc) + : this (li.block, name, li.flags, loc) + { + } + + #region Properties + + public bool AddressTaken { + get { + return (flags & Flags.AddressTaken) != 0; + } + } + + public Block Block { + get { + return block; + } + } + + public Constant ConstantValue { + get { + return const_value; + } + set { + const_value = value; + } + } + + // + // Hoisted local variable variant + // + public HoistedVariable HoistedVariant { + get { + return hoisted_variant; + } + set { + hoisted_variant = value; + } + } + + public bool IsDeclared { + get { + return type != null; + } + } + + public bool IsCompilerGenerated { + get { + return (flags & Flags.CompilerGenerated) != 0; + } + } + + public bool IsConstant { + get { + return (flags & Flags.Constant) != 0; + } + } + + public bool IsLocked { + get { + return (flags & Flags.IsLocked) != 0; + } + set { + flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked; + } + } + + public bool IsThis { + get { + return (flags & Flags.IsThis) != 0; + } + } + + public bool IsFixed { + get { + return (flags & Flags.FixedVariable) != 0; + } + } + + bool INamedBlockVariable.IsParameter { + get { + return false; + } + } + + public bool IsReadonly { + get { + return (flags & Flags.ReadonlyMask) != 0; + } + } + + public Location Location { + get { + return loc; + } + } + + public string Name { + get { + return name; + } + } + + public TypeSpec Type { + get { + return type; + } + set { + type = value; + } + } + + #endregion + + public void CreateBuilder (EmitContext ec) + { + if ((flags & Flags.Used) == 0) { + if (VariableInfo == null) { + // Missing flow analysis or wrong variable flags + throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name); + } + + if (VariableInfo.IsEverAssigned) + ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name); + else + ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name); + } + + if (HoistedVariant != null) + return; + + if (builder != null) { + if ((flags & Flags.CompilerGenerated) != 0) + return; + + // To avoid Used warning duplicates + throw new InternalErrorException ("Already created variable `{0}'", name); + } + + // + // All fixed variabled are pinned, a slot has to be alocated + // + builder = ec.DeclareLocal (Type, IsFixed); + if (!ec.HasSet (BuilderContext.Options.OmitDebugInfo) && (flags & Flags.CompilerGenerated) == 0) + ec.DefineLocalVariable (name, builder); + } + + public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc) + { + LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc); + li.Type = type; + return li; + } + + public Expression CreateReferenceExpression (ResolveContext rc, Location loc) + { + if (IsConstant && const_value != null) + return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc); + + return new LocalVariableReference (this, loc); + } + + public void Emit (EmitContext ec) + { + // TODO: Need something better for temporary variables + if ((flags & Flags.CompilerGenerated) != 0) + CreateBuilder (ec); + + ec.Emit (OpCodes.Ldloc, builder); + } + + public void EmitAssign (EmitContext ec) + { + // TODO: Need something better for temporary variables + if ((flags & Flags.CompilerGenerated) != 0) + CreateBuilder (ec); + + ec.Emit (OpCodes.Stloc, builder); + } + + public void EmitAddressOf (EmitContext ec) + { + // TODO: Need something better for temporary variables + if ((flags & Flags.CompilerGenerated) != 0) + CreateBuilder (ec); + + ec.Emit (OpCodes.Ldloca, builder); + } + + public static string GetCompilerGeneratedName (Block block) + { + // HACK: Debugger depends on the name semantics + return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X"); + } + + public string GetReadOnlyContext () + { + switch (flags & Flags.ReadonlyMask) { + case Flags.FixedVariable: + return "fixed variable"; + case Flags.ForeachVariable: + return "foreach iteration variable"; + case Flags.UsingVariable: + return "using variable"; + } + + throw new InternalErrorException ("Variable is not readonly"); + } + + public bool IsThisAssigned (FlowAnalysisContext fc, Block block) + { + if (VariableInfo == null) + throw new Exception (); + + if (IsAssigned (fc)) + return true; + + return VariableInfo.IsFullyInitialized (fc, block.StartLocation); + } + + public bool IsAssigned (FlowAnalysisContext fc) + { + return fc.IsDefinitelyAssigned (VariableInfo); + } + + public void PrepareAssignmentAnalysis (BlockContext bc) + { + // + // No need to run assignment analysis for these guys + // + if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0) + return; + + VariableInfo = VariableInfo.Create (bc, this); + } + + // + // Mark the variables as referenced in the user code + // + public void SetIsUsed () + { + flags |= Flags.Used; + } + + public void SetHasAddressTaken () + { + flags |= (Flags.AddressTaken | Flags.Used); + } + + public override string ToString () + { + return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location); + } + } + + /// + /// Block represents a C# block. + /// + /// + /// + /// This class is used in a number of places: either to represent + /// explicit blocks that the programmer places or implicit blocks. + /// + /// Implicit blocks are used as labels or to introduce variable + /// declarations. + /// + /// Top-level blocks derive from Block, and they are called ToplevelBlock + /// they contain extra information that is not necessary on normal blocks. + /// + public class Block : Statement { + [Flags] + public enum Flags + { + Unchecked = 1, + ReachableEnd = 8, + Unsafe = 16, + HasCapturedVariable = 64, + HasCapturedThis = 1 << 7, + IsExpressionTree = 1 << 8, + CompilerGenerated = 1 << 9, + HasAsyncModifier = 1 << 10, + Resolved = 1 << 11, + YieldBlock = 1 << 12, + AwaitBlock = 1 << 13, + FinallyBlock = 1 << 14, + CatchBlock = 1 << 15, + Iterator = 1 << 20, + NoFlowAnalysis = 1 << 21, + InitializationEmitted = 1 << 22 + } + + public Block Parent; + public Location StartLocation; + public Location EndLocation; + + public ExplicitBlock Explicit; + public ParametersBlock ParametersBlock; + + protected Flags flags; + + // + // The statements in this block + // + protected List statements; + + protected List scope_initializers; + + int? resolving_init_idx; + + Block original; + +#if DEBUG + static int id; + public int ID = id++; + + static int clone_id_counter; + int clone_id; +#endif + +// int assignable_slots; + + public Block (Block parent, Location start, Location end) + : this (parent, 0, start, end) + { + } + + public Block (Block parent, Flags flags, Location start, Location end) + { + if (parent != null) { + // the appropriate constructors will fixup these fields + ParametersBlock = parent.ParametersBlock; + Explicit = parent.Explicit; + } + + this.Parent = parent; + this.flags = flags; + this.StartLocation = start; + this.EndLocation = end; + this.loc = start; + statements = new List (4); + + this.original = this; + } + + #region Properties + + public Block Original { + get { + return original; + } + protected set { + original = value; + } + } + + public bool IsCompilerGenerated { + get { return (flags & Flags.CompilerGenerated) != 0; } + set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; } + } + + + public bool IsCatchBlock { + get { + return (flags & Flags.CatchBlock) != 0; + } + } + + public bool IsFinallyBlock { + get { + return (flags & Flags.FinallyBlock) != 0; + } + } + + public bool Unchecked { + get { return (flags & Flags.Unchecked) != 0; } + set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; } + } + + public bool Unsafe { + get { return (flags & Flags.Unsafe) != 0; } + set { flags |= Flags.Unsafe; } + } + + public List Statements { + get { return statements; } + } + + #endregion + + public void SetEndLocation (Location loc) + { + EndLocation = loc; + } + + public void AddLabel (LabeledStatement target) + { + ParametersBlock.TopBlock.AddLabel (target.Name, target); + } + + public void AddLocalName (LocalVariable li) + { + AddLocalName (li.Name, li); + } + + public void AddLocalName (string name, INamedBlockVariable li) + { + ParametersBlock.TopBlock.AddLocalName (name, li, false); + } + + public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason) + { + if (reason == null) { + Error_AlreadyDeclared (name, variable); + return; + } + + ParametersBlock.TopBlock.Report.Error (136, variable.Location, + "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " + + "to `{0}', which is already used in a `{1}' scope to denote something else", + name, reason); + } + + public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable) + { + var pi = variable as ParametersBlock.ParameterInfo; + if (pi != null) { + pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report); + } else { + ParametersBlock.TopBlock.Report.Error (128, variable.Location, + "A local variable named `{0}' is already defined in this scope", name); + } + } + + public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc) + { + ParametersBlock.TopBlock.Report.Error (412, loc, + "The type parameter name `{0}' is the same as local variable or parameter name", + name); + } + + // + // It should be used by expressions which require to + // register a statement during resolve process. + // + public void AddScopeStatement (Statement s) + { + if (scope_initializers == null) + scope_initializers = new List (); + + // + // Simple recursive helper, when resolve scope initializer another + // new scope initializer can be added, this ensures it's initialized + // before existing one. For now this can happen with expression trees + // in base ctor initializer only + // + if (resolving_init_idx.HasValue) { + scope_initializers.Insert (resolving_init_idx.Value, s); + ++resolving_init_idx; + } else { + scope_initializers.Add (s); + } + } + + public void InsertStatement (int index, Statement s) + { + statements.Insert (index, s); + } + + public void AddStatement (Statement s) + { + statements.Add (s); + } + + public LabeledStatement LookupLabel (string name) + { + return ParametersBlock.GetLabel (name, this); + } + + public override Reachability MarkReachable (Reachability rc) + { + if (rc.IsUnreachable) + return rc; + + MarkReachableScope (rc); + + foreach (var s in statements) { + rc = s.MarkReachable (rc); + if (rc.IsUnreachable) { + if ((flags & Flags.ReachableEnd) != 0) + return new Reachability (); + + return rc; + } + } + + flags |= Flags.ReachableEnd; + + return rc; + } + + public void MarkReachableScope (Reachability rc) + { + base.MarkReachable (rc); + + if (scope_initializers != null) { + foreach (var si in scope_initializers) + si.MarkReachable (rc); + } + } + + public override bool Resolve (BlockContext bc) + { + if ((flags & Flags.Resolved) != 0) + return true; + + Block prev_block = bc.CurrentBlock; + bc.CurrentBlock = this; + + // + // Compiler generated scope statements + // + if (scope_initializers != null) { + for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) { + scope_initializers[resolving_init_idx.Value].Resolve (bc); + } + + resolving_init_idx = null; + } + + bool ok = true; + int statement_count = statements.Count; + for (int ix = 0; ix < statement_count; ix++){ + Statement s = statements [ix]; + + if (!s.Resolve (bc)) { + ok = false; + if (!bc.IsInProbingMode) + statements [ix] = new EmptyStatement (s.loc); + + continue; + } + } + + bc.CurrentBlock = prev_block; + + flags |= Flags.Resolved; + return ok; + } + + protected override void DoEmit (EmitContext ec) + { + for (int ix = 0; ix < statements.Count; ix++){ + statements [ix].Emit (ec); + } + } + + public override void Emit (EmitContext ec) + { + if (scope_initializers != null) + EmitScopeInitializers (ec); + + DoEmit (ec); + } + + protected void EmitScopeInitializers (EmitContext ec) + { + foreach (Statement s in scope_initializers) + s.Emit (ec); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + if (scope_initializers != null) { + foreach (var si in scope_initializers) + si.FlowAnalysis (fc); + } + + return DoFlowAnalysis (fc, 0); + } + + bool DoFlowAnalysis (FlowAnalysisContext fc, int startIndex) + { + bool end_unreachable = !reachable; + for (; startIndex < statements.Count; ++startIndex) { + var s = statements[startIndex]; + + end_unreachable = s.FlowAnalysis (fc); + if (s.IsUnreachable) { + statements [startIndex] = RewriteUnreachableStatement (s); + continue; + } + + // + // Statement end reachability is needed mostly due to goto support. Consider + // + // if (cond) { + // goto X; + // } else { + // goto Y; + // } + // X: + // + // X label is reachable only via goto not as another statement after if. We need + // this for flow-analysis only to carry variable info correctly. + // + if (end_unreachable) { + for (++startIndex; startIndex < statements.Count; ++startIndex) { + s = statements[startIndex]; + if (s is SwitchLabel) { + s.FlowAnalysis (fc); + break; + } + + if (s.IsUnreachable) { + s.FlowAnalysis (fc); + statements [startIndex] = RewriteUnreachableStatement (s); + } + } + } + } + + // + // The condition should be true unless there is forward jumping goto + // + // if (this is ExplicitBlock && end_unreachable != Explicit.HasReachableClosingBrace) + // Debug.Fail (); + + return !Explicit.HasReachableClosingBrace; + } + + static Statement RewriteUnreachableStatement (Statement s) + { + // LAMESPEC: It's not clear whether declararion statement should be part of reachability + // analysis. Even csc report unreachable warning for it but it's actually used hence + // we try to emulate this behaviour + // + // Consider: + // goto L; + // int v; + // L: + // v = 1; + + if (s is BlockVariable) + return s; + + return new EmptyStatement (s.loc); + } + + public void ScanGotoJump (Statement label) + { + int i; + for (i = 0; i < statements.Count; ++i) { + if (statements[i] == label) + break; + } + + var rc = new Reachability (); + for (++i; i < statements.Count; ++i) { + var s = statements[i]; + rc = s.MarkReachable (rc); + if (rc.IsUnreachable) + return; + } + + flags |= Flags.ReachableEnd; + } + + public void ScanGotoJump (Statement label, FlowAnalysisContext fc) + { + int i; + for (i = 0; i < statements.Count; ++i) { + if (statements[i] == label) + break; + } + + DoFlowAnalysis (fc, ++i); + } + +#if DEBUG + public override string ToString () + { + return String.Format ("{0}: ID={1} Clone={2} Location={3}", GetType (), ID, clone_id != 0, StartLocation); + } +#endif + + protected override void CloneTo (CloneContext clonectx, Statement t) + { + Block target = (Block) t; +#if DEBUG + target.clone_id = ++clone_id_counter; +#endif + + clonectx.AddBlockMap (this, target); + if (original != this) + clonectx.AddBlockMap (original, target); + + target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock)); + target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit)); + + if (Parent != null) + target.Parent = clonectx.RemapBlockCopy (Parent); + + target.statements = new List (statements.Count); + foreach (Statement s in statements) + target.statements.Add (s.Clone (clonectx)); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class ExplicitBlock : Block + { + protected AnonymousMethodStorey am_storey; + + public ExplicitBlock (Block parent, Location start, Location end) + : this (parent, (Flags) 0, start, end) + { + } + + public ExplicitBlock (Block parent, Flags flags, Location start, Location end) + : base (parent, flags, start, end) + { + this.Explicit = this; + } + + #region Properties + + public AnonymousMethodStorey AnonymousMethodStorey { + get { + return am_storey; + } + } + + public bool HasAwait { + get { + return (flags & Flags.AwaitBlock) != 0; + } + } + + public bool HasCapturedThis { + set { + flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; + } + get { + return (flags & Flags.HasCapturedThis) != 0; + } + } + + // + // Used to indicate that the block has reference to parent + // block and cannot be made static when defining anonymous method + // + public bool HasCapturedVariable { + set { + flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; + } + get { + return (flags & Flags.HasCapturedVariable) != 0; + } + } + + public bool HasReachableClosingBrace { + get { + return (flags & Flags.ReachableEnd) != 0; + } + set { + flags = value ? flags | Flags.ReachableEnd : flags & ~Flags.ReachableEnd; + } + } + + public bool HasYield { + get { + return (flags & Flags.YieldBlock) != 0; + } + } + + #endregion + + // + // Creates anonymous method storey in current block + // + public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec) + { + // + // Return same story for iterator and async blocks unless we are + // in nested anonymous method + // + if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original) + return ec.CurrentAnonymousMethod.Storey; + + if (am_storey == null) { + MemberBase mc = ec.MemberContext as MemberBase; + + // + // Creates anonymous method storey for this block + // + am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class); + } + + return am_storey; + } + + public void EmitScopeInitialization (EmitContext ec) + { + if ((flags & Flags.InitializationEmitted) != 0) + return; + + if (am_storey != null) { + DefineStoreyContainer (ec, am_storey); + am_storey.EmitStoreyInstantiation (ec, this); + } + + if (scope_initializers != null) + EmitScopeInitializers (ec); + + flags |= Flags.InitializationEmitted; + } + + public override void Emit (EmitContext ec) + { + EmitScopeInitialization (ec); + + if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) { + ec.Emit (OpCodes.Nop); + } + + if (Parent != null) + ec.BeginScope (); + + DoEmit (ec); + + if (Parent != null) + ec.EndScope (); + + if (ec.EmitAccurateDebugInfo && HasReachableClosingBrace && !(this is ParametersBlock) && + !IsCompilerGenerated && ec.Mark (EndLocation)) { + ec.Emit (OpCodes.Nop); + } + } + + protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey) + { + if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) { + storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey); + storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator; + } + + // + // Creates anonymous method storey + // + storey.CreateContainer (); + storey.DefineContainer (); + + if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) { + + // + // Only first storey in path will hold this reference. All children blocks will + // reference it indirectly using $ref field + // + for (Block b = Original.Explicit; b != null; b = b.Parent) { + if (b.Parent != null) { + var s = b.Parent.Explicit.AnonymousMethodStorey; + if (s != null) { + storey.HoistedThis = s.HoistedThis; + break; + } + } + + if (b.Explicit == b.Explicit.ParametersBlock && b.Explicit.ParametersBlock.StateMachine != null) { + if (storey.HoistedThis == null) + storey.HoistedThis = b.Explicit.ParametersBlock.StateMachine.HoistedThis; + + if (storey.HoistedThis != null) + break; + } + } + + // + // We are the first storey on path and 'this' has to be hoisted + // + if (storey.HoistedThis == null) { + foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) { + // + // ThisReferencesFromChildrenBlock holds all reference even if they + // are not on this path. It saves some memory otherwise it'd have to + // be in every explicit block. We run this check to see if the reference + // is valid for this storey + // + Block block_on_path = ref_block; + for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent); + + if (block_on_path == null) + continue; + + if (storey.HoistedThis == null) { + storey.AddCapturedThisField (ec, null); + } + + for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) { + ParametersBlock pb; + AnonymousMethodStorey b_storey = b.AnonymousMethodStorey; + + if (b_storey != null) { + // + // Don't add storey cross reference for `this' when the storey ends up not + // beeing attached to any parent + // + if (b.ParametersBlock.StateMachine == null) { + AnonymousMethodStorey s = null; + for (Block ab = b.AnonymousMethodStorey.OriginalSourceBlock.Parent; ab != null; ab = ab.Parent) { + s = ab.Explicit.AnonymousMethodStorey; + if (s != null) + break; + } + + // Needs to be in sync with AnonymousMethodBody::DoCreateMethodHost + if (s == null) { + var parent = storey == null || storey.Kind == MemberKind.Struct ? null : storey; + b.AnonymousMethodStorey.AddCapturedThisField (ec, parent); + break; + } + + } + + // + // Stop propagation inside same top block + // + if (b.ParametersBlock == ParametersBlock.Original) { + b_storey.AddParentStoreyReference (ec, storey); +// b_storey.HoistedThis = storey.HoistedThis; + break; + } + + b = pb = b.ParametersBlock; + } else { + pb = b as ParametersBlock; + } + + if (pb != null && pb.StateMachine != null) { + if (pb.StateMachine == storey) + break; + + // + // If we are state machine with no parent. We can hook into parent without additional + // reference and capture this directly + // + ExplicitBlock parent_storey_block = pb; + while (parent_storey_block.Parent != null) { + parent_storey_block = parent_storey_block.Parent.Explicit; + if (parent_storey_block.AnonymousMethodStorey != null) { + break; + } + } + + if (parent_storey_block.AnonymousMethodStorey == null) { + pb.StateMachine.AddCapturedThisField (ec, null); + b.HasCapturedThis = true; + continue; + } + + pb.StateMachine.AddParentStoreyReference (ec, storey); + } + + // + // Add parent storey reference only when this is not captured directly + // + if (b_storey != null) { + b_storey.AddParentStoreyReference (ec, storey); + b_storey.HoistedThis = storey.HoistedThis; + } + } + } + } + } + + var ref_blocks = storey.ReferencesFromChildrenBlock; + if (ref_blocks != null) { + foreach (ExplicitBlock ref_block in ref_blocks) { + for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) { + if (b.AnonymousMethodStorey != null) { + b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey); + + // + // Stop propagation inside same top block + // + if (b.ParametersBlock == ParametersBlock.Original) + break; + + b = b.ParametersBlock; + } + + var pb = b as ParametersBlock; + if (pb != null && pb.StateMachine != null) { + if (pb.StateMachine == storey) + break; + + pb.StateMachine.AddParentStoreyReference (ec, storey); + } + + b.HasCapturedVariable = true; + } + } + } + + storey.Define (); + storey.PrepareEmit (); + storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey); + } + + public void RegisterAsyncAwait () + { + var block = this; + while ((block.flags & Flags.AwaitBlock) == 0) { + block.flags |= Flags.AwaitBlock; + + if (block is ParametersBlock) + return; + + block = block.Parent.Explicit; + } + } + + public void RegisterIteratorYield () + { + ParametersBlock.TopBlock.IsIterator = true; + + var block = this; + while ((block.flags & Flags.YieldBlock) == 0) { + block.flags |= Flags.YieldBlock; + + if (block.Parent == null) + return; + + block = block.Parent.Explicit; + } + } + + public void SetCatchBlock () + { + flags |= Flags.CatchBlock; + } + + public void SetFinallyBlock () + { + flags |= Flags.FinallyBlock; + } + + public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock) + { + tryBlock.statements = statements; + statements = new List (1); + statements.Add (tf); + } + } + + // + // ParametersBlock was introduced to support anonymous methods + // and lambda expressions + // + public class ParametersBlock : ExplicitBlock + { + public class ParameterInfo : INamedBlockVariable + { + readonly ParametersBlock block; + readonly int index; + public VariableInfo VariableInfo; + bool is_locked; + + public ParameterInfo (ParametersBlock block, int index) + { + this.block = block; + this.index = index; + } + + #region Properties + + public ParametersBlock Block { + get { + return block; + } + } + + Block INamedBlockVariable.Block { + get { + return block; + } + } + + public bool IsDeclared { + get { + return true; + } + } + + public bool IsParameter { + get { + return true; + } + } + + public bool IsLocked { + get { + return is_locked; + } + set { + is_locked = value; + } + } + + public Location Location { + get { + return Parameter.Location; + } + } + + public Parameter Parameter { + get { + return block.Parameters [index]; + } + } + + public TypeSpec ParameterType { + get { + return Parameter.Type; + } + } + + #endregion + + public Expression CreateReferenceExpression (ResolveContext rc, Location loc) + { + return new ParameterReference (this, loc); + } + } + + // + // Block is converted into an expression + // + sealed class BlockScopeExpression : Expression + { + Expression child; + readonly ParametersBlock block; + + public BlockScopeExpression (Expression child, ParametersBlock block) + { + this.child = child; + this.block = block; + } + + public override bool ContainsEmitWithAwait () + { + return child.ContainsEmitWithAwait (); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + throw new NotSupportedException (); + } + + protected override Expression DoResolve (ResolveContext ec) + { + if (child == null) + return null; + + child = child.Resolve (ec); + if (child == null) + return null; + + eclass = child.eclass; + type = child.Type; + return this; + } + + public override void Emit (EmitContext ec) + { + block.EmitScopeInitializers (ec); + child.Emit (ec); + } + } + + protected ParametersCompiled parameters; + protected ParameterInfo[] parameter_info; + protected bool resolved; + protected ToplevelBlock top_block; + protected StateMachine state_machine; + protected Dictionary labels; + + public ParametersBlock (Block parent, ParametersCompiled parameters, Location start, Flags flags = 0) + : base (parent, 0, start, start) + { + if (parameters == null) + throw new ArgumentNullException ("parameters"); + + this.parameters = parameters; + ParametersBlock = this; + + this.flags |= flags | (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock)); + + this.top_block = parent.ParametersBlock.top_block; + ProcessParameters (); + } + + protected ParametersBlock (ParametersCompiled parameters, Location start) + : base (null, 0, start, start) + { + if (parameters == null) + throw new ArgumentNullException ("parameters"); + + this.parameters = parameters; + ParametersBlock = this; + } + + // + // It's supposed to be used by method body implementation of anonymous methods + // + protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters) + : base (null, 0, source.StartLocation, source.EndLocation) + { + this.parameters = parameters; + this.statements = source.statements; + this.scope_initializers = source.scope_initializers; + + this.resolved = true; + this.reachable = source.reachable; + this.am_storey = source.am_storey; + this.state_machine = source.state_machine; + this.flags = source.flags & Flags.ReachableEnd; + + ParametersBlock = this; + + // + // Overwrite original for comparison purposes when linking cross references + // between anonymous methods + // + Original = source.Original; + } + + #region Properties + + public bool IsAsync { + get { + return (flags & Flags.HasAsyncModifier) != 0; + } + set { + flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier; + } + } + + // + // Block has been converted to expression tree + // + public bool IsExpressionTree { + get { + return (flags & Flags.IsExpressionTree) != 0; + } + } + + // + // The parameters for the block. + // + public ParametersCompiled Parameters { + get { + return parameters; + } + } + + public StateMachine StateMachine { + get { + return state_machine; + } + } + + public ToplevelBlock TopBlock { + get { + return top_block; + } + set { + top_block = value; + } + } + + public bool Resolved { + get { + return (flags & Flags.Resolved) != 0; + } + } + + public int TemporaryLocalsCount { get; set; } + + #endregion + + // + // Checks whether all `out' parameters have been assigned. + // + public void CheckControlExit (FlowAnalysisContext fc) + { + CheckControlExit (fc, fc.DefiniteAssignment); + } + + public virtual void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat) + { + if (parameter_info == null) + return; + + foreach (var p in parameter_info) { + if (p.VariableInfo == null) + continue; + + if (p.VariableInfo.IsAssigned (dat)) + continue; + + fc.Report.Error (177, p.Location, + "The out parameter `{0}' must be assigned to before control leaves the current method", + p.Parameter.Name); + } + } + + protected override void CloneTo (CloneContext clonectx, Statement t) + { + base.CloneTo (clonectx, t); + + var target = (ParametersBlock) t; + + // + // Clone label statements as well as they contain block reference + // + var pb = this; + while (true) { + if (pb.labels != null) { + target.labels = new Dictionary (); + + foreach (var entry in pb.labels) { + var list = entry.Value as List; + + if (list != null) { + var list_clone = new List (); + foreach (var lentry in list) { + list_clone.Add (RemapLabeledStatement (lentry, lentry.Block, clonectx.RemapBlockCopy (lentry.Block))); + } + + target.labels.Add (entry.Key, list_clone); + } else { + var labeled = (LabeledStatement) entry.Value; + target.labels.Add (entry.Key, RemapLabeledStatement (labeled, labeled.Block, clonectx.RemapBlockCopy (labeled.Block))); + } + } + + break; + } + + if (pb.Parent == null) + break; + + pb = pb.Parent.ParametersBlock; + } + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + if (statements.Count == 1) { + Expression expr = statements[0].CreateExpressionTree (ec); + if (scope_initializers != null) + expr = new BlockScopeExpression (expr, this); + + return expr; + } + + return base.CreateExpressionTree (ec); + } + + public override void Emit (EmitContext ec) + { + if (state_machine != null && state_machine.OriginalSourceBlock != this) { + DefineStoreyContainer (ec, state_machine); + state_machine.EmitStoreyInstantiation (ec, this); + } + + base.Emit (ec); + } + + public void EmitEmbedded (EmitContext ec) + { + if (state_machine != null && state_machine.OriginalSourceBlock != this) { + DefineStoreyContainer (ec, state_machine); + state_machine.EmitStoreyInstantiation (ec, this); + } + + base.Emit (ec); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + var res = base.DoFlowAnalysis (fc); + + if (HasReachableClosingBrace) + CheckControlExit (fc); + + return res; + } + + public LabeledStatement GetLabel (string name, Block block) + { + // + // Cloned parameters blocks can have their own cloned version of top-level labels + // + if (labels == null) { + if (Parent != null) + return Parent.ParametersBlock.GetLabel (name, block); + + return null; + } + + object value; + if (!labels.TryGetValue (name, out value)) { + return null; + } + + var label = value as LabeledStatement; + Block b = block; + if (label != null) { + if (IsLabelVisible (label, b)) + return label; + + } else { + List list = (List) value; + for (int i = 0; i < list.Count; ++i) { + label = list[i]; + if (IsLabelVisible (label, b)) + return label; + } + } + + return null; + } + + static bool IsLabelVisible (LabeledStatement label, Block b) + { + do { + if (label.Block == b) + return true; + b = b.Parent; + } while (b != null); + + return false; + } + + public ParameterInfo GetParameterInfo (Parameter p) + { + for (int i = 0; i < parameters.Count; ++i) { + if (parameters[i] == p) + return parameter_info[i]; + } + + throw new ArgumentException ("Invalid parameter"); + } + + public ParameterReference GetParameterReference (int index, Location loc) + { + return new ParameterReference (parameter_info[index], loc); + } + + public Statement PerformClone () + { + CloneContext clonectx = new CloneContext (); + return Clone (clonectx); + } + + protected void ProcessParameters () + { + if (parameters.Count == 0) + return; + + parameter_info = new ParameterInfo[parameters.Count]; + for (int i = 0; i < parameter_info.Length; ++i) { + var p = parameters.FixedParameters[i]; + if (p == null) + continue; + + // TODO: Should use Parameter only and more block there + parameter_info[i] = new ParameterInfo (this, i); + if (p.Name != null) + AddLocalName (p.Name, parameter_info[i]); + } + } + + static LabeledStatement RemapLabeledStatement (LabeledStatement stmt, Block src, Block dst) + { + var src_stmts = src.Statements; + for (int i = 0; i < src_stmts.Count; ++i) { + if (src_stmts[i] == stmt) + return (LabeledStatement) dst.Statements[i]; + } + + throw new InternalErrorException ("Should never be reached"); + } + + public override bool Resolve (BlockContext bc) + { + // TODO: if ((flags & Flags.Resolved) != 0) + + if (resolved) + return true; + + resolved = true; + + if (bc.HasSet (ResolveContext.Options.ExpressionTreeConversion)) + flags |= Flags.IsExpressionTree; + + try { + PrepareAssignmentAnalysis (bc); + + if (!base.Resolve (bc)) + return false; + + } catch (Exception e) { + if (e is CompletionResult || bc.Report.IsDisabled || e is FatalException || bc.Report.Printer is NullReportPrinter || bc.Module.Compiler.Settings.BreakOnInternalError) + throw; + + if (bc.CurrentBlock != null) { + bc.Report.Error (584, bc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message); + } else { + bc.Report.Error (587, "Internal compiler error: {0}", e.Message); + } + } + + // + // If an asynchronous body of F is either an expression classified as nothing, or a + // statement block where no return statements have expressions, the inferred return type is Task + // + if (IsAsync) { + var am = bc.CurrentAnonymousMethod as AnonymousMethodBody; + if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) { + am.ReturnTypeInference = null; + am.ReturnType = bc.Module.PredefinedTypes.Task.TypeSpec; + return true; + } + } + + return true; + } + + void PrepareAssignmentAnalysis (BlockContext bc) + { + for (int i = 0; i < parameters.Count; ++i) { + var par = parameters.FixedParameters[i]; + + if ((par.ModFlags & Parameter.Modifier.OUT) == 0) + continue; + + parameter_info [i].VariableInfo = VariableInfo.Create (bc, (Parameter) par); + } + } + + public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable) + { + var iterator = new Iterator (this, method, host, iterator_type, is_enumerable); + var stateMachine = new IteratorStorey (iterator); + + state_machine = stateMachine; + iterator.SetStateMachine (stateMachine); + + var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null, Flags.CompilerGenerated); + tlb.Original = this; + tlb.state_machine = stateMachine; + tlb.AddStatement (new Return (iterator, iterator.Location)); + return tlb; + } + + public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, TypeSpec delegateType, Location loc) + { + for (int i = 0; i < parameters.Count; i++) { + Parameter p = parameters[i]; + Parameter.Modifier mod = p.ModFlags; + if ((mod & Parameter.Modifier.RefOutMask) != 0) { + host.Compiler.Report.Error (1988, p.Location, + "Async methods cannot have ref or out parameters"); + return this; + } + + if (p is ArglistParameter) { + host.Compiler.Report.Error (4006, p.Location, + "__arglist is not allowed in parameter list of async methods"); + return this; + } + + if (parameters.Types[i].IsPointer) { + host.Compiler.Report.Error (4005, p.Location, + "Async methods cannot have unsafe parameters"); + return this; + } + } + + if (!HasAwait) { + host.Compiler.Report.Warning (1998, 1, loc, + "Async block lacks `await' operator and will run synchronously"); + } + + var block_type = host.Module.Compiler.BuiltinTypes.Void; + var initializer = new AsyncInitializer (this, host, block_type); + initializer.Type = block_type; + initializer.DelegateType = delegateType; + + var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType); + + state_machine = stateMachine; + initializer.SetStateMachine (stateMachine); + + const Flags flags = Flags.CompilerGenerated; + + var b = this is ToplevelBlock ? + new ToplevelBlock (host.Compiler, Parameters, Location.Null, flags) : + new ParametersBlock (Parent, parameters, Location.Null, flags | Flags.HasAsyncModifier); + + b.Original = this; + b.state_machine = stateMachine; + b.AddStatement (new AsyncInitializerStatement (initializer)); + return b; + } + } + + // + // + // + public class ToplevelBlock : ParametersBlock + { + LocalVariable this_variable; + CompilerContext compiler; + Dictionary names; + + List this_references; + + public ToplevelBlock (CompilerContext ctx, Location loc) + : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc) + { + } + + public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start, Flags flags = 0) + : base (parameters, start) + { + this.compiler = ctx; + this.flags = flags; + top_block = this; + + ProcessParameters (); + } + + // + // Recreates a top level block from parameters block. Used for + // compiler generated methods where the original block comes from + // explicit child block. This works for already resolved blocks + // only to ensure we resolve them in the correct flow order + // + public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters) + : base (source, parameters) + { + this.compiler = source.TopBlock.compiler; + top_block = this; + } + + public bool IsIterator { + get { + return (flags & Flags.Iterator) != 0; + } + set { + flags = value ? flags | Flags.Iterator : flags & ~Flags.Iterator; + } + } + + public Report Report { + get { + return compiler.Report; + } + } + + // + // Used by anonymous blocks to track references of `this' variable + // + public List ThisReferencesFromChildrenBlock { + get { + return this_references; + } + } + + // + // Returns the "this" instance variable of this block. + // See AddThisVariable() for more information. + // + public LocalVariable ThisVariable { + get { + return this_variable; + } + } + + public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks) + { + if (names == null) + names = new Dictionary (); + + object value; + if (!names.TryGetValue (name, out value)) { + names.Add (name, li); + return; + } + + INamedBlockVariable existing = value as INamedBlockVariable; + List existing_list; + if (existing != null) { + existing_list = new List (); + existing_list.Add (existing); + names[name] = existing_list; + } else { + existing_list = (List) value; + } + + // + // A collision checking between local names + // + var variable_block = li.Block.Explicit; + for (int i = 0; i < existing_list.Count; ++i) { + existing = existing_list[i]; + Block b = existing.Block.Explicit; + + // Collision at same level + if (variable_block == b) { + li.Block.Error_AlreadyDeclared (name, li); + break; + } + + // Collision with parent + Block parent = variable_block; + while ((parent = parent.Parent) != null) { + if (parent == b) { + li.Block.Error_AlreadyDeclared (name, li, "parent or current"); + i = existing_list.Count; + break; + } + } + + if (!ignoreChildrenBlocks && variable_block.Parent != b.Parent) { + // Collision with children + while ((b = b.Parent) != null) { + if (variable_block == b) { + li.Block.Error_AlreadyDeclared (name, li, "child"); + i = existing_list.Count; + break; + } + } + } + } + + existing_list.Add (li); + } + + public void AddLabel (string name, LabeledStatement label) + { + if (labels == null) + labels = new Dictionary (); + + object value; + if (!labels.TryGetValue (name, out value)) { + labels.Add (name, label); + return; + } + + LabeledStatement existing = value as LabeledStatement; + List existing_list; + if (existing != null) { + existing_list = new List (); + existing_list.Add (existing); + labels[name] = existing_list; + } else { + existing_list = (List) value; + } + + // + // A collision checking between labels + // + for (int i = 0; i < existing_list.Count; ++i) { + existing = existing_list[i]; + Block b = existing.Block; + + // Collision at same level + if (label.Block == b) { + Report.SymbolRelatedToPreviousError (existing.loc, name); + Report.Error (140, label.loc, "The label `{0}' is a duplicate", name); + break; + } + + // Collision with parent + b = label.Block; + while ((b = b.Parent) != null) { + if (existing.Block == b) { + Report.Error (158, label.loc, + "The label `{0}' shadows another label by the same name in a contained scope", name); + i = existing_list.Count; + break; + } + } + + // Collision with with children + b = existing.Block; + while ((b = b.Parent) != null) { + if (label.Block == b) { + Report.Error (158, label.loc, + "The label `{0}' shadows another label by the same name in a contained scope", name); + i = existing_list.Count; + break; + } + } + } + + existing_list.Add (label); + } + + public void AddThisReferenceFromChildrenBlock (ExplicitBlock block) + { + if (this_references == null) + this_references = new List (); + + if (!this_references.Contains (block)) + this_references.Add (block); + } + + public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block) + { + this_references.Remove (block); + } + + // + // Creates an arguments set from all parameters, useful for method proxy calls + // + public Arguments GetAllParametersArguments () + { + int count = parameters.Count; + Arguments args = new Arguments (count); + for (int i = 0; i < count; ++i) { + var pi = parameter_info[i]; + var arg_expr = GetParameterReference (i, pi.Location); + + Argument.AType atype_modifier; + switch (pi.Parameter.ParameterModifier & Parameter.Modifier.RefOutMask) { + case Parameter.Modifier.REF: + atype_modifier = Argument.AType.Ref; + break; + case Parameter.Modifier.OUT: + atype_modifier = Argument.AType.Out; + break; + default: + atype_modifier = 0; + break; + } + + args.Add (new Argument (arg_expr, atype_modifier)); + } + + return args; + } + + // + // Lookup inside a block, the returned value can represent 3 states + // + // true+variable: A local name was found and it's valid + // false+variable: A local name was found in a child block only + // false+null: No local name was found + // + public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable) + { + if (names == null) + return false; + + object value; + if (!names.TryGetValue (name, out value)) + return false; + + variable = value as INamedBlockVariable; + Block b = block; + if (variable != null) { + do { + if (variable.Block == b.Original) + return true; + + b = b.Parent; + } while (b != null); + + b = variable.Block; + do { + if (block == b) + return false; + + b = b.Parent; + } while (b != null); + } else { + List list = (List) value; + for (int i = 0; i < list.Count; ++i) { + variable = list[i]; + do { + if (variable.Block == b.Original) + return true; + + b = b.Parent; + } while (b != null); + + b = variable.Block; + do { + if (block == b) + return false; + + b = b.Parent; + } while (b != null); + + b = block; + } + } + + variable = null; + return false; + } + + public void IncludeBlock (ParametersBlock pb, ToplevelBlock block) + { + if (block.names != null) { + foreach (var n in block.names) { + var variable = n.Value as INamedBlockVariable; + if (variable != null) { + if (variable.Block.ParametersBlock == pb) + AddLocalName (n.Key, variable, false); + continue; + } + + foreach (var v in (List) n.Value) + if (v.Block.ParametersBlock == pb) + AddLocalName (n.Key, v, false); + } + } + } + + // + // This is used by non-static `struct' constructors which do not have an + // initializer - in this case, the constructor must initialize all of the + // struct's fields. To do this, we add a "this" variable and use the flow + // analysis code to ensure that it's been fully initialized before control + // leaves the constructor. + // + public void AddThisVariable (BlockContext bc) + { + if (this_variable != null) + throw new InternalErrorException (StartLocation.ToString ()); + + this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation); + this_variable.Type = bc.CurrentType; + this_variable.PrepareAssignmentAnalysis (bc); + } + + public override void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat) + { + // + // If we're a non-static struct constructor which doesn't have an + // initializer, then we must initialize all of the struct's fields. + // + if (this_variable != null) + this_variable.IsThisAssigned (fc, this); + + base.CheckControlExit (fc, dat); + } + + public override void Emit (EmitContext ec) + { + if (Report.Errors > 0) + return; + + try { + if (IsCompilerGenerated) { + using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) { + base.Emit (ec); + } + } else { + base.Emit (ec); + } + + // + // If `HasReturnLabel' is set, then we already emitted a + // jump to the end of the method, so we must emit a `ret' + // there. + // + // Unfortunately, System.Reflection.Emit automatically emits + // a leave to the end of a finally block. This is a problem + // if no code is following the try/finally block since we may + // jump to a point after the end of the method. + // As a workaround, we're always creating a return label in + // this case. + // + if (ec.HasReturnLabel || HasReachableClosingBrace) { + if (ec.HasReturnLabel) + ec.MarkLabel (ec.ReturnLabel); + + if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated) + ec.Mark (EndLocation); + + if (ec.ReturnType.Kind != MemberKind.Void) + ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ()); + + ec.Emit (OpCodes.Ret); + } + + } catch (Exception e) { + throw new InternalErrorException (e, StartLocation); + } + } + + public bool Resolve (BlockContext bc, IMethodData md) + { + if (resolved) + return true; + + var errors = bc.Report.Errors; + + base.Resolve (bc); + + if (bc.Report.Errors > errors) + return false; + + MarkReachable (new Reachability ()); + + if (HasReachableClosingBrace && bc.ReturnType.Kind != MemberKind.Void) { + // TODO: var md = bc.CurrentMemberDefinition; + bc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ()); + } + + if ((flags & Flags.NoFlowAnalysis) != 0) + return true; + + var fc = new FlowAnalysisContext (bc.Module.Compiler, this, bc.AssignmentInfoOffset); + try { + FlowAnalysis (fc); + } catch (Exception e) { + throw new InternalErrorException (e, StartLocation); + } + + return true; + } + } + + public class SwitchLabel : Statement + { + Constant converted; + Expression label; + + Label? il_label; + + // + // if expr == null, then it is the default case. + // + public SwitchLabel (Expression expr, Location l) + { + label = expr; + loc = l; + } + + public bool IsDefault { + get { + return label == null; + } + } + + public Expression Label { + get { + return label; + } + } + + public Location Location { + get { + return loc; + } + } + + public Constant Converted { + get { + return converted; + } + set { + converted = value; + } + } + + public bool SectionStart { get; set; } + + public Label GetILLabel (EmitContext ec) + { + if (il_label == null){ + il_label = ec.DefineLabel (); + } + + return il_label.Value; + } + + protected override void DoEmit (EmitContext ec) + { + ec.MarkLabel (GetILLabel (ec)); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + if (!SectionStart) + return false; + + fc.DefiniteAssignment = new DefiniteAssignmentBitSet (fc.SwitchInitialDefinitiveAssignment); + return false; + } + + public override bool Resolve (BlockContext bc) + { + if (ResolveAndReduce (bc)) + bc.Switch.RegisterLabel (bc, this); + + return true; + } + + // + // Resolves the expression, reduces it to a literal if possible + // and then converts it to the requested type. + // + bool ResolveAndReduce (BlockContext rc) + { + if (IsDefault) + return true; + + var c = label.ResolveLabelConstant (rc); + if (c == null) + return false; + + if (rc.Switch.IsNullable && c is NullLiteral) { + converted = c; + return true; + } + + converted = c.ImplicitConversionRequired (rc, rc.Switch.SwitchType); + return converted != null; + } + + public void Error_AlreadyOccurs (ResolveContext ec, SwitchLabel collision_with) + { + ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null); + ec.Report.Error (152, loc, "The label `{0}' already occurs in this switch statement", GetSignatureForError ()); + } + + protected override void CloneTo (CloneContext clonectx, Statement target) + { + var t = (SwitchLabel) target; + if (label != null) + t.label = label.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + + public string GetSignatureForError () + { + string label; + if (converted == null) + label = "default"; + else + label = converted.GetValueAsLiteral (); + + return string.Format ("case {0}:", label); + } + } + + public class Switch : LoopStatement + { + // structure used to hold blocks of keys while calculating table switch + sealed class LabelsRange : IComparable + { + public readonly long min; + public long max; + public readonly List label_values; + + public LabelsRange (long value) + { + min = max = value; + label_values = new List (); + label_values.Add (value); + } + + public LabelsRange (long min, long max, ICollection values) + { + this.min = min; + this.max = max; + this.label_values = new List (values); + } + + public long Range { + get { + return max - min + 1; + } + } + + public bool AddValue (long value) + { + var gap = value - min + 1; + // Ensure the range has > 50% occupancy + if (gap > 2 * (label_values.Count + 1) || gap <= 0) + return false; + + max = value; + label_values.Add (value); + return true; + } + + public int CompareTo (LabelsRange other) + { + int nLength = label_values.Count; + int nLengthOther = other.label_values.Count; + if (nLengthOther == nLength) + return (int) (other.min - min); + + return nLength - nLengthOther; + } + } + + sealed class DispatchStatement : Statement + { + readonly Switch body; + + public DispatchStatement (Switch body) + { + this.body = body; + } + + protected override void CloneTo (CloneContext clonectx, Statement target) + { + throw new NotImplementedException (); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + return false; + } + + protected override void DoEmit (EmitContext ec) + { + body.EmitDispatch (ec); + } + } + + class MissingBreak : Statement + { + readonly SwitchLabel label; + + public MissingBreak (SwitchLabel sl) + { + this.label = sl; + this.loc = sl.loc; + } + + public bool FallOut { get; set; } + + protected override void DoEmit (EmitContext ec) + { + } + + protected override void CloneTo (CloneContext clonectx, Statement target) + { + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + if (FallOut) { + fc.Report.Error (8070, loc, "Control cannot fall out of switch statement through final case label `{0}'", + label.GetSignatureForError ()); + } else { + fc.Report.Error (163, loc, "Control cannot fall through from one case label `{0}' to another", + label.GetSignatureForError ()); + } + return true; + } + } + + public Expression Expr; + + // + // Mapping of all labels to their SwitchLabels + // + Dictionary labels; + Dictionary string_labels; + List case_labels; + + List> goto_cases; + List end_reachable_das; + + /// + /// The governing switch type + /// + public TypeSpec SwitchType; + + Expression new_expr; + + SwitchLabel case_null; + SwitchLabel case_default; + + Label defaultLabel, nullLabel; + VariableReference value; + ExpressionStatement string_dictionary; + FieldExpr switch_cache_field; + ExplicitBlock block; + bool end_reachable; + + // + // Nullable Types support + // + Nullable.Unwrap unwrap; + + public Switch (Expression e, ExplicitBlock block, Location l) + : base (block) + { + Expr = e; + this.block = block; + loc = l; + } + + public SwitchLabel ActiveLabel { get; set; } + + public ExplicitBlock Block { + get { + return block; + } + } + + public SwitchLabel DefaultLabel { + get { + return case_default; + } + } + + public bool IsNullable { + get { + return unwrap != null; + } + } + + public List RegisteredLabels { + get { + return case_labels; + } + } + + // + // Determines the governing type for a switch. The returned + // expression might be the expression from the switch, or an + // expression that includes any potential conversions to + // + static Expression SwitchGoverningType (ResolveContext rc, Expression expr, bool unwrapExpr) + { + switch (expr.Type.BuiltinType) { + case BuiltinTypeSpec.Type.Byte: + case BuiltinTypeSpec.Type.SByte: + case BuiltinTypeSpec.Type.UShort: + case BuiltinTypeSpec.Type.Short: + case BuiltinTypeSpec.Type.UInt: + case BuiltinTypeSpec.Type.Int: + case BuiltinTypeSpec.Type.ULong: + case BuiltinTypeSpec.Type.Long: + case BuiltinTypeSpec.Type.Char: + case BuiltinTypeSpec.Type.String: + case BuiltinTypeSpec.Type.Bool: + return expr; + } + + if (expr.Type.IsEnum) + return expr; + + // + // Try to find a *user* defined implicit conversion. + // + // If there is no implicit conversion, or if there are multiple + // conversions, we have to report an error + // + Expression converted = null; + foreach (TypeSpec tt in rc.Module.PredefinedTypes.SwitchUserTypes) { + + if (!unwrapExpr && tt.IsNullableType && expr.Type.IsNullableType) + break; + + var restr = Convert.UserConversionRestriction.ImplicitOnly | + Convert.UserConversionRestriction.ProbingOnly; + + if (unwrapExpr) + restr |= Convert.UserConversionRestriction.NullableSourceOnly; + + var e = Convert.UserDefinedConversion (rc, expr, tt, restr, Location.Null); + if (e == null) + continue; + + // + // Ignore over-worked ImplicitUserConversions that do + // an implicit conversion in addition to the user conversion. + // + var uc = e as UserCast; + if (uc == null) + continue; + + if (converted != null){ +// rc.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous "); + return null; + } + + converted = e; + } + return converted; + } + + public static TypeSpec[] CreateSwitchUserTypes (ModuleContainer module, TypeSpec nullable) + { + var types = module.Compiler.BuiltinTypes; + + // LAMESPEC: For some reason it does not contain bool which looks like csc bug + TypeSpec[] stypes = new[] { + types.SByte, + types.Byte, + types.Short, + types.UShort, + types.Int, + types.UInt, + types.Long, + types.ULong, + types.Char, + types.String + }; + + if (nullable != null) { + + Array.Resize (ref stypes, stypes.Length + 9); + + for (int i = 0; i < 9; ++i) { + stypes [10 + i] = nullable.MakeGenericType (module, new [] { stypes [i] }); + } + } + + return stypes; + } + + public void RegisterLabel (BlockContext rc, SwitchLabel sl) + { + case_labels.Add (sl); + + if (sl.IsDefault) { + if (case_default != null) { + sl.Error_AlreadyOccurs (rc, case_default); + } else { + case_default = sl; + } + + return; + } + + try { + if (string_labels != null) { + string string_value = sl.Converted.GetValue () as string; + if (string_value == null) + case_null = sl; + else + string_labels.Add (string_value, sl); + } else { + if (sl.Converted is NullLiteral) { + case_null = sl; + } else { + labels.Add (sl.Converted.GetValueAsLong (), sl); + } + } + } catch (ArgumentException) { + if (string_labels != null) + sl.Error_AlreadyOccurs (rc, string_labels[(string) sl.Converted.GetValue ()]); + else + sl.Error_AlreadyOccurs (rc, labels[sl.Converted.GetValueAsLong ()]); + } + } + + // + // This method emits code for a lookup-based switch statement (non-string) + // Basically it groups the cases into blocks that are at least half full, + // and then spits out individual lookup opcodes for each block. + // It emits the longest blocks first, and short blocks are just + // handled with direct compares. + // + void EmitTableSwitch (EmitContext ec, Expression val) + { + if (labels != null && labels.Count > 0) { + List ranges; + if (string_labels != null) { + // We have done all hard work for string already + // setup single range only + ranges = new List (1); + ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys)); + } else { + var element_keys = new long[labels.Count]; + labels.Keys.CopyTo (element_keys, 0); + Array.Sort (element_keys); + + // + // Build possible ranges of switch labes to reduce number + // of comparisons + // + ranges = new List (element_keys.Length); + var range = new LabelsRange (element_keys[0]); + ranges.Add (range); + for (int i = 1; i < element_keys.Length; ++i) { + var l = element_keys[i]; + if (range.AddValue (l)) + continue; + + range = new LabelsRange (l); + ranges.Add (range); + } + + // sort the blocks so we can tackle the largest ones first + ranges.Sort (); + } + + Label lbl_default = defaultLabel; + TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType; + + for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) { + LabelsRange kb = ranges[range_index]; + lbl_default = (range_index == 0) ? defaultLabel : ec.DefineLabel (); + + // Optimize small ranges using simple equality check + if (kb.Range <= 2) { + foreach (var key in kb.label_values) { + SwitchLabel sl = labels[key]; + if (sl == case_default || sl == case_null) + continue; + + if (sl.Converted.IsZeroInteger) { + val.EmitBranchable (ec, sl.GetILLabel (ec), false); + } else { + val.Emit (ec); + sl.Converted.Emit (ec); + ec.Emit (OpCodes.Beq, sl.GetILLabel (ec)); + } + } + } else { + // TODO: if all the keys in the block are the same and there are + // no gaps/defaults then just use a range-check. + if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) { + // TODO: optimize constant/I4 cases + + // check block range (could be > 2^31) + val.Emit (ec); + ec.EmitLong (kb.min); + ec.Emit (OpCodes.Blt, lbl_default); + + val.Emit (ec); + ec.EmitLong (kb.max); + ec.Emit (OpCodes.Bgt, lbl_default); + + // normalize range + val.Emit (ec); + if (kb.min != 0) { + ec.EmitLong (kb.min); + ec.Emit (OpCodes.Sub); + } + + ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels! + } else { + // normalize range + val.Emit (ec); + int first = (int) kb.min; + if (first > 0) { + ec.EmitInt (first); + ec.Emit (OpCodes.Sub); + } else if (first < 0) { + ec.EmitInt (-first); + ec.Emit (OpCodes.Add); + } + } + + // first, build the list of labels for the switch + int iKey = 0; + long cJumps = kb.Range; + Label[] switch_labels = new Label[cJumps]; + for (int iJump = 0; iJump < cJumps; iJump++) { + var key = kb.label_values[iKey]; + if (key == kb.min + iJump) { + switch_labels[iJump] = labels[key].GetILLabel (ec); + iKey++; + } else { + switch_labels[iJump] = lbl_default; + } + } + + // emit the switch opcode + ec.Emit (OpCodes.Switch, switch_labels); + } + + // mark the default for this block + if (range_index != 0) + ec.MarkLabel (lbl_default); + } + + // the last default just goes to the end + if (ranges.Count > 0) + ec.Emit (OpCodes.Br, lbl_default); + } + } + + public SwitchLabel FindLabel (Constant value) + { + SwitchLabel sl = null; + + if (string_labels != null) { + string s = value.GetValue () as string; + if (s == null) { + if (case_null != null) + sl = case_null; + else if (case_default != null) + sl = case_default; + } else { + string_labels.TryGetValue (s, out sl); + } + } else { + if (value is NullLiteral) { + sl = case_null; + } else { + labels.TryGetValue (value.GetValueAsLong (), out sl); + } + } + + return sl; + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + Expr.FlowAnalysis (fc); + + var prev_switch = fc.SwitchInitialDefinitiveAssignment; + var InitialDefinitiveAssignment = fc.DefiniteAssignment; + fc.SwitchInitialDefinitiveAssignment = InitialDefinitiveAssignment; + + block.FlowAnalysis (fc); + + fc.SwitchInitialDefinitiveAssignment = prev_switch; + + if (end_reachable_das != null) { + var sections_das = DefiniteAssignmentBitSet.And (end_reachable_das); + InitialDefinitiveAssignment |= sections_das; + end_reachable_das = null; + } + + fc.DefiniteAssignment = InitialDefinitiveAssignment; + + return case_default != null && !end_reachable; + } + + public override bool Resolve (BlockContext ec) + { + Expr = Expr.Resolve (ec); + if (Expr == null) + return false; + + // + // LAMESPEC: User conversion from non-nullable governing type has a priority + // + new_expr = SwitchGoverningType (ec, Expr, false); + + if (new_expr == null) { + if (Expr.Type.IsNullableType) { + unwrap = Nullable.Unwrap.Create (Expr, false); + if (unwrap == null) + return false; + + // + // Unwrap + user conversion using non-nullable type is not allowed but user operator + // involving nullable Expr and nullable governing type is + // + new_expr = SwitchGoverningType (ec, unwrap, true); + } + } + + if (new_expr == null) { + if (Expr.Type != InternalType.ErrorType) { + ec.Report.Error (151, loc, + "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type", + Expr.Type.GetSignatureForError ()); + } + + return false; + } + + SwitchType = new_expr.Type; + if (SwitchType.IsNullableType) { + new_expr = unwrap = Nullable.Unwrap.Create (new_expr, true); + SwitchType = Nullable.NullableInfo.GetUnderlyingType (SwitchType); + } + + if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) { + ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type"); + return false; + } + + if (block.Statements.Count == 0) + return true; + + if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) { + string_labels = new Dictionary (); + } else { + labels = new Dictionary (); + } + + case_labels = new List (); + + var constant = new_expr as Constant; + + // + // Don't need extra variable for constant switch or switch with + // only default case + // + if (constant == null) { + // + // Store switch expression for comparison purposes + // + value = new_expr as VariableReference; + if (value == null && !HasOnlyDefaultSection ()) { + var current_block = ec.CurrentBlock; + ec.CurrentBlock = Block; + // Create temporary variable inside switch scope + value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc); + value.Resolve (ec); + ec.CurrentBlock = current_block; + } + } + + Switch old_switch = ec.Switch; + ec.Switch = this; + var parent_los = ec.EnclosingLoopOrSwitch; + ec.EnclosingLoopOrSwitch = this; + + var ok = Statement.Resolve (ec); + + ec.EnclosingLoopOrSwitch = parent_los; + ec.Switch = old_switch; + + // + // Check if all goto cases are valid. Needs to be done after switch + // is resolved because goto can jump forward in the scope. + // + if (goto_cases != null) { + foreach (var gc in goto_cases) { + if (gc.Item1 == null) { + if (DefaultLabel == null) { + Goto.Error_UnknownLabel (ec, "default", loc); + } + + continue; + } + + var sl = FindLabel (gc.Item2); + if (sl == null) { + Goto.Error_UnknownLabel (ec, "case " + gc.Item2.GetValueAsLiteral (), loc); + } else { + gc.Item1.Label = sl; + } + } + } + + if (!ok) + return false; + + if (constant == null && SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && string_labels.Count > 6) { + ResolveStringSwitchMap (ec); + } + + // + // Anonymous storey initialization has to happen before + // any generated switch dispatch + // + block.InsertStatement (0, new DispatchStatement (this)); + + return true; + } + + bool HasOnlyDefaultSection () + { + for (int i = 0; i < block.Statements.Count; ++i) { + var s = block.Statements[i] as SwitchLabel; + + if (s == null || s.IsDefault) + continue; + + return false; + } + + return true; + } + + public override Reachability MarkReachable (Reachability rc) + { + if (rc.IsUnreachable) + return rc; + + base.MarkReachable (rc); + + block.MarkReachableScope (rc); + + if (block.Statements.Count == 0) + return rc; + + SwitchLabel constant_label = null; + var constant = new_expr as Constant; + + if (constant != null) { + constant_label = FindLabel (constant) ?? case_default; + if (constant_label == null) { + block.Statements.RemoveAt (0); + return rc; + } + } + + var section_rc = new Reachability (); + SwitchLabel prev_label = null; + + for (int i = 0; i < block.Statements.Count; ++i) { + var s = block.Statements[i]; + var sl = s as SwitchLabel; + + if (sl != null && sl.SectionStart) { + // + // Section is marked already via goto case + // + if (!sl.IsUnreachable) { + section_rc = new Reachability (); + continue; + } + + if (constant_label != null && constant_label != sl) + section_rc = Reachability.CreateUnreachable (); + else if (section_rc.IsUnreachable) { + section_rc = new Reachability (); + } else { + if (prev_label != null) { + sl.SectionStart = false; + s = new MissingBreak (prev_label); + s.MarkReachable (rc); + block.Statements.Insert (i - 1, s); + ++i; + } + } + + prev_label = sl; + } + + section_rc = s.MarkReachable (section_rc); + } + + if (!section_rc.IsUnreachable && prev_label != null) { + prev_label.SectionStart = false; + var s = new MissingBreak (prev_label) { + FallOut = true + }; + + s.MarkReachable (rc); + block.Statements.Add (s); + } + + // + // Reachability can affect parent only when all possible paths are handled but + // we still need to run reachability check on switch body to check for fall-through + // + if (case_default == null && constant_label == null) + return rc; + + // + // We have at least one local exit from the switch + // + if (end_reachable) + return rc; + + return Reachability.CreateUnreachable (); + } + + public void RegisterGotoCase (GotoCase gotoCase, Constant value) + { + if (goto_cases == null) + goto_cases = new List> (); + + goto_cases.Add (Tuple.Create (gotoCase, value)); + } + + // + // Converts string switch into string hashtable + // + void ResolveStringSwitchMap (ResolveContext ec) + { + FullNamedExpression string_dictionary_type; + if (ec.Module.PredefinedTypes.Dictionary.Define ()) { + string_dictionary_type = new TypeExpression ( + ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec, + new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }), + loc); + } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) { + string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc); + } else { + ec.Module.PredefinedTypes.Dictionary.Resolve (); + return; + } + + var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer; + Field field = new Field (ctype, string_dictionary_type, + Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED, + new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null); + if (!field.Define ()) + return; + ctype.AddField (field); + + var init = new List (); + int counter = -1; + labels = new Dictionary (string_labels.Count); + string value = null; + + foreach (SwitchLabel sl in case_labels) { + + if (sl.SectionStart) + labels.Add (++counter, sl); + + if (sl == case_default || sl == case_null) + continue; + + value = (string) sl.Converted.GetValue (); + var init_args = new List (2); + init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location)); + + sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc); + init_args.Add (sl.Converted); + + init.Add (new CollectionElementInitializer (init_args, loc)); + } + + Arguments args = new Arguments (1); + args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc))); + Expression initializer = new NewInitialize (string_dictionary_type, args, + new CollectionOrObjectInitializers (init, loc), loc); + + switch_cache_field = new FieldExpr (field, loc); + string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec)); + } + + void DoEmitStringSwitch (EmitContext ec) + { + Label l_initialized = ec.DefineLabel (); + + // + // Skip initialization when value is null + // + value.EmitBranchable (ec, nullLabel, false); + + // + // Check if string dictionary is initialized and initialize + // + switch_cache_field.EmitBranchable (ec, l_initialized, true); + using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) { + string_dictionary.EmitStatement (ec); + } + ec.MarkLabel (l_initialized); + + LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int); + + ResolveContext rc = new ResolveContext (ec.MemberContext); + + if (switch_cache_field.Type.IsGeneric) { + Arguments get_value_args = new Arguments (2); + get_value_args.Add (new Argument (value)); + get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out)); + Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc); + if (get_item == null) + return; + + // + // A value was not found, go to default case + // + get_item.EmitBranchable (ec, defaultLabel, false); + } else { + Arguments get_value_args = new Arguments (1); + get_value_args.Add (new Argument (value)); + + Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc); + if (get_item == null) + return; + + LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object); + get_item_object.EmitAssign (ec, get_item, true, false); + ec.Emit (OpCodes.Brfalse, defaultLabel); + + ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable, + new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc); + + get_item_int.EmitStatement (ec); + get_item_object.Release (ec); + } + + EmitTableSwitch (ec, string_switch_variable); + string_switch_variable.Release (ec); + } + + // + // Emits switch using simple if/else comparison for small label count (4 + optional default) + // + void EmitShortSwitch (EmitContext ec) + { + MethodSpec equal_method = null; + if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) { + equal_method = ec.Module.PredefinedMembers.StringEqual.Resolve (loc); + } + + if (equal_method != null) { + value.EmitBranchable (ec, nullLabel, false); + } + + for (int i = 0; i < case_labels.Count; ++i) { + var label = case_labels [i]; + if (label == case_default || label == case_null) + continue; + + var constant = label.Converted; + + if (equal_method != null) { + value.Emit (ec); + constant.Emit (ec); + + var call = new CallEmitter (); + call.EmitPredefined (ec, equal_method, new Arguments (0)); + ec.Emit (OpCodes.Brtrue, label.GetILLabel (ec)); + continue; + } + + if (constant.IsZeroInteger && constant.Type.BuiltinType != BuiltinTypeSpec.Type.Long && constant.Type.BuiltinType != BuiltinTypeSpec.Type.ULong) { + value.EmitBranchable (ec, label.GetILLabel (ec), false); + continue; + } + + value.Emit (ec); + constant.Emit (ec); + ec.Emit (OpCodes.Beq, label.GetILLabel (ec)); + } + + ec.Emit (OpCodes.Br, defaultLabel); + } + + void EmitDispatch (EmitContext ec) + { + if (value == null) { + // + // Constant switch, we've already done the work if there is only 1 label + // referenced + // + int reachable = 0; + foreach (var sl in case_labels) { + if (sl.IsUnreachable) + continue; + + if (reachable++ > 0) { + var constant = (Constant) new_expr; + var constant_label = FindLabel (constant) ?? case_default; + + ec.Emit (OpCodes.Br, constant_label.GetILLabel (ec)); + break; + } + } + + return; + } + + if (string_dictionary != null) { + DoEmitStringSwitch (ec); + } else if (case_labels.Count < 4 || string_labels != null) { + EmitShortSwitch (ec); + } else { + EmitTableSwitch (ec, value); + } + } + + protected override void DoEmit (EmitContext ec) + { + // + // Setup the codegen context + // + Label old_end = ec.LoopEnd; + Switch old_switch = ec.Switch; + + ec.LoopEnd = ec.DefineLabel (); + ec.Switch = this; + + defaultLabel = case_default == null ? ec.LoopEnd : case_default.GetILLabel (ec); + nullLabel = case_null == null ? defaultLabel : case_null.GetILLabel (ec); + + if (value != null) { + ec.Mark (loc); + if (IsNullable) { + unwrap.EmitCheck (ec); + ec.Emit (OpCodes.Brfalse, nullLabel); + value.EmitAssign (ec, new_expr, false, false); + } else if (new_expr != value) { + value.EmitAssign (ec, new_expr, false, false); + } + + + // + // Next statement is compiler generated we don't need extra + // nop when we can use the statement for sequence point + // + ec.Mark (block.StartLocation); + block.IsCompilerGenerated = true; + } else { + new_expr.EmitSideEffect (ec); + } + + block.Emit (ec); + + // Restore context state. + ec.MarkLabel (ec.LoopEnd); + + // + // Restore the previous context + // + ec.LoopEnd = old_end; + ec.Switch = old_switch; + } + + protected override void CloneTo (CloneContext clonectx, Statement t) + { + Switch target = (Switch) t; + + target.Expr = Expr.Clone (clonectx); + target.Statement = target.block = (ExplicitBlock) block.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + + public override void AddEndDefiniteAssignment (FlowAnalysisContext fc) + { + if (case_default == null && !(new_expr is Constant)) + return; + + if (end_reachable_das == null) + end_reachable_das = new List (); + + end_reachable_das.Add (fc.DefiniteAssignment); + } + + public override void SetEndReachable () + { + end_reachable = true; + } + } + + // A place where execution can restart in a state machine + public abstract class ResumableStatement : Statement + { + bool prepared; + protected Label resume_point; + + public Label PrepareForEmit (EmitContext ec) + { + if (!prepared) { + prepared = true; + resume_point = ec.DefineLabel (); + } + return resume_point; + } + + public virtual Label PrepareForDispose (EmitContext ec, Label end) + { + return end; + } + + public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher) + { + } + } + + public abstract class TryFinallyBlock : ExceptionStatement + { + protected Statement stmt; + Label dispose_try_block; + bool prepared_for_dispose, emitted_dispose; + Method finally_host; + + protected TryFinallyBlock (Statement stmt, Location loc) + : base (loc) + { + this.stmt = stmt; + } + + #region Properties + + public Statement Statement { + get { + return stmt; + } + } + + #endregion + + protected abstract void EmitTryBody (EmitContext ec); + public abstract void EmitFinallyBody (EmitContext ec); + + public override Label PrepareForDispose (EmitContext ec, Label end) + { + if (!prepared_for_dispose) { + prepared_for_dispose = true; + dispose_try_block = ec.DefineLabel (); + } + return dispose_try_block; + } + + protected sealed override void DoEmit (EmitContext ec) + { + EmitTryBodyPrepare (ec); + EmitTryBody (ec); + + bool beginFinally = EmitBeginFinallyBlock (ec); + + Label start_finally = ec.DefineLabel (); + if (resume_points != null && beginFinally) { + var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod; + + ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally); + ec.Emit (OpCodes.Brfalse_S, start_finally); + ec.Emit (OpCodes.Endfinally); + } + + ec.MarkLabel (start_finally); + + if (finally_host != null) { + finally_host.Define (); + finally_host.PrepareEmit (); + finally_host.Emit (); + + // Now it's safe to add, to close it properly and emit sequence points + finally_host.Parent.AddMember (finally_host); + + var ce = new CallEmitter (); + ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc); + ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true); + } else { + EmitFinallyBody (ec); + } + + if (beginFinally) + ec.EndExceptionBlock (); + } + + public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher) + { + if (emitted_dispose) + return; + + emitted_dispose = true; + + Label end_of_try = ec.DefineLabel (); + + // Ensure that the only way we can get into this code is through a dispatcher + if (have_dispatcher) + ec.Emit (OpCodes.Br, end); + + ec.BeginExceptionBlock (); + + ec.MarkLabel (dispose_try_block); + + Label[] labels = null; + for (int i = 0; i < resume_points.Count; ++i) { + ResumableStatement s = resume_points[i]; + Label ret = s.PrepareForDispose (ec, end_of_try); + if (ret.Equals (end_of_try) && labels == null) + continue; + if (labels == null) { + labels = new Label[resume_points.Count]; + for (int j = 0; j < i; ++j) + labels[j] = end_of_try; + } + labels[i] = ret; + } + + if (labels != null) { + int j; + for (j = 1; j < labels.Length; ++j) + if (!labels[0].Equals (labels[j])) + break; + bool emit_dispatcher = j < labels.Length; + + if (emit_dispatcher) { + ec.Emit (OpCodes.Ldloc, pc); + ec.EmitInt (first_resume_pc); + ec.Emit (OpCodes.Sub); + ec.Emit (OpCodes.Switch, labels); + } + + foreach (ResumableStatement s in resume_points) + s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher); + } + + ec.MarkLabel (end_of_try); + + ec.BeginFinallyBlock (); + + if (finally_host != null) { + var ce = new CallEmitter (); + ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc); + ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true); + } else { + EmitFinallyBody (ec); + } + + ec.EndExceptionBlock (); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + var res = stmt.FlowAnalysis (fc); + parent = null; + return res; + } + + protected virtual bool EmitBeginFinallyBlock (EmitContext ec) + { + ec.BeginFinallyBlock (); + return true; + } + + public override Reachability MarkReachable (Reachability rc) + { + base.MarkReachable (rc); + return Statement.MarkReachable (rc); + } + + public override bool Resolve (BlockContext bc) + { + bool ok; + + parent = bc.CurrentTryBlock; + bc.CurrentTryBlock = this; + + using (bc.Set (ResolveContext.Options.TryScope)) { + ok = stmt.Resolve (bc); + } + + bc.CurrentTryBlock = parent; + + // + // Finally block inside iterator is called from MoveNext and + // Dispose methods that means we need to lift the block into + // newly created host method to emit the body only once. The + // original block then simply calls the newly generated method. + // + if (bc.CurrentIterator != null && !bc.IsInProbingMode) { + var b = stmt as Block; + if (b != null && b.Explicit.HasYield) { + finally_host = bc.CurrentIterator.CreateFinallyHost (this); + } + } + + return base.Resolve (bc) && ok; + } + } + + // + // Base class for blocks using exception handling + // + public abstract class ExceptionStatement : ResumableStatement + { + protected List resume_points; + protected int first_resume_pc; + protected ExceptionStatement parent; + + protected ExceptionStatement (Location loc) + { + this.loc = loc; + } + + protected virtual void EmitTryBodyPrepare (EmitContext ec) + { + StateMachineInitializer state_machine = null; + if (resume_points != null) { + state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod; + + ec.EmitInt ((int) IteratorStorey.State.Running); + ec.Emit (OpCodes.Stloc, state_machine.CurrentPC); + } + + ec.BeginExceptionBlock (); + + if (resume_points != null) { + ec.MarkLabel (resume_point); + + // For normal control flow, we want to fall-through the Switch + // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above + ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC); + ec.EmitInt (first_resume_pc); + ec.Emit (OpCodes.Sub); + + Label[] labels = new Label[resume_points.Count]; + for (int i = 0; i < resume_points.Count; ++i) + labels[i] = resume_points[i].PrepareForEmit (ec); + ec.Emit (OpCodes.Switch, labels); + } + } + + public virtual int AddResumePoint (ResumableStatement stmt, int pc, StateMachineInitializer stateMachine) + { + if (parent != null) { + // TODO: MOVE to virtual TryCatch + var tc = this as TryCatch; + var s = tc != null && tc.IsTryCatchFinally ? stmt : this; + + pc = parent.AddResumePoint (s, pc, stateMachine); + } else { + pc = stateMachine.AddResumePoint (this); + } + + if (resume_points == null) { + resume_points = new List (); + first_resume_pc = pc; + } + + if (pc != first_resume_pc + resume_points.Count) + throw new InternalErrorException ("missed an intervening AddResumePoint?"); + + resume_points.Add (stmt); + return pc; + } + } + + public class Lock : TryFinallyBlock + { + Expression expr; + TemporaryVariableReference expr_copy; + TemporaryVariableReference lock_taken; + + public Lock (Expression expr, Statement stmt, Location loc) + : base (stmt, loc) + { + this.expr = expr; + } + + public Expression Expr { + get { + return this.expr; + } + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + expr.FlowAnalysis (fc); + return base.DoFlowAnalysis (fc); + } + + public override bool Resolve (BlockContext ec) + { + expr = expr.Resolve (ec); + if (expr == null) + return false; + + if (!TypeSpec.IsReferenceType (expr.Type)) { + ec.Report.Error (185, loc, + "`{0}' is not a reference type as required by the lock statement", + expr.Type.GetSignatureForError ()); + } + + if (expr.Type.IsGenericParameter) { + expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object); + } + + VariableReference lv = expr as VariableReference; + bool locked; + if (lv != null) { + locked = lv.IsLockedByStatement; + lv.IsLockedByStatement = true; + } else { + lv = null; + locked = false; + } + + // + // Have to keep original lock value around to unlock same location + // in the case of original value has changed or is null + // + expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc); + expr_copy.Resolve (ec); + + // + // Ensure Monitor methods are available + // + if (ResolvePredefinedMethods (ec) > 1) { + lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc); + lock_taken.Resolve (ec); + } + + using (ec.Set (ResolveContext.Options.LockScope)) { + base.Resolve (ec); + } + + if (lv != null) { + lv.IsLockedByStatement = locked; + } + + return true; + } + + protected override void EmitTryBodyPrepare (EmitContext ec) + { + expr_copy.EmitAssign (ec, expr); + + if (lock_taken != null) { + // + // Initialize ref variable + // + lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc)); + } else { + // + // Monitor.Enter (expr_copy) + // + expr_copy.Emit (ec); + ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ()); + } + + base.EmitTryBodyPrepare (ec); + } + + protected override void EmitTryBody (EmitContext ec) + { + // + // Monitor.Enter (expr_copy, ref lock_taken) + // + if (lock_taken != null) { + expr_copy.Emit (ec); + lock_taken.LocalInfo.CreateBuilder (ec); + lock_taken.AddressOf (ec, AddressOp.Load); + ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ()); + } + + Statement.Emit (ec); + } + + public override void EmitFinallyBody (EmitContext ec) + { + // + // if (lock_taken) Monitor.Exit (expr_copy) + // + Label skip = ec.DefineLabel (); + + if (lock_taken != null) { + lock_taken.Emit (ec); + ec.Emit (OpCodes.Brfalse_S, skip); + } + + expr_copy.Emit (ec); + var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc); + if (m != null) + ec.Emit (OpCodes.Call, m); + + ec.MarkLabel (skip); + } + + int ResolvePredefinedMethods (ResolveContext rc) + { + // Try 4.0 Monitor.Enter (object, ref bool) overload first + var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get (); + if (m != null) + return 4; + + m = rc.Module.PredefinedMembers.MonitorEnter.Get (); + if (m != null) + return 1; + + rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc); + return 0; + } + + protected override void CloneTo (CloneContext clonectx, Statement t) + { + Lock target = (Lock) t; + + target.expr = expr.Clone (clonectx); + target.stmt = Statement.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + + } + + public class Unchecked : Statement { + public Block Block; + + public Unchecked (Block b, Location loc) + { + Block = b; + b.Unchecked = true; + this.loc = loc; + } + + public override bool Resolve (BlockContext ec) + { + using (ec.With (ResolveContext.Options.AllCheckStateFlags, false)) + return Block.Resolve (ec); + } + + protected override void DoEmit (EmitContext ec) + { + using (ec.With (EmitContext.Options.CheckedScope, false)) + Block.Emit (ec); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + return Block.FlowAnalysis (fc); + } + + public override Reachability MarkReachable (Reachability rc) + { + base.MarkReachable (rc); + return Block.MarkReachable (rc); + } + + protected override void CloneTo (CloneContext clonectx, Statement t) + { + Unchecked target = (Unchecked) t; + + target.Block = clonectx.LookupBlock (Block); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class Checked : Statement { + public Block Block; + + public Checked (Block b, Location loc) + { + Block = b; + b.Unchecked = false; + this.loc = loc; + } + + public override bool Resolve (BlockContext ec) + { + using (ec.With (ResolveContext.Options.AllCheckStateFlags, true)) + return Block.Resolve (ec); + } + + protected override void DoEmit (EmitContext ec) + { + using (ec.With (EmitContext.Options.CheckedScope, true)) + Block.Emit (ec); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + return Block.FlowAnalysis (fc); + } + + public override Reachability MarkReachable (Reachability rc) + { + base.MarkReachable (rc); + return Block.MarkReachable (rc); + } + + protected override void CloneTo (CloneContext clonectx, Statement t) + { + Checked target = (Checked) t; + + target.Block = clonectx.LookupBlock (Block); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class Unsafe : Statement { + public Block Block; + + public Unsafe (Block b, Location loc) + { + Block = b; + Block.Unsafe = true; + this.loc = loc; + } + + public override bool Resolve (BlockContext ec) + { + if (ec.CurrentIterator != null) + ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators"); + + using (ec.Set (ResolveContext.Options.UnsafeScope)) + return Block.Resolve (ec); + } + + protected override void DoEmit (EmitContext ec) + { + Block.Emit (ec); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + return Block.FlowAnalysis (fc); + } + + public override Reachability MarkReachable (Reachability rc) + { + base.MarkReachable (rc); + return Block.MarkReachable (rc); + } + + protected override void CloneTo (CloneContext clonectx, Statement t) + { + Unsafe target = (Unsafe) t; + + target.Block = clonectx.LookupBlock (Block); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + // + // Fixed statement + // + public class Fixed : Statement + { + abstract class Emitter : ShimExpression + { + protected LocalVariable vi; + + protected Emitter (Expression expr, LocalVariable li) + : base (expr) + { + vi = li; + } + + public abstract void EmitExit (EmitContext ec); + + public override void FlowAnalysis (FlowAnalysisContext fc) + { + expr.FlowAnalysis (fc); + } + } + + class ExpressionEmitter : Emitter { + public ExpressionEmitter (Expression converted, LocalVariable li) : + base (converted, li) + { + } + + protected override Expression DoResolve (ResolveContext rc) + { + throw new NotImplementedException (); + } + + public override void Emit (EmitContext ec) { + // + // Store pointer in pinned location + // + expr.Emit (ec); + vi.EmitAssign (ec); + } + + public override void EmitExit (EmitContext ec) + { + ec.EmitInt (0); + ec.Emit (OpCodes.Conv_U); + vi.EmitAssign (ec); + } + } + + class StringEmitter : Emitter + { + LocalVariable pinned_string; + + public StringEmitter (Expression expr, LocalVariable li) + : base (expr, li) + { + } + + protected override Expression DoResolve (ResolveContext rc) + { + pinned_string = new LocalVariable (vi.Block, "$pinned", + LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used, + vi.Location); + pinned_string.Type = rc.BuiltinTypes.String; + + eclass = ExprClass.Variable; + type = rc.BuiltinTypes.Int; + return this; + } + + public override void Emit (EmitContext ec) + { + pinned_string.CreateBuilder (ec); + + expr.Emit (ec); + pinned_string.EmitAssign (ec); + + // TODO: Should use Binary::Add + pinned_string.Emit (ec); + ec.Emit (OpCodes.Conv_I); + + var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc); + if (m == null) + return; + + PropertyExpr pe = new PropertyExpr (m, pinned_string.Location); + //pe.InstanceExpression = pinned_string; + pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec); + + ec.Emit (OpCodes.Add); + vi.EmitAssign (ec); + } + + public override void EmitExit (EmitContext ec) + { + ec.EmitNull (); + pinned_string.EmitAssign (ec); + } + } + + public class VariableDeclaration : BlockVariable + { + public VariableDeclaration (FullNamedExpression type, LocalVariable li) + : base (type, li) + { + } + + protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer) + { + if (!Variable.Type.IsPointer && li == Variable) { + bc.Report.Error (209, TypeExpression.Location, + "The type of locals declared in a fixed statement must be a pointer type"); + return null; + } + + // + // The rules for the possible declarators are pretty wise, + // but the production on the grammar is more concise. + // + // So we have to enforce these rules here. + // + // We do not resolve before doing the case 1 test, + // because the grammar is explicit in that the token & + // is present, so we need to test for this particular case. + // + + if (initializer is Cast) { + bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression"); + return null; + } + + initializer = initializer.Resolve (bc); + + if (initializer == null) + return null; + + // + // Case 1: Array + // + if (initializer.Type.IsArray) { + TypeSpec array_type = TypeManager.GetElementType (initializer.Type); + + // + // Provided that array_type is unmanaged, + // + if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc)) + return null; + + // + // and T* is implicitly convertible to the + // pointer type given in the fixed statement. + // + ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc); + + Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc); + if (converted == null) + return null; + + // + // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0]) + // + converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr, + new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc)), + new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))), + new NullLiteral (loc), + converted, loc); + + converted = converted.Resolve (bc); + + return new ExpressionEmitter (converted, li); + } + + // + // Case 2: string + // + if (initializer.Type.BuiltinType == BuiltinTypeSpec.Type.String) { + return new StringEmitter (initializer, li).Resolve (bc); + } + + // Case 3: fixed buffer + if (initializer is FixedBufferPtr) { + return new ExpressionEmitter (initializer, li); + } + + // + // Case 4: & object. + // + bool already_fixed = true; + Unary u = initializer as Unary; + if (u != null && u.Oper == Unary.Operator.AddressOf) { + IVariableReference vr = u.Expr as IVariableReference; + if (vr == null || !vr.IsFixed) { + already_fixed = false; + } + } + + if (already_fixed) { + bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression"); + } + + initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc); + return new ExpressionEmitter (initializer, li); + } + } + + + VariableDeclaration decl; + Statement statement; + bool has_ret; + + public Fixed (VariableDeclaration decl, Statement stmt, Location l) + { + this.decl = decl; + statement = stmt; + loc = l; + } + + #region Properties + + public Statement Statement { + get { + return statement; + } + } + + public BlockVariable Variables { + get { + return decl; + } + } + + #endregion + + public override bool Resolve (BlockContext bc) + { + using (bc.Set (ResolveContext.Options.FixedInitializerScope)) { + if (!decl.Resolve (bc)) + return false; + } + + return statement.Resolve (bc); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + decl.FlowAnalysis (fc); + return statement.FlowAnalysis (fc); + } + + protected override void DoEmit (EmitContext ec) + { + decl.Variable.CreateBuilder (ec); + decl.Initializer.Emit (ec); + if (decl.Declarators != null) { + foreach (var d in decl.Declarators) { + d.Variable.CreateBuilder (ec); + d.Initializer.Emit (ec); + } + } + + statement.Emit (ec); + + if (has_ret) + return; + + // + // Clear the pinned variable + // + ((Emitter) decl.Initializer).EmitExit (ec); + if (decl.Declarators != null) { + foreach (var d in decl.Declarators) { + ((Emitter)d.Initializer).EmitExit (ec); + } + } + } + + public override Reachability MarkReachable (Reachability rc) + { + base.MarkReachable (rc); + + decl.MarkReachable (rc); + + rc = statement.MarkReachable (rc); + + // TODO: What if there is local exit? + has_ret = rc.IsUnreachable; + return rc; + } + + protected override void CloneTo (CloneContext clonectx, Statement t) + { + Fixed target = (Fixed) t; + + target.decl = (VariableDeclaration) decl.Clone (clonectx); + target.statement = statement.Clone (clonectx); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + + public class Catch : Statement + { + class FilterStatement : Statement + { + readonly Catch ctch; + + public FilterStatement (Catch ctch) + { + this.ctch = ctch; + } + + protected override void CloneTo (CloneContext clonectx, Statement target) + { + } + + protected override void DoEmit (EmitContext ec) + { + if (ctch.li != null) { + if (ctch.hoisted_temp != null) + ctch.hoisted_temp.Emit (ec); + else + ctch.li.Emit (ec); + + if (!ctch.IsGeneral && ctch.type.Kind == MemberKind.TypeParameter) + ec.Emit (OpCodes.Box, ctch.type); + } + + var expr_start = ec.DefineLabel (); + var end = ec.DefineLabel (); + + ec.Emit (OpCodes.Brtrue_S, expr_start); + ec.EmitInt (0); + ec.Emit (OpCodes.Br, end); + ec.MarkLabel (expr_start); + + ctch.Filter.Emit (ec); + + ec.MarkLabel (end); + ec.Emit (OpCodes.Endfilter); + ec.BeginFilterHandler (); + ec.Emit (OpCodes.Pop); + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + ctch.Filter.FlowAnalysis (fc); + return true; + } + + public override bool Resolve (BlockContext bc) + { + ctch.Filter = ctch.Filter.Resolve (bc); + + if (ctch.Filter != null) { + if (ctch.Filter.ContainsEmitWithAwait ()) { + bc.Report.Error (7094, ctch.Filter.Location, "The `await' operator cannot be used in the filter expression of a catch clause"); + } + + var c = ctch.Filter as Constant; + if (c != null && !c.IsDefaultValue) { + bc.Report.Warning (7095, 1, ctch.Filter.Location, "Exception filter expression is a constant"); + } + } + + return true; + } + } + + ExplicitBlock block; + LocalVariable li; + FullNamedExpression type_expr; + CompilerAssign assign; + TypeSpec type; + LocalTemporary hoisted_temp; + + public Catch (ExplicitBlock block, Location loc) + { + this.block = block; + this.loc = loc; + } + + #region Properties + + public ExplicitBlock Block { + get { + return block; + } + } + + public TypeSpec CatchType { + get { + return type; + } + } + + public Expression Filter { + get; set; + } + + public bool IsGeneral { + get { + return type_expr == null; + } + } + + public FullNamedExpression TypeExpression { + get { + return type_expr; + } + set { + type_expr = value; + } + } + + public LocalVariable Variable { + get { + return li; + } + set { + li = value; + } + } + + #endregion + + protected override void DoEmit (EmitContext ec) + { + if (Filter != null) { + ec.BeginExceptionFilterBlock (); + ec.Emit (OpCodes.Isinst, IsGeneral ? ec.BuiltinTypes.Object : CatchType); + + if (li != null) + EmitCatchVariableStore (ec); + + if (Block.HasAwait) { + Block.EmitScopeInitialization (ec); + } else { + Block.Emit (ec); + } + + return; + } + + if (IsGeneral) + ec.BeginCatchBlock (ec.BuiltinTypes.Object); + else + ec.BeginCatchBlock (CatchType); + + if (li != null) { + EmitCatchVariableStore (ec); + } else { + ec.Emit (OpCodes.Pop); + } + + if (!Block.HasAwait) + Block.Emit (ec); + } + + void EmitCatchVariableStore (EmitContext ec) + { + li.CreateBuilder (ec); + + // + // Special case hoisted catch variable, we have to use a temporary variable + // to pass via anonymous storey initialization with the value still on top + // of the stack + // + if (li.HoistedVariant != null) { + hoisted_temp = new LocalTemporary (li.Type); + hoisted_temp.Store (ec); + + // switch to assignment from temporary variable and not from top of the stack + assign.UpdateSource (hoisted_temp); + } + } + + public override bool Resolve (BlockContext bc) + { + using (bc.Set (ResolveContext.Options.CatchScope)) { + if (type_expr == null) { + if (CreateExceptionVariable (bc.Module.Compiler.BuiltinTypes.Object)) { + Expression source = new EmptyExpression (li.Type); + assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null); + Block.AddScopeStatement (new StatementExpression (assign, Location.Null)); + } + } else { + type = type_expr.ResolveAsType (bc); + if (type == null) + return false; + + if (li == null) + CreateExceptionVariable (type); + + if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, bc.BuiltinTypes.Exception, false)) { + bc.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception"); + } else if (li != null) { + li.Type = type; + li.PrepareAssignmentAnalysis (bc); + + // source variable is at the top of the stack + Expression source = new EmptyExpression (li.Type); + if (li.Type.IsGenericParameter) + source = new UnboxCast (source, li.Type); + + // + // Uses Location.Null to hide from symbol file + // + assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null); + Block.AddScopeStatement (new StatementExpression (assign, Location.Null)); + } + } + + if (Filter != null) { + Block.AddScopeStatement (new FilterStatement (this)); + } + + Block.SetCatchBlock (); + return Block.Resolve (bc); + } + } + + bool CreateExceptionVariable (TypeSpec type) + { + if (!Block.HasAwait) + return false; + + // TODO: Scan the block for rethrow expression + //if (!Block.HasRethrow) + // return; + + li = LocalVariable.CreateCompilerGenerated (type, block, Location.Null); + return true; + } + + protected override bool DoFlowAnalysis (FlowAnalysisContext fc) + { + if (li != null && !li.IsCompilerGenerated) { + fc.SetVariableAssigned (li.VariableInfo, true); + } + + return block.FlowAnalysis (fc); + } + + public override Reachability MarkReachable (Reachability rc) + { + base.MarkReachable (rc); + + var c = Filter as Constant; + if (c != null && c.IsDefaultValue) + return Reachability.CreateUnreachable (); + + return block.MarkReachable (rc); + } + + protected override void CloneTo (CloneContext clonectx, Statement t) + { + Catch target = (Catch) t; + + if (type_expr != null) + target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx); + + if (Filter != null) + target.Filter = Filter.Clone (clonectx); + + target.block = (ExplicitBlock) clonectx.LookupBlock (block); + } + } + + public class TryFinally : TryFinallyBlock + { + ExplicitBlock fini; + List try_exit_dat; + List