Skip to content
This repository was archived by the owner on Apr 14, 2022. It is now read-only.

Linter for undefined variables #693

Merged
merged 41 commits into from
Mar 14, 2019
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
8bca56b
Fix #668 (partial)
Mar 1, 2019
a731c2a
Merge branch 'master' of https://github.com/Microsoft/python-language…
Mar 1, 2019
ba126e7
Undef variables, first cut
Mar 4, 2019
bde1db1
Reorg
Mar 4, 2019
55cc25f
Tests
Mar 4, 2019
8b79615
Tests
Mar 4, 2019
069910b
Merge branch 'master' of https://github.com/Microsoft/python-language…
Mar 5, 2019
7ffc9db
Tests
Mar 5, 2019
6303c45
Revert "Tests"
Mar 5, 2019
945451c
Merge branch 'master' of https://github.com/Microsoft/python-language…
Mar 5, 2019
6429bfa
Options and tests
Mar 5, 2019
42dc0a0
Don't squiggle builtin-ins
Mar 5, 2019
9b130fd
Test for function arguments
Mar 5, 2019
2490a0a
Fix tuple assignment in analysis
Mar 5, 2019
f9216e3
Don't false positive on functions and classes forward references
Mar 5, 2019
58a0d66
Merge master
Mar 5, 2019
034e437
Merge branch 'master' of https://github.com/Microsoft/python-language…
Mar 5, 2019
3b1682f
Merge branch 'master' of https://github.com/Microsoft/python-language…
Mar 6, 2019
5cea68d
Disable tracking assignment location
Mar 6, 2019
af542a4
Merge branch 'master' of https://github.com/Microsoft/python-language…
Mar 6, 2019
8dfc390
Track multiple locations
Mar 6, 2019
a30eea3
Tests
Mar 6, 2019
8a1c83a
using
Mar 6, 2019
33d10e9
Merge locations
Mar 6, 2019
4404e5a
Properly look at locations
Mar 6, 2019
6c47d09
Merge branch 'master' of https://github.com/Microsoft/python-language…
Mar 11, 2019
13e6e99
Comprehensions etc
Mar 12, 2019
df7cdfd
Merge branch 'master' of https://github.com/Microsoft/python-language…
Mar 12, 2019
4adf172
Test update
Mar 12, 2019
1aaa445
Add support for lambdas
Mar 12, 2019
8861171
Add lambda linting
Mar 12, 2019
e76540c
Handle tuple assignment better
Mar 12, 2019
fe099b5
Test update
Mar 13, 2019
861d487
Merge master
Mar 13, 2019
29e3dd1
Correct comprehension iterator handlint
Mar 13, 2019
aa2a415
Fix race condition at builtins load
Mar 13, 2019
0739ad3
Merge master
Mar 14, 2019
add8358
Merge master
Mar 14, 2019
91a7215
Restore linter
Mar 14, 2019
35c7f0e
Merge branch 'master' of https://github.com/Microsoft/python-language…
Mar 14, 2019
3e18aea
Merge branch 'master' of https://github.com/Microsoft/python-language…
Mar 14, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

using System;
using System.Collections.Generic;
using Microsoft.Python.Analysis.Analyzer.Evaluation;
using Microsoft.Python.Analysis.Diagnostics;
using Microsoft.Python.Analysis.Types;
using Microsoft.Python.Analysis.Values;
Expand Down Expand Up @@ -57,7 +58,7 @@ public interface IExpressionEvaluator {
/// </summary>
IMember GetValueFromExpression(Expression expr);

IMember LookupNameInScopes(string name, out IScope scope);
IMember LookupNameInScopes(string name, out IScope scope, LookupOptions options = LookupOptions.Normal);

IPythonType GetTypeFromString(string typeString);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@

using System;

namespace Microsoft.Python.Analysis.Analyzer.Evaluation {
namespace Microsoft.Python.Analysis.Analyzer {
[Flags]
internal enum LookupOptions {
public enum LookupOptions {
None = 0,
Local,
Nonlocal,
Expand Down
1 change: 1 addition & 0 deletions src/Analysis/Ast/Impl/Analyzer/ModuleWalker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

using System.Diagnostics;
using Microsoft.Python.Analysis.Documents;
using Microsoft.Python.Analysis.Linting;
using Microsoft.Python.Analysis.Modules;
using Microsoft.Python.Analysis.Types;
using Microsoft.Python.Analysis.Values;
Expand Down
8 changes: 7 additions & 1 deletion src/Analysis/Ast/Impl/Analyzer/PythonAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Python.Analysis.Core.DependencyResolution;
using Microsoft.Python.Analysis.Dependencies;
using Microsoft.Python.Analysis.Documents;
using Microsoft.Python.Analysis.Linting;
using Microsoft.Python.Analysis.Modules;
using Microsoft.Python.Analysis.Types;
using Microsoft.Python.Core;
Expand Down Expand Up @@ -267,6 +267,12 @@ private void Analyze(IDependencyChainNode<PythonAnalyzerEntry> node, int version
cancellationToken.ThrowIfCancellationRequested();
var analysis = new DocumentAnalysis((IDocument)module, version, walker.GlobalScope, walker.Eval);

var optionsProvider = _services.GetService<IAnalysisOptionsProvider>();
if (optionsProvider?.Options?.LintingEnabled != false) {
var linter = new LinterAggregator();
linter.Lint(analysis, _services);
}

(module as IAnalyzable)?.NotifyAnalysisComplete(analysis);
node.Value.TrySetAnalysis(analysis, version, _syncObj);

Expand Down
20 changes: 20 additions & 0 deletions src/Analysis/Ast/Impl/Definitions/AnalysisOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright(c) Microsoft Corporation
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the License); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
//
// THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
// OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
// IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABILITY OR NON-INFRINGEMENT.
//
// See the Apache Version 2.0 License for specific language governing
// permissions and limitations under the License.

namespace Microsoft.Python.Analysis {
public class AnalysisOptions {
public bool LintingEnabled { get; set; }
}
}
20 changes: 20 additions & 0 deletions src/Analysis/Ast/Impl/Definitions/IAnalysisOptionsProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright(c) Microsoft Corporation
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the License); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
//
// THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
// OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
// IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABILITY OR NON-INFRINGEMENT.
//
// See the Apache Version 2.0 License for specific language governing
// permissions and limitations under the License.

namespace Microsoft.Python.Analysis {
public interface IAnalysisOptionsProvider {
AnalysisOptions Options { get; }
}
}
1 change: 1 addition & 0 deletions src/Analysis/Ast/Impl/Diagnostics/ErrorCodes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,6 @@ public static class ErrorCodes {
public const string ParameterAlreadySpecified = "parameter-already-specified";
public const string ParameterMissing = "parameter-missing";
public const string UnresolvedImport = "unresolved-import";
public const string UndefinedVariable = "undefined-variable";
}
}
28 changes: 28 additions & 0 deletions src/Analysis/Ast/Impl/Extensions/SourceSpanExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Python Tools for Visual Studio
// Copyright(c) Microsoft Corporation
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the License); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
//
// THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
// OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
// IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABILITY OR NON-INFRINGEMENT.
//
// See the Apache Version 2.0 License for specific language governing
// permissions and limitations under the License.

using Microsoft.Python.Core.Text;

namespace Microsoft.Python.Analysis {
public static class SourceSpanExtensions {
public static bool IsAfter(this SourceSpan span, SourceSpan other) {
if (!span.IsValid || !other.IsValid) {
return false;
}
return span.Start > other.Start;
}
}
}
22 changes: 22 additions & 0 deletions src/Analysis/Ast/Impl/Linting/ILinter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright(c) Microsoft Corporation
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the License); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
//
// THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
// OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
// IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABILITY OR NON-INFRINGEMENT.
//
// See the Apache Version 2.0 License for specific language governing
// permissions and limitations under the License.

using Microsoft.Python.Core;

namespace Microsoft.Python.Analysis.Linting {
public interface ILinter {
void Lint(IDocumentAnalysis analysis, IServiceContainer services);
}
}
34 changes: 34 additions & 0 deletions src/Analysis/Ast/Impl/Linting/LinterAggregator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright(c) Microsoft Corporation
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the License); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
//
// THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
// OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
// IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABILITY OR NON-INFRINGEMENT.
//
// See the Apache Version 2.0 License for specific language governing
// permissions and limitations under the License.

using System.Collections.Generic;
using Microsoft.Python.Analysis.Linting.UndefinedVariables;
using Microsoft.Python.Core;

namespace Microsoft.Python.Analysis.Linting {
internal sealed class LinterAggregator {
private readonly List<ILinter> _linters = new List<ILinter>();

public LinterAggregator() {
// TODO: develop mechanism for dynamic and external linter discovery.
_linters.Add(new UndefinedVariablesLinter());
}
public void Lint(IDocumentAnalysis analysis, IServiceContainer services) {
foreach (var l in _linters) {
l.Lint(analysis, services);
}
}
}
}
47 changes: 47 additions & 0 deletions src/Analysis/Ast/Impl/Linting/LinterWalker.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright(c) Microsoft Corporation
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the License); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
//
// THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
// OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
// IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABILITY OR NON-INFRINGEMENT.
//
// See the Apache Version 2.0 License for specific language governing
// permissions and limitations under the License.

using System;
using System.Collections.Generic;
using Microsoft.Python.Analysis.Analyzer;
using Microsoft.Python.Core;
using Microsoft.Python.Parsing.Ast;

namespace Microsoft.Python.Analysis.Linting {
internal abstract class LinterWalker: PythonWalker {
private readonly Stack<IDisposable> _scopeStack = new Stack<IDisposable>();

protected IDocumentAnalysis Analysis { get; }
protected IExpressionEvaluator Eval => Analysis.ExpressionEvaluator;
protected IServiceContainer Services { get; }

protected LinterWalker(IDocumentAnalysis analysis, IServiceContainer services) {
Analysis = analysis;
Services = services;
}

public override bool Walk(ClassDefinition cd) {
_scopeStack.Push(Eval.OpenScope(Analysis.Document, cd));
return true;
}
public override void PostWalk(ClassDefinition cd) => _scopeStack.Pop().Dispose();

public override bool Walk(FunctionDefinition fd) {
_scopeStack.Push(Eval.OpenScope(Analysis.Document, fd));
return true;
}
public override void PostWalk(FunctionDefinition cd) => _scopeStack.Pop().Dispose();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright(c) Microsoft Corporation
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the License); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
//
// THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
// OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
// IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABILITY OR NON-INFRINGEMENT.
//
// See the Apache Version 2.0 License for specific language governing
// permissions and limitations under the License.

using Microsoft.Python.Analysis.Diagnostics;
using Microsoft.Python.Core;
using Microsoft.Python.Parsing;
using Microsoft.Python.Parsing.Ast;
using ErrorCodes = Microsoft.Python.Analysis.Diagnostics.ErrorCodes;

namespace Microsoft.Python.Analysis.Linting.UndefinedVariables {
internal static class AnalysisExtensions {
public static void ReportUndefinedVariable(this IDocumentAnalysis analysis, NameExpression node) {
var eval = analysis.ExpressionEvaluator;
eval.ReportDiagnostics(analysis.Document.Uri, new DiagnosticsEntry(
Resources.UndefinedVariable.FormatInvariant(node.Name),
eval.GetLocation(node).Span, ErrorCodes.UndefinedVariable, Severity.Warning));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Copyright(c) Microsoft Corporation
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the License); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
//
// THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
// OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
// IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABILITY OR NON-INFRINGEMENT.
//
// See the Apache Version 2.0 License for specific language governing
// permissions and limitations under the License.

using System.Collections.Generic;
using System.Linq;
using Microsoft.Python.Parsing.Ast;

namespace Microsoft.Python.Analysis.Linting.UndefinedVariables {
internal sealed class ComprehensionWalker : PythonWalker {
private readonly IDocumentAnalysis _analysis;
private readonly HashSet<string> _names = new HashSet<string>();
private readonly HashSet<NameExpression> _additionalNameNodes = new HashSet<NameExpression>();

public ComprehensionWalker(IDocumentAnalysis analysis) {
_analysis = analysis;
}

public override bool Walk(GeneratorExpression node) {
ProcessComprehension(node, node.Item, node.Iterators);
return false;
}

public override bool Walk(ListComprehension node) {
ProcessComprehension(node, node.Item, node.Iterators);
return false;
}

public override bool Walk(SetComprehension node) {
ProcessComprehension(node, node.Item, node.Iterators);
return false;
}

public override bool Walk(DictionaryComprehension node) {
CollectNames(node);
node.Key?.Walk(new ExpressionWalker(_analysis, _names, _additionalNameNodes));
node.Value?.Walk(new ExpressionWalker(_analysis, _names, _additionalNameNodes));
foreach (var iter in node.Iterators) {
iter?.Walk(new ExpressionWalker(_analysis, null, _additionalNameNodes));
}

return true;
}

private void CollectNames(Comprehension c) {
var nc = new NameCollectorWalker(_names, _additionalNameNodes);
foreach (var cfor in c.Iterators.OfType<ComprehensionFor>()) {
cfor.Left?.Walk(nc);
}
}

private void ProcessComprehension(Comprehension c, Node item, IEnumerable<ComprehensionIterator> iterators) {
CollectNames(c);
item?.Walk(new ExpressionWalker(_analysis, _names, _additionalNameNodes));
foreach (var iter in iterators) {
iter.Walk(new ExpressionWalker(_analysis, null, _additionalNameNodes));
}
}

private sealed class NameCollectorWalker : PythonWalker {
private readonly HashSet<string> _names;
private readonly HashSet<NameExpression> _additionalNameNodes;

public NameCollectorWalker(HashSet<string> names, HashSet<NameExpression> additionalNameNodes) {
_names = names;
_additionalNameNodes = additionalNameNodes;
}

public override bool Walk(NameExpression nex) {
if (!string.IsNullOrEmpty(nex.Name)) {
_names.Add(nex.Name);
_additionalNameNodes.Add(nex);
}

return false;
}
}
}
}
Loading