|
| 1 | +using System.IO; |
| 2 | +using System.Linq; |
1 | 3 | using Microsoft.CodeAnalysis;
|
2 | 4 | using Microsoft.CodeAnalysis.CSharp.Syntax;
|
3 | 5 | using Semmle.Extraction.Entities;
|
4 |
| -using System.IO; |
5 | 6 |
|
6 | 7 | namespace Semmle.Extraction.CSharp.Entities
|
7 | 8 | {
|
8 |
| - internal class Attribute : FreshEntity, IExpressionParentEntity |
| 9 | + internal class Attribute : CachedEntity<AttributeData>, IExpressionParentEntity |
9 | 10 | {
|
10 | 11 | bool IExpressionParentEntity.IsTopLevelParent => true;
|
11 | 12 |
|
12 |
| - private readonly AttributeData attribute; |
| 13 | + private readonly AttributeSyntax attributeSyntax; |
13 | 14 | private readonly IEntity entity;
|
14 | 15 |
|
15 |
| - public Attribute(Context cx, AttributeData attribute, IEntity entity) |
16 |
| - : base(cx) |
| 16 | + private Attribute(Context cx, AttributeData attributeData, IEntity entity) |
| 17 | + : base(cx, attributeData) |
17 | 18 | {
|
18 |
| - this.attribute = attribute; |
| 19 | + this.attributeSyntax = attributeData.ApplicationSyntaxReference?.GetSyntax() as AttributeSyntax; |
19 | 20 | this.entity = entity;
|
20 |
| - TryPopulate(); |
21 | 21 | }
|
22 | 22 |
|
23 |
| - protected override void Populate(TextWriter trapFile) |
| 23 | + public override void WriteId(TextWriter trapFile) |
24 | 24 | {
|
25 |
| - if (attribute.ApplicationSyntaxReference != null) |
| 25 | + if (ReportingLocation?.IsInSource == true) |
| 26 | + { |
| 27 | + trapFile.WriteSubId(Location); |
| 28 | + trapFile.Write(";attribute"); |
| 29 | + } |
| 30 | + else |
26 | 31 | {
|
27 |
| - // !! Extract attributes from assemblies. |
28 |
| - // This is harder because the "expression" entities presume the |
29 |
| - // existence of a syntax tree. This is not the case for compiled |
30 |
| - // attributes. |
31 |
| - var syntax = attribute.ApplicationSyntaxReference.GetSyntax() as AttributeSyntax; |
32 |
| - ExtractAttribute(cx.TrapWriter.Writer, syntax, attribute.AttributeClass, entity); |
| 32 | + trapFile.Write('*'); |
33 | 33 | }
|
34 | 34 | }
|
35 | 35 |
|
36 |
| - public Attribute(Context cx, AttributeSyntax attribute, IEntity entity) |
37 |
| - : base(cx) |
| 36 | + public override void WriteQuotedId(TextWriter trapFile) |
38 | 37 | {
|
39 |
| - var info = cx.GetSymbolInfo(attribute); |
40 |
| - ExtractAttribute(cx.TrapWriter.Writer, attribute, info.Symbol.ContainingType, entity); |
| 38 | + if (ReportingLocation?.IsInSource == true) |
| 39 | + { |
| 40 | + base.WriteQuotedId(trapFile); |
| 41 | + } |
| 42 | + else |
| 43 | + { |
| 44 | + trapFile.Write('*'); |
| 45 | + } |
41 | 46 | }
|
42 | 47 |
|
43 |
| - private void ExtractAttribute(System.IO.TextWriter trapFile, AttributeSyntax syntax, ITypeSymbol attributeClass, IEntity entity) |
| 48 | + public override void Populate(TextWriter trapFile) |
44 | 49 | {
|
45 |
| - var type = Type.Create(cx, attributeClass); |
| 50 | + var type = Type.Create(Context, symbol.AttributeClass); |
46 | 51 | trapFile.attributes(this, type.TypeRef, entity);
|
| 52 | + trapFile.attribute_location(this, Location); |
47 | 53 |
|
48 |
| - trapFile.attribute_location(this, cx.Create(syntax.Name.GetLocation())); |
| 54 | + if (attributeSyntax is object) |
| 55 | + { |
| 56 | + if (Context.Extractor.OutputPath != null) |
| 57 | + { |
| 58 | + trapFile.attribute_location(this, Assembly.CreateOutputAssembly(Context)); |
| 59 | + } |
| 60 | + |
| 61 | + TypeMention.Create(Context, attributeSyntax.Name, this, type); |
| 62 | + } |
49 | 63 |
|
50 |
| - if (cx.Extractor.OutputPath != null) |
51 |
| - trapFile.attribute_location(this, Assembly.CreateOutputAssembly(cx)); |
| 64 | + ExtractArguments(trapFile); |
| 65 | + } |
52 | 66 |
|
53 |
| - TypeMention.Create(cx, syntax.Name, this, type); |
| 67 | + private void ExtractArguments(TextWriter trapFile) |
| 68 | + { |
| 69 | + var childIndex = 0; |
| 70 | + foreach (var constructorArgument in symbol.ConstructorArguments) |
| 71 | + { |
| 72 | + CreateExpressionFromArgument( |
| 73 | + constructorArgument, |
| 74 | + attributeSyntax?.ArgumentList.Arguments[childIndex].Expression, |
| 75 | + this, |
| 76 | + childIndex++); |
| 77 | + } |
54 | 78 |
|
55 |
| - if (syntax.ArgumentList != null) |
| 79 | + foreach (var namedArgument in symbol.NamedArguments) |
56 | 80 | {
|
57 |
| - cx.PopulateLater(() => |
| 81 | + var expr = CreateExpressionFromArgument( |
| 82 | + namedArgument.Value, |
| 83 | + attributeSyntax?.ArgumentList.Arguments.Single(a => a.NameEquals?.Name?.Identifier.Text == namedArgument.Key).Expression, |
| 84 | + this, |
| 85 | + childIndex++); |
| 86 | + |
| 87 | + if (expr is object) |
58 | 88 | {
|
59 |
| - var child = 0; |
60 |
| - foreach (var arg in syntax.ArgumentList.Arguments) |
61 |
| - { |
62 |
| - var expr = Expression.Create(cx, arg.Expression, this, child++); |
63 |
| - if (!(arg.NameEquals is null)) |
64 |
| - { |
65 |
| - trapFile.expr_argument_name(expr, arg.NameEquals.Name.Identifier.Text); |
66 |
| - } |
67 |
| - } |
68 |
| - }); |
| 89 | + trapFile.expr_argument_name(expr, namedArgument.Key); |
| 90 | + } |
69 | 91 | }
|
70 | 92 | }
|
71 | 93 |
|
| 94 | + private Expression CreateExpressionFromArgument(TypedConstant constant, ExpressionSyntax syntax, IExpressionParentEntity parent, |
| 95 | + int childIndex) |
| 96 | + { |
| 97 | + return syntax is null |
| 98 | + ? Expression.CreateGenerated(Context, constant, parent, childIndex, Location) |
| 99 | + : Expression.Create(Context, syntax, parent, childIndex); |
| 100 | + } |
| 101 | + |
| 102 | + public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.OptionalLabel; |
| 103 | + |
| 104 | + public override Microsoft.CodeAnalysis.Location ReportingLocation => attributeSyntax?.Name.GetLocation(); |
| 105 | + |
| 106 | + private Semmle.Extraction.Entities.Location ___location; |
| 107 | + private Semmle.Extraction.Entities.Location Location => |
| 108 | + ___location ?? (___location = Semmle.Extraction.Entities.Location.Create(Context, attributeSyntax is null ? entity.ReportingLocation : attributeSyntax.Name.GetLocation())); |
| 109 | + |
| 110 | + public override bool NeedsPopulation => true; |
| 111 | + |
72 | 112 | public static void ExtractAttributes(Context cx, ISymbol symbol, IEntity entity)
|
73 | 113 | {
|
74 | 114 | foreach (var attribute in symbol.GetAttributes())
|
75 | 115 | {
|
76 |
| - new Attribute(cx, attribute, entity); |
| 116 | + Create(cx, attribute, entity); |
77 | 117 | }
|
78 | 118 | }
|
79 | 119 |
|
80 |
| - public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.OptionalLabel; |
| 120 | + public static Attribute Create(Context cx, AttributeData attributeData, IEntity entity) |
| 121 | + { |
| 122 | + var init = (attributeData, entity); |
| 123 | + return AttributeFactory.Instance.CreateEntity(cx, attributeData, init); |
| 124 | + } |
| 125 | + |
| 126 | + private class AttributeFactory : ICachedEntityFactory<(AttributeData attributeData, IEntity receiver), Attribute> |
| 127 | + { |
| 128 | + public static readonly AttributeFactory Instance = new AttributeFactory(); |
| 129 | + |
| 130 | + public Attribute Create(Context cx, (AttributeData attributeData, IEntity receiver) init) => |
| 131 | + new Attribute(cx, init.attributeData, init.receiver); |
| 132 | + } |
81 | 133 | }
|
82 | 134 | }
|
0 commit comments