PageRenderTime 62ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 0ms

/Python/Product/Analysis/Parsing/Ast/FunctionDefinition.cs

https://gitlab.com/SplatoonModdingHub/PTVS
C# | 372 lines | 293 code | 45 blank | 34 comment | 70 complexity | fef1304460eb62f4d355a415140ee180 MD5 | raw file
  1. // Python Tools for Visual Studio
  2. // Copyright(c) Microsoft Corporation
  3. // All rights reserved.
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the License); you may not use
  6. // this file except in compliance with the License. You may obtain a copy of the
  7. // License at http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
  10. // OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
  11. // IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
  12. // MERCHANTABLITY OR NON-INFRINGEMENT.
  13. //
  14. // See the Apache Version 2.0 License for specific language governing
  15. // permissions and limitations under the License.
  16. using System;
  17. using System.Collections.Generic;
  18. using System.Text;
  19. namespace Microsoft.PythonTools.Parsing.Ast {
  20. public class FunctionDefinition : ScopeStatement {
  21. protected Statement _body;
  22. private readonly NameExpression/*!*/ _name;
  23. private readonly Parameter[] _parameters;
  24. private Expression _returnAnnotation;
  25. private DecoratorStatement _decorators;
  26. private bool _generator; // The function is a generator
  27. private bool _coroutine;
  28. private bool _isLambda;
  29. private PythonVariable _variable; // The variable corresponding to the function name or null for lambdas
  30. internal PythonVariable _nameVariable; // the variable that refers to the global __name__
  31. internal bool _hasReturn;
  32. private int _headerIndex;
  33. internal static readonly object WhitespaceAfterAsync = new object();
  34. public FunctionDefinition(NameExpression name, Parameter[] parameters)
  35. : this(name, parameters, (Statement)null) {
  36. }
  37. public FunctionDefinition(NameExpression name, Parameter[] parameters, Statement body, DecoratorStatement decorators = null) {
  38. if (name == null) {
  39. _name = new NameExpression("<lambda>");
  40. _isLambda = true;
  41. } else {
  42. _name = name;
  43. }
  44. _parameters = parameters;
  45. _body = body;
  46. _decorators = decorators;
  47. }
  48. public bool IsLambda {
  49. get {
  50. return _isLambda;
  51. }
  52. }
  53. public IList<Parameter> Parameters {
  54. get { return _parameters; }
  55. }
  56. internal override int ArgCount {
  57. get {
  58. return _parameters.Length;
  59. }
  60. }
  61. public Expression ReturnAnnotation {
  62. get { return _returnAnnotation; }
  63. set { _returnAnnotation = value; }
  64. }
  65. public override Statement Body {
  66. get { return _body; }
  67. }
  68. internal void SetBody(Statement body) {
  69. _body = body;
  70. }
  71. public int HeaderIndex {
  72. get { return _headerIndex; }
  73. set { _headerIndex = value; }
  74. }
  75. public override string/*!*/ Name {
  76. get { return _name.Name ?? ""; }
  77. }
  78. public NameExpression NameExpression {
  79. get { return _name; }
  80. }
  81. public DecoratorStatement Decorators {
  82. get { return _decorators; }
  83. internal set { _decorators = value; }
  84. }
  85. /// <summary>
  86. /// True if the function is a generator. Generators contain at least one yield
  87. /// expression and instead of returning a value when called they return a generator
  88. /// object which implements the iterator protocol.
  89. /// </summary>
  90. public bool IsGenerator {
  91. get { return _generator; }
  92. set { _generator = value; }
  93. }
  94. /// <summary>
  95. /// True if the function is a coroutine. Coroutines are defined using
  96. /// 'async def'.
  97. /// </summary>
  98. public bool IsCoroutine {
  99. get { return _coroutine; }
  100. set { _coroutine = value; }
  101. }
  102. /// <summary>
  103. /// Gets the variable that this function is assigned to.
  104. /// </summary>
  105. public PythonVariable Variable {
  106. get { return _variable; }
  107. set { _variable = value; }
  108. }
  109. /// <summary>
  110. /// Gets the variable reference for the specific assignment to the variable for this function definition.
  111. /// </summary>
  112. public PythonReference GetVariableReference(PythonAst ast) {
  113. return GetVariableReference(this, ast);
  114. }
  115. internal override bool ExposesLocalVariable(PythonVariable variable) {
  116. return NeedsLocalsDictionary;
  117. }
  118. internal override bool TryBindOuter(ScopeStatement from, string name, bool allowGlobals, out PythonVariable variable) {
  119. // Functions expose their locals to direct access
  120. ContainsNestedFreeVariables = true;
  121. if (TryGetVariable(name, out variable)) {
  122. variable.AccessedInNestedScope = true;
  123. if (variable.Kind == VariableKind.Local || variable.Kind == VariableKind.Parameter) {
  124. from.AddFreeVariable(variable, true);
  125. for (ScopeStatement scope = from.Parent; scope != this; scope = scope.Parent) {
  126. scope.AddFreeVariable(variable, false);
  127. }
  128. AddCellVariable(variable);
  129. } else if(allowGlobals) {
  130. from.AddReferencedGlobal(name);
  131. }
  132. return true;
  133. }
  134. return false;
  135. }
  136. internal override PythonVariable BindReference(PythonNameBinder binder, string name) {
  137. PythonVariable variable;
  138. // First try variables local to this scope
  139. if (TryGetVariable(name, out variable) && variable.Kind != VariableKind.Nonlocal) {
  140. if (variable.Kind == VariableKind.Global) {
  141. AddReferencedGlobal(name);
  142. }
  143. return variable;
  144. }
  145. // Try to bind in outer scopes
  146. for (ScopeStatement parent = Parent; parent != null; parent = parent.Parent) {
  147. if (parent.TryBindOuter(this, name, true, out variable)) {
  148. return variable;
  149. }
  150. }
  151. return null;
  152. }
  153. internal override void Bind(PythonNameBinder binder) {
  154. base.Bind(binder);
  155. Verify(binder);
  156. }
  157. private void Verify(PythonNameBinder binder) {
  158. if (ContainsImportStar && IsClosure) {
  159. binder.ReportSyntaxError(
  160. String.Format(
  161. System.Globalization.CultureInfo.InvariantCulture,
  162. "import * is not allowed in function '{0}' because it is a nested function",
  163. Name),
  164. this);
  165. }
  166. if (ContainsImportStar && Parent is FunctionDefinition) {
  167. binder.ReportSyntaxError(
  168. String.Format(
  169. System.Globalization.CultureInfo.InvariantCulture,
  170. "import * is not allowed in function '{0}' because it is a nested function",
  171. Name),
  172. this);
  173. }
  174. if (ContainsImportStar && ContainsNestedFreeVariables) {
  175. binder.ReportSyntaxError(
  176. String.Format(
  177. System.Globalization.CultureInfo.InvariantCulture,
  178. "import * is not allowed in function '{0}' because it contains a nested function with free variables",
  179. Name),
  180. this);
  181. }
  182. if (ContainsUnqualifiedExec && ContainsNestedFreeVariables) {
  183. binder.ReportSyntaxError(
  184. String.Format(
  185. System.Globalization.CultureInfo.InvariantCulture,
  186. "unqualified exec is not allowed in function '{0}' because it contains a nested function with free variables",
  187. Name),
  188. this);
  189. }
  190. if (ContainsUnqualifiedExec && IsClosure) {
  191. binder.ReportSyntaxError(
  192. String.Format(
  193. System.Globalization.CultureInfo.InvariantCulture,
  194. "unqualified exec is not allowed in function '{0}' because it is a nested function",
  195. Name),
  196. this);
  197. }
  198. }
  199. public override void Walk(PythonWalker walker) {
  200. if (walker.Walk(this)) {
  201. if (_parameters != null) {
  202. foreach (Parameter p in _parameters) {
  203. p.Walk(walker);
  204. }
  205. }
  206. if (_decorators != null) {
  207. _decorators.Walk(walker);
  208. }
  209. if (_body != null) {
  210. _body.Walk(walker);
  211. }
  212. if (_returnAnnotation != null) {
  213. _returnAnnotation.Walk(walker);
  214. }
  215. }
  216. walker.PostWalk(this);
  217. }
  218. public SourceLocation Header {
  219. get { return GlobalParent.IndexToLocation(_headerIndex); }
  220. }
  221. public override string GetLeadingWhiteSpace(PythonAst ast) {
  222. if (Decorators != null) {
  223. return Decorators.GetLeadingWhiteSpace(ast);
  224. }
  225. return base.GetLeadingWhiteSpace(ast);
  226. }
  227. public override void SetLeadingWhiteSpace(PythonAst ast, string whiteSpace) {
  228. if (Decorators != null) {
  229. Decorators.SetLeadingWhiteSpace(ast, whiteSpace);
  230. return;
  231. }
  232. base.SetLeadingWhiteSpace(ast, whiteSpace);
  233. }
  234. internal override void AppendCodeStringStmt(StringBuilder res, PythonAst ast, CodeFormattingOptions format) {
  235. var decorateWhiteSpace = this.GetNamesWhiteSpace(ast);
  236. if (Decorators != null) {
  237. Decorators.AppendCodeString(res, ast, format);
  238. }
  239. format.ReflowComment(res, this.GetProceedingWhiteSpaceDefaultNull(ast));
  240. if (IsCoroutine) {
  241. res.Append("async");
  242. res.Append(NodeAttributes.GetWhiteSpace(this, ast, WhitespaceAfterAsync));
  243. }
  244. res.Append("def");
  245. var name = this.GetVerbatimImage(ast) ?? Name;
  246. if (!String.IsNullOrEmpty(name)) {
  247. res.Append(this.GetSecondWhiteSpace(ast));
  248. res.Append(name);
  249. if (!this.IsIncompleteNode(ast)) {
  250. format.Append(
  251. res,
  252. format.SpaceBeforeFunctionDeclarationParen,
  253. " ",
  254. "",
  255. this.GetThirdWhiteSpaceDefaultNull(ast)
  256. );
  257. res.Append('(');
  258. if (Parameters.Count != 0) {
  259. var commaWhiteSpace = this.GetListWhiteSpace(ast);
  260. ParamsToString(res,
  261. ast,
  262. commaWhiteSpace,
  263. format,
  264. format.SpaceWithinFunctionDeclarationParens != null ?
  265. format.SpaceWithinFunctionDeclarationParens.Value ? " " : "" :
  266. null
  267. );
  268. }
  269. string namedOnly = this.GetExtraVerbatimText(ast);
  270. if (namedOnly != null) {
  271. res.Append(namedOnly);
  272. }
  273. if (!this.IsMissingCloseGrouping(ast)) {
  274. format.Append(
  275. res,
  276. Parameters.Count != 0 ?
  277. format.SpaceWithinFunctionDeclarationParens :
  278. format.SpaceWithinEmptyParameterList,
  279. " ",
  280. "",
  281. this.GetFourthWhiteSpaceDefaultNull(ast)
  282. );
  283. res.Append(')');
  284. }
  285. if (ReturnAnnotation != null) {
  286. format.Append(
  287. res,
  288. format.SpaceAroundAnnotationArrow,
  289. " ",
  290. "",
  291. this.GetFifthWhiteSpace(ast)
  292. );
  293. res.Append("->");
  294. _returnAnnotation.AppendCodeString(
  295. res,
  296. ast,
  297. format,
  298. format.SpaceAroundAnnotationArrow != null ?
  299. format.SpaceAroundAnnotationArrow.Value ? " " : "" :
  300. null
  301. );
  302. }
  303. if (Body != null) {
  304. Body.AppendCodeString(res, ast, format);
  305. }
  306. }
  307. }
  308. }
  309. internal void ParamsToString(StringBuilder res, PythonAst ast, string[] commaWhiteSpace, CodeFormattingOptions format, string initialLeadingWhiteSpace = null) {
  310. for (int i = 0; i < Parameters.Count; i++) {
  311. if (i > 0) {
  312. if (commaWhiteSpace != null) {
  313. res.Append(commaWhiteSpace[i - 1]);
  314. }
  315. res.Append(',');
  316. }
  317. Parameters[i].AppendCodeString(res, ast, format, initialLeadingWhiteSpace);
  318. initialLeadingWhiteSpace = null;
  319. }
  320. if (commaWhiteSpace != null && commaWhiteSpace.Length == Parameters.Count && Parameters.Count != 0) {
  321. // trailing comma
  322. res.Append(commaWhiteSpace[commaWhiteSpace.Length - 1]);
  323. res.Append(",");
  324. }
  325. }
  326. }
  327. }