Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2680384
ArcanistLinter.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Advanced/Developer...
View Handle
View Hovercard
Size
15 KB
Referenced Files
None
Subscribers
None
ArcanistLinter.php
View Options
<?php
/**
* Implements lint rules, like syntax checks for a specific language.
*
* @task info Human Readable Information
* @task state Runtime State
* @task exec Executing Linters
*/
abstract
class
ArcanistLinter
extends
Phobject
{
const
GRANULARITY_FILE
=
1
;
const
GRANULARITY_DIRECTORY
=
2
;
const
GRANULARITY_REPOSITORY
=
3
;
const
GRANULARITY_GLOBAL
=
4
;
private
$id
;
protected
$paths
=
array
(
)
;
private
$filteredPaths
=
null
;
protected
$data
=
array
(
)
;
protected
$engine
;
protected
$activePath
;
protected
$messages
=
array
(
)
;
protected
$stopAllLinters
=
false
;
private
$customSeverityMap
=
array
(
)
;
private
$customSeverityRules
=
array
(
)
;
/* -( Human Readable Information )----------------------------------------- */
/**
* Return an optional informative URI where humans can learn more about this
* linter.
*
* For most linters, this should return a link to the project home page. This
* is shown on `arc linters`.
*
* @return string|null Optionally, return an informative URI.
* @task info
*/
public
function
getInfoURI
(
)
{
return
null
;
}
/**
* Return a brief human-readable description of the linter.
*
* These should be a line or two, and are shown on `arc linters`.
*
* @return string|null Optionally, return a brief human-readable description.
* @task info
*/
public
function
getInfoDescription
(
)
{
return
null
;
}
/**
* Return arbitrary additional information.
*
* Linters can use this method to provide arbitrary additional information to
* be included in the output of `arc linters`.
*
* @return map<string, string> A mapping of header to body content for the
* additional information sections.
* @task info
*/
public
function
getAdditionalInformation
(
)
{
return
array
(
)
;
}
/**
* Return a human-readable linter name.
*
* These are used by `arc linters`, and can let you give a linter a more
* presentable name.
*
* @return string Human-readable linter name.
* @task info
*/
public
function
getInfoName
(
)
{
return
nonempty
(
$this
->
getLinterName
(
)
,
$this
->
getLinterConfigurationName
(
)
,
get_class
(
$this
)
)
;
}
/* -( Runtime State )------------------------------------------------------ */
/**
* @task state
*/
final
public
function
getActivePath
(
)
{
return
$this
->
activePath
;
}
/**
* @task state
*/
final
public
function
setActivePath
(
$path
)
{
$this
->
stopAllLinters
=
false
;
$this
->
activePath
=
$path
;
return
$this
;
}
/**
* @task state
*/
final
public
function
setEngine
(
ArcanistLintEngine
$engine
)
{
$this
->
engine
=
$engine
;
return
$this
;
}
/**
* @task state
*/
final
protected
function
getEngine
(
)
{
return
$this
->
engine
;
}
/**
* Set the internal ID for this linter.
*
* This ID is assigned automatically by the @{class:ArcanistLintEngine}.
*
* @param string $id Unique linter ID.
* @return $this
* @task state
*/
final
public
function
setLinterID
(
$id
)
{
$this
->
id
=
$id
;
return
$this
;
}
/**
* Get the internal ID for this linter.
*
* Retrieves an internal linter ID managed by the @{class:ArcanistLintEngine}.
* This ID is a unique scalar which distinguishes linters in a list.
*
* @return string Unique linter ID.
* @task state
*/
final
public
function
getLinterID
(
)
{
return
$this
->
id
;
}
/* -( Executing Linters )-------------------------------------------------- */
/**
* Hook called before a list of paths are linted.
*
* Parallelizable linters can start multiple requests in parallel here,
* to improve performance. They can implement @{method:didLintPaths} to
* collect results.
*
* Linters which are not parallelizable should normally ignore this callback
* and implement @{method:lintPath} instead.
*
* @param list<string> $paths A list of paths to be linted
* @return void
* @task exec
*/
public
function
willLintPaths
(
array
$paths
)
{
return
;
}
/**
* Hook called for each path to be linted.
*
* Linters which are not parallelizable can do work here.
*
* Linters which are parallelizable may want to ignore this callback and
* implement @{method:willLintPaths} and @{method:didLintPaths} instead.
*
* @param string $path Path to lint.
* @return void
* @task exec
*/
public
function
lintPath
(
$path
)
{
return
;
}
/**
* Hook called after a list of paths are linted.
*
* Parallelizable linters can collect results here.
*
* Linters which are not paralleizable should normally ignore this callback
* and implement @{method:lintPath} instead.
*
* @param list<string> $paths A list of paths which were linted.
* @return void
* @task exec
*/
public
function
didLintPaths
(
array
$paths
)
{
return
;
}
public
function
getLinterPriority
(
)
{
return
1.0
;
}
public
function
setCustomSeverityMap
(
array
$map
)
{
$this
->
customSeverityMap
=
$map
;
return
$this
;
}
public
function
addCustomSeverityMap
(
array
$map
)
{
$this
->
customSeverityMap
=
$this
->
customSeverityMap
+
$map
;
return
$this
;
}
public
function
setCustomSeverityRules
(
array
$rules
)
{
$this
->
customSeverityRules
=
$rules
;
return
$this
;
}
final
public
function
getProjectRoot
(
)
{
$engine
=
$this
->
getEngine
(
)
;
if
(
!
$engine
)
{
throw
new
Exception
(
pht
(
'You must call %s before you can call %s.'
,
'setEngine()'
,
__FUNCTION__
.
'()'
)
)
;
}
$working_copy
=
$engine
->
getWorkingCopy
(
)
;
if
(
!
$working_copy
)
{
return
null
;
}
return
$working_copy
->
getProjectRoot
(
)
;
}
final
public
function
getOtherLocation
(
$offset
,
$path
=
null
)
{
if
(
$path
===
null
)
{
$path
=
$this
->
getActivePath
(
)
;
}
list
(
$line
,
$char
)
=
$this
->
getEngine
(
)
->
getLineAndCharFromOffset
(
$path
,
$offset
)
;
return
array
(
'path'
=>
$path
,
'line'
=>
$line
+
1
,
'char'
=>
$char
,
)
;
}
final
public
function
stopAllLinters
(
)
{
$this
->
stopAllLinters
=
true
;
return
$this
;
}
final
public
function
didStopAllLinters
(
)
{
return
$this
->
stopAllLinters
;
}
final
public
function
addPath
(
$path
)
{
$this
->
paths
[
$path
]
=
$path
;
$this
->
filteredPaths
=
null
;
return
$this
;
}
final
public
function
setPaths
(
array
$paths
)
{
$this
->
paths
=
$paths
;
$this
->
filteredPaths
=
null
;
return
$this
;
}
/**
* Filter out paths which this linter doesn't act on (for example, because
* they are binaries and the linter doesn't apply to binaries).
*
* @param list<string> $paths
* @return list<string>
*/
private
function
filterPaths
(
array
$paths
)
{
$engine
=
$this
->
getEngine
(
)
;
$keep
=
array
(
)
;
foreach
(
$paths
as
$path
)
{
if
(
!
$this
->
shouldLintDeletedFiles
(
)
&&
!
$engine
->
pathExists
(
$path
)
)
{
continue
;
}
if
(
!
$this
->
shouldLintDirectories
(
)
&&
$engine
->
isDirectory
(
$path
)
)
{
continue
;
}
if
(
!
$this
->
shouldLintBinaryFiles
(
)
&&
$engine
->
isBinaryFile
(
$path
)
)
{
continue
;
}
if
(
!
$this
->
shouldLintSymbolicLinks
(
)
&&
$engine
->
isSymbolicLink
(
$path
)
)
{
continue
;
}
$keep
[
]
=
$path
;
}
return
$keep
;
}
final
public
function
getPaths
(
)
{
if
(
$this
->
filteredPaths
===
null
)
{
$this
->
filteredPaths
=
$this
->
filterPaths
(
array_values
(
$this
->
paths
)
)
;
}
return
$this
->
filteredPaths
;
}
final
public
function
addData
(
$path
,
$data
)
{
$this
->
data
[
$path
]
=
$data
;
return
$this
;
}
final
protected
function
getData
(
$path
)
{
if
(
!
array_key_exists
(
$path
,
$this
->
data
)
)
{
$this
->
data
[
$path
]
=
$this
->
getEngine
(
)
->
loadData
(
$path
)
;
}
return
$this
->
data
[
$path
]
;
}
public
function
getCacheVersion
(
)
{
return
0
;
}
final
public
function
getLintMessageFullCode
(
$short_code
)
{
return
$this
->
getLinterName
(
)
.
$short_code
;
}
final
public
function
getLintMessageSeverity
(
$code
)
{
$map
=
$this
->
customSeverityMap
;
if
(
isset
(
$map
[
$code
]
)
)
{
return
$map
[
$code
]
;
}
foreach
(
$this
->
customSeverityRules
as
$rule
=>
$severity
)
{
if
(
preg_match
(
$rule
,
$code
)
)
{
return
$severity
;
}
}
$map
=
$this
->
getLintSeverityMap
(
)
;
if
(
isset
(
$map
[
$code
]
)
)
{
return
$map
[
$code
]
;
}
return
$this
->
getDefaultMessageSeverity
(
$code
)
;
}
protected
function
getDefaultMessageSeverity
(
$code
)
{
return
ArcanistLintSeverity
::
SEVERITY_ERROR
;
}
final
public
function
isMessageEnabled
(
$code
)
{
return
(
$this
->
getLintMessageSeverity
(
$code
)
!==
ArcanistLintSeverity
::
SEVERITY_DISABLED
)
;
}
final
public
function
getLintMessageName
(
$code
)
{
$map
=
$this
->
getLintNameMap
(
)
;
if
(
isset
(
$map
[
$code
]
)
)
{
return
$map
[
$code
]
;
}
return
pht
(
'Unknown lint message!'
)
;
}
final
protected
function
addLintMessage
(
ArcanistLintMessage
$message
)
{
$root
=
$this
->
getProjectRoot
(
)
;
$path
=
Filesystem
::
resolvePath
(
$message
->
getPath
(
)
,
$root
)
;
$message
->
setPath
(
Filesystem
::
readablePath
(
$path
,
$root
)
)
;
$this
->
messages
[
]
=
$message
;
return
$message
;
}
final
public
function
getLintMessages
(
)
{
return
$this
->
messages
;
}
final
public
function
raiseLintAtLine
(
$line
,
$char
,
$code
,
$description
,
$original
=
null
,
$replacement
=
null
)
{
$message
=
id
(
new
ArcanistLintMessage
(
)
)
->
setPath
(
$this
->
getActivePath
(
)
)
->
setLine
(
$line
)
->
setChar
(
$char
)
->
setCode
(
$this
->
getLintMessageFullCode
(
$code
)
)
->
setSeverity
(
$this
->
getLintMessageSeverity
(
$code
)
)
->
setName
(
$this
->
getLintMessageName
(
$code
)
)
->
setDescription
(
$description
)
->
setOriginalText
(
$original
)
->
setReplacementText
(
$replacement
)
;
return
$this
->
addLintMessage
(
$message
)
;
}
final
public
function
raiseLintAtPath
(
$code
,
$desc
)
{
return
$this
->
raiseLintAtLine
(
null
,
null
,
$code
,
$desc
,
null
,
null
)
;
}
final
public
function
raiseLintAtOffset
(
$offset
,
$code
,
$description
,
$original
=
null
,
$replacement
=
null
)
{
$path
=
$this
->
getActivePath
(
)
;
$engine
=
$this
->
getEngine
(
)
;
if
(
$offset
===
null
)
{
$line
=
null
;
$char
=
null
;
}
else
{
list
(
$line
,
$char
)
=
$engine
->
getLineAndCharFromOffset
(
$path
,
$offset
)
;
}
return
$this
->
raiseLintAtLine
(
$line
+
1
,
$char
+
1
,
$code
,
$description
,
$original
,
$replacement
)
;
}
public
function
canRun
(
)
{
return
true
;
}
abstract
public
function
getLinterName
(
)
;
public
function
getVersion
(
)
{
return
null
;
}
final
protected
function
isCodeEnabled
(
$code
)
{
$severity
=
$this
->
getLintMessageSeverity
(
$code
)
;
return
$this
->
getEngine
(
)
->
isSeverityEnabled
(
$severity
)
;
}
public
function
getLintSeverityMap
(
)
{
return
array
(
)
;
}
public
function
getLintNameMap
(
)
{
return
array
(
)
;
}
public
function
getCacheGranularity
(
)
{
return
self
::
GRANULARITY_FILE
;
}
/**
* If this linter is selectable via `.arclint` configuration files, return
* a short, human-readable name to identify it. For example, `"jshint"` or
* `"pep8"`.
*
* If you do not implement this method, the linter will not be selectable
* through `.arclint` files.
*/
public
function
getLinterConfigurationName
(
)
{
return
null
;
}
public
function
getLinterConfigurationOptions
(
)
{
if
(
!
$this
->
canCustomizeLintSeverities
(
)
)
{
return
array
(
)
;
}
return
array
(
'severity'
=>
array
(
'type'
=>
'optional map<string|int, string>'
,
'help'
=>
pht
(
'Provide a map from lint codes to adjusted severity levels: error, '
.
'warning, advice, autofix or disabled.'
)
,
)
,
'severity.rules'
=>
array
(
'type'
=>
'optional map<string, string>'
,
'help'
=>
pht
(
'Provide a map of regular expressions to severity levels. All '
.
'matching codes have their severity adjusted.'
)
,
)
,
'standard'
=>
array
(
'type'
=>
'optional string | list<string>'
,
'help'
=>
pht
(
'The coding standard(s) to apply.'
)
,
)
,
)
;
}
public
function
setLinterConfigurationValue
(
$key
,
$value
)
{
$sev_map
=
array
(
'error'
=>
ArcanistLintSeverity
::
SEVERITY_ERROR
,
'warning'
=>
ArcanistLintSeverity
::
SEVERITY_WARNING
,
'autofix'
=>
ArcanistLintSeverity
::
SEVERITY_AUTOFIX
,
'advice'
=>
ArcanistLintSeverity
::
SEVERITY_ADVICE
,
'disabled'
=>
ArcanistLintSeverity
::
SEVERITY_DISABLED
,
)
;
switch
(
$key
)
{
case
'severity'
:
if
(
!
$this
->
canCustomizeLintSeverities
(
)
)
{
break
;
}
$custom
=
array
(
)
;
foreach
(
$value
as
$code
=>
$severity
)
{
if
(
empty
(
$sev_map
[
$severity
]
)
)
{
$valid
=
implode
(
', '
,
array_keys
(
$sev_map
)
)
;
throw
new
Exception
(
pht
(
'Unknown lint severity "%s". Valid severities are: %s.'
,
$severity
,
$valid
)
)
;
}
$code
=
$this
->
getLintCodeFromLinterConfigurationKey
(
$code
)
;
$custom
[
$code
]
=
$severity
;
}
$this
->
setCustomSeverityMap
(
$custom
)
;
return
;
case
'severity.rules'
:
if
(
!
$this
->
canCustomizeLintSeverities
(
)
)
{
break
;
}
foreach
(
$value
as
$rule
=>
$severity
)
{
if
(
@
preg_match
(
$rule
,
''
)
===
false
)
{
throw
new
Exception
(
pht
(
'Severity rule "%s" is not a valid regular expression.'
,
$rule
)
)
;
}
if
(
empty
(
$sev_map
[
$severity
]
)
)
{
$valid
=
implode
(
', '
,
array_keys
(
$sev_map
)
)
;
throw
new
Exception
(
pht
(
'Unknown lint severity "%s". Valid severities are: %s.'
,
$severity
,
$valid
)
)
;
}
}
$this
->
setCustomSeverityRules
(
$value
)
;
return
;
case
'standard'
:
$standards
=
(array)
$value
;
foreach
(
$standards
as
$standard_name
)
{
$standard
=
ArcanistLinterStandard
::
getStandard
(
$standard_name
,
$this
)
;
foreach
(
$standard
->
getLinterConfiguration
(
)
as
$k
=>
$v
)
{
$this
->
setLinterConfigurationValue
(
$k
,
$v
)
;
}
$this
->
addCustomSeverityMap
(
$standard
->
getLinterSeverityMap
(
)
)
;
}
return
;
}
throw
new
Exception
(
pht
(
'Incomplete implementation: %s!'
,
$key
)
)
;
}
protected
function
canCustomizeLintSeverities
(
)
{
return
true
;
}
protected
function
shouldLintBinaryFiles
(
)
{
return
false
;
}
protected
function
shouldLintDeletedFiles
(
)
{
return
false
;
}
protected
function
shouldLintDirectories
(
)
{
return
false
;
}
protected
function
shouldLintSymbolicLinks
(
)
{
return
false
;
}
/**
* Map a configuration lint code to an `arc` lint code. Primarily, this is
* intended for validation, but can also be used to normalize case or
* otherwise be more permissive in accepted inputs.
*
* If the code is not recognized, you should throw an exception.
*
* @param string $code Code specified in configuration.
* @return string Normalized code to use in severity map.
*/
protected
function
getLintCodeFromLinterConfigurationKey
(
$code
)
{
return
$code
;
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Thu, Dec 19, 17:00 (21 h, 59 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1014847
Default Alt Text
ArcanistLinter.php (15 KB)
Attached To
Mode
rARC Arcanist
Attached
Detach File
Event Timeline
Log In to Comment