Skip to content

Commit 77495df

Browse files
authored
Merge pull request #2550 from github/koesie10/refactor-duplication-queries
Refactor data extensions editor queries to reduce duplication
2 parents ee68156 + bd0e560 commit 77495df

File tree

3 files changed

+58
-317
lines changed

3 files changed

+58
-317
lines changed

extensions/ql-vscode/src/data-extensions-editor/queries/csharp.ts

Lines changed: 28 additions & 176 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,19 @@ export const fetchExternalApisQuery: Query = {
99
* @id cs/telemetry/fetch-external-apis
1010
*/
1111
12-
import csharp
13-
import ExternalApi
14-
15-
private Call aUsage(ExternalApi api) { result.getTarget().getUnboundDeclaration() = api }
12+
private import csharp
13+
private import AutomodelVsCode
1614
17-
private boolean isSupported(ExternalApi api) {
18-
api.isSupported() and result = true
19-
or
20-
not api.isSupported() and
21-
result = false
15+
class ExternalApi extends CallableMethod {
16+
ExternalApi() {
17+
this.isUnboundDeclaration() and
18+
this.fromLibrary() and
19+
this.(Modifiable).isEffectivelyPublic()
20+
}
2221
}
2322
23+
private Call aUsage(ExternalApi api) { result.getTarget().getUnboundDeclaration() = api }
24+
2425
from ExternalApi api, string apiName, boolean supported, Call usage
2526
where
2627
apiName = api.getApiName() and
@@ -29,142 +30,30 @@ where
2930
select usage, apiName, supported.toString(), "supported", api.getFile().getBaseName(), "library"
3031
`,
3132
frameworkModeQuery: `/**
32-
* @name Usage of APIs coming from external libraries
33-
* @description A list of 3rd party APIs used in the codebase.
33+
* @name Public methods
34+
* @description A list of APIs callable by consumers. Excludes test and generated code.
3435
* @tags telemetry
3536
* @kind problem
36-
* @id cs/telemetry/fetch-external-apis
37+
* @id cs/telemetry/fetch-public-methods
3738
*/
3839
3940
private import csharp
40-
private import cil
4141
private import dotnet
42-
private import semmle.code.csharp.dispatch.Dispatch
43-
private import semmle.code.csharp.dataflow.ExternalFlow
44-
private import semmle.code.csharp.dataflow.FlowSummary
45-
private import semmle.code.csharp.dataflow.internal.DataFlowImplCommon as DataFlowImplCommon
46-
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate
47-
private import semmle.code.csharp.dataflow.internal.DataFlowDispatch as DataFlowDispatch
48-
private import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
49-
private import semmle.code.csharp.dataflow.internal.TaintTrackingPrivate
50-
private import semmle.code.csharp.security.dataflow.flowsources.Remote
51-
52-
pragma[nomagic]
53-
private predicate isTestNamespace(Namespace ns) {
54-
ns.getFullName()
55-
.matches([
56-
"NUnit.Framework%", "Xunit%", "Microsoft.VisualStudio.TestTools.UnitTesting%", "Moq%"
57-
])
58-
}
59-
60-
/**
61-
* A test library.
62-
*/
63-
class TestLibrary extends RefType {
64-
TestLibrary() { isTestNamespace(this.getNamespace()) }
65-
}
66-
67-
/** Holds if the given callable is not worth supporting. */
68-
private predicate isUninteresting(DotNet::Callable c) {
69-
c.getDeclaringType() instanceof TestLibrary or
70-
c.(Constructor).isParameterless() or
71-
c.getDeclaringType() instanceof AnonymousClass
72-
}
73-
74-
class PublicMethod extends DotNet::Member {
75-
PublicMethod() { this.isPublic() and not isUninteresting(this) }
76-
77-
/**
78-
* Gets the unbound type, name and parameter types of this API.
79-
*/
80-
bindingset[this]
81-
private string getSignature() {
82-
result =
83-
this.getDeclaringType().getUnboundDeclaration() + "." + this.getName() + "(" +
84-
parameterQualifiedTypeNamesToString(this) + ")"
85-
}
86-
87-
/**
88-
* Gets the namespace of this API.
89-
*/
90-
bindingset[this]
91-
string getNamespace() { this.getDeclaringType().hasQualifiedName(result, _) }
92-
93-
/**
94-
* Gets the namespace and signature of this API.
95-
*/
96-
bindingset[this]
97-
string getApiName() { result = this.getNamespace() + "#" + this.getSignature() }
98-
99-
/** Gets a node that is an input to a call to this API. */
100-
private ArgumentNode getAnInput() {
101-
result
102-
.getCall()
103-
.(DataFlowDispatch::NonDelegateDataFlowCall)
104-
.getATarget(_)
105-
.getUnboundDeclaration() = this
106-
}
107-
108-
/** Gets a node that is an output from a call to this API. */
109-
private DataFlow::Node getAnOutput() {
110-
exists(
111-
Call c, DataFlowDispatch::NonDelegateDataFlowCall dc, DataFlowImplCommon::ReturnKindExt ret
112-
|
113-
dc.getDispatchCall().getCall() = c and
114-
c.getTarget().getUnboundDeclaration() = this
115-
|
116-
result = ret.getAnOutNode(dc)
117-
)
118-
}
119-
120-
/** Holds if this API has a supported summary. */
121-
pragma[nomagic]
122-
predicate hasSummary() {
123-
this instanceof SummarizedCallable
124-
or
125-
defaultAdditionalTaintStep(this.getAnInput(), _)
126-
}
127-
128-
/** Holds if this API is a known source. */
129-
pragma[nomagic]
130-
predicate isSource() {
131-
this.getAnOutput() instanceof RemoteFlowSource or sourceNode(this.getAnOutput(), _)
132-
}
133-
134-
/** Holds if this API is a known sink. */
135-
pragma[nomagic]
136-
predicate isSink() { sinkNode(this.getAnInput(), _) }
137-
138-
/** Holds if this API is a known neutral. */
139-
pragma[nomagic]
140-
predicate isNeutral() { this instanceof FlowSummaryImpl::Public::NeutralCallable }
141-
142-
/**
143-
* Holds if this API is supported by existing CodeQL libraries, that is, it is either a
144-
* recognized source, sink or neutral or it has a flow summary.
145-
*/
146-
predicate isSupported() {
147-
this.hasSummary() or this.isSource() or this.isSink() or this.isNeutral()
148-
}
149-
}
42+
private import AutomodelVsCode
15043
151-
private boolean isSupported(PublicMethod publicMethod) {
152-
publicMethod.isSupported() and result = true
153-
or
154-
not publicMethod.isSupported() and
155-
result = false
44+
class PublicMethod extends CallableMethod {
45+
PublicMethod() { this.fromSource() }
15646
}
15747
15848
from PublicMethod publicMethod, string apiName, boolean supported
15949
where
16050
apiName = publicMethod.getApiName() and
161-
publicMethod.getDeclaringType().fromSource() and
16251
supported = isSupported(publicMethod)
16352
select publicMethod, apiName, supported.toString(), "supported",
16453
publicMethod.getFile().getBaseName(), "library"
16554
`,
16655
dependencies: {
167-
"ExternalApi.qll": `/** Provides classes and predicates related to handling APIs from external libraries. */
56+
"AutomodelVsCode.qll": `/** Provides classes and predicates related to handling APIs for the VS Code extension. */
16857
16958
private import csharp
17059
private import dotnet
@@ -194,18 +83,17 @@ class TestLibrary extends RefType {
19483
}
19584
19685
/** Holds if the given callable is not worth supporting. */
197-
private predicate isUninteresting(DotNet::Callable c) {
86+
private predicate isUninteresting(DotNet::Declaration c) {
19887
c.getDeclaringType() instanceof TestLibrary or
199-
c.(Constructor).isParameterless()
88+
c.(Constructor).isParameterless() or
89+
c.getDeclaringType() instanceof AnonymousClass
20090
}
20191
20292
/**
203-
* An external API from either the C# Standard Library or a 3rd party library.
93+
* An callable method from either the C# Standard Library, a 3rd party library, or from the source.
20494
*/
205-
class ExternalApi extends DotNet::Callable {
206-
ExternalApi() {
207-
this.isUnboundDeclaration() and
208-
this.fromLibrary() and
95+
class CallableMethod extends DotNet::Declaration {
96+
CallableMethod() {
20997
this.(Modifiable).isEffectivelyPublic() and
21098
not isUninteresting(this)
21199
}
@@ -284,47 +172,11 @@ class ExternalApi extends DotNet::Callable {
284172
}
285173
}
286174
287-
/**
288-
* Gets the limit for the number of results produced by a telemetry query.
289-
*/
290-
int resultLimit() { result = 1000 }
291-
292-
/**
293-
* Holds if it is relevant to count usages of \`api\`.
294-
*/
295-
signature predicate relevantApi(ExternalApi api);
296-
297-
/**
298-
* Given a predicate to count relevant API usages, this module provides a predicate
299-
* for restricting the number or returned results based on a certain limit.
300-
*/
301-
module Results<relevantApi/1 getRelevantUsages> {
302-
private int getUsages(string apiName) {
303-
result =
304-
strictcount(Call c, ExternalApi api |
305-
c.getTarget().getUnboundDeclaration() = api and
306-
apiName = api.getApiName() and
307-
getRelevantUsages(api)
308-
)
309-
}
310-
311-
private int getOrder(string apiName) {
312-
apiName =
313-
rank[result](string name, int usages |
314-
usages = getUsages(name)
315-
|
316-
name order by usages desc, name
317-
)
318-
}
319-
320-
/**
321-
* Holds if there exists an API with \`apiName\` that is being used \`usages\` times
322-
* and if it is in the top results (guarded by resultLimit).
323-
*/
324-
predicate restrict(string apiName, int usages) {
325-
usages = getUsages(apiName) and
326-
getOrder(apiName) <= resultLimit()
327-
}
175+
boolean isSupported(CallableMethod callableMethod) {
176+
callableMethod.isSupported() and result = true
177+
or
178+
not callableMethod.isSupported() and
179+
result = false
328180
}
329181
`,
330182
},

0 commit comments

Comments
 (0)