Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MSBuild linter #1777

Open
rainersigwald opened this issue Mar 1, 2017 · 18 comments
Open

MSBuild linter #1777

rainersigwald opened this issue Mar 1, 2017 · 18 comments
Labels
Feature Request Feature: Warning Waves Warnings to enable in opt-in waves. Formerly "strict mode".

Comments

@rainersigwald
Copy link
Member

MSBuild could have an opt-in mode that would apply rules and heuristics to give suggestions about project "health".

Possible warnings:

  • You have something that is almost an item transform but not well formed so falls back to string (like @(I->'%(Identity)) with a missing closing ').
  • You have referred to a property that is not defined and it expanded to the empty string.
  • You have referred to a property that is not defined, but an item of the same name is defined.
  • You have referred to an item that is not defined, but a property of the same name is defined.
@rainersigwald
Copy link
Member Author

Thanks, @danmosemsft: #1774 (comment).

@AndyGerlicher AndyGerlicher added Feature Request needs-design Requires discussion with the dev team before attempting a fix. labels Mar 2, 2017
@stan-sz
Copy link
Contributor

stan-sz commented Nov 29, 2021

My favorite linter rule would be to catch ItemGroups with wildcards that scan the entire drive:

<ItemGroup>
  <MyItem Include="$(SomeProperty)/**" />
</ItemGroup>

Where SomeProperty resolves to empty sting.

@rainersigwald
Copy link
Member Author

@stan-sz that is a great linter use case but also see #3642 (comment) and the upcoming #7029.

@stan-sz
Copy link
Contributor

stan-sz commented Oct 26, 2022

Another case are conditions in form of

Condition="$(SomeProperty)">

where at least the linter should signal the missing single quotes and comparison with empty string like:

Condition=" '$(SomeProperty)' != '' ">

@stan-sz
Copy link
Contributor

stan-sz commented Jan 10, 2023

Some of the items listed in the initial scope for linter are captured in Feature: Warning Waves

@hknielsen
Copy link
Contributor

hknielsen commented Aug 2, 2023

We had an interesting issue today in our team, where:

<ProjectReference...><Properties></Properties>

Broke Publishing, and was not trivial what was happening, until we remembered that ProjectReference mapped to MsBuild task and removed the Properties delegated trough, removing the TargetFramework.

Interesting case as well for a linter warning, suggest to use AdditinalProperties instead

@stan-sz
Copy link
Contributor

stan-sz commented Sep 12, 2023

For the record: a linter to catch missing single quotes in string comparison

Condition=" $(SomeProperty) != 'true' ">

@KalleOlaviNiemitalo
Copy link

What's the difference between $(SomeProperty) and '$(SomeProperty)'?
It looks like Microsoft.Build.Evaluation.Parser.Arg(string expression) creates a StringExpressionNode in both cases.

@stan-sz
Copy link
Contributor

stan-sz commented Sep 12, 2023

This is to handle case when a property evaluates to an empty value. From: https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-conditions?view=vs-2022

Single quotes are not required for simple alphanumeric strings or boolean values. However, single quotes are required for empty values. This check is case insensitive.

@KalleOlaviNiemitalo
Copy link

KalleOlaviNiemitalo commented Sep 12, 2023

quoted.proj:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Run">
  <PropertyGroup>
    <Property />
  </PropertyGroup>

  <Target Name="Run">
    <Message Condition="$(Property) == ''" Importance="high" Text="Is empty without quotation marks" />
    <Message Condition="$(Property) != ''" Importance="high" Text="Is not empty without quotation marks" />
    <Message Condition="'$(Property)' == ''" Importance="high" Text="Is empty with quotation marks" />
    <Message Condition="'$(Property)' != ''" Importance="high" Text="Is not empty with quotation marks" />
  </Target>
</Project>
C:\Projects\quoted>C:\Windows\Microsoft.NET\Framework64\v2.0.50727\MSBuild.exe quoted.proj
Microsoft (R) Build Engine Version 2.0.50727.9149
[Microsoft .NET Framework, Version 2.0.50727.9174]
Copyright (C) Microsoft Corporation 2005. All rights reserved.

Build started 12.9.2023 21.12.13.
__________________________________________________
Project "C:\Projects\quoted\quoted.proj" (default targets):

Target Run:
    Is empty without quotation marks
    Is empty with quotation marks

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:00.26
$ dotnet msbuild quoted.proj
MSBuild version 17.8.0-preview-23367-03+0ff2a83e9 for .NET
  Is empty without quotation marks
  Is empty with quotation marks

Perhaps the documentation means you cannot compare to an empty unquoted string literal like Condition="$(Property) == ".

@Piedone
Copy link
Member

Piedone commented Nov 5, 2023

This could also include formatting checks like Prettier for XML does.

Ideally, I'm thinking of full static code analysis for csproj, props, and targets files.

@danmoseley
Copy link
Member

danmoseley commented Nov 5, 2023

Another possibility (in strict mode?) could be to help with Boolean properties. Properties of course have no type but in practice many are Boolean existing in a world where empty string is default. This can lead to ambiguity about what blank means, and since often we don't want to add a line to explicitly set a default, blank is treated as default. If that means blank is true, then properties end up with confusing negative names like say "DoNotOptimize". The obvious sources of errors here are the confusion of seeing a negative property to false, or assuming blank means true (or false) when it doesn't, or comparing against blank when it was set explicitly, or setting anything other than true or false as the value.

Years ago when we wrote the original "targets" I tried to have a rule that either blank was assumed to be false or else the value was explicitly defaulted to "true", then comparisons were always and only against "true", "false" and "" never appeared in conditionals on these Booleans, and hopefully property names were not negative (in practice some were inherited from the old format and already were).

A linter could possibly help by

  • flagging comparisons between a literal Boolean and empty string
  • inferring a property is Boolean (maybe indirectly) and tracking flow and doing something similar
  • flagging where a property held literal true or false but is set to anything else

This would of course be noisy. Perhaps the real answer is to have some way to annotate a property as Boolean with a default.

Note of course that the conditional parser IIRC does have some special casing for Booleans for example you can negate: "'$(Optimize)' == !'$(Debug)'" ...IIRC

@stan-sz
Copy link
Contributor

stan-sz commented Jan 17, 2024

Another linting ideas:

@stan-sz
Copy link
Contributor

stan-sz commented Jan 31, 2024

Another linter idea: #6277

@stan-sz
Copy link
Contributor

stan-sz commented Feb 13, 2024

Another idea: #3976

@JustinSchneiderPBI
Copy link

Fail\warn on property overwrite without Overwrite="true" metadata.

@MattKotsenas
Copy link
Member

Another idea:

A colleague just ran into a case where a project specified <TargetFrameworks> (plural) that was silently overwritten by an imported <TargetFramework> (singular).

@DaveCousineau
Copy link

DaveCousineau commented Mar 20, 2024

Not sure if it's a bug, something to catch with a linter, or maybe if there's already a workaround, but a problem I'm noticing is that this is an error (MSB4035):

<ProjectReference Include="" />

But these are not errors or even warnings:

<ProjectReference Include="$(empty)" />
<ProjectReference Include=" " />

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature Request Feature: Warning Waves Warnings to enable in opt-in waves. Formerly "strict mode".
Projects
None yet
Development

No branches or pull requests

10 participants