Merge pull request #2462 from hvitved/csharp/localvars-refactor

C#: Handle tuple patterns in `is` expressions
This commit is contained in:
Calum Grant
2019-12-06 12:59:14 +00:00
committed by GitHub
11 changed files with 71 additions and 134 deletions

View File

@@ -24,7 +24,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
if (cx.GetModel(syntax).GetDeclaredSymbol(designation) is ILocalSymbol symbol)
{
var type = Type.Create(cx, symbol.GetAnnotatedType());
return VariableDeclaration.Create(cx, symbol, type, declPattern.Type, cx.Create(syntax.GetLocation()), cx.Create(designation.GetLocation()), false, parent, child);
return VariableDeclaration.Create(cx, symbol, type, declPattern.Type, cx.Create(syntax.GetLocation()), false, parent, child);
}
if (designation is DiscardDesignationSyntax)
{
@@ -48,7 +48,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
{
var type = Type.Create(cx, symbol.GetAnnotatedType());
return VariableDeclaration.Create(cx, symbol, type, null, cx.Create(syntax.GetLocation()), cx.Create(varDesignation.GetLocation()), false, parent, child);
return VariableDeclaration.Create(cx, symbol, type, null, cx.Create(syntax.GetLocation()), true, parent, child);
}
else
{
@@ -117,7 +117,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
{
var type = Entities.Type.Create(cx, symbol.GetAnnotatedType());
VariableDeclaration.Create(cx, symbol, type, null, cx.Create(syntax.GetLocation()), cx.Create(designation.GetLocation()), false, this, 0);
VariableDeclaration.Create(cx, symbol, type, null, cx.Create(syntax.GetLocation()), false, this, 0);
}
if (syntax.PositionalPatternClause is PositionalPatternClauseSyntax posPc)
@@ -138,38 +138,10 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
{
}
private void PopulatePattern(PatternSyntax pattern, TypeSyntax optionalType, SyntaxToken varKeyword, VariableDesignationSyntax designation)
{
var isVar = optionalType is null;
if (!(designation is null) && cx.GetModel(pattern).GetDeclaredSymbol(designation) is ILocalSymbol symbol)
{
var type = Entities.Type.Create(cx, symbol.GetAnnotatedType());
VariableDeclaration.Create(cx, symbol, type, optionalType, cx.Create(pattern.GetLocation()), cx.Create(designation.GetLocation()), isVar, this, 1);
}
else if (!isVar)
Expressions.TypeAccess.Create(cx, optionalType, this, 1);
}
protected override void PopulateExpression(TextWriter trapFile)
{
Create(cx, Syntax.Expression, this, 0);
switch (Syntax.Pattern)
{
case ConstantPatternSyntax constantPattern:
Create(cx, constantPattern.Expression, this, 1);
return;
case VarPatternSyntax varPattern:
PopulatePattern(varPattern, null, varPattern.VarKeyword, varPattern.Designation);
return;
case DeclarationPatternSyntax declPattern:
PopulatePattern(declPattern, declPattern.Type, default(SyntaxToken), declPattern.Designation);
return;
case RecursivePatternSyntax recPattern:
new RecursivePattern(cx, recPattern, this, 1);
return;
default:
throw new InternalError(Syntax, "Is pattern not handled");
}
cx.CreatePattern(Syntax.Pattern, this, 1);
}
public static Expression Create(ExpressionNodeInfo info) => new IsPattern(info).TryPopulate();

View File

@@ -54,10 +54,10 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
case SymbolKind.Local:
case SymbolKind.RangeVariable:
return Access.Create(info, target, false, LocalVariable.GetAlreadyCreated(info.Context, target));
return Access.Create(info, target, false, LocalVariable.Create(info.Context, target));
case SymbolKind.Parameter:
return Access.Create(info, target, false, Parameter.GetAlreadyCreated(info.Context, (IParameterSymbol)target));
return Access.Create(info, target, false, Parameter.Create(info.Context, (IParameterSymbol)target));
case SymbolKind.Namespace:
return Access.Create(info, target, false, Namespace.Create(info.Context, (INamespaceSymbol)target));

View File

@@ -65,7 +65,6 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
protected Expression DeclareRangeVariable(Context cx, IExpressionParentEntity parent, int child, bool getElement, ISymbol variableSymbol, SyntaxToken name)
{
var type = Type.Create(cx, cx.GetType(Expr));
Extraction.Entities.Location nameLoc;
AnnotatedType declType;
TypeSyntax declTypeSyntax = null;
@@ -90,7 +89,6 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
declType,
declTypeSyntax,
cx.Create(node.GetLocation()),
nameLoc = cx.Create(name.GetLocation()),
true,
parent,
child
@@ -98,8 +96,9 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
Expression.Create(cx, Expr, decl, 0);
var nameLoc = cx.Create(name.GetLocation());
var access = new Expression(new ExpressionInfo(cx, type, nameLoc, ExprKind.LOCAL_VARIABLE_ACCESS, decl, 1, false, null));
cx.TrapWriter.Writer.expr_access(access, LocalVariable.GetAlreadyCreated(cx, variableSymbol));
cx.TrapWriter.Writer.expr_access(access, LocalVariable.Create(cx, variableSymbol));
return decl;
}

View File

@@ -11,12 +11,13 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
{
VariableDeclaration(IExpressionInfo info) : base(info) { }
public static VariableDeclaration Create(Context cx, ISymbol symbol, AnnotatedType type, TypeSyntax optionalSyntax, Extraction.Entities.Location exprLocation, Extraction.Entities.Location declLocation, bool isVar, IExpressionParentEntity parent, int child)
public static VariableDeclaration Create(Context cx, ISymbol symbol, AnnotatedType type, TypeSyntax optionalSyntax, Extraction.Entities.Location exprLocation, bool isVar, IExpressionParentEntity parent, int child)
{
var ret = new VariableDeclaration(new ExpressionInfo(cx, type, exprLocation, ExprKind.LOCAL_VAR_DECL, parent, child, false, null));
cx.Try(null, null, () =>
{
LocalVariable.Create(cx, symbol, ret, isVar, declLocation);
var l = LocalVariable.Create(cx, symbol);
l.PopulateManual(ret, isVar);
if (optionalSyntax != null)
TypeMention.Create(cx, optionalSyntax, parent, type);
});
@@ -25,20 +26,20 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
static VariableDeclaration CreateSingle(Context cx, DeclarationExpressionSyntax node, SingleVariableDesignationSyntax designation, IExpressionParentEntity parent, int child)
{
bool isVar = node.Type.IsVar;
var variableSymbol = cx.GetModel(designation).GetDeclaredSymbol(designation) as ILocalSymbol;
if (variableSymbol == null)
{
cx.ModelError(node, "Failed to determine local variable");
return Create(cx, node, NullType.Create(cx), isVar, parent, child);
return Create(cx, node, NullType.Create(cx), parent, child);
}
var type = Entities.Type.Create(cx, variableSymbol.GetAnnotatedType());
var location = cx.Create(designation.GetLocation());
var ret = Create(cx, designation, type, isVar, parent, child);
cx.Try(null, null, () => LocalVariable.Create(cx, variableSymbol, ret, isVar, location));
var ret = Create(cx, designation, type, parent, child);
cx.Try(null, null, () =>
{
var l = LocalVariable.Create(cx, variableSymbol);
l.PopulateManual(ret, node.Type.IsVar);
});
return ret;
}
@@ -78,10 +79,9 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
case SingleVariableDesignationSyntax single:
if (cx.GetModel(variable).GetDeclaredSymbol(single) is ILocalSymbol local)
{
var decl = Create(cx, variable, Entities.Type.Create(cx, local.GetAnnotatedType()), true, tuple, child0++);
var id = single.Identifier;
var location = cx.Create(id.GetLocation());
LocalVariable.Create(cx, local, decl, true, location);
var decl = Create(cx, variable, Entities.Type.Create(cx, local.GetAnnotatedType()), tuple, child0++);
var l = LocalVariable.Create(cx, local);
l.PopulateManual(decl, true);
}
else
{
@@ -111,30 +111,29 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
case DiscardDesignationSyntax discard:
var ti = cx.GetType(discard);
var type = Entities.Type.Create(cx, ti);
return Create(cx, node, type, node.Type.IsVar, parent, child);
return Create(cx, node, type, parent, child);
default:
cx.ModelError(node, "Failed to determine designation type");
return Create(cx, node, Entities.NullType.Create(cx), node.Type.IsVar, parent, child);
return Create(cx, node, NullType.Create(cx), parent, child);
}
}
public static Expression Create(Context cx, DeclarationExpressionSyntax node, IExpressionParentEntity parent, int child) =>
Create(cx, node, node.Designation, parent, child);
public static VariableDeclaration Create(Context cx, CSharpSyntaxNode c, AnnotatedType type, bool isVar, IExpressionParentEntity parent, int child) =>
public static VariableDeclaration Create(Context cx, CSharpSyntaxNode c, AnnotatedType type, IExpressionParentEntity parent, int child) =>
new VariableDeclaration(new ExpressionInfo(cx, type, cx.Create(c.FixedLocation()), ExprKind.LOCAL_VAR_DECL, parent, child, false, null));
public static VariableDeclaration Create(Context cx, CatchDeclarationSyntax d, bool isVar, IExpressionParentEntity parent, int child)
{
var symbol = cx.GetModel(d).GetDeclaredSymbol(d);
var type = Entities.Type.Create(cx, symbol.GetAnnotatedType());
var ret = Create(cx, d, type, isVar, parent, child);
var ret = Create(cx, d, type, parent, child);
cx.Try(d, null, () =>
{
var id = d.Identifier;
var declSymbol = cx.GetModel(d).GetDeclaredSymbol(d);
var location = cx.Create(id.GetLocation());
LocalVariable.Create(cx, declSymbol, ret, isVar, location);
var l = LocalVariable.Create(cx, declSymbol);
l.PopulateManual(ret, isVar);
TypeMention.Create(cx, d.Type, ret, type);
});
return ret;
@@ -142,20 +141,19 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
public static VariableDeclaration CreateDeclarator(Context cx, VariableDeclaratorSyntax d, AnnotatedType type, bool isVar, IExpressionParentEntity parent, int child)
{
var ret = Create(cx, d, type, isVar, parent, child);
var ret = Create(cx, d, type, parent, child);
cx.Try(d, null, () =>
{
var id = d.Identifier;
var declSymbol = cx.GetModel(d).GetDeclaredSymbol(d);
var location = cx.Create(id.GetLocation());
var localVar = LocalVariable.Create(cx, declSymbol, ret, isVar, location);
var localVar = LocalVariable.Create(cx, declSymbol);
localVar.PopulateManual(ret, isVar);
if (d.Initializer != null)
{
Create(cx, d.Initializer.Value, ret, 0);
// Create an access
var access = new Expression(new ExpressionInfo(cx, type, location, ExprKind.LOCAL_VARIABLE_ACCESS, ret, 1, false, null));
var access = new Expression(new ExpressionInfo(cx, type, localVar.Location, ExprKind.LOCAL_VARIABLE_ACCESS, ret, 1, false, null));
cx.TrapWriter.Writer.expr_access(access, localVar);
}

View File

@@ -1,34 +1,31 @@
using System;
using System.IO;
using Microsoft.CodeAnalysis;
using Semmle.Extraction.Entities;
namespace Semmle.Extraction.CSharp.Entities
{
class LocalVariable : CachedSymbol<ISymbol>
{
LocalVariable(Context cx, ISymbol init, Expression parent, bool isVar, Extraction.Entities.Location declLocation)
: base(cx, init)
{
Parent = parent;
IsVar = isVar;
DeclLocation = declLocation;
}
readonly Expression Parent;
readonly bool IsVar;
readonly Extraction.Entities.Location DeclLocation;
LocalVariable(Context cx, ISymbol init) : base(cx, init) { }
public override void WriteId(TextWriter trapFile)
{
trapFile.WriteSubId(Parent);
trapFile.WriteSubId(Location);
trapFile.Write('_');
trapFile.Write(symbol.Name);
trapFile.Write(";localvar");
}
public override void Populate(TextWriter trapFile)
public override void Populate(TextWriter trapFile) { }
public void PopulateManual(Expression parent, bool isVar)
{
var trapFile = Context.TrapWriter.Writer;
var (kind, type) =
symbol is ILocalSymbol l ?
(l.IsRef ? 3 : l.IsConst ? 2 : 1, Type.Create(Context, l.GetAnnotatedType())) :
(1, parent.Type);
trapFile.localvars(this, kind, symbol.Name, isVar ? 1 : 0, type.Type.TypeRef, parent);
if (symbol is ILocalSymbol local)
{
PopulateNullability(trapFile, local.GetAnnotatedType());
@@ -36,55 +33,14 @@ namespace Semmle.Extraction.CSharp.Entities
trapFile.type_annotation(this, Kinds.TypeAnnotation.Ref);
}
trapFile.localvars(
this,
IsRef ? 3 : IsConst ? 2 : 1,
symbol.Name,
IsVar ? 1 : 0,
Type.Type.TypeRef,
Parent);
trapFile.localvar_location(this, DeclLocation);
trapFile.localvar_location(this, Location);
DefineConstantValue(trapFile);
}
public static LocalVariable Create(Context cx, ISymbol local, Expression parent, bool isVar, Extraction.Entities.Location declLocation)
public static LocalVariable Create(Context cx, ISymbol local)
{
return LocalVariableFactory.Instance.CreateEntity(cx, local, parent, isVar, declLocation);
}
/// <summary>
/// Gets the local variable entity for <paramref name="local"/> which must
/// already have been created.
/// </summary>
public static LocalVariable GetAlreadyCreated(Context cx, ISymbol local) => LocalVariableFactory.Instance.CreateEntity(cx, local, null, false, null);
bool IsConst
{
get
{
var local = symbol as ILocalSymbol;
return local != null && local.IsConst;
}
}
bool IsRef
{
get
{
var local = symbol as ILocalSymbol;
return local != null && local.IsRef;
}
}
AnnotatedType Type
{
get
{
var local = symbol as ILocalSymbol;
return local == null ? Parent.Type : Entities.Type.Create(Context, local.GetAnnotatedType());
}
return LocalVariableFactory.Instance.CreateEntity(cx, local);
}
void DefineConstantValue(TextWriter trapFile)
@@ -96,12 +52,11 @@ namespace Semmle.Extraction.CSharp.Entities
}
}
class LocalVariableFactory : ICachedEntityFactory<(ISymbol, Expression, bool, Extraction.Entities.Location), LocalVariable>
class LocalVariableFactory : ICachedEntityFactory<ISymbol, LocalVariable>
{
public static readonly LocalVariableFactory Instance = new LocalVariableFactory();
public LocalVariable Create(Context cx, (ISymbol, Expression, bool, Extraction.Entities.Location) init) =>
new LocalVariable(cx, init.Item1, init.Item2, init.Item3, init.Item4);
public LocalVariable Create(Context cx, ISymbol init) => new LocalVariable(cx, init);
}
public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.NeedsLabel;

View File

@@ -1,4 +1,3 @@
using System;
using Microsoft.CodeAnalysis;
using Semmle.Extraction.CSharp.Populators;
using System.Linq;
@@ -69,11 +68,7 @@ namespace Semmle.Extraction.CSharp.Entities
public static Parameter Create(Context cx, IParameterSymbol param, IEntity parent, Parameter original = null) =>
ParameterFactory.Instance.CreateEntity(cx, param, parent, original);
/// <summary>
/// Gets the parameter entity for <paramref name="param"/> which must
/// already have been created.
/// </summary>
public static Parameter GetAlreadyCreated(Context cx, IParameterSymbol param) =>
public static Parameter Create(Context cx, IParameterSymbol param) =>
ParameterFactory.Instance.CreateEntity(cx, param, null, null);
public override void WriteId(TextWriter trapFile)

View File

@@ -77,7 +77,7 @@ namespace Semmle.Extraction.CSharp.Entities.Statements
if (cx.GetModel(pattern).GetDeclaredSymbol(designation) is ILocalSymbol symbol)
{
var type = Type.Create(cx, symbol.GetAnnotatedType());
Expressions.VariableDeclaration.Create(cx, symbol, type, optionalType, cx.Create(pattern.GetLocation()), cx.Create(designation.GetLocation()), isVar, this, 0);
Expressions.VariableDeclaration.Create(cx, symbol, type, optionalType, cx.Create(pattern.GetLocation()), isVar, this, 0);
}
break;
case DiscardDesignationSyntax discard:

View File

@@ -27,7 +27,7 @@ namespace Semmle.Extraction.CSharp.Entities.Statements
var location = cx.Create(Stmt.Identifier.GetLocation());
Expressions.VariableDeclaration.Create(cx, typeSymbol, type, Stmt.Type, location, location, Stmt.Type.IsVar, this, 0);
Expressions.VariableDeclaration.Create(cx, typeSymbol, type, Stmt.Type, location, Stmt.Type.IsVar, this, 0);
Statement.Create(cx, Stmt.Statement, this, 2);
}

View File

@@ -30,7 +30,7 @@ namespace Semmle.Extraction.CSharp.Populators
public override IEntity VisitNamespace(INamespaceSymbol ns) => Namespace.Create(cx, ns);
public override IEntity VisitParameter(IParameterSymbol param) => Parameter.GetAlreadyCreated(cx, param);
public override IEntity VisitParameter(IParameterSymbol param) => Parameter.Create(cx, param);
public override IEntity VisitProperty(IPropertySymbol symbol) => Property.Create(cx, symbol);

View File

@@ -1,4 +1,4 @@
// semmle-extractor-options: /langversion:8.0
using System;
using System.Threading.Tasks;
@@ -180,4 +180,13 @@ class UsingDiscard
}
}
class TupleMatching
{
(int, object) G(object o1, object o2)
{
(object, object)? pair = (o1, o2);
return (0, pair is var (x, y) ? x : null);
}
}
// semmle-extractor-options: /r:System.Dynamic.Runtime.dll

View File

@@ -82,3 +82,12 @@
| Program.cs:169:5:169:10 | String |
| Program.cs:174:5:174:8 | Void |
| Program.cs:176:17:176:19 | IDisposable |
| Program.cs:185:5:185:17 | (Int32,Object) |
| Program.cs:185:6:185:8 | Int32 |
| Program.cs:185:11:185:16 | Object |
| Program.cs:185:21:185:26 | Object |
| Program.cs:185:32:185:37 | Object |
| Program.cs:187:9:187:24 | (Object,Object) |
| Program.cs:187:9:187:25 | Nullable<(Object,Object)> |
| Program.cs:187:10:187:15 | Object |
| Program.cs:187:18:187:23 | Object |